前言
太长了,这里接着Android重学系列 PackageManagerService的启动与安装(中)。
如果遇到什么问题欢迎来到本文https://www.jianshu.com/p/033e6a9a3d7c进行讨论
PMS installStage PMS中的安装步骤
void installStage(String packageName, File stagedDir,
IPackageInstallObserver2 observer, PackageInstaller.SessionParams sessionParams,
String installerPackageName, int installerUid, UserHandle user,
PackageParser.SigningDetails signingDetails) {
final VerificationInfo verificationInfo = new VerificationInfo(
sessionParams.originatingUri, sessionParams.referrerUri,
sessionParams.originatingUid, installerUid);
final OriginInfo origin = OriginInfo.fromStagedFile(stagedDir);
final Message msg = mHandler.obtainMessage(INIT_COPY);
final int installReason = fixUpInstallReason(installerPackageName, installerUid,
sessionParams.installReason);
final InstallParams params = new InstallParams(origin, null, observer,
sessionParams.installFlags, installerPackageName, sessionParams.volumeUuid,
verificationInfo, user, sessionParams.abiOverride,
sessionParams.grantedRuntimePermissions, signingDetails, installReason);
msg.obj = params;
...
mHandler.sendMessage(msg);
}
static OriginInfo fromStagedFile(File file) {
return new OriginInfo(file, true, false);
}
private OriginInfo(File file, boolean staged, boolean existing) {
this.file = file;
this.staged = staged;
this.existing = existing;
if (file != null) {
resolvedPath = file.getAbsolutePath();
resolvedFile = file;
} else {
resolvedPath = null;
resolvedFile = null;
}
}
很见到就是发送了一个INIT_COPY的handler消息,并且把stagedDir信息存储到OriginInfo中。OriginInfo记录了stageDir的路径也就是/data/app/vmdlsessionId.tmp
PackageHandler 处理INIT_COPY消息
case INIT_COPY: {
HandlerParams params = (HandlerParams) msg.obj;
int idx = mPendingInstalls.size();
if (!mBound) {
if (!connectToService()) {
params.serviceError();
return;
} else {
mPendingInstalls.add(idx, params);
}
} else {
mPendingInstalls.add(idx, params);
if (idx == 0) {
mHandler.sendEmptyMessage(MCS_BOUND);
}
}
}
首先第一次安装的时候mBound必定是false,就会调用connectToService尝试的绑定一个Service,如果绑定成功则把此时的安装参数HandlerParams保存到mPendingInstalls。
PackageHandler connectToService
public static final ComponentName DEFAULT_CONTAINER_COMPONENT = new ComponentName(
DEFAULT_CONTAINER_PACKAGE,
"com.android.defcontainer.DefaultContainerService");
private boolean connectToService() {
Intent service = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT);
Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
if (mContext.bindServiceAsUser(service, mDefContainerConn,
Context.BIND_AUTO_CREATE, UserHandle.SYSTEM)) {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
mBound = true;
return true;
}
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
return false;
}
能看到此时先提高进程优先级到THREAD_PRIORITY_DEFAULT;并启动了一个DefaultContainerService服务,同时绑定了mDefContainerConn。成功后把进程设置回THREAD_PRIORITY_BACKGROUND。
class DefaultContainerConnection implements ServiceConnection {
public void onServiceConnected(ComponentName name, IBinder service) {
final IMediaContainerService imcs = IMediaContainerService.Stub
.asInterface(Binder.allowBlocking(service));
mHandler.sendMessage(mHandler.obtainMessage(MCS_BOUND, imcs));
}
public void onServiceDisconnected(ComponentName name) {
}
}
值得注意的是,DefaultContainerConnection会监听Service启动后返回的Binder并且发送一个MCS_BOUND消息。
DefaultContainerService的Binder对象
实际上我们不需要关系这哥Service是启动都做了什么,我们关心的是返回的Binder是什么,具体的实现先不看,直接看看它的aidl接口
interface IMediaContainerService {
int copyPackage(String packagePath, in IParcelFileDescriptorFactory target);
PackageInfoLite getMinimalPackageInfo(String packagePath, int flags, String abiOverride);
ObbInfo getObbInfo(String filename);
void clearDirectory(String directory);
long calculateInstalledSize(String packagePath, String abiOverride);
}
很简单,就是关于PMS中进行apk包相关的操作。
PackageHandler 处理MCS_BOUND消息
case MCS_BOUND: {
if (msg.obj != null) {
mContainerService = (IMediaContainerService) msg.obj;
}
if (mContainerService == null) {
...
} else if (mPendingInstalls.size() > 0) {
HandlerParams params = mPendingInstalls.get(0);
if (params != null) {
if (params.startCopy()) {
if (mPendingInstalls.size() > 0) {
mPendingInstalls.remove(0);
}
if (mPendingInstalls.size() == 0) {
if (mBound) {
removeMessages(MCS_UNBIND);
Message ubmsg = obtainMessage(MCS_UNBIND);
sendMessageDelayed(ubmsg, 10000);
}
} else {
mHandler.sendEmptyMessage(MCS_BOUND);
}
}
}
} else {
...
}
break;
}
能看到在PMS的ServiceThread handleThread线程中会检查mPendingInstalls是否有需要安装的对象,有则从mPendingInstalls的头部获取HandlerParams,调用HandlerParams.startCopy方法进行拷贝,知道消费完mPendingInstalls中所有的任务则进行解绑。
PMS.HandlerParams startCopy
private static final int MAX_RETRIES = 4;
final boolean startCopy() {
boolean res;
try {
if (++mRetries > MAX_RETRIES) {
mHandler.sendEmptyMessage(MCS_GIVE_UP);
handleServiceError();
return false;
} else {
handleStartCopy();
res = true;
}
} catch (RemoteException e) {
mHandler.sendEmptyMessage(MCS_RECONNECT);
res = false;
}
handleReturnCode();
return res;
}
在这个拷贝的步骤会重新的尝试4次,但是正常步骤则是调用handleStartCopy开始拷贝安装后,再调用handleReturnCode返回状态码
PMS.HandlerParams handleStartCopy
public void handleStartCopy() throws RemoteException {
int ret = PackageManager.INSTALL_SUCCEEDED;
if (origin.staged) {
if (origin.file != null) {
installFlags |= PackageManager.INSTALL_INTERNAL;
installFlags &= ~PackageManager.INSTALL_EXTERNAL;
} else {
}
}
final boolean onSd = (installFlags & PackageManager.INSTALL_EXTERNAL) != 0;
final boolean onInt = (installFlags & PackageManager.INSTALL_INTERNAL) != 0;
final boolean ephemeral = (installFlags & PackageManager.INSTALL_INSTANT_APP) != 0;
PackageInfoLite pkgLite = null;
if (onInt && onSd) {
...
} else if (onSd && ephemeral) {
...
} else {
pkgLite = mContainerService.getMinimalPackageInfo(origin.resolvedPath, installFlags,
packageAbiOverride);
...
}
...
final InstallArgs args = createInstallArgs(this);
mArgs = args;
if (ret == PackageManager.INSTALL_SUCCEEDED) {
UserHandle verifierUser = getUser();
if (verifierUser == UserHandle.ALL) {
verifierUser = UserHandle.SYSTEM;
}
final int requiredUid = mRequiredVerifierPackage == null ? -1
: getPackageUid(mRequiredVerifierPackage, MATCH_DEBUG_TRIAGED_MISSING,
verifierUser.getIdentifier());
final int installerUid =
verificationInfo == null ? -1 : verificationInfo.installerUid;
if (!origin.existing && requiredUid != -1
&& isVerificationEnabled(
verifierUser.getIdentifier(), installFlags, installerUid)) {
final Intent verification = new Intent(
Intent.ACTION_PACKAGE_NEEDS_VERIFICATION);
verification.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
verification.setDataAndType(Uri.fromFile(new File(origin.resolvedPath)),
PACKAGE_MIME_TYPE);
verification.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
final List receivers = queryIntentReceiversInternal(verification,
PACKAGE_MIME_TYPE, 0, verifierUser.getIdentifier(),
false /*allowDynamicSplits*/);
final int verificationId = mPendingVerificationToken++;
verification.putExtra(PackageManager.EXTRA_VERIFICATION_ID, verificationId);
verification.putExtra(PackageManager.EXTRA_VERIFICATION_INSTALLER_PACKAGE,
installerPackageName);
verification.putExtra(PackageManager.EXTRA_VERIFICATION_INSTALL_FLAGS,
installFlags);
verification.putExtra(PackageManager.EXTRA_VERIFICATION_PACKAGE_NAME,
pkgLite.packageName);
verification.putExtra(PackageManager.EXTRA_VERIFICATION_VERSION_CODE,
pkgLite.versionCode);
verification.putExtra(PackageManager.EXTRA_VERIFICATION_LONG_VERSION_CODE,
pkgLite.getLongVersionCode());
if (verificationInfo != null) {
if (verificationInfo.originatingUri != null) {
verification.putExtra(Intent.EXTRA_ORIGINATING_URI,
verificationInfo.originatingUri);
}
if (verificationInfo.referrer != null) {
verification.putExtra(Intent.EXTRA_REFERRER,
verificationInfo.referrer);
}
if (verificationInfo.originatingUid >= 0) {
verification.putExtra(Intent.EXTRA_ORIGINATING_UID,
verificationInfo.originatingUid);
}
if (verificationInfo.installerUid >= 0) {
verification.putExtra(PackageManager.EXTRA_VERIFICATION_INSTALLER_UID,
verificationInfo.installerUid);
}
}
final PackageVerificationState verificationState = new PackageVerificationState(
requiredUid, args);
mPendingVerification.append(verificationId, verificationState);
final List sufficientVerifiers = matchVerifiers(pkgLite,
receivers, verificationState);
DeviceIdleController.LocalService idleController = getDeviceIdleController();
final long idleDuration = getVerificationTimeout();
if (sufficientVerifiers != null) {
final int N = sufficientVerifiers.size();
if (N == 0) {
ret = PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE;
} else {
for (int i = 0; i < N; i++) {
final ComponentName verifierComponent = sufficientVerifiers.get(i);
idleController.addPowerSaveTempWhitelistApp(Process.myUid(),
verifierComponent.getPackageName(), idleDuration,
verifierUser.getIdentifier(), false, "package verifier");
final Intent sufficientIntent = new Intent(verification);
sufficientIntent.setComponent(verifierComponent);
mContext.sendBroadcastAsUser(sufficientIntent, verifierUser);
}
}
}
final ComponentName requiredVerifierComponent = matchComponentForVerifier(
mRequiredVerifierPackage, receivers);
if (ret == PackageManager.INSTALL_SUCCEEDED
&& mRequiredVerifierPackage != null) {
verification.setComponent(requiredVerifierComponent);
idleController.addPowerSaveTempWhitelistApp(Process.myUid(),
mRequiredVerifierPackage, idleDuration,
verifierUser.getIdentifier(), false, "package verifier");
mContext.sendOrderedBroadcastAsUser(verification, verifierUser,
android.Manifest.permission.PACKAGE_VERIFICATION_AGENT,
new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
final Message msg = mHandler
.obtainMessage(CHECK_PENDING_VERIFICATION);
msg.arg1 = verificationId;
mHandler.sendMessageDelayed(msg, getVerificationTimeout());
}
}, null, 0, null, null);
mArgs = null;
}
} else {
ret = args.copyApk(mContainerService, true);
}
}
mRet = ret;
}
1.调用IMediaContainerService的getMinimalPackageInfo方法,扫描安装apk到Android系统中。
2.通过createInstallArgs构建InstallArgs对象,如果getMinimalPackageInfo的安装成功了,则会进行判断该包是否已经安装过且isVerificationEnabled是否允许进行包校验器进行校验。
private InstallArgs createInstallArgs(InstallParams params) {
if (params.move != null) {
return new MoveInstallArgs(params);
} else {
return new FileInstallArgs(params);
}
}
注意我们此时是FileInstallArgs,我们只考虑第一次安装apk包的情况
- 3.如果可以进行包校验,则通过data为
"application/vnd.android.package-archive"
调用queryIntentReceiversInternal方法查找有没有注册在Android系统中的包校验广播接受者;通过matchVerifiers筛选出当前合适的包校验器,最后依次发送广播,让包校验接受者根据包名,versionCode,packageName,安装来源等信息进行校验
- 4.不如出现不满足进行校验的条件,则调用InstallArgs.copyApk进行进一步的拷贝操作。
关于包校验器这里就不多聊了,核心是第1点和第4点,让我们依次看看IMediaContainerService.getMinimalPackageInfo以及InstallArgs.copyApk
DefaultContainerService getMinimalPackageInfo
public PackageInfoLite getMinimalPackageInfo(String packagePath, int flags,
String abiOverride) {
final Context context = DefaultContainerService.this;
PackageInfoLite ret = new PackageInfoLite();
final File packageFile = new File(packagePath);
final PackageParser.PackageLite pkg;
final long sizeBytes;
try {
pkg = PackageParser.parsePackageLite(packageFile, 0);
sizeBytes = PackageHelper.calculateInstalledSize(pkg, abiOverride);
} catch (PackageParserException | IOException e) {
...
}
final int recommendedInstallLocation;
final long token = Binder.clearCallingIdentity();
try {
recommendedInstallLocation = PackageHelper.resolveInstallLocation(context,
pkg.packageName, pkg.installLocation, sizeBytes, flags);
} finally {
Binder.restoreCallingIdentity(token);
}
ret.packageName = pkg.packageName;
ret.splitNames = pkg.splitNames;
ret.versionCode = pkg.versionCode;
ret.versionCodeMajor = pkg.versionCodeMajor;
ret.baseRevisionCode = pkg.baseRevisionCode;
ret.splitRevisionCodes = pkg.splitRevisionCodes;
ret.installLocation = pkg.installLocation;
ret.verifiers = pkg.verifiers;
ret.recommendedInstallLocation = recommendedInstallLocation;
ret.multiArch = pkg.multiArch;
return ret;
}
- 1.PackageParser.parsePackageLite 对apk包进行解析
- 2.PackageHelper.calculateInstalledSize 对安装后的包进行大小统计
- 3.PackageHelper.resolveInstallLocation 计算出返回的结果
PackageParser.parsePackageLite
public static PackageLite parsePackageLite(File packageFile, int flags)
throws PackageParserException {
if (packageFile.isDirectory()) {
return parseClusterPackageLite(packageFile, flags);
} else {
return parseMonolithicPackageLite(packageFile, flags);
}
}
到这个方法就是解析包中的AndroidManifest最外层的数据.拿到版本号,包名,包的路径等等不包含四大组件的基础信息。注意这里将会走目录的分支,parseClusterPackageLite。这个方法其实和parseMonolithicPackageLite作用十分相似,核心还是遍历这个目录里面所有的apk文件,以splitName为key,ApkLite为value保存起来,并且找出null为key对应的baseApk的value,从而确认这个apk安装包的基础包是什么。
PackageHelper.calculateInstalledSize
public static long calculateInstalledSize(PackageLite pkg, NativeLibraryHelper.Handle handle,
String abiOverride) throws IOException {
long sizeBytes = 0;
for (String codePath : pkg.getAllCodePaths()) {
final File codeFile = new File(codePath);
sizeBytes += codeFile.length();
}
sizeBytes += DexMetadataHelper.getPackageDexMetadataSize(pkg);
sizeBytes += NativeLibraryHelper.sumNativeBinariesWithOverride(handle, abiOverride);
return sizeBytes;
}
计算结果是:
安装后大小 = 每一个包安装路径下文件的大小(
/data/app/vmdlsessionId.tmp/base.apk
)+.dm
后缀的文件大小 +so
库大小
FileInstallArgs.copyApk
int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyApk");
try {
return doCopyApk(imcs, temp);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}
private int doCopyApk(IMediaContainerService imcs, boolean temp) throws RemoteException {
if (origin.staged) {
codeFile = origin.file;
resourceFile = origin.file;
return PackageManager.INSTALL_SUCCEEDED;
}
try {
final boolean isEphemeral = (installFlags & PackageManager.INSTALL_INSTANT_APP) != 0;
final File tempDir =
mInstallerService.allocateStageDirLegacy(volumeUuid, isEphemeral);
codeFile = tempDir;
resourceFile = tempDir;
} catch (IOException e) {
...
}
final IParcelFileDescriptorFactory target = new IParcelFileDescriptorFactory.Stub() {
@Override
public ParcelFileDescriptor open(String name, int mode) throws RemoteException {
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) {
}
}
};
int ret = PackageManager.INSTALL_SUCCEEDED;
ret = imcs.copyPackage(origin.file.getAbsolutePath(), target);
if (ret != PackageManager.INSTALL_SUCCEEDED) {
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) {
...
} finally {
IoUtils.closeQuietly(handle);
}
return ret;
}
1.PackageInstallerService.allocateStageDirLegacy 获取一个全新的sessionId对应目录:
/data/app/vmdlsessionId.tmp/
目录,把code和资源的路径都设置到同一处。2.实例化一个IParcelFileDescriptorFactory接口,用于设置每一个创建文件时候确定权限为0644。
3.IMediaContainerService.copyPackage 把保存在OriginInfo的File(
/data/app/vmdlsessionId.tmp/
)拷贝到IParcelFileDescriptorFactory的路径中。3.codeFile 下设置一个lib文件夹,里面包含了不同平台的so库。
IMediaContainerService.copyPackage
核心方法如下:
public int copyPackage(String packagePath, IParcelFileDescriptorFactory target) {
if (packagePath == null || target == null) {
return PackageManager.INSTALL_FAILED_INVALID_URI;
}
PackageLite pkg = null;
try {
final File packageFile = new File(packagePath);
pkg = PackageParser.parsePackageLite(packageFile, 0);
return copyPackageInner(pkg, target);
} catch (PackageParserException | IOException | RemoteException e) {
...
}
}
private int copyPackageInner(PackageLite pkg, IParcelFileDescriptorFactory target)
throws IOException, RemoteException {
copyFile(pkg.baseCodePath, target, "base.apk");
if (!ArrayUtils.isEmpty(pkg.splitNames)) {
for (int i = 0; i < pkg.splitNames.length; i++) {
copyFile(pkg.splitCodePaths[i], target, "split_" + pkg.splitNames[i] + ".apk");
}
}
return PackageManager.INSTALL_SUCCEEDED;
}
把/data/app/vmdlsessionId.tmp/
下的文件apk资源往另一个sessionId对应的/data/app/vmdlsessionId.tmp/base.apk
复制拷贝。
PMS.HandlerParams handleReturnCode
void handleReturnCode() {
if (mArgs != null) {
processPendingInstall(mArgs, mRet);
}
}
private void processPendingInstall(final InstallArgs args, final int currentStatus) {
mHandler.post(new Runnable() {
public void run() {
mHandler.removeCallbacks(this);
PackageInstalledInfo res = new PackageInstalledInfo();
res.setReturnCode(currentStatus);
res.uid = -1;
res.pkg = null;
res.removedInfo = null;
if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
args.doPreInstall(res.returnCode);
synchronized (mInstallLock) {
installPackageTracedLI(args, res);
}
args.doPostInstall(res.returnCode, res.uid);
}
....
if (!doRestore) {
Message msg = mHandler.obtainMessage(POST_INSTALL, token, 0);
mHandler.sendMessage(msg);
}
});
}
- 1.InstallArgs.doPreInstall 安装失败则清除安装过程中的临时文件
- 2.installPackageTracedLI 安装扫描apk包
- 3.InstallArgs.doPostInstall 清除缓存在Installd的缓存
- 4.发送POST_INSTALL Handler消息到ServiceThread中处理
PMS installPackageTracedLI
private void installPackageLI(InstallArgs args, PackageInstalledInfo res) {
...
final PackageParser pp = new PackageParser();
pp.setSeparateProcesses(mSeparateProcesses);
pp.setDisplayMetrics(mMetrics);
pp.setCallback(mPackageParserCallback);
final PackageParser.Package pkg;
try {
pkg = pp.parsePackage(tmpPackageFile, parseFlags);
DexMetadataHelper.validatePackageDexMetadata(pkg);
} catch (PackageParserException e) {
...
} finally {
...
}
...
if (TextUtils.isEmpty(pkg.cpuAbiOverride)) {
pkg.cpuAbiOverride = args.abiOverride;
}
String pkgName = res.name = pkg.packageName;
...
try {
if (args.signingDetails != PackageParser.SigningDetails.UNKNOWN) {
pkg.setSigningDetails(args.signingDetails);
} else {
PackageParser.collectCertificates(pkg, false /* skipVerify */);
}
} catch (PackageParserException e) {
res.setError("Failed collect during installPackageLI", e);
return;
}
...
pp = null;
String oldCodePath = null;
boolean systemApp = false;
synchronized (mPackages) {
...
PackageSetting ps = mSettings.mPackages.get(pkgName);
if (ps != null) {
...
}
int N = pkg.permissions.size();
for (int i = N-1; i >= 0; i--) {
final PackageParser.Permission perm = pkg.permissions.get(i);
final BasePermission bp =
(BasePermission) mPermissionManager.getPermissionTEMP(perm.info.name);
if ((perm.info.protectionLevel & PermissionInfo.PROTECTION_FLAG_INSTANT) != 0
&& !systemApp) {
perm.info.protectionLevel &= ~PermissionInfo.PROTECTION_FLAG_INSTANT;
}
if (bp != null) {
final boolean sigsOk;
final String sourcePackageName = bp.getSourcePackageName();
final PackageSettingBase sourcePackageSetting = bp.getSourcePackageSetting();
final KeySetManagerService ksms = mSettings.mKeySetManagerService;
if (sourcePackageName.equals(pkg.packageName)
&& (ksms.shouldCheckUpgradeKeySetLocked(
sourcePackageSetting, scanFlags))) {
sigsOk = ksms.checkUpgradeKeySetLocked(sourcePackageSetting, pkg);
} else {
if (sourcePackageSetting.signatures.mSigningDetails.checkCapability(
pkg.mSigningDetails,
PackageParser.SigningDetails.CertCapabilities.PERMISSION)) {
sigsOk = true;
} else if (pkg.mSigningDetails.checkCapability(
sourcePackageSetting.signatures.mSigningDetails,
PackageParser.SigningDetails.CertCapabilities.PERMISSION)) {
sourcePackageSetting.signatures.mSigningDetails = pkg.mSigningDetails;
sigsOk = true;
} else {
sigsOk = false;
}
}
if (!sigsOk) {
...
} else if (!PLATFORM_PACKAGE_NAME.equals(pkg.packageName)) {
if ((perm.info.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE)
== PermissionInfo.PROTECTION_DANGEROUS) {
if (bp != null && !bp.isRuntime()) {
perm.info.protectionLevel = bp.getProtectionLevel();
}
}
}
}
}
}
...
if (args.move != null) {
...
} else if (!forwardLocked && !pkg.applicationInfo.isExternalAsec()) {
....
}
if (!args.doRename(res.returnCode, pkg, oldCodePath)) {
return;
}
if (PackageManagerServiceUtils.isApkVerityEnabled()) {
String apkPath = null;
synchronized (mPackages) {
final PackageSetting ps = mSettings.mPackages.get(pkgName);
if (ps != null && ps.isPrivileged()) {
apkPath = pkg.baseCodePath;
}
}
if (apkPath != null) {
final VerityUtils.SetupResult result =
VerityUtils.generateApkVeritySetupData(apkPath);
if (result.isOk()) {
FileDescriptor fd = result.getUnownedFileDescriptor();
try {
final byte[] signedRootHash = VerityUtils.generateFsverityRootHash(apkPath);
mInstaller.installApkVerity(apkPath, fd, result.getContentSize());
mInstaller.assertFsverityRootHashMatches(apkPath, signedRootHash);
} catch (InstallerException | IOException | DigestException |
...
} finally {
IoUtils.closeQuietly(fd);
}
} else if (result.isFailed()) {
...
} else {
}
}
}
..
try (PackageFreezer freezer = freezePackageForInstall(pkgName, installFlags,
"installPackageLI")) {
if (replace) {
...
} else {
installNewPackageLIF(pkg, parseFlags, scanFlags | SCAN_DELETE_DATA_ON_FAILURES,
args.user, installerPackageName, volumeUuid, res, args.installReason);
}
}
mArtManagerService.prepareAppProfiles(pkg, resolveUserIds(args.user.getIdentifier()));
final boolean performDexopt = (res.returnCode == PackageManager.INSTALL_SUCCEEDED)
&& !forwardLocked
&& !pkg.applicationInfo.isExternalAsec()
&& (!instantApp || Global.getInt(mContext.getContentResolver(),
Global.INSTANT_APP_DEXOPT_ENABLED, 0) != 0)
&& ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) == 0);
if (performDexopt) {
DexoptOptions dexoptOptions = new DexoptOptions(pkg.packageName,
REASON_INSTALL,
DexoptOptions.DEXOPT_BOOT_COMPLETE |
DexoptOptions.DEXOPT_INSTALL_WITH_DEX_METADATA_FILE);
mPackageDexOptimizer.performDexOpt(pkg, pkg.usesLibraryFiles,
null /* instructionSets */,
getOrCreateCompilerPackageStats(pkg),
mDexManager.getPackageUseInfoOrDefault(pkg.packageName),
dexoptOptions);
}
BackgroundDexOptService.notifyPackageChanged(pkg.packageName);
synchronized (mPackages) {
final PackageSetting ps = mSettings.mPackages.get(pkgName);
if (ps != null) {
res.newUsers = ps.queryInstalledUsers(sUserManager.getUserIds(), true);
ps.setUpdateAvailable(false /*updateAvailable*/);
}
...
}
}
分为如下几步:
1.PackageParser.parsePackage 扫描apk包中完整的
AndroidManifest.xml
内容,并把解析的package结果缓存到磁盘和内存中2.往Package对象中设置好签名信息
3.从Settings中获取是否有当前包名对应的PackageSetting 包配置信息,此时还没有则跳开这里的if。获取PermissionManagerService查询权限信息。如果还是能查到,说明是包的升级,此时会进行包权限的校验。判断前后两次是不是内部包含的包权限内容都是一致,而只是顺序变了。当然如果之前没有安装,或者内容不一致,都会触发KeySetManagerService的checkUpgradeKeySetLocked进行更新
4.调用InstallArgs.doRename 把当前的包的名字给更新了
5.判断当前是否允许对包进行校验,如果允许则调用VerityUtils.generateApkVeritySetupData 对包的签名进行校验,校验通过根路径则调用Installd服务进一步调用installApkVerity进行校验。详细就不说了
6.installNewPackageLIF
private void installNewPackageLIF(PackageParser.Package pkg, final @ParseFlags int parseFlags,
final @ScanFlags int scanFlags, UserHandle user, String installerPackageName,
String volumeUuid, PackageInstalledInfo res, int installReason) {
String pkgName = pkg.packageName;
synchronized(mPackages) {
final String renamedPackage = mSettings.getRenamedPackageLPr(pkgName);
if (renamedPackage != null) {
return;
}
if (mPackages.containsKey(pkgName)) {
return;
}
}
try {
PackageParser.Package newPackage = scanPackageTracedLI(pkg, parseFlags, scanFlags,
System.currentTimeMillis(), user);
updateSettingsLI(newPackage, installerPackageName, null, res, user, installReason);
if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
prepareAppDataAfterInstallLIF(newPackage);
} else {
...
}
} catch (PackageManagerException e) {
}
}
1.调用scanPackageTracedLI 扫描包内容,此时其实已经解析过一次包内容,在这里能直接获得缓存。
2.updateSettingsLI 更新Settings中的包配置对象也就是PackageSettings对象以及往Settings的
package.list
和package.xml
写入配置内容。关于这两个文件可以阅读PackageManagerService的启动与安装(上)。-
3.prepareAppDataAfterInstallLIF核心还是调用了prepareAppDataLeafLIF方法,关于这个方法还是可以阅读PackageManagerService的启动与安装(上)。
3.2.实际上就是给下面编译优化的结果文件,创建对应的目录在每一个包下面创建
cache
或者code_cache
文件夹,并且生成加速dex2oat编译的.prof
文件.3.3另一点就是调用了Installd的fixupAppData方法,创建一个/data/user/用户id和/data/data目录,也就是我们常见的/data/user/0./data/user/用户id是/data/data的软链接
- 7.prepareAppProfiles 调用
/system/bin/profman
生成加速dex2oat编译的.prof
文件
- 8.PackageDexOptimizer.performDexOpt 进行dexopt 对dex文件进行优化
我们再来看看其中核心的scanPackageTracedLI 以及InstallArgs.doRename
PMS scanPackageTracedLI
private PackageParser.Package scanPackageLI(File scanFile, int parseFlags, int scanFlags,
long currentTime, UserHandle user) throws PackageManagerException {
PackageParser pp = new PackageParser();
pp.setSeparateProcesses(mSeparateProcesses);
pp.setOnlyCoreApps(mOnlyCore);
pp.setDisplayMetrics(mMetrics);
pp.setCallback(mPackageParserCallback);
final PackageParser.Package pkg;
try {
pkg = pp.parsePackage(scanFile, parseFlags);
} catch (PackageParserException e) {
} finally {
}
if (pkg.applicationInfo.isStaticSharedLibrary()) {
renameStaticSharedLibraryPackage(pkg);
}
return scanPackageChildLI(pkg, parseFlags, scanFlags, currentTime, user);
}
这里面的很简单又一次的调用了parsePackage,这一次是直接从缓存中读取出了完成的package信息,接着调用scanPackageChildLI。
scanPackageChildLI
private PackageParser.Package scanPackageChildLI(PackageParser.Package pkg,
final @ParseFlags int parseFlags, @ScanFlags int scanFlags, long currentTime,
@Nullable UserHandle user)
throws PackageManagerException {
if ((scanFlags & SCAN_CHECK_ONLY) == 0) {
if (pkg.childPackages != null && pkg.childPackages.size() > 0) {
scanFlags |= SCAN_CHECK_ONLY;
}
} else {
scanFlags &= ~SCAN_CHECK_ONLY;
}
// Scan the parent
PackageParser.Package scannedPkg = addForInitLI(pkg, parseFlags,
scanFlags, currentTime, user);
final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
for (int i = 0; i < childCount; i++) {
PackageParser.Package childPackage = pkg.childPackages.get(i);
addForInitLI(childPackage, parseFlags, scanFlags,
currentTime, user);
}
if ((scanFlags & SCAN_CHECK_ONLY) != 0) {
return scanPackageChildLI(pkg, parseFlags, scanFlags, currentTime, user);
}
return scannedPkg;
}
在这里面会不断的递归整个目录结构中所有的可能的子包。我们不需要理解也ok,关键是addForInitLI 对每一个安装包的package会进行一次初始化。这个方法对于新包来说核心的逻辑在于其中的scanPackageNewLI
scanPackageNewLI
private PackageParser.Package scanPackageNewLI(@NonNull PackageParser.Package pkg,
final @ParseFlags int parseFlags, @ScanFlags int scanFlags, long currentTime,
@Nullable UserHandle user) throws PackageManagerException {
...
scanFlags = adjustScanFlags(scanFlags, pkgSetting, disabledPkgSetting, user, pkg);
synchronized (mPackages) {
...
boolean scanSucceeded = false;
try {
final ScanRequest request = new ScanRequest(pkg, sharedUserSetting,
pkgSetting == null ? null : pkgSetting.pkg, pkgSetting, disabledPkgSetting,
originalPkgSetting, realPkgName, parseFlags, scanFlags,
(pkg == mPlatformPackage), user);
final ScanResult result = scanPackageOnlyLI(request, mFactoryTest, currentTime);
if (result.success) {
commitScanResultsLocked(request, result);
}
scanSucceeded = true;
} finally {
...
}
}
return pkg;
}
核心是调用了commitScanResultsLocked,这个方法对把扫描后的内容同步到PackageSettings,等待后续的写入到缓存文件中。对于全新的包则会做如下处理:
if (newPkgSettingCreated) {
if (originalPkgSetting != null) {
mSettings.addRenamedPackageLPw(pkg.packageName, originalPkgSetting.name);
}
mSettings.addUserToSettingLPw(pkgSetting);
if (originalPkgSetting != null && (scanFlags & SCAN_CHECK_ONLY) == 0) {
mTransferedPackages.add(originalPkgSetting.name);
}
}
核心就是Settings的addUserToSettingLPw 为新的包分配全新的userId。
void addUserToSettingLPw(PackageSetting p) throws PackageManagerException {
if (p.appId == 0) {
p.appId = newUserIdLPw(p);
} else {
addUserIdLPw(p.appId, p, p.name);
}
if (p.appId < 0) {
...
}
}
在ApplicationInfo 没有记录appid也就是没有分配过userId的情况下,会调用newUserIdLPw进行分配
public static final int FIRST_APPLICATION_UID = 10000;
public static final int LAST_APPLICATION_UID = 19999;
private int newUserIdLPw(Object obj) {
final int N = mUserIds.size();
for (int i = mFirstAvailableUid; i < N; i++) {
if (mUserIds.get(i) == null) {
mUserIds.set(i, obj);
return Process.FIRST_APPLICATION_UID + i;
}
}
if (N > (Process.LAST_APPLICATION_UID-Process.FIRST_APPLICATION_UID)) {
return -1;
}
mUserIds.add(obj);
return Process.FIRST_APPLICATION_UID + N;
}
实际上就查找当前的mUserIds中有多少空闲的uid,找到则拿到对应的index,设置UserId为:
uid = 10000 + index
InstallArgs.doRename
boolean doRename(int status, PackageParser.Package pkg, String oldCodePath) {
final File targetDir = codeFile.getParentFile();
final File beforeCodeFile = codeFile;
final File afterCodeFile = getNextCodePath(targetDir, pkg.packageName);
try {
Os.rename(beforeCodeFile.getAbsolutePath(), afterCodeFile.getAbsolutePath());
} catch (ErrnoException e) {
return false;
}
if (!SELinux.restoreconRecursive(afterCodeFile)) {
return false;
}
codeFile = afterCodeFile;
resourceFile = afterCodeFile;
try {
pkg.setCodePath(afterCodeFile.getCanonicalPath());
} catch (IOException e) {
return false;
}
pkg.setBaseCodePath(FileUtils.rewriteAfterRename(beforeCodeFile,
afterCodeFile, pkg.baseCodePath));
pkg.setSplitCodePaths(FileUtils.rewriteAfterRename(beforeCodeFile,
afterCodeFile, pkg.splitCodePaths));
// Reflect the rename in app info
pkg.setApplicationVolumeUuid(pkg.volumeUuid);
pkg.setApplicationInfoCodePath(pkg.codePath);
pkg.setApplicationInfoBaseCodePath(pkg.baseCodePath);
pkg.setApplicationInfoSplitCodePaths(pkg.splitCodePaths);
pkg.setApplicationInfoResourcePath(pkg.codePath);
pkg.setApplicationInfoBaseResourcePath(pkg.baseCodePath);
pkg.setApplicationInfoSplitResourcePaths(pkg.splitCodePaths);
return true;
}
在这里存在两种路径,一种是变化前的路径,名字为/data/app/vmdlsessionId/base.apk
,另一种是安装后的路径,通过如下getNextCodePath方法获得:
private File getNextCodePath(File targetDir, String packageName) {
File result;
SecureRandom random = new SecureRandom();
byte[] bytes = new byte[16];
do {
random.nextBytes(bytes);
String suffix = Base64.encodeToString(bytes, Base64.URL_SAFE | Base64.NO_WRAP);
result = new File(targetDir, packageName + "-" + suffix);
} while (result.exists());
return result;
}
targetDir是指/data/app/vmdlsessionId.tmp/
的父目录/data/app/
那么此时就是设置为/data/app/包名-随机数
发送POST_INSTALL Handler消息到PackageHandler中处理
当安装完成后就会发送POST_INSTALL 处理安装完的内容
case POST_INSTALL: {
PostInstallData data = mRunningInstalls.get(msg.arg1);
final boolean didRestore = (msg.arg2 != 0);
mRunningInstalls.delete(msg.arg1);
if (data != null) {
InstallArgs args = data.args;
PackageInstalledInfo parentRes = data.res;
final boolean grantPermissions = (args.installFlags
& PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS) != 0;
final boolean killApp = (args.installFlags
& PackageManager.INSTALL_DONT_KILL_APP) == 0;
final boolean virtualPreload = ((args.installFlags
& PackageManager.INSTALL_VIRTUAL_PRELOAD) != 0);
final String[] grantedPermissions = args.installGrantPermissions;
handlePackagePostInstall(parentRes, grantPermissions, killApp,
virtualPreload, grantedPermissions, didRestore,
args.installerPackageName, args.observer);
final int childCount = (parentRes.addedChildPackages != null)
? parentRes.addedChildPackages.size() : 0;
for (int i = 0; i < childCount; i++) {
PackageInstalledInfo childRes = parentRes.addedChildPackages.valueAt(i);
handlePackagePostInstall(childRes, grantPermissions, killApp,
virtualPreload, grantedPermissions, false /*didRestore*/,
args.installerPackageName, args.observer);
}
} else {
}
} break;
核心方法是handlePackagePostInstall,这个方法发送了如下几种广播:
- 1.Intent.ACTION_PACKAGE_ADDED 包添加广播
- 2.Intent.ACTION_PACKAGE_REPLACED 包替换广播
最后还会获取到保存在InstallArgs的IPackageInstallObserver2 ,回调onPackageInstalled。也就是在PackageInstallSession的commit流程中生成的对象,而这个方法又会调用dispatchSessionFinished
PackageInstallSession dispatchSessionFinished
private void dispatchSessionFinished(int returnCode, String msg, Bundle extras) {
final IPackageInstallObserver2 observer;
final String packageName;
synchronized (mLock) {
mFinalStatus = returnCode;
mFinalMessage = msg;
observer = mRemoteObserver;
packageName = mPackageName;
}
if (observer != null) {
final SomeArgs args = SomeArgs.obtain();
args.arg1 = packageName;
args.arg2 = msg;
args.arg3 = extras;
args.arg4 = observer;
args.argi1 = returnCode;
mHandler.obtainMessage(MSG_ON_PACKAGE_INSTALLED, args).sendToTarget();
}
final boolean success = (returnCode == PackageManager.INSTALL_SUCCEEDED);
final boolean isNewInstall = extras == null || !extras.getBoolean(Intent.EXTRA_REPLACING);
if (success && isNewInstall) {
mPm.sendSessionCommitBroadcast(generateInfo(), userId);
}
mCallback.onSessionFinished(this, success);
}
做了三件事情:
- 1.发送Handler消息MSG_ON_PACKAGE_INSTALLED
- 2.发送了一个ACTION_SESSION_COMMITTED广播 发送到InstallEventReceiver
- 3.回调了onSessionFinished
其中第一点的MSG_ON_PACKAGE_INSTALLED的消息处理中又调用了Binder对象mRemoteObserver的onPackageInstalled。此时就会调用PackageInstallObserver也就是PackageInstallerService. PackageInstallObserverAdapter的onPackageInstalled。
PackageInstallObserverAdapter的onPackageInstalled
public void onPackageInstalled(String basePackageName, int returnCode, String msg,
Bundle extras) {
if (PackageManager.INSTALL_SUCCEEDED == returnCode && mShowNotification) {
boolean update = (extras != null) && extras.getBoolean(Intent.EXTRA_REPLACING);
Notification notification = buildSuccessNotification(mContext,
mContext.getResources()
.getString(update ? R.string.package_updated_device_owner :
R.string.package_installed_device_owner),
basePackageName,
mUserId);
if (notification != null) {
NotificationManager notificationManager = (NotificationManager)
mContext.getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(basePackageName,
SystemMessage.NOTE_PACKAGE_STATE,
notification);
}
}
final Intent fillIn = new Intent();
fillIn.putExtra(PackageInstaller.EXTRA_PACKAGE_NAME, basePackageName);
fillIn.putExtra(PackageInstaller.EXTRA_SESSION_ID, mSessionId);
fillIn.putExtra(PackageInstaller.EXTRA_STATUS,
PackageManager.installStatusToPublicStatus(returnCode));
fillIn.putExtra(PackageInstaller.EXTRA_STATUS_MESSAGE,
PackageManager.installStatusToString(returnCode, msg));
fillIn.putExtra(PackageInstaller.EXTRA_LEGACY_STATUS, returnCode);
if (extras != null) {
final String existing = extras.getString(
PackageManager.EXTRA_FAILURE_EXISTING_PACKAGE);
if (!TextUtils.isEmpty(existing)) {
fillIn.putExtra(PackageInstaller.EXTRA_OTHER_PACKAGE_NAME, existing);
}
}
try {
mTarget.sendIntent(mContext, 0, fillIn, null, null);
} catch (SendIntentException ignored) {
}
}
}
能看到此时就会调用NotificationManager 出现一个通知栏告诉用户已经安装好了apk了。
InstallEventReceiver 接受到广播后的处理
当这个对象接受到广播后,就会调用EventResultPersister的onEventReceived
void onEventReceived(@NonNull Context context, @NonNull Intent intent) {
int status = intent.getIntExtra(PackageInstaller.EXTRA_STATUS, 0);
if (status == PackageInstaller.STATUS_PENDING_USER_ACTION) {
context.startActivity(intent.getParcelableExtra(Intent.EXTRA_INTENT));
return;
}
int id = intent.getIntExtra(EXTRA_ID, 0);
String statusMessage = intent.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE);
int legacyStatus = intent.getIntExtra(PackageInstaller.EXTRA_LEGACY_STATUS, 0);
EventResultObserver observerToCall = null;
synchronized (mLock) {
int numObservers = mObservers.size();
for (int i = 0; i < numObservers; i++) {
if (mObservers.keyAt(i) == id) {
observerToCall = mObservers.valueAt(i);
mObservers.removeAt(i);
break;
}
}
if (observerToCall != null) {
observerToCall.onResult(status, legacyStatus, statusMessage);
} else {
mResults.put(id, new EventResult(status, legacyStatus, statusMessage));
writeState();
}
}
}
这里很简单从EXTRA_ID 中获取当前EventResultObserver对应的id,并且回调onResult。此时就会回调到InstallInstalling的launchFinishBasedOnResult方法中。
InstallInstalling launchFinishBasedOnResult
private void launchFinishBasedOnResult(int statusCode, int legacyStatus, String statusMessage) {
if (statusCode == PackageInstaller.STATUS_SUCCESS) {
launchSuccess();
} else {
launchFailure(legacyStatus, statusMessage);
}
}
很简单就是显示成功或者失败的页面。
总结
到这里PMS的安装全流程就解析完了,虽然有不少的地方说不详细,但是总体架构在这里了,需要探索哪一点可以继续根据这个框架去阅读源码即可。老规矩,先上一个时序图,流程很长,注意这里我没有把所有的AysncTask中的流程显示出来,能意会就好:
在PMS的安装过程,总结来说就是两个步骤:
- 1.拷贝apk
- 2.解析apk,把解析的内容缓存到Andoid系统,把包内容保存在配置中。
apk的拷贝过程,经过了如下几次变化,我们以此为线索梳理其流程:
1.
来源apk uri
->/data/no_backup/packagexxx.apk
.2.
/data/no_backup/packagexxx.apk
->/data/app/vmdlsessionId.tmp/PackageInstaller
这个过程中还记录了PackageInstallerSession的记录,保存在/data/system/install_sessions.xml
中。这个过程是通过PackageInstallerService创建一个Session对象时候进行记录,这样就能得知Android系统中曾经出现过多少次安装记录,可能出现多少残余的数据需要清除。3.
/data/app/vmdlsessionId.tmp/PackageInstaller
-> 会变化为另外一个sessionId对应的/data/app/vmdlsessionId.tmp/base.apk
这个过程因为可能会复用sessionId,因此需要先删除一些之前可能遗留下来的残留文件,接着就会创建一个临时的/data/app/vmdlsessionId.tmp/lib
保存so库
-
4.
/data/app/vmdlsessionId2.tmp/base.apk
重命名为/data/app/包名-随机数
在这个过程最为核心,就是整个PMS的安装核心如下:4.1.PackageParser.parsePackage 解析包AndroidManifest所有内容,缓存到
data/system/package_cache/包名_0
中4.2.scanPackageTracedLI 虽然这个方法也有扫描包内容的能力,但是这里更加重要的是为每一个apk包设置好PackageSettings对象,准备写入到Settings中
packages.xml
中,并为每一个新安装的apk设置好自己的userid(10000 + index).4.3. updateSettingsLI 把包配置设置到
packages.xml
-
4.4. prepareAppDataAfterInstallLIF, prepareAppProfiles,PackageDexOptimizer.performDexOpt都是对dex文件的优化提供环境:
- 4.4.1.prepareAppDataAfterInstallLIF提供目录结构
/data/user/用户ID/包名/cache
或/data/user/用户ID/包名/code_cache
- 4.4.2.调用了Installd的fixupAppData方法,创建一个/data/user/用户id和/data/data目录,也就是我们常见的/data/user/0./data/user/用户id是/data/data的软链接
- 4.4.2.prepareAppProfiles 提供加速编译的文件
- 4.4.3.performDexOpt 执行dexopt
- 4.4.1.prepareAppDataAfterInstallLIF提供目录结构
4.5. doRename 重命名
5.最后发送POST_INSTALL消息,通过IPackageInstallObserver2回调,通知此时又调用DefaultContainerService记录好的Binder的onPackageInstalled 展示一个通知栏通知,告诉用户已经安装完毕
6.IPackageInstallObserver2回调 回调中也会发送一个广播消息,告诉正在等待安装结果的InstallInstalling 页面展示安装成功提示。
后话
到这里PMS的全流程就结束了,能看到有一个很核心的内容,performDexOpt是是如何优化dex还没有聊到。这是一个比较重要的内容,但是学习的门槛有点高,需要熟悉ART 虚拟机。
不过没关系,我对ART虚拟机源码也来回看了几遍,之后我会开启新的篇章,名为JVM基础系列,让我们恶补一下关于Java的“基础”吧,看看网上哪些优化的言论,看看网上哪些常用的说法是否正确。要达到这个能力还需要自己亲自去底层看看ART是怎么设计的。通过对JVM的理解,可以加深对Java编程的理解。
在这之前,我会补上Activity和Binder的总结,以及一篇概述。之后的计划,应该是来总结网络相关的知识。就以解析OkHttp开始,看看socket的底层设计吧。等理解了socket,再来看看JVM相关的篇章吧。