背景
安装影响的目录
- /system/app
- /data/app
- /data/data
- /data/dalvik-cache
- /data/system
- /data/system/package.xml 和 /data/system/package.list
安装的种类
安装流程
零、PackageInstaller
系统App的代码目录
- package/apps/
- framework/base/packages
1)android 7.0 PackageInstallerActivity
private void processPackageUri(final Uri packageUri) {
mPackageURI = packageUri;
final String scheme = packageUri.getScheme();
final PackageUtil.AppSnippet as;
switch (scheme) {
case SCHEME_PACKAGE: {
try {
mPkgInfo = mPm.getPackageInfo(packageUri.getSchemeSpecificPart(),
PackageManager.GET_PERMISSIONS
| PackageManager.GET_UNINSTALLED_PACKAGES);
} catch (NameNotFoundException e) {
}
if (mPkgInfo == null) {
Log.w(TAG, "Requested package " + packageUri.getScheme()
+ " not available. Discontinuing installation");
showDialogInner(DLG_PACKAGE_ERROR);
setPmResult(PackageManager.INSTALL_FAILED_INVALID_APK);
return;
}
as = new PackageUtil.AppSnippet(mPm.getApplicationLabel(mPkgInfo.applicationInfo),
mPm.getApplicationIcon(mPkgInfo.applicationInfo));
} break;
case SCHEME_FILE: {
File sourceFile = new File(packageUri.getPath());
PackageParser.Package parsed = PackageUtil.getPackageInfo(sourceFile);
// Check for parse errors
if (parsed == null) {
Log.w(TAG, "Parse error when parsing manifest. Discontinuing installation");
showDialogInner(DLG_PACKAGE_ERROR);
setPmResult(PackageManager.INSTALL_FAILED_INVALID_APK);
return;
}
mPkgInfo = PackageParser.generatePackageInfo(parsed, null,
PackageManager.GET_PERMISSIONS, 0, 0, null,
new PackageUserState());
as = PackageUtil.getAppSnippet(this, mPkgInfo.applicationInfo, sourceFile);
} break;
case SCHEME_CONTENT: {
mStagingAsynTask = new StagingAsyncTask();
mStagingAsynTask.execute(packageUri);
return;
}
default: {
Log.w(TAG, "Unsupported scheme " + scheme);
setPmResult(PackageManager.INSTALL_FAILED_INVALID_URI);
clearCachedApkIfNeededAndFinish();
return;
}
}
PackageUtil.initSnippetForNewApp(this, as, R.id.app_snippet);
initiateInstall();
}
APP会执行拷贝,保存在/data/data/com.android.packageinstall/cache/
2)android 8.0 InstallStart.java
if (PackageInstaller.ACTION_CONFIRM_PERMISSIONS.equals(intent.getAction())) {
nextActivity.setClass(this, PackageInstallerActivity.class);
} else {
Uri packageUri = intent.getData();
if (packageUri == null) {
// if there's nothing to do, quietly slip into the ether
Intent result = new Intent();
result.putExtra(Intent.EXTRA_INSTALL_RESULT,
PackageManager.INSTALL_FAILED_INVALID_URI);
setResult(RESULT_FIRST_USER, result);
nextActivity = null;
} else {
if (packageUri.getScheme().equals(SCHEME_CONTENT)) {
nextActivity.setClass(this, InstallStaging.class);
} else {
nextActivity.setClass(this, PackageInstallerActivity.class);
}
}
}
APP会执行拷贝,保存在/data/data/com.android.packageinstall/no_backup
检查
1、判断是否为未知来源,是否允许未知来源。
2、授予安装权限。
3、对安装包进行简单解析。
4、判断是否覆盖安装。
5、启动确认安装界面。
开始安装
7.0安装状态监听
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(BROADCAST_ACTION);
registerReceiver(
mBroadcastReceiver, intentFilter, BROADCAST_SENDER_PERMISSION, null /*scheduler*/);
private static final String BROADCAST_ACTION =
"com.android.packageinstaller.ACTION_INSTALL_COMMIT";
private static final String BROADCAST_SENDER_PERMISSION =
"android.permission.INSTALL_PACKAGES";
<!-- @SystemApi Allows an application to install packages.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.INSTALL_PACKAGES"
android:protectionLevel="signature|privileged" />
8.0状态监听
在原有的基础上增加了
getPackageManager().getPackageInstaller().registerSessionCallback(mSessionCallback);
getPackageManager().getPackageInstaller().unregisterSessionCallback(mSessionCallback);
用来显示安装进度
PackageInstallerSession
通过Session与PackageManager建立起通信,并调用了installStage方法。
一、PackageManagerService.installStage
二、Handler.INIT_COPY
InstallParams
绑定ContainerService
frameworks/base/packages/DefaultContainerService
解析apk,获取APK安装大小。
Service that offers to inspect and copy files that may reside on removable
storage. This is designed to prevent the system process from holding onto
open files that cause the kernel to kill it when the underlying device is
removed.
三、Handler.MCS_BOUND
InstallParams.startCopy
继承于HandlerParams
四、InstallParams.handleStartCopy()
1、PackageParser.parsePackageLite 轻量级解析
包名、版本号、单一的apk、唯一的拆分名称、签名信息(需要添加特定的flag)
装应用的期望位置installLocation,新版本是不起作用的。
android:installLocation=["auto" | "internalOnly" | "preferExternal"] >
2、getMinimalPackageInfo
获取PackageInfoLite。
1)PackageHelper.resolveInstallLocation
PackageInfoLite.recommendedInstallLocationAPK的安装位置,之后的安装流程,也在不停的对安装位置进行修正。如果解析失败,后边会尝试一次空间释放,重新解析。installLocation是
- PackageHelper.RECOMMEND_FAILED_INVALID_URI
- PackageHelper.RECOMMEND_FAILED_INVALID_APK
- RECOMMEND_INSTALL_INTERNAL
installFlags == PackageManager.INSTALL_INSTANT_APP
installFlags == PackageManager.INSTALL_INTERNAL
installLocation == PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY
installLocation == PackageInfo.INSTALL_LOCATION_AUTO
若为auto,并且已经安装过,则选择之前的安装目录,若没有则选择内部。 - RECOMMEND_INSTALL_EXTERNAL
installFlags=PackageManager.INSTALL_EXTERNAL
installLocation=PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL - RECOMMEND_FAILED_INSUFFICIENT_STORAGE
如果什么都不是则返回此值。
3、计算安装空间
APK大小加上SO文件大小。
public static long calculateInstalledSize (PackageLite pkg, NativeLibraryHelper.Handle
handle, boolean isForwardLocked, String abiOverride) throws IOException {
long sizeBytes = 0;
// Include raw APKs, and possibly unpacked resources
for (String codePath : pkg.getAllCodePaths()) {
final File codeFile = new File(codePath);
sizeBytes += codeFile.length();
if (isForwardLocked) {
sizeBytes += PackageHelper.extractPublicFiles(codeFile, null);
}
}
// Include all relevant native code
sizeBytes += NativeLibraryHelper.sumNativeBinariesWithOverride(handle, abiOverride);
return sizeBytes;
}
public List<String> getAllCodePaths () {
ArrayList<String> paths = new ArrayList<>();
paths.add(baseCodePath);
if (!ArrayUtils.isEmpty(splitCodePaths)) {
Collections.addAll(paths, splitCodePaths);
}
return paths;
}
final long lowThreshold = storage.getStorageLowBytes(Environment.getDataDirectory());
mInstaller.freeCache(null, sizeBytes + lowThreshold, 0, 0);
private static final int DEFAULT_THRESHOLD_PERCENTAGE = 5;
private static final long DEFAULT_THRESHOLD_MAX_BYTES = 500 * MB_IN_BYTES;
public long getStorageLowBytes(File path) {
final long lowPercent = Settings.Global.getInt(mResolver,
Settings.Global.SYS_STORAGE_THRESHOLD_PERCENTAGE, DEFAULT_THRESHOLD_PERCENTAGE);
final long lowBytes = (path.getTotalSpace() * lowPercent) / 100;
final long maxLowBytes = Settings.Global.getLong(mResolver,
Settings.Global.SYS_STORAGE_THRESHOLD_MAX_BYTES, DEFAULT_THRESHOLD_MAX_BYTES);
return Math.min(lowBytes, maxLowBytes);
}
4、installLocationPolicy
1)检查APK降级安装,只有当系统或者apk处于debug状态才允许。
2)对安装位置做修正。
5、InstallArgs
创建InstallArgs,分为三种
- FileInstallArgs
- AsecInstallArgs
- MoveInstallArgs。
6、判断是否需要包验证
Intent.ACTION_PACKAGE_NEEDS_VERIFICATION
7、FileInstallArgs.copyApk
拷贝文件与so
private File buildStageDir(String volumeUuid, int sessionId, boolean isEphemeral) {
final File stagingDir = buildStagingDir(volumeUuid, isEphemeral);
return new File(stagingDir, "vmdl" + sessionId + ".tmp");
}
//--------------
final IParcelFileDescriptorFactory target = new IParcelFileDescriptorFactory.Stub() {
@Override
public ParcelFileDescriptor open(String name, int mode) throws RemoteException {
if (!FileUtils.isValidExtFilename(name)) {
throw new IllegalArgumentException("Invalid filename: " + name);
}
try {
final File file = new File(codeFile, name);
final FileDescriptor fd = Os.open(file.getAbsolutePath(),
O_RDWR | O_CREAT, 0644);
Os.chmod(file.getAbsolutePath(), 0644);
return new ParcelFileDescriptor(fd);
} catch (ErrnoException e) {
throw new RemoteException("Failed to open: " + e.getMessage());
}
}
};
int ret = PackageManager.INSTALL_SUCCEEDED;
ret = imcs.copyPackage(origin.file.getAbsolutePath(), target);
if (ret != PackageManager.INSTALL_SUCCEEDED) {
Slog.e(TAG, "Failed to copy package");
return ret;
}
final File libraryRoot = new File(codeFile, LIB_DIR_NAME);
NativeLibraryHelper.Handle handle = null;
try {
handle = NativeLibraryHelper.Handle.create(codeFile);
ret = NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libraryRoot,
abiOverride);
} catch (IOException e) {
Slog.e(TAG, "Copying native libraries failed", e);
ret = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
} finally {
IoUtils.closeQuietly(handle);
}
return ret;
}
五、handleReturnCode(processPendingInstall)
- installPackageLI
- 备份部分。安装成功,需要备份服务。这里是在安装中进行数据恢复。
FLAG_ALLOW_BACKUP,在manifest配置android:allowBackup="true"后。就可以使用adb对应用数据进行备份和还原。
adb backup -f back.ab -noapk com.demo.allowbackup
adb restore back.ab - 安装结束
六、installPackageLI
1)解析APK
PackageParser.parsePackage
PackageParser pp = new PackageParser();
pp.setSeparateProcesses(mSeparateProcesses);
pp.setDisplayMetrics(mMetrics);
final PackageParser.Package pkg;
try {
pkg = pp.parsePackage(tmpPackageFile, parseFlags);
} catch (PackageParserException e) {
res.setError("Failed parse during installPackageLI", e);
return;
}
//-------------------
String separateProcesses = SystemProperties.get("debug.separate_processes");
if (separateProcesses != null && separateProcesses.length() > 0) {
if ("*".equals(separateProcesses)) {
mDefParseFlags = PackageParser.PARSE_IGNORE_PROCESSES;
mSeparateProcesses = null;
Slog.w(TAG, "Running with debug.separate_processes: * (ALL)");
} else {
mDefParseFlags = 0;
mSeparateProcesses = separateProcesses.split(",");
Slog.w(TAG, "Running with debug.separate_processes: "
+ separateProcesses);
}
} else {
mDefParseFlags = 0;
mSeparateProcesses = null;
}
2)签名信息验证
PackageParser.collectCertificates
并对V1与V2签名进行验证。
try {
// either use what we've been given or parse directly from the APK
if (args.certificates != null) {
try {
PackageParser.populateCertificates(pkg, args.certificates);
} catch (PackageParserException e) {
// there was something wrong with the certificates we were given;
// try to pull them from the APK
PackageParser.collectCertificates(pkg, parseFlags);
}
} else {
PackageParser.collectCertificates(pkg, parseFlags);
}
} catch (PackageParserException e) {
res.setError("Failed collect during installPackageLI", e);
return;
}
3) 一些检查
对于覆盖安装
if (replace) {
// Prevent apps opting out from runtime permissions
PackageParser.Package oldPackage = mPackages.get(pkgName);
final int oldTargetSdk = oldPackage.applicationInfo.targetSdkVersion;
final int newTargetSdk = pkg.applicationInfo.targetSdkVersion;
if (oldTargetSdk > Build.VERSION_CODES.LOLLIPOP_MR1
&& newTargetSdk <= Build.VERSION_CODES.LOLLIPOP_MR1) {
res.setError(PackageManager.INSTALL_FAILED_PERMISSION_MODEL_DOWNGRADE,
"Package " + pkg.packageName + " new target SDK " + newTargetSdk
+ " doesn't support runtime permissions but the old"
+ " target SDK " + oldTargetSdk + " does.");
return;
}
// Prevent apps from downgrading their targetSandbox.
final int oldTargetSandbox = oldPackage.applicationInfo.targetSandboxVersion;
final int newTargetSandbox = pkg.applicationInfo.targetSandboxVersion;
if (oldTargetSandbox == 2 && newTargetSandbox != 2) {
res.setError(PackageManager.INSTALL_FAILED_SANDBOX_VERSION_DOWNGRADE,
"Package " + pkg.packageName + " new target sandbox "
+ newTargetSandbox + " is incompatible with the previous value of"
+ oldTargetSandbox + ".");
return;
}
// Prevent installing of child packages
if (oldPackage.parentPackage != null) {
res.setError(PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME,
"Package " + pkg.packageName + " is child of package "
+ oldPackage.parentPackage + ". Child packages "
+ "can be updated only through the parent package.");
return;
}
}
}
//-------------------------------
The target sandbox for this app to use. The higher the sandbox version number, the higher the level of security. Its default value is `1`; you can also set it to `2`. Setting this attribute to `2` switches the app to a different SELinux sandbox.
The following restrictions apply to a level 2 sandbox:
* The default value of [`usesCleartextTraffic`](https://developer.android.com/guide/topics/manifest/application-element.html#usesCleartextTraffic) in the Network Security Config is false.
* Uid sharing is not permitted.
For Android Instant Apps targeting Android 8.0 (API level 26) or higher, this attribute must be set to 2\. You can set the sandbox level in the installed version of your app to the less restrictive level 1, but if you do so, your app does not persist app data from the instant app to the installed version of your app. You must set the installed app's sandbox value to 2 in order for the data to persist from the instant app to the installed version.
Once an app is installed, you can only update its target sandbox value to a higher value. To downgrade the target sandbox value, you must uninstall the app and replace it with a version whose manifest contains a lower value for this attribute.
//--------------------------------
// Quick sanity check that we're signed correctly if updating;
// we'll check this again later when scanning, but we want to
// bail early here before tripping over redefined permissions.
if (shouldCheckUpgradeKeySetLP(signatureCheckPs, scanFlags)) {
if (!checkUpgradeKeySetLP(signatureCheckPs, pkg)) {
res.setError(INSTALL_FAILED_UPDATE_INCOMPATIBLE, "Package "
+ pkg.packageName + " upgrade keys do not match the "
+ "previously installed version");
return;
}
} else {
try {
verifySignaturesLP(signatureCheckPs, pkg);
} catch (PackageManagerException e) {
res.setError(e.error, e.getMessage());
return;
}
}
检查APK中已经定义的权限是否已经被其他应用定义了。如果重定义的时系统的权限,则忽略这个权限。
如果是APP重复定义,则安装错误。
**4)拷贝so文件
try {
derivePackageAbi(pkg, new File(pkg.codePath), args.abiOverride,
true /* extract libs */);
} catch (PackageManagerException pme) {
Slog.e(TAG, "Error deriving application ABI", pme);
res.setError(INSTALL_FAILED_INTERNAL_ERROR, "Error deriving application ABI");
return;
}
6)执行Dex优化
int result = mPackageDexOptimizer
.performDexOpt(pkg, null /* instruction sets */, false /* forceDex */,
false /* defer */, false /* inclDependencies */,
true /* boot complete */);
if (result == PackageDexOptimizer.DEX_OPT_FAILED) {
res.setError(INSTALL_FAILED_DEXOPT, "Dexopt failed for " + pkg.codePath);
return;
}
7)重命名
if (!args.doRename(res.returnCode, pkg, oldCodePath)) {
// 如果重命名失败,则报错,退出,安装失败原因:无法重命名
res.setError(INSTALL_FAILED_INSUFFICIENT_STORAGE, "Failed rename");
return;
}
8)IntentFilter
startIntentFilterVerifications(args.user.getIdentifier(), replace, pkg);
9)安装或者覆盖
// ******* 第八步 *******
if (replace) {
replacePackageLI(pkg, parseFlags, scanFlags | SCAN_REPLACING, args.user,
installerPackageName, volumeUuid, res);
} else {
installNewPackageLI(pkg, parseFlags, scanFlags | SCAN_DELETE_DATA_ON_FAILURES,
args.user, installerPackageName, volumeUuid, res);
}
10)
synchronized (mPackages) {
final PackageSetting ps = mSettings.mPackages.get(pkgName);
if (ps != null) {
res.newUsers = ps.queryInstalledUsers(sUserManager.getUserIds(), true);
}
}
七、安装包信息扫描与设置
scanPackageLI
updateSettingsLI
八、POST_INSTALL
安装收尾,发送广播
ACTION_PACKAGE_ADDED
ACTION_PACKAGE_REPLACED
ACTION_MY_PACKAGE_REPLACED
告诉PackageInstaller安装结果
if (installObserver != null) {
try {
Bundle extras = extrasForInstallResult(res);
installObserver.onPackageInstalled(res.name, res.returnCode,
res.returnMsg, extras);
} catch (RemoteException e) {
Slog.i(TAG, "Observer no longer exists.");
}
}
其他安装方式
pm install
private int doCreateSession (SessionParams params, String installerPackageName,int userId)
throws RemoteException {
userId = translateUserId(userId, "runInstallCreate");
if (userId == UserHandle.USER_ALL) {
userId = UserHandle.USER_SYSTEM;
params.installFlags |= PackageManager.INSTALL_ALL_USERS;
}
final int sessionId = mInstaller.createSession(params, installerPackageName, userId);
return sessionId;
}
Pm : Error
Pm : java.lang.SecurityException: Permission Denial: runInstallCreate from pm command asks to run as user -1 but is calling from user 0; this requires android.permission.INTERACT_ACROSS_USERS_FULL
Pm : at android.os.Parcel.readException(Parcel.java:1700)
Pm : at android.os.Parcel.readException(Parcel.java:1653)
Pm : at android.app.ActivityManagerProxy.handleIncomingUser(ActivityManagerNative.java:4880)
Pm : at android.app.ActivityManager.handleIncomingUser(ActivityManager.java:3430)
Pm : at com.android.commands.pm.Pm.translateUserId(Pm.java:345)
Pm : at com.android.commands.pm.Pm.doCreateSession(Pm.java:555)
Pm : at com.android.commands.pm.Pm.runInstall(Pm.java:401)
Pm : at com.android.commands.pm.Pm.run(Pm.java:151)
Pm : at com.android.commands.pm.Pm.main(Pm.java:108)
Pm : at com.android.internal.os.RuntimeInit.nativeFinishInit(Native Method)
Pm : at com.android.internal.os.RuntimeInit.main(RuntimeInit.java:364)
art : System.exit called, status: 1
<!-- @SystemApi Fuller form of {@link android.Manifest.permission#INTERACT_ACROSS_USERS}
that removes restrictions on where broadcasts can be sent and allows other
types of interactions
@hide -- >
<permission android:name = "android.permission.INTERACT_ACROSS_USERS_FULL"
android:protectionLevel = "signature|installer" / >