Android15 am命令 APP安装流程
一. PM 安装命令
使用命令
pm install -r xxx.apk
pm命令安装app 会触发PackageManagerShellCommand 中runInstall()方法
frameworks/base/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
1. onCommand 函数:
public int onCommand(String cmd) {
if (cmd == null) {
return handleDefaultCommands(cmd);
}final PrintWriter pw = getOutPrintWriter();
try {
switch (cmd) {case "install":
return runInstall();}
2. runInstall 函数:
private int runInstall() throws RemoteException {
//UNSUPPORTED_INSTALL_CMD_OPTS 就是参数:Set.of( "--multi-package");
//相当于isMultiPackage 为true
return doRunInstall(makeInstallParams(UNSUPPORTED_INSTALL_CMD_OPTS));
}
3. makeInstallParams 函数:
解析参数:
private InstallParams makeInstallParams(Set<String> unsupportedOptions) {
// MODE_FULL_INSTALL:安装会话的模式,其暂存的 APK 应该完全替换目标应用的任何现有 APK。
final SessionParams sessionParams = new SessionParams(SessionParams.MODE_FULL_INSTALL);
final InstallParams params = new InstallParams();params.sessionParams = sessionParams;
// Allowlist all permissions by default
sessionParams.installFlags |= PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS;
// Set package source to other by default. Can be overridden by "--package-source"// 默认将包源设置为其他。可以通过“--package-source”覆盖
sessionParams.setPackageSource(PackageInstaller.PACKAGE_SOURCE_OTHER);// Encodes one of the states:
// 1. Install request explicitly specified --staged, then value will be true.
// 2. Install request explicitly specified --non-staged, then value will be false.
// 3. Install request did not specify either --staged or --non-staged, then for APEX
// installs the value will be true, and for apk installs it will be false.// 对以下状态之一进行编码:
// 1. 安装请求明确指定 --staged,则值为 true。
// 2. 安装请求明确指定 --non-staged,则值为 false。
// 3. 安装请求未指定 --staged 或 --non-staged,则对于 APEX
// 安装,则值为 true,对于 apk 安装,则值为 false。
Boolean staged = null;String opt;
boolean replaceExisting = true;
boolean forceNonStaged = false;
while ((opt = getNextOption()) != null) {
if (unsupportedOptions.contains(opt)) {
throw new IllegalArgumentException("Unsupported option " + opt);
}
switch (opt) {
case "-r": // ignore
break;
case "-R":
replaceExisting = false;
break;
case "-i":
params.installerPackageName = getNextArg();
if (params.installerPackageName == null) {
throw new IllegalArgumentException("Missing installer package");
}
break;
case "-t":
sessionParams.installFlags |= PackageManager.INSTALL_ALLOW_TEST;
break;
case "-f":
sessionParams.installFlags |= PackageManager.INSTALL_INTERNAL;
break;
case "-d":
sessionParams.installFlags |= PackageManager.INSTALL_REQUEST_DOWNGRADE;
break;
case "-g":
sessionParams.installFlags |=
PackageManager.INSTALL_GRANT_ALL_REQUESTED_PERMISSIONS;
break;
case "--restrict-permissions":
sessionParams.installFlags &=
~PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS;
break;
case "--dont-kill":
sessionParams.installFlags |= PackageManager.INSTALL_DONT_KILL_APP;
break;
case "--originating-uri":
sessionParams.originatingUri = Uri.parse(getNextArg());
break;
case "--referrer":
sessionParams.referrerUri = Uri.parse(getNextArg());
break;
case "-p":
sessionParams.mode = SessionParams.MODE_INHERIT_EXISTING;
sessionParams.appPackageName = getNextArg();
if (sessionParams.appPackageName == null) {
throw new IllegalArgumentException("Missing inherit package name");
}
break;
case "--pkg":
sessionParams.appPackageName = getNextArg();
if (sessionParams.appPackageName == null) {
throw new IllegalArgumentException("Missing package name");
}
break;
case "-S":
final long sizeBytes = Long.parseLong(getNextArg());
if (sizeBytes <= 0) {
throw new IllegalArgumentException("Size must be positive");
}
sessionParams.setSize(sizeBytes);
break;
case "--abi":
sessionParams.abiOverride = checkAbiArgument(getNextArg());
break;
case "--ephemeral":
case "--instant":
case "--instantapp":
sessionParams.setInstallAsInstantApp(true /*isInstantApp*/);
break;
case "--full":
sessionParams.setInstallAsInstantApp(false /*isInstantApp*/);
break;
case "--preload":
sessionParams.setInstallAsVirtualPreload();
break;
case "--user":
params.userId = UserHandle.parseUserArg(getNextArgRequired());
break;
case "--install-location":
sessionParams.installLocation = Integer.parseInt(getNextArg());
break;
case "--install-reason":
sessionParams.installReason = Integer.parseInt(getNextArg());
break;
case "--update-ownership":
if (params.installerPackageName == null) {
// Enabling update ownership enforcement needs an installer. Since the
// default installer is null when using adb install, that effectively
// disable this enforcement.
params.installerPackageName = "com.android.shell";
}
sessionParams.installFlags |= PackageManager.INSTALL_REQUEST_UPDATE_OWNERSHIP;
break;
case "--force-uuid":
sessionParams.installFlags |= PackageManager.INSTALL_FORCE_VOLUME_UUID;
sessionParams.volumeUuid = getNextArg();
if ("internal".equals(sessionParams.volumeUuid)) {
sessionParams.volumeUuid = null;
}
break;
case "--force-sdk": // ignore
break;
case "--apex":
sessionParams.setInstallAsApex();
break;
case "--force-non-staged":
forceNonStaged = true;
break;
case "--multi-package":
sessionParams.setMultiPackage();
break;
case "--staged":
staged = true;
break;
case "--non-staged":
staged = false;
break;
case "--force-queryable":
sessionParams.setForceQueryable();
break;
case "--enable-rollback":
if (params.installerPackageName == null) {
// com.android.shell has the TEST_MANAGE_ROLLBACKS
// permission needed to enable rollback for non-module
// packages, which is likely what the user wants when
// enabling rollback through the shell command. Set
// the installer to com.android.shell if no installer
// has been provided so that the user doesn't have to
// remember to set it themselves.
params.installerPackageName = "com.android.shell";
}
int rollbackStrategy = PackageManager.ROLLBACK_DATA_POLICY_RESTORE;
try {
rollbackStrategy = Integer.parseInt(peekNextArg());
if (rollbackStrategy < PackageManager.ROLLBACK_DATA_POLICY_RESTORE
|| rollbackStrategy > PackageManager.ROLLBACK_DATA_POLICY_RETAIN) {
throw new IllegalArgumentException(
rollbackStrategy + " is not a valid rollback data policy.");
}
getNextArg(); // pop the argument
} catch (NumberFormatException e) {
// not followed by a number assume ROLLBACK_DATA_POLICY_RESTORE.
}
sessionParams.setEnableRollback(true, rollbackStrategy);
break;
case "--rollback-impact-level":
if (!Flags.recoverabilityDetection()) {
throw new IllegalArgumentException("Unknown option " + opt);
}
int rollbackImpactLevel = Integer.parseInt(peekNextArg());
if (rollbackImpactLevel < PackageManager.ROLLBACK_USER_IMPACT_LOW
|| rollbackImpactLevel
> PackageManager.ROLLBACK_USER_IMPACT_ONLY_MANUAL) {
throw new IllegalArgumentException(
rollbackImpactLevel + " is not a valid rollback impact level.");
}
sessionParams.setRollbackImpactLevel(rollbackImpactLevel);
case "--staged-ready-timeout":
params.stagedReadyTimeoutMs = Long.parseLong(getNextArgRequired());
break;
case "--skip-verification":
sessionParams.installFlags |= PackageManager.INSTALL_DISABLE_VERIFICATION;
break;
case "--skip-enable":
sessionParams.setApplicationEnabledSettingPersistent();
break;
case "--bypass-low-target-sdk-block":
sessionParams.installFlags |=
PackageManager.INSTALL_BYPASS_LOW_TARGET_SDK_BLOCK;
break;
case "--ignore-dexopt-profile":
sessionParams.installFlags |= PackageManager.INSTALL_IGNORE_DEXOPT_PROFILE;
break;
case "--package-source":
sessionParams.setPackageSource(Integer.parseInt(getNextArg()));
break;
case "--dexopt-compiler-filter":
sessionParams.dexoptCompilerFilter = getNextArgRequired();
// An early check that throws IllegalArgumentException if the compiler filter is
// invalid.
new DexoptParams.Builder(ReasonMapping.REASON_INSTALL)
.setCompilerFilter(sessionParams.dexoptCompilerFilter)
.build();
break;
default:
throw new IllegalArgumentException("Unknown option " + opt);
}
}
if (staged == null) {
staged = (sessionParams.installFlags & PackageManager.INSTALL_APEX) != 0;
}
if (replaceExisting) {
sessionParams.installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
}
if (forceNonStaged) {
sessionParams.isStaged = false;
sessionParams.developmentInstallFlags |=
PackageManager.INSTALL_DEVELOPMENT_FORCE_NON_STAGED_APEX_UPDATE;
} else if (staged) {
sessionParams.setStaged();
}
if ((sessionParams.installFlags & PackageManager.INSTALL_APEX) != 0
&& (sessionParams.installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0
&& sessionParams.rollbackDataPolicy == PackageManager.ROLLBACK_DATA_POLICY_WIPE) {
throw new IllegalArgumentException("Data policy 'wipe' is not supported for apex.");
}
return params;
}
4. doRunInstall 函数:
private int doRunInstall(final InstallParams params) throws RemoteException {
final PrintWriter pw = getOutPrintWriter();// 默认为:UserHandle.USER_ALL
int requestUserId = params.userId;
if (requestUserId != UserHandle.USER_ALL && requestUserId != UserHandle.USER_CURRENT) {
UserManagerInternal umi =
LocalServices.getService(UserManagerInternal.class);
UserInfo userInfo = umi.getUserInfo(requestUserId);
if (userInfo == null) {
pw.println("Failure [user " + requestUserId + " doesn't exist]");
return 1;
}
}final boolean isStreaming = params.sessionParams.dataLoaderParams != null;
final boolean isApex =
(params.sessionParams.installFlags & PackageManager.INSTALL_APEX) != 0;
final boolean installArchived =
(params.sessionParams.installFlags & PackageManager.INSTALL_ARCHIVED) != 0;// 将剩余的参数变成集合.
ArrayList<String> args = getRemainingArgs();
final boolean fromStdIn = args.isEmpty() || STDIN_PATH.equals(args.get(0));
final boolean hasSplits = args.size() > 1;if (fromStdIn && params.sessionParams.sizeBytes == -1) {
pw.println("Error: must either specify a package size or an APK file");
return 1;
}//默认情况下都不会满足
if (isApex && hasSplits) {
pw.println("Error: can't specify SPLIT(s) for APEX");
return 1;
}//默认情况下都不会满足
if (installArchived) {
if (hasSplits) {
pw.println("Error: can't have SPLIT(s) for Archival install");
return 1;
}
}if (!isStreaming) {
if (fromStdIn && hasSplits) {
pw.println("Error: can't specify SPLIT(s) along with STDIN");
return 1;
}if (args.isEmpty()) {
args.add(STDIN_PATH);
} else {//默认会设置参数的大小.
setParamsSize(params, args);
}
}//创建session
final int sessionId = doCreateSession(params.sessionParams,
params.installerPackageName, params.userId);
boolean abandonSession = true;
try {
if (isStreaming) {
if (doAddFiles(sessionId, args, params.sessionParams.sizeBytes, isApex,
installArchived) != PackageInstaller.STATUS_SUCCESS) {
return 1;
}
} else {
if (doWriteSplits(sessionId, args, params.sessionParams.sizeBytes, isApex)
!= PackageInstaller.STATUS_SUCCESS) {
return 1;
}
}//安装应用开发.
if (doCommitSession(sessionId, false /*logSuccess*/)
!= PackageInstaller.STATUS_SUCCESS) {
return 1;
}
abandonSession = false;if (params.sessionParams.isStaged && params.stagedReadyTimeoutMs > 0) {
return doWaitForStagedSessionReady(sessionId, params.stagedReadyTimeoutMs, pw);
}pw.println("Success");
return 0;
} finally {
if (abandonSession) {
try {
doAbandonSession(sessionId, false /*logSuccess*/);
} catch (Exception ignore) {
}
}
}
}
5. setParamsSize 函数:
private void setParamsSize(InstallParams params, List<String> inPaths) {
if (params.sessionParams.sizeBytes != -1 || STDIN_PATH.equals(inPaths.get(0))) {
return;
}long sessionSize = 0;
ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
for (String inPath : inPaths) {// 获取到apk的路径
final ParcelFileDescriptor fd = openFileForSystem(inPath, "r");
if (fd == null) {
getErrPrintWriter().println("Error: Can't open file: " + inPath);
throw new IllegalArgumentException("Error: Can't open file: " + inPath);
}
try {//解析此apk内容。
ParseResult<ApkLite> apkLiteResult = ApkLiteParseUtils.parseApkLite(
input.reset(), fd.getFileDescriptor(), inPath, 0);
if (apkLiteResult.isError()) {//解析失败,报异常。
throw new IllegalArgumentException(
"Error: Failed to parse APK file: " + inPath + ": "
+ apkLiteResult.getErrorMessage(),
apkLiteResult.getException());
}//获取到ApkLite 对象
final ApkLite apkLite = apkLiteResult.getResult();
final PackageLite pkgLite = new PackageLite(null, apkLite.getPath(), apkLite,
null /* splitNames */, null /* isFeatureSplits */,
null /* usesSplitNames */, null /* configForSplit */,
null /* splitApkPaths */, null /* splitRevisionCodes */,
apkLite.getTargetSdkVersion(), null /* requiredSplitTypes */,
null /* splitTypes */);//计算安装文件大小。
sessionSize += InstallLocationUtils.calculateInstalledSize(pkgLite,
params.sessionParams.abiOverride, fd.getFileDescriptor());
} catch (IOException e) {
getErrPrintWriter().println("Error: Failed to parse APK file: " + inPath);
throw new IllegalArgumentException(
"Error: Failed to parse APK file: " + inPath, e);
} finally {
try {
fd.close();
} catch (IOException e) {
}
}
}//将计算文件大小存入。
params.sessionParams.setSize(sessionSize);
}
6. openFileForSystem 函数:
/**
* Helper for just system services to ask the shell to open an output file.
* @hide
*/
public ParcelFileDescriptor openFileForSystem(String path, String mode) {
if (DEBUG) Slog.d(TAG, "openFileForSystem: " + path + " mode=" + mode);
try {//获取文件,并且具有可打开的权限。
ParcelFileDescriptor pfd = getShellCallback().openFile(path,
"u:r:system_server:s0", mode);
if (pfd != null) {
if (DEBUG) Slog.d(TAG, "Got file: " + pfd);//获取到此文件
return pfd;
}
} catch (RuntimeException e) {
if (DEBUG) Slog.d(TAG, "Failure opening file: " + e.getMessage());
getErrPrintWriter().println("Failure opening file: " + e.getMessage());
}
if (DEBUG) Slog.d(TAG, "Error: Unable to open file: " + path);//否则就获取不到文件.
getErrPrintWriter().println("Error: Unable to open file: " + path);String suggestedPath = "/data/local/tmp/";
if (path == null || !path.startsWith(suggestedPath)) {
getErrPrintWriter().println("Consider using a file under " + suggestedPath);
}
return null;
}
7. doCreateSession 函数:
private int doCreateSession(SessionParams params, String installerPackageName, int userId)
throws RemoteException {//默认是USER_ALL
if (userId == UserHandle.USER_ALL) {
params.installFlags |= PackageManager.INSTALL_ALL_USERS;
}
final int translatedUserId =
translateUserId(userId, UserHandle.USER_SYSTEM, "doCreateSession");// 创建createSession
final int sessionId = mInterface.getPackageInstaller()
.createSession(params, installerPackageName, null /*installerAttributionTag*/,
translatedUserId);
return sessionId;
}
8. createSession 函数:
frameworks/base/services/core/java/com/android/server/pm/PackageInstallerService.java
@Override
public int createSession(SessionParams params, String installerPackageName,
String callingAttributionTag, int userId) {
try {
if (params.dataLoaderParams != null
&& mContext.checkCallingOrSelfPermission(Manifest.permission.USE_INSTALLER_V2)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("You need the "
+ "com.android.permission.USE_INSTALLER_V2 permission "
+ "to use a data loader");
}// Draft sessions cannot be created through the public API.
params.installFlags &= ~PackageManager.INSTALL_UNARCHIVE_DRAFT;
return createSessionInternal(params, installerPackageName, callingAttributionTag,
Binder.getCallingUid(), userId);
} catch (IOException e) {
throw ExceptionUtils.wrap(e);
}
}
9. createSessionInternal 函数:
int createSessionInternal(SessionParams params, String installerPackageName,
String installerAttributionTag, int callingUid, int userId)
throws IOException {
final Computer snapshot = mPm.snapshotComputer();
snapshot.enforceCrossUserPermission(callingUid, userId, true, true, "createSession");if (mPm.isUserRestricted(userId, UserManager.DISALLOW_INSTALL_APPS)) {
// 若包含DISALLOW_INSTALL_APPS, 说明是禁止安装,则直接报错。
throw new SecurityException("User restriction prevents installing");
}// INSTALL_REASON_ROLLBACK allows an app to be rolled back without requiring the ROLLBACK
// capability; ensure if this is set as the install reason the app has one of the necessary
// signature permissions to perform the rollback.
if (params.installReason == PackageManager.INSTALL_REASON_ROLLBACK) {//安装原因是INSTALL_REASON_ROLLBACK
if (mContext.checkCallingOrSelfPermission(Manifest.permission.MANAGE_ROLLBACKS)
!= PackageManager.PERMISSION_GRANTED &&
mContext.checkCallingOrSelfPermission(Manifest.permission.TEST_MANAGE_ROLLBACKS)
!= PackageManager.PERMISSION_GRANTED) {//若没有授权,直接抛出异常。
throw new SecurityException(
"INSTALL_REASON_ROLLBACK requires the MANAGE_ROLLBACKS permission or the "
+ "TEST_MANAGE_ROLLBACKS permission");
}
}// App package name and label length is restricted so that really long strings aren't
// written to disk.
if (params.appPackageName != null && !isValidPackageName(params.appPackageName)) {//若包名是无效的,直接将包名设置为null.
params.appPackageName = null;
}params.appLabel = TextUtils.trimToSize(params.appLabel,
PackageItemInfo.MAX_SAFE_LABEL_LENGTH);// Validate requested installer package name.
if (params.installerPackageName != null && !isValidPackageName(
params.installerPackageName)) {// 若安装包名是无效的,直接直接设置为null
params.installerPackageName = null;
}// Validate installer package name.
if (installerPackageName != null && !isValidPackageName(installerPackageName)) {
installerPackageName = null;
}String requestedInstallerPackageName =
params.installerPackageName != null ? params.installerPackageName
: installerPackageName;if (PackageManagerServiceUtils.isRootOrShell(callingUid)
|| PackageInstallerSession.isSystemDataLoaderInstallation(params)
|| PackageManagerServiceUtils.isAdoptedShell(callingUid, mContext)) {
params.installFlags |= PackageManager.INSTALL_FROM_ADB;
// adb installs can override the installingPackageName, but not the
// initiatingPackageName//安装来源于adb
installerPackageName = SHELL_PACKAGE_NAME;
} else {
if (callingUid != SYSTEM_UID) {
// The supplied installerPackageName must always belong to the calling app.//若调用者不是system,则要检查权限。
mAppOps.checkPackage(callingUid, installerPackageName);
}
// Only apps with INSTALL_PACKAGES are allowed to set an installer that is not the
// caller.
if (!TextUtils.equals(requestedInstallerPackageName, installerPackageName)) {
if (mContext.checkCallingOrSelfPermission(Manifest.permission.INSTALL_PACKAGES)
!= PackageManager.PERMISSION_GRANTED) {// 若请求安装包名和安装包名不一致,
// 需要判断是否有INSTALL_PACKAGES权限,若没有的话,进行权限检查。
mAppOps.checkPackage(callingUid, requestedInstallerPackageName);
}
}//移除 FLAG:INSTALL_FROM_ADB,INSTALL_ALL_USERS,INSTALL_ARCHIVED
params.installFlags &= ~PackageManager.INSTALL_FROM_ADB;
params.installFlags &= ~PackageManager.INSTALL_ALL_USERS;
params.installFlags &= ~PackageManager.INSTALL_ARCHIVED;//增加flag:INSTALL_REPLACE_EXISTING
params.installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
if ((params.installFlags & PackageManager.INSTALL_VIRTUAL_PRELOAD) != 0
&& !mPm.isCallerVerifier(snapshot, callingUid)) {
params.installFlags &= ~PackageManager.INSTALL_VIRTUAL_PRELOAD;
}
if (mContext.checkCallingOrSelfPermission(Manifest.permission.INSTALL_TEST_ONLY_PACKAGE)
!= PackageManager.PERMISSION_GRANTED) {
params.installFlags &= ~PackageManager.INSTALL_ALLOW_TEST;
}// developmentInstallFlags can ony be set by shell or root.
params.developmentInstallFlags = 0;
}String originatingPackageName = null;
if (params.originatingUid != SessionParams.UID_UNKNOWN
&& params.originatingUid != callingUid) {
String[] packages = snapshot.getPackagesForUid(params.originatingUid);
if (packages != null && packages.length > 0) {
// Choose an arbitrary representative package in the case of a shared UID.
originatingPackageName = packages[0];
}
}if (Build.IS_DEBUGGABLE || PackageManagerServiceUtils.isSystemOrRoot(callingUid)) {
params.installFlags |= PackageManager.INSTALL_ALLOW_DOWNGRADE;
} else {
params.installFlags &= ~PackageManager.INSTALL_ALLOW_DOWNGRADE;
}if (mDisableVerificationForUid != INVALID_UID) {
if (callingUid == mDisableVerificationForUid) {
params.installFlags |= PackageManager.INSTALL_DISABLE_VERIFICATION;
} else {
// Clear the flag if current calling uid doesn't match the requested uid.
params.installFlags &= ~PackageManager.INSTALL_DISABLE_VERIFICATION;
}
// Reset the field as this is a one-off request.
mDisableVerificationForUid = INVALID_UID;
} else if ((params.installFlags & ADB_DEV_MODE) != ADB_DEV_MODE) {
// Only tools under specific conditions (test app installed through ADB, and
// verification disabled flag specified) can disable verification.
params.installFlags &= ~PackageManager.INSTALL_DISABLE_VERIFICATION;
}if (Flags.rollbackLifetime()) {
if (params.rollbackLifetimeMillis > 0) {
if ((params.installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) == 0) {
throw new IllegalArgumentException(
"Can't set rollbackLifetimeMillis when rollback is not enabled");
}
if (mContext.checkCallingOrSelfPermission(Manifest.permission.MANAGE_ROLLBACKS)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException(
"Setting rollback lifetime requires the MANAGE_ROLLBACKS permission");
}
} else if (params.rollbackLifetimeMillis < 0) {
throw new IllegalArgumentException("rollbackLifetimeMillis can't be negative.");
}
}if (Flags.recoverabilityDetection()) {
if (params.rollbackImpactLevel == PackageManager.ROLLBACK_USER_IMPACT_HIGH
|| params.rollbackImpactLevel
== PackageManager.ROLLBACK_USER_IMPACT_ONLY_MANUAL) {
if ((params.installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) == 0) {
throw new IllegalArgumentException(
"Can't set rollbackImpactLevel when rollback is not enabled");
}
if (mContext.checkCallingOrSelfPermission(Manifest.permission.MANAGE_ROLLBACKS)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException(
"Setting rollbackImpactLevel requires the MANAGE_ROLLBACKS permission");
}
} else if (params.rollbackImpactLevel < 0) {
throw new IllegalArgumentException("rollbackImpactLevel can't be negative.");
}
}boolean isApex = (params.installFlags & PackageManager.INSTALL_APEX) != 0;
if (isApex) {
if (mContext.checkCallingOrSelfPermission(Manifest.permission.INSTALL_PACKAGE_UPDATES)
== PackageManager.PERMISSION_DENIED
&& mContext.checkCallingOrSelfPermission(Manifest.permission.INSTALL_PACKAGES)
== PackageManager.PERMISSION_DENIED) {
throw new SecurityException("Not allowed to perform APEX updates");
}
} else if (params.isStaged) {
mContext.enforceCallingOrSelfPermission(Manifest.permission.INSTALL_PACKAGES, TAG);
}if (isApex) {
if (!mApexManager.isApexSupported()) {
throw new IllegalArgumentException(
"This device doesn't support the installation of APEX files");
}
if (params.isMultiPackage) {
throw new IllegalArgumentException("A multi-session can't be set as APEX.");
}
if (PackageManagerServiceUtils.isSystemOrRootOrShell(callingUid)
|| mBypassNextAllowedApexUpdateCheck) {
params.installFlags |= PackageManager.INSTALL_DISABLE_ALLOWED_APEX_UPDATE_CHECK;
} else {
// Only specific APEX updates (installed through ADB, or for CTS tests) can disable
// allowed APEX update check.
params.installFlags &= ~PackageManager.INSTALL_DISABLE_ALLOWED_APEX_UPDATE_CHECK;
}
}if ((params.installFlags & PackageManager.INSTALL_BYPASS_LOW_TARGET_SDK_BLOCK) != 0
&& !PackageManagerServiceUtils.isSystemOrRootOrShell(callingUid)
&& !Build.IS_DEBUGGABLE
&& !PackageManagerServiceUtils.isAdoptedShell(callingUid, mContext)) {
// If the bypass flag is set, but not running as system root or shell then remove
// the flag
params.installFlags &= ~PackageManager.INSTALL_BYPASS_LOW_TARGET_SDK_BLOCK;
}params.installFlags &= ~PackageManager.INSTALL_UNARCHIVE;
if (isArchivingEnabled() && params.appPackageName != null) {
PackageStateInternal ps = mPm.snapshotComputer().getPackageStateInternal(
params.appPackageName, SYSTEM_UID);
if (ps != null
&& PackageArchiver.isArchived(ps.getUserStateOrDefault(userId))
&& PackageArchiver.getResponsibleInstallerPackage(ps)
.equals(requestedInstallerPackageName)) {
params.installFlags |= PackageManager.INSTALL_UNARCHIVE;
}
}if ((params.installFlags & PackageManager.INSTALL_INSTANT_APP) != 0
&& !PackageManagerServiceUtils.isSystemOrRootOrShell(callingUid)
&& (snapshot.getFlagsForUid(callingUid) & ApplicationInfo.FLAG_SYSTEM)
== 0) {
throw new SecurityException(
"Only system apps could use the PackageManager.INSTALL_INSTANT_APP flag.");
}if (params.isStaged && !PackageManagerServiceUtils.isSystemOrRootOrShell(callingUid)) {
if (!mBypassNextStagedInstallerCheck
&& !isStagedInstallerAllowed(requestedInstallerPackageName)) {
throw new SecurityException("Installer not allowed to commit staged install");
}
}
if (isApex && !PackageManagerServiceUtils.isSystemOrRootOrShell(callingUid)) {
if (!mBypassNextStagedInstallerCheck
&& !isStagedInstallerAllowed(requestedInstallerPackageName)) {
throw new SecurityException(
"Installer not allowed to commit non-staged APEX install");
}
}mBypassNextStagedInstallerCheck = false;
mBypassNextAllowedApexUpdateCheck = false;if (!params.isMultiPackage) {
var hasInstallGrantRuntimePermissions = mContext.checkCallingOrSelfPermission(
Manifest.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS)
== PackageManager.PERMISSION_GRANTED;// Only system components can circumvent runtime permissions when installing.
if ((params.installFlags & PackageManager.INSTALL_GRANT_ALL_REQUESTED_PERMISSIONS) != 0
&& !hasInstallGrantRuntimePermissions) {
throw new SecurityException("You need the "
+ Manifest.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS
+ " permission to use the"
+ " PackageManager.INSTALL_GRANT_ALL_REQUESTED_PERMISSIONS flag");
}var permissionStates = params.getPermissionStates();
if (!permissionStates.isEmpty()) {
if (!hasInstallGrantRuntimePermissions) {
for (int index = 0; index < permissionStates.size(); index++) {
var permissionName = permissionStates.keyAt(index);
if (!INSTALLER_CHANGEABLE_APP_OP_PERMISSIONS.contains(permissionName)) {
throw new SecurityException("You need the "
+ Manifest.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS
+ " permission to grant runtime permissions for a session");
}
}
}
}// Defensively resize giant app icons
if (params.appIcon != null) {
final ActivityManager am = (ActivityManager) mContext.getSystemService(
Context.ACTIVITY_SERVICE);
final int iconSize = am.getLauncherLargeIconSize();
if ((params.appIcon.getWidth() > iconSize * 2)
|| (params.appIcon.getHeight() > iconSize * 2)) {
params.appIcon = Bitmap.createScaledBitmap(params.appIcon, iconSize, iconSize,
true);
}
}switch (params.mode) {
case SessionParams.MODE_FULL_INSTALL:
case SessionParams.MODE_INHERIT_EXISTING:
break;
default:
throw new IllegalArgumentException("Invalid install mode: " + params.mode);
}// If caller requested explicit location, validity check it, otherwise
// resolve the best internal or adopted location.
if ((params.installFlags & PackageManager.INSTALL_INTERNAL) != 0) {
if (!InstallLocationUtils.fitsOnInternal(mContext, params)) {
throw new IOException("No suitable internal storage available");
}
} else if ((params.installFlags & PackageManager.INSTALL_FORCE_VOLUME_UUID) != 0) {
// For now, installs to adopted media are treated as internal from
// an install flag point-of-view.
params.installFlags |= PackageManager.INSTALL_INTERNAL;
} else {
params.installFlags |= PackageManager.INSTALL_INTERNAL;// Resolve best location for install, based on combination of
// requested install flags, delta size, and manifest settings.
final long ident = Binder.clearCallingIdentity();
try {
params.volumeUuid = InstallLocationUtils.resolveInstallVolume(mContext, params);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
}int requestedInstallerPackageUid = INVALID_UID;
if (requestedInstallerPackageName != null) {
requestedInstallerPackageUid = snapshot.getPackageUid(requestedInstallerPackageName,
0 /* flags */, userId);
}
if (requestedInstallerPackageUid == INVALID_UID) {
// Requested installer package is invalid, reset it
requestedInstallerPackageName = null;
}final int sessionId;
final PackageInstallerSession session;
synchronized (mSessions) {
// Check that the installer does not have too many active sessions.
final int activeCount = getSessionCount(mSessions, callingUid);
if (mContext.checkCallingOrSelfPermission(Manifest.permission.INSTALL_PACKAGES)
== PackageManager.PERMISSION_GRANTED) {
if (activeCount >= MAX_ACTIVE_SESSIONS_WITH_PERMISSION) {
throw new IllegalStateException(
"Too many active sessions for UID " + callingUid);
}
} else if (activeCount >= MAX_ACTIVE_SESSIONS_NO_PERMISSION) {
throw new IllegalStateException(
"Too many active sessions for UID " + callingUid);
}
final int historicalCount = mHistoricalSessionsByInstaller.get(callingUid);
if (historicalCount >= MAX_HISTORICAL_SESSIONS) {
throw new IllegalStateException(
"Too many historical sessions for UID " + callingUid);
}
final int existingDraftSessionId =
getExistingDraftSessionId(requestedInstallerPackageUid, params, userId);sessionId = existingDraftSessionId != SessionInfo.INVALID_ID ? existingDraftSessionId
: allocateSessionIdLocked();
}final long createdMillis = System.currentTimeMillis();
// We're staging to exactly one location
File stageDir = null;
String stageCid = null;
if (!params.isMultiPackage) {
if ((params.installFlags & PackageManager.INSTALL_INTERNAL) != 0) {
stageDir = buildSessionDir(sessionId, params);
} else {
stageCid = buildExternalStageCid(sessionId);
}
}// reset the force queryable param if it's not called by an approved caller.
if (params.forceQueryableOverride) {
if (!PackageManagerServiceUtils.isRootOrShell(callingUid)) {
params.forceQueryableOverride = false;
}
}final var dpmi = LocalServices.getService(DevicePolicyManagerInternal.class);
if (dpmi != null && dpmi.isUserOrganizationManaged(userId)) {
params.installFlags |= PackageManager.INSTALL_FROM_MANAGED_USER_OR_PROFILE;
}if (isApex || mContext.checkCallingOrSelfPermission(
Manifest.permission.ENFORCE_UPDATE_OWNERSHIP) == PackageManager.PERMISSION_DENIED) {
params.installFlags &= ~PackageManager.INSTALL_REQUEST_UPDATE_OWNERSHIP;
}InstallSource installSource = InstallSource.create(installerPackageName,
originatingPackageName, requestedInstallerPackageName, requestedInstallerPackageUid,
requestedInstallerPackageName, installerAttributionTag, params.packageSource);
session = new PackageInstallerSession(mInternalCallback, mContext, mPm, this,
mSilentUpdatePolicy, mInstallThread.getLooper(), mStagingManager, sessionId,
userId, callingUid, installSource, params, createdMillis, 0L, stageDir, stageCid,
null, null, false, false, false, false, null, SessionInfo.INVALID_ID,
false, false, false, PackageManager.INSTALL_UNKNOWN, "", null);synchronized (mSessions) {
mSessions.put(sessionId, session);
}
mPm.addInstallerPackageName(session.getInstallSource());mCallbacks.notifySessionCreated(session.sessionId, session.userId);
mSettingsWriteRequest.schedule();
if (LOGD) {
Slog.d(TAG, "Created session id=" + sessionId + " staged=" + params.isStaged);
}
return sessionId;
}
相关文章:
Android15 am命令 APP安装流程
一. PM 安装命令 使用命令 pm install -r xxx.apk pm命令安装app 会触发PackageManagerShellCommand 中runInstall()方法 frameworks/base/services/core/java/com/android/server/pm/PackageManagerShellCommand.java1. onCommand 函数: public int onCommand(String cmd…...
SpringMVC学习(初识与复习Web程序的工作流程)(1)
目录 一、SpringMVC(框架)的简要概述。 (1)SpringMVC与Servlet。 (2)技术方向。 (3)最终学习目标。 二、Web程序的基本工作流程。 (1)工作流程。 <1>浏览器。前后端任务。 <…...
解锁网络防御新思维:D3FEND 五大策略如何对抗 ATTCK
D3FEND 简介 背景介绍 2021年6月22日(美国时间),美国MITRE公司正式发布了D3FEND——一个网络安全对策知识图谱。该项目由美国国家安全局(NSA)资助,并由MITRE的国家安全工程中心(NSECÿ…...
评估自动驾驶(AD)策略性能的关键指标
以下是针对自动驾驶(AD)策略性能评测指标的详细解读,结合其物理意义与工程价值: 核心评测指标分类与含义 1. 安全性指标(Safety) 动态碰撞率(Dynamic Collision Ratio, DCR) 定义&a…...
【领域】百度OCR识别
一、定义 OCR(Optical Character Recognition,光学字符识别)是计算机视觉重要方向之一。传统定义的OCR一般面向扫描文档类对象,现在我们常说的OCR一般指场景文字识别(Scene Text Recognition,STRÿ…...
Docker 学习(一)
一、Docker 核心概念 Docker 是一个开源的容器化平台,允许开发者将应用及其所有依赖(代码、运行时、系统工具、库等)打包成一个轻量级、可移植的“容器”,实现 “一次构建,随处运行”。 1、容器(Container…...
15. C++多线程编程-网络编程-GUI编程(如Qt)学习建议
1. 多线程编程 多线程编程允许程序同时执行多个任务,从而提高性能和响应速度。多线程常用于处理并发任务、提高CPU利用率、优化I/O操作等。 学习内容: 线程与进程的区别:理解线程和进程的基本概念及其区别。 线程的创建与管理:…...
【vscode-解决方案】vscode 无法登录远程服务器的两种解决办法
解决方案一: 查找原因 命令 ps ajx | grep vscode 可能会看到一下这堆信息(如果没有大概率不是这个原因导致) 这堆信息的含义:当你使用 vscode 远程登录服务器时,我们远程机器服务端要给你启动一个叫做 vscode serv…...
5个GitHub热点开源项目!!
1.自托管 Moonlight 游戏串流服务:Sunshine 主语言:C,Star:14.4k,周增长:500 这是一个自托管的 Moonlight 游戏串流服务器端项目,支持所有 Moonlight 客户端。用户可以在自己电脑上搭建一个游戏…...
化学工业领域 - 基础化工、精细化工、煤化工极简理解
引入 基础化工、精细化工和煤化工是化学工业中的三个重要分支 它们在原料、产品、工艺、应用方面各有特点 一、基础化工(Basic Chemical Industry) 1、基本介绍 基础化工是指以石油、天然气、煤炭等为原料,生产大宗化学品和基础化学原料的…...
慢sql治理
一、慢SQL的定义与影响 慢SQL通常指的是执行时间超过合理阈值的SQL语句。这个阈值可以根据系统的实际情况进行设定,例如1秒或更长。慢SQL会导致系统响应时间延迟、资源占用增加、数据库连接池被占满、锁竞争增加等一系列问题,严重影响系统的稳定性和用户…...
基于SpringBoot的美妆购物网站系统设计与实现现(源码+SQL脚本+LW+部署讲解等)
专注于大学生项目实战开发,讲解,毕业答疑辅导,欢迎高校老师/同行前辈交流合作✌。 技术范围:SpringBoot、Vue、SSM、HLMT、小程序、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容:…...
计算机毕业设计Hadoop+Spark+DeepSeek-R1大模型音乐推荐系统 音乐数据分析 音乐可视化 音乐爬虫 知识图谱 大数据毕业设计
温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 作者简介:Java领…...
mysql5.7离线安装及问题解决
这次主要是讲解mysql5.7离线安装教程和一主一从数据库配置 1、去官网下载自己对应的mysql https://downloads.mysql.com/archives/community/2、查看需要安装mysql服务器的linux的类型 uname -a第二步看一下系统有没有安装mysql rpm -qa|grep -i mysql3、上传安装包 用远程…...
Matlab 大量接单
分享一个matlab接私活、兼职的平台 1、技术方向满足任一即可 2、技术要求 3、最后 技术方向满足即可 MATLAB:熟练掌握MATLAB编程语言,能够使用MATLAB进行数据处理、机器学习和深度学习等相关工作。 机器学习、深度学习、强化学习、仿真、复现、算法、…...
C++数据结构之数组(详解)
1.介绍 在C中,数组是一种基本的数据结构,用于存储相同类型的元素的集合。数组的元素在内存中是连续存储的,可以通过索引访问。下面将详细介绍C数组的相关内容。 2.数组的定义 数组的定义需要指定元素的类型和数组的大小。 type arrayName[a…...
AWS API Gateway灰度验证实现
在微服务架构中,灰度发布(金丝雀发布)是验证新版本稳定性的核心手段。通过将小部分流量(如 10%)导向新版本服务,可以在不影响整体系统的情况下快速发现问题。AWS API Gateway 原生支持流量按比例分配功能,无需复杂编码即可实现灰度验证。本文将详细解析其实现方法、最佳…...
【Elasticsearch】Elasticsearch 的`path.settings`是用于配置 Elasticsearch 数据和日志存储路径的重要设置
Elasticsearch 的path.settings是用于配置 Elasticsearch 数据和日志存储路径的重要设置,这些路径在elasticsearch.yml配置文件中定义。以下是关于 Elasticsearch 的路径设置(path.data和path.logs)以及快照存储库配置的详细说明:…...
Redis 实战篇 ——《黑马点评》(下)
《引言》 (下)篇将记录 Redis 实战篇 最后的一些学习内容,希望大家能够点赞、收藏支持一下 Thanks♪ (・ω・)ノ,谢谢大家。 传送门(上):Redis 实战篇 ——《黑马…...
蓝桥杯自我复习打卡
总复习,打卡1. 一。排序 1。选段排序 太可恶了,直接全排输出,一个测试点都没过。 AC 首先,这个【l,r】区间一定要包含p,或者q,pq一个都不包含的,[l,r]区间无论怎么变,都对ans没有影响。 其次&…...
Spring Boot拦截器(Interceptor)与过滤器(Filter)详细教程
Spring Boot拦截器(Interceptor)与过滤器(Filter)详细教程 目录 概述 什么是拦截器(Interceptor)?什么是过滤器(Filter)?两者的核心区别 使用场景 拦截器的典…...
Java零基础入门笔记:(6)面向对象
前言 本笔记是学习狂神的java教程,建议配合视频,学习体验更佳。 【狂神说Java】Java零基础学习视频通俗易懂_哔哩哔哩_bilibili 第1-2章:Java零基础入门笔记:(1-2)入门(简介、基础知识)-CSDN博客 第3章…...
【3天快速入门WPF】13-MVVM进阶
目录 1. 窗体设置2. 字体图标3. 控件模板4. 页面逻辑4.1. 不使用MVVM4.2. MVVM模式实现本篇我们开发一个基于MVVM的登录页面,用来回顾下之前学习的内容 登录页面如下: 窗体取消了默认的标题栏,调整为带阴影的圆角窗体,左侧放一张登录背景图,右边自绘了一个关闭按钮,文本框…...
【MongoDB】在Windows11下安装与使用
官网下载链接:Download MongoDB Community Server 官方参考文档:https://www.mongodb.com/zh-cn/docs/manual/tutorial/install-mongodb-on-windows/#std-label-install-mdb-community-windows 选择custom类型,其他默认 注意,此选…...
Kotlin 5种单例模式
在Kotlin中实现单例模式有多种方法,以下是几种常见的方法: 饿汉式 饿汉式是最简单的一种实现方式,在类加载时就完成了实例的初始化。 //饿汉式 object Singleton1 {fun printMessage() {println("饿汉式")} }懒汉式 懒汉式是延迟…...
C语言复习5:字符串的定义,字符串的常用函数
## 字符串变量的定义方式 - 在C语言中,没有单独的字符串变量,但可以利用字符数组来存字符串 - 占位符:%s - 定义1: 数据类型 变量名[内存占用大小] "字符串"; eg: char s…...
【Multipath网络层协议】MPTCP工作原理
常见网络层多路径协议介绍 MPTCP(Multipath TCP) MPTCP 是在传统 TCP 基础上进行扩展的协议,它允许在源端和目的端之间建立多个 TCP子流,这些子流可以通过不同的网络路径传输数据。 例如,一台笔记本电脑同时连接了 W…...
deepseek使用记录18——文化基因美食篇
子篇:薪火相传的味觉辩证法——从燧人氏到预制菜的文化突围 一、石器时代的启蒙:食物探索中的原始辩证法 在贾湖遗址的陶罐残片上,碳化稻米与蜂蜜的结晶层相互交叠,这是9000年前先民对"甘"与"饱"的首次辩证…...
2025学年安徽省职业院校技能大赛 “信息安全管理与评估”赛项 比赛样题任务书
2024-2025 学年广东省职业院校技能大赛 “信息安全管理与评估”赛项 技能测试试卷(五) 第一部分:网络平台搭建与设备安全防护任务书第二部分:网络安全事件响应、数字取证调查、应用程序安全任务书任务1 :内存取证&…...
在 Ansys Maxwell 中分析磁场
在 Ansys Maxwell 中分析磁场 分析磁场的能力对于理解电磁系统至关重要。Ansys Maxwell 为工程师提供了强大的工具,帮助他们探索磁场数据并从中提取有价值的见解。在本指南中,我将深入研究 Ansys Maxwell 中的几种基本技术和方法,以有效地分…...
