Android 应用安装过程分析详解手机开发

在之前的文章中,我们对PakageManagerService启动流程分析 做了简单的介绍,并对PMS系统的启动流程做了详细的解析。上面只是说到了Android的PMS的运行流程,而对于Android apk的整个安装流程并没有过多的介绍。本篇将重点介绍下Android apk的运行启动流程。
总结一下,关于apk的安装流程主要分为以下步骤:

  1. 将apk文件复制到data/app目录
  2. 解析apk信息
  3. dexopt操作
  4. 更新权限信息
  5. 完成安装,发送Intent.ACTION_PACKAGE_ADDED广播

用一张图说明一下:
这里写图片描述

此图可见,从PakageManagerService的启动到apk的安装完成,中间还是经历了很多的流程。

1, 将apk文件copy至data/app目录

在之前的文章我们说过,安装应用 Intent 的是 PackageInstallerActivity,但这个类厂商可以随意修改,这个类也并没有在 android.jar 中。PackageInstallerActivity 在安装过程中,实际调用的是 ApplicationPackageManager 里面的代码。

private void installCommon(Uri packageURI, 
        PackageInstallObserver observer, int flags, String installerPackageName, 
        VerificationParams verificationParams, ContainerEncryptionParams encryptionParams) { 
    if (!"file".equals(packageURI.getScheme())) { 
        throw new UnsupportedOperationException("Only file:// URIs are supported"); 
    } 
    if (encryptionParams != null) { 
        throw new UnsupportedOperationException("ContainerEncryptionParams not supported"); 
    } 
 
    final String originPath = packageURI.getPath(); 
    try { 
        mPM.installPackage(originPath, observer.getBinder(), flags, installerPackageName, 
                verificationParams, null); 
    } catch (RemoteException ignored) { 
    } 
}

在 ApplicationPackageManager 中是通过 Binder 机制调用了 PackageManagerService 中的 installPackage 方法,在这个方法中有个核心的方法installPackageAsUser,也就是我们前面流程图看到的。

@Override 
public void installPackage(String originPath, IPackageInstallObserver2 observer, 
        int installFlags, String installerPackageName, VerificationParams verificationParams, 
        String packageAbiOverride) { 
    installPackageAsUser(originPath, observer, installFlags, installerPackageName, 
            verificationParams, packageAbiOverride, UserHandle.getCallingUserId()); 
}

installPackageAsUser

mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, null); 
 
        final int callingUid = Binder.getCallingUid(); 
        ... 
        ... 
 
        if ((callingUid == Process.SHELL_UID) || (callingUid == Process.ROOT_UID)) { 
            installFlags |= PackageManager.INSTALL_FROM_ADB; 
 
        } else { 
            // Caller holds INSTALL_PACKAGES permission, so we're less strict 
            // about installerPackageName. 
 
            installFlags &= ~PackageManager.INSTALL_FROM_ADB; 
            installFlags &= ~PackageManager.INSTALL_ALL_USERS; 
        } 
 
        UserHandle user; 
        if ((installFlags & PackageManager.INSTALL_ALL_USERS) != 0) { 
            user = UserHandle.ALL; 
        } else { 
            user = new UserHandle(userId); 
        } 
 
        verificationParams.setInstallerUid(callingUid); 
 
        final File originFile = new File(originPath); 
        final OriginInfo origin = OriginInfo.fromUntrustedFile(originFile); 
 
        final Message msg = mHandler.obtainMessage(INIT_COPY); 
        msg.obj = new InstallParams(origin, observer, installFlags, 
                installerPackageName, verificationParams, user, packageAbiOverride); 
        mHandler.sendMessage(msg);

这个方法主要是判断安装来源,包括adb,shell,all_user,然后像PMS的mHandler发送INIT_COPY的消息,这个mHandler运行在一个HandlerThread中。在接受到 INIT_COPY 消息后,将要安装的参数信息加入到 PendingInstalls 中去,如果是第一个安装,还需要发送 MCS_BOUND 消息,用于触发实际安装过程。

case INIT_COPY: { 
    HandlerParams params = (HandlerParams) msg.obj; 
    int idx = mPendingInstalls.size(); 
    if (DEBUG_INSTALL) Slog.i(TAG, "init_copy idx=" + idx + ": " + params); 
    // If a bind was already initiated we dont really 
    // need to do anything. The pending install 
    // will be processed later on. 
    if (!mBound) { 
        // If this is the only one pending we might 
        // have to bind to the service again. 
        if (!connectToService()) { 
            Slog.e(TAG, "Failed to bind to media container service"); 
            params.serviceError(); 
            return; 
        } else { 
            // Once we bind to the service, the first 
            // pending request will be processed. 
            mPendingInstalls.add(idx, params); 
        } 
    } else { 
        mPendingInstalls.add(idx, params); 
        // Already bound to the service. Just make 
        // sure we trigger off processing the first request. 
        if (idx == 0) { 
            mHandler.sendEmptyMessage(MCS_BOUND); 
        } 
    } 
    break; 
}

HandlerParams.startCopy

在 MCS_BOUND 消息中取出第一个安装请求,并调用 startCopy 方法。

HandlerParams params = mPendingInstalls.get(0); 
if (params != null) { 
   if (params.startCopy()) { 
     // ... 
   } 
   // ... 
}

handleStartCopy()

在 startCopy 中调用 handleStartCopy 方法,由于这个类,需要与 MCS (MediaContainerService) 进行通信,有可能发生异常,因而这里设置了重试机制。

if (++mRetries > MAX_RETRIES) { 
    Slog.w(TAG, "Failed to invoke remote methods on default container service. Giving up"); 
    mHandler.sendEmptyMessage(MCS_GIVE_UP); 
    handleServiceError(); 
    return false; 
} else { 
    handleStartCopy(); 
    res = true; 
} 
handleReturnCode();

handleStartCopy的核心就是copyApk,其他的都是些存储空间检查,权限检查等等安全校验。

2, 解析apk信息

完成apk copy到data/app目录的操作后,下一步就到了 handleReturnCode,这个方法又跳转到processPendingInstall()方法,下面先来看看processPendingInstall()方法。

private void processPendingInstall(final InstallArgs args, final int currentStatus) { 
        // Queue up an async operation since the package installation may take a little while. 
        mHandler.post(new Runnable() { 
            public void run() { 
                mHandler.removeCallbacks(this); 
                 // Result object to be returned 
                PackageInstalledInfo res = new PackageInstalledInfo(); 
                res.returnCode = currentStatus; 
                res.uid = -1; 
                res.pkg = null; 
                res.removedInfo = new PackageRemovedInfo(); 
                if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) { 
                    args.doPreInstall(res.returnCode); 
                    synchronized (mInstallLock) { 
                        installPackageLI(args, res); //1.安装 
                    } 
                    args.doPostInstall(res.returnCode, res.uid); 
                } 
 
                // A restore should be performed at this point if (a) the install 
                // succeeded, (b) the operation is not an update, and (c) the new 
                // package has not opted out of backup participation. 
                final boolean update = res.removedInfo.removedPackage != null; 
                final int flags = (res.pkg == null) ? 0 : res.pkg.applicationInfo.flags; 
                boolean doRestore = !update 
                        && ((flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0); 
 
                // Set up the post-install work request bookkeeping.  This will be used 
                // and cleaned up by the post-install event handling regardless of whether 
                // there's a restore pass performed.  Token values are >= 1. 
                int token; 
                if (mNextInstallToken < 0) mNextInstallToken = 1; 
                token = mNextInstallToken++; 
 
                PostInstallData data = new PostInstallData(args, res); 
                mRunningInstalls.put(token, data); 
                if (DEBUG_INSTALL) Log.v(TAG, "+ starting restore round-trip " + token); 
 
                if (res.returnCode == PackageManager.INSTALL_SUCCEEDED && doRestore) { 
                    // Pass responsibility to the Backup Manager.  It will perform a 
                    // restore if appropriate, then pass responsibility back to the 
                    // Package Manager to run the post-install observer callbacks 
                    // and broadcasts. 
                    IBackupManager bm = IBackupManager.Stub.asInterface( 
                            ServiceManager.getService(Context.BACKUP_SERVICE)); 
                    if (bm != null) { 
                        if (DEBUG_INSTALL) Log.v(TAG, "token " + token 
                                + " to BM for possible restore"); 
                        try { 
                            bm.restoreAtInstall(res.pkg.applicationInfo.packageName, token); //2.调用backup服务 
                        } catch (RemoteException e) { 
                            // can't happen; the backup manager is local 
                        } catch (Exception e) { 
                            Slog.e(TAG, "Exception trying to enqueue restore", e); 
                            doRestore = false; 
                        } 
                    } else { 
                        Slog.e(TAG, "Backup Manager not found!"); 
                        doRestore = false; 
                    } 
                } 
 
                if (!doRestore) { 
                    // No restore possible, or the Backup Manager was mysteriously not 
                    // available -- just fire the post-install work request directly. 
                    if (DEBUG_INSTALL) Log.v(TAG, "No restore - queue post-install for " + token); 
                    Message msg = mHandler.obtainMessage(POST_INSTALL, token, 0); 
                    mHandler.sendMessage(msg); 
                } 
            } 
        }); 
    }

在这个方法有几个关键步骤:
一是installPackageLI(args, res);,这个方法具体执行了解析package和后续操作,
而再installPackageLI(args, res);执行完毕后会走到bm.restoreAtInstall(res.pkg.applicationInfo.packageName, token);,会调用backupservice的restoreAtInstall方法,而restoreAtInstall方法最终又会调用PMS的finishPackageInstall()方法,完成安装。

installPackageLI

private void installPackageLI(InstallArgs args, PackageInstalledInfo res) { 
final int installFlags = args.installFlags; 
String installerPackageName = args.installerPackageName; 
File tmpPackageFile = new File(args.getCodePath()); 
boolean forwardLocked = ((installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0); 
boolean onSd = ((installFlags & PackageManager.INSTALL_EXTERNAL) != 0); 
boolean replace = false; 
final int scanFlags = SCAN_NEW_INSTALL | SCAN_FORCE_DEX | SCAN_UPDATE_SIGNATURE; 
// Result object to be returned 
res.returnCode = PackageManager.INSTALL_SUCCEEDED; 
if (DEBUG_INSTALL) Slog.d(TAG, "installPackageLI: path=" + tmpPackageFile); 
// Retrieve PackageSettings and parse package 
final int parseFlags = mDefParseFlags | PackageParser.PARSE_CHATTY 
| (forwardLocked ? PackageParser.PARSE_FORWARD_LOCK : 0) 
| (onSd ? PackageParser.PARSE_ON_SDCARD : 0); 
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; 
} 
// Mark that we have an install time CPU ABI override. 
pkg.cpuAbiOverride = args.abiOverride; 
String pkgName = res.name = pkg.packageName; 
if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_TEST_ONLY) != 0) { 
if ((installFlags & PackageManager.INSTALL_ALLOW_TEST) == 0) { 
res.setError(INSTALL_FAILED_TEST_ONLY, "installPackageLI"); 
return; 
} 
} 
try { 
pp.collectCertificates(pkg, parseFlags); 
pp.collectManifestDigest(pkg); 
} catch (PackageParserException e) { 
res.setError("Failed collect during installPackageLI", e); 
return; 
} 
/* If the installer passed in a manifest digest, compare it now. */ 
if (args.manifestDigest != null) { 
if (DEBUG_INSTALL) { 
final String parsedManifest = pkg.manifestDigest == null ? "null" 
: pkg.manifestDigest.toString(); 
Slog.d(TAG, "Comparing manifests: " + args.manifestDigest.toString() + " vs. " 
+ parsedManifest); 
} 
if (!args.manifestDigest.equals(pkg.manifestDigest)) { 
res.setError(INSTALL_FAILED_PACKAGE_CHANGED, "Manifest digest changed"); 
return; 
} 
} else if (DEBUG_INSTALL) { 
final String parsedManifest = pkg.manifestDigest == null 
? "null" : pkg.manifestDigest.toString(); 
Slog.d(TAG, "manifestDigest was not present, but parser got: " + parsedManifest); 
} 
// Get rid of all references to package scan path via parser. 
pp = null; 
String oldCodePath = null; 
boolean systemApp = false; 
synchronized (mPackages) { 
// Check whether the newly-scanned package wants to define an already-defined perm 
int N = pkg.permissions.size(); 
for (int i = N-1; i >= 0; i--) { 
PackageParser.Permission perm = pkg.permissions.get(i); 
BasePermission bp = mSettings.mPermissions.get(perm.info.name); 
if (bp != null) { 
// If the defining package is signed with our cert, it's okay.  This 
// also includes the "updating the same package" case, of course. 
// "updating same package" could also involve key-rotation. 
final boolean sigsOk; 
if (!bp.sourcePackage.equals(pkg.packageName) 
|| !(bp.packageSetting instanceof PackageSetting) 
|| !bp.packageSetting.keySetData.isUsingUpgradeKeySets() 
|| ((PackageSetting) bp.packageSetting).sharedUser != null) { 
sigsOk = compareSignatures(bp.packageSetting.signatures.mSignatures, 
pkg.mSignatures) == PackageManager.SIGNATURE_MATCH; 
} else { 
sigsOk = checkUpgradeKeySetLP((PackageSetting) bp.packageSetting, pkg); 
} 
if (!sigsOk) { 
// If the owning package is the system itself, we log but allow 
// install to proceed; we fail the install on all other permission 
// redefinitions. 
if (!bp.sourcePackage.equals("android")) { 
res.setError(INSTALL_FAILED_DUPLICATE_PERMISSION, "Package " 
+ pkg.packageName + " attempting to redeclare permission " 
+ perm.info.name + " already owned by " + bp.sourcePackage); 
res.origPermission = perm.info.name; 
res.origPackage = bp.sourcePackage; 
return; 
} else { 
Slog.w(TAG, "Package " + pkg.packageName 
+ " attempting to redeclare system permission " 
+ perm.info.name + "; ignoring new declaration"); 
pkg.permissions.remove(i); 
} 
} 
} 
} 
// Check if installing already existing package 
if ((installFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) { 
String oldName = mSettings.mRenamedPackages.get(pkgName); 
if (pkg.mOriginalPackages != null 
&& pkg.mOriginalPackages.contains(oldName) 
&& mPackages.containsKey(oldName)) { 
// This package is derived from an original package, 
// and this device has been updating from that original 
// name.  We must continue using the original name, so 
// rename the new package here. 
pkg.setPackageName(oldName); 
pkgName = pkg.packageName; 
replace = true; 
if (DEBUG_INSTALL) Slog.d(TAG, "Replacing existing renamed package: oldName=" 
+ oldName + " pkgName=" + pkgName); 
} else if (mPackages.containsKey(pkgName)) { 
// This package, under its official name, already exists 
// on the device; we should replace it. 
replace = true; 
if (DEBUG_INSTALL) Slog.d(TAG, "Replace existing pacakge: " + pkgName); 
} 
} 
PackageSetting ps = mSettings.mPackages.get(pkgName); 
if (ps != null) { 
if (DEBUG_INSTALL) Slog.d(TAG, "Existing package: " + ps); 
oldCodePath = mSettings.mPackages.get(pkgName).codePathString; 
if (ps.pkg != null && ps.pkg.applicationInfo != null) { 
systemApp = (ps.pkg.applicationInfo.flags & 
ApplicationInfo.FLAG_SYSTEM) != 0; 
} 
res.origUsers = ps.queryInstalledUsers(sUserManager.getUserIds(), true); 
} 
} 
if (systemApp && onSd) { 
// Disable updates to system apps on sdcard 
res.setError(INSTALL_FAILED_INVALID_INSTALL_LOCATION, 
"Cannot install updates to system apps on sdcard"); 
return; 
} 
if (!args.doRename(res.returnCode, pkg, oldCodePath)) { 
res.setError(INSTALL_FAILED_INSUFFICIENT_STORAGE, "Failed rename"); 
return; 
} 
if (replace) { 
replacePackageLI(pkg, parseFlags, scanFlags | SCAN_REPLACING, args.user, 
installerPackageName, res); 
} else { 
installNewPackageLI(pkg, parseFlags, scanFlags | SCAN_DELETE_DATA_ON_FAILURES, 
args.user, installerPackageName, res); 
} 
synchronized (mPackages) { 
final PackageSetting ps = mSettings.mPackages.get(pkgName); 
if (ps != null) { 
res.newUsers = ps.queryInstalledUsers(sUserManager.getUserIds(), true); 
} 
} 
}

这个方法先是解析了package包,然后做了大量签名和权限校验的工作,最终走到判断是安装新的apk还是覆盖安装的判断语句上。

 if (replace) { 
replacePackageLI(pkg, parseFlags, scanFlags | SCAN_REPLACING, args.user, 
installerPackageName, res); 
} else { 
installNewPackageLI(pkg, parseFlags, scanFlags | SCAN_DELETE_DATA_ON_FAILURES, 
args.user, installerPackageName, res); 
}

installNewPackageLI()

 private void installNewPackageLI(PackageParser.Package pkg, 
int parseFlags, int scanFlags, UserHandle user, 
String installerPackageName, PackageInstalledInfo res) { 
// Remember this for later, in case we need to rollback this install 
String pkgName = pkg.packageName; 
if (DEBUG_INSTALL) Slog.d(TAG, "installNewPackageLI: " + pkg); 
boolean dataDirExists = getDataPathForPackage(pkg.packageName, 0).exists(); 
synchronized(mPackages) { 
if (mSettings.mRenamedPackages.containsKey(pkgName)) { 
// A package with the same name is already installed, though 
// it has been renamed to an older name.  The package we 
// are trying to install should be installed as an update to 
// the existing one, but that has not been requested, so bail. 
res.setError(INSTALL_FAILED_ALREADY_EXISTS, "Attempt to re-install " + pkgName 
+ " without first uninstalling package running as " 
+ mSettings.mRenamedPackages.get(pkgName)); 
return; 
} 
if (mPackages.containsKey(pkgName)) { 
// Don't allow installation over an existing package with the same name. 
res.setError(INSTALL_FAILED_ALREADY_EXISTS, "Attempt to re-install " + pkgName 
+ " without first uninstalling."); 
return; 
} 
} 
try { 
PackageParser.Package newPackage = scanPackageLI(pkg, parseFlags, scanFlags, 
System.currentTimeMillis(), user); 
updateSettingsLI(newPackage, installerPackageName, null, null, res); 
// delete the partially installed application. the data directory will have to be 
// restored if it was already existing 
if (res.returnCode != PackageManager.INSTALL_SUCCEEDED) { 
// remove package from internal structures.  Note that we want deletePackageX to 
// delete the package data and cache directories that it created in 
// scanPackageLocked, unless those directories existed before we even tried to 
// install. 
deletePackageLI(pkgName, UserHandle.ALL, false, null, null, 
dataDirExists ? PackageManager.DELETE_KEEP_DATA : 0, 
res.removedInfo, true); 
} 
} catch (PackageManagerException e) { 
res.setError("Package couldn't be installed in " + pkg.codePath, e); 
} 
}

这个方法核心的步骤有两个:

  • PackageParser.Package newPackage = scanPackageLI(pkg, parseFlags,
    scanFlags,System.currentTimeMillis(), user);
  • updateSettingsLI(newPackage, installerPackageName, null, null, res);

scanPackageLI负责安装,而updateSettingLI则是完成安装后的设置信息更新。

scanPackageLI()

scanPackageLI()方法主要逻辑是由scanPackageDirtyLI()实现的,在这个方法中主要做了如下的操作:

  • 设置系统App的一些参数
  • 校验签名
  • 解析app的provider,校验是否与已有的provider冲突
  • 32/64位abi的一些设置
  • 四大组件的解析,注册

3,dexopt操作

Apk文件其实就是一个归档zip压缩包,而我们编写的代码最终都编译成了.dex文件,但为了提高运行性能,android系统并不会直接执行.dex,而是会在安装过程中执行dexopt操作来优化.dex文件,最终android系统执行的时优化后的’odex’文件(注意:这个odex文件的后缀也是.dex,其路径在data/dalvik-cache)。对于dalvik虚拟机,dexopt就是优化操作,而对于art虚拟机,dexopt执行的则是dex2oat操作,既将.dex文件翻译成oat文件。关于art和dex2oat的更多信息请自行搜索相关资料。

performDexOptLI()

performDexOptLI的一个核心方法:

final int ret = mInstaller.dexopt(path, sharedGid, !isForwardLocked(pkg), pkg.packageName, dexCodeInstructionSet, vmSafeMode);

本方法的核心就是调用PMS的mInstaller成员变量的dexopt操作。

Installer.dexopt

Installer类的dexopt方法又调用InstallerConnection类的dexopt方法。

public int dexopt(String apkPath, int uid, boolean isPublic, String pkgName, 
String instructionSet, boolean vmSafeMode) { 
StringBuilder builder = new StringBuilder("dexopt"); 
builder.append(' '); 
builder.append(apkPath); 
builder.append(' '); 
builder.append(uid); 
builder.append(isPublic ? " 1" : " 0"); 
builder.append(' '); 
builder.append(pkgName); 
builder.append(' '); 
builder.append(instructionSet); 
builder.append(' '); 
builder.append(vmSafeMode ? " 1" : " 0"); 
return execute(builder.toString()); 
} 
public synchronized String transact(String cmd) { 
if (!connect()) { 
Slog.e(TAG, "connection failed"); 
return "-1"; 
} 
if (!writeCommand(cmd)) { 
/* 
* If installd died and restarted in the background (unlikely but 
* possible) we'll fail on the next write (this one). Try to 
* reconnect and write the command one more time before giving up. 
*/ 
Slog.e(TAG, "write command failed? reconnect!"); 
if (!connect() || !writeCommand(cmd)) { 
return "-1"; 
} 
} 
if (LOCAL_DEBUG) { 
Slog.i(TAG, "send: '" + cmd + "'"); 
} 
final int replyLength = readReply(); 
if (replyLength > 0) { 
String s = new String(buf, 0, replyLength); 
if (LOCAL_DEBUG) { 
Slog.i(TAG, "recv: '" + s + "'"); 
} 
return s; 
} else { 
if (LOCAL_DEBUG) { 
Slog.i(TAG, "fail"); 
} 
return "-1"; 
} 
} 
public int execute(String cmd) { 
String res = transact(cmd); 
try { 
return Integer.parseInt(res); 
} catch (NumberFormatException ex) { 
return -1; 
} 
} 
private boolean connect() { 
if (mSocket != null) { 
return true; 
} 
Slog.i(TAG, "connecting..."); 
try { 
mSocket = new LocalSocket(); 
LocalSocketAddress address = new LocalSocketAddress("installd", 
LocalSocketAddress.Namespace.RESERVED); 
mSocket.connect(address); 
mIn = mSocket.getInputStream(); 
mOut = mSocket.getOutputStream(); 
} catch (IOException ex) { 
disconnect(); 
return false; 
} 
return true; 
}

由上面的几个方法可以知道,最终dexopt操作是通过socket的方式来跨进程通知守护进程installd,由其去执行dexopt操作。

dexopt()

最终守护进程installd会调用Commands.c文件(位于/source/framework/native/cmds/installd)的dexopt方法。

int dexopt(const char *apk_path, uid_t uid, bool is_public, 
const char *pkgname, const char *instruction_set, 
bool vm_safe_mode, bool is_patchoat) 
{ 
struct utimbuf ut; 
struct stat input_stat, dex_stat; 
char out_path[PKG_PATH_MAX]; 
char persist_sys_dalvik_vm_lib[PROPERTY_VALUE_MAX]; 
char *end; 
const char *input_file; 
char in_odex_path[PKG_PATH_MAX]; 
int res, input_fd=-1, out_fd=-1; 
... 
... 
pid_t pid; 
pid = fork(); 
if (pid == 0) { 
/* child -- drop privileges before continuing */ 
if (setgid(uid) != 0) { 
ALOGE("setgid(%d) failed in installd during dexopt/n", uid); 
exit(64); 
} 
if (setuid(uid) != 0) { 
ALOGE("setuid(%d) failed in installd during dexopt/n", uid); 
exit(65); 
} 
// drop capabilities 
struct __user_cap_header_struct capheader; 
struct __user_cap_data_struct capdata[2]; 
memset(&capheader, 0, sizeof(capheader)); 
memset(&capdata, 0, sizeof(capdata)); 
capheader.version = _LINUX_CAPABILITY_VERSION_3; 
if (capset(&capheader, &capdata[0]) < 0) { 
ALOGE("capset failed: %s/n", strerror(errno)); 
exit(66); 
} 
if (set_sched_policy(0, SP_BACKGROUND) < 0) { 
ALOGE("set_sched_policy failed: %s/n", strerror(errno)); 
exit(70); 
} 
if (flock(out_fd, LOCK_EX | LOCK_NB) != 0) { 
ALOGE("flock(%s) failed: %s/n", out_path, strerror(errno)); 
exit(67); 
} 
if (strncmp(persist_sys_dalvik_vm_lib, "libdvm", 6) == 0) { 
run_dexopt(input_fd, out_fd, input_file, out_path); 
} else if (strncmp(persist_sys_dalvik_vm_lib, "libart", 6) == 0) { 
if (is_patchoat) { 
run_patchoat(input_fd, out_fd, input_file, out_path, pkgname, instruction_set); 
} else { 
run_dex2oat(input_fd, out_fd, input_file, out_path, pkgname, instruction_set, 
vm_safe_mode); 
} 
} else { 
exit(69);   /* Unexpected persist.sys.dalvik.vm.lib value */ 
} 
exit(68);   /* only get here on exec failure */ 
} else { 
res = wait_child(pid); 
if (res == 0) { 
ALOGV("DexInv: --- END '%s' (success) ---/n", input_file); 
} else { 
ALOGE("DexInv: --- END '%s' --- status=0x%04x, process failed/n", input_file, res); 
goto fail; 
} 
} 
ut.actime = input_stat.st_atime; 
ut.modtime = input_stat.st_mtime; 
utime(out_path, &ut); 
close(out_fd); 
close(input_fd); 
return 0; 
fail: 
if (out_fd >= 0) { 
close(out_fd); 
unlink(out_path); 
} 
if (input_fd >= 0) { 
close(input_fd); 
} 
return -1; 
}

4,更新权限信息

dexopt操作执行完后,installNewPackageLI()方法就会走到updateSettingsLI()来更新设置信息,而更新设置信息主要是权限信息。更新权限信息最核心的方法是:updatePermissionsLPw()。

updatePermissionsLPw

private void updatePermissionsLPw(String changingPkg, 
PackageParser.Package pkgInfo, int flags) { 
// Make sure there are no dangling permission trees. 
Iterator<BasePermission> it = mSettings.mPermissionTrees.values().iterator(); 
while (it.hasNext()) { 
final BasePermission bp = it.next(); 
if (bp.packageSetting == null) { 
// We may not yet have parsed the package, so just see if 
// we still know about its settings. 
bp.packageSetting = mSettings.mPackages.get(bp.sourcePackage); 
} 
if (bp.packageSetting == null) { 
Slog.w(TAG, "Removing dangling permission tree: " + bp.name 
+ " from package " + bp.sourcePackage); 
it.remove(); 
} else if (changingPkg != null && changingPkg.equals(bp.sourcePackage)) { 
if (pkgInfo == null || !hasPermission(pkgInfo, bp.name)) { 
Slog.i(TAG, "Removing old permission tree: " + bp.name 
+ " from package " + bp.sourcePackage); 
flags |= UPDATE_PERMISSIONS_ALL; 
it.remove(); 
} 
} 
} 
// Make sure all dynamic permissions have been assigned to a package, 
// and make sure there are no dangling permissions. 
it = mSettings.mPermissions.values().iterator(); 
while (it.hasNext()) { 
final BasePermission bp = it.next(); 
if (bp.type == BasePermission.TYPE_DYNAMIC) { 
if (DEBUG_SETTINGS) Log.v(TAG, "Dynamic permission: name=" 
+ bp.name + " pkg=" + bp.sourcePackage 
+ " info=" + bp.pendingInfo); 
if (bp.packageSetting == null && bp.pendingInfo != null) { 
final BasePermission tree = findPermissionTreeLP(bp.name); 
if (tree != null && tree.perm != null) { 
bp.packageSetting = tree.packageSetting; 
bp.perm = new PackageParser.Permission(tree.perm.owner, 
new PermissionInfo(bp.pendingInfo)); 
bp.perm.info.packageName = tree.perm.info.packageName; 
bp.perm.info.name = bp.name; 
bp.uid = tree.uid; 
} 
} 
} 
if (bp.packageSetting == null) { 
// We may not yet have parsed the package, so just see if 
// we still know about its settings. 
bp.packageSetting = mSettings.mPackages.get(bp.sourcePackage); 
} 
if (bp.packageSetting == null) { 
Slog.w(TAG, "Removing dangling permission: " + bp.name 
+ " from package " + bp.sourcePackage); 
it.remove(); 
} else if (changingPkg != null && changingPkg.equals(bp.sourcePackage)) { 
if (pkgInfo == null || !hasPermission(pkgInfo, bp.name)) { 
Slog.i(TAG, "Removing old permission: " + bp.name 
+ " from package " + bp.sourcePackage); 
flags |= UPDATE_PERMISSIONS_ALL; 
it.remove(); 
} 
} 
} 
// Now update the permissions for all packages, in particular 
// replace the granted permissions of the system packages. 
if ((flags&UPDATE_PERMISSIONS_ALL) != 0) { 
for (PackageParser.Package pkg : mPackages.values()) { 
if (pkg != pkgInfo) { 
grantPermissionsLPw(pkg, (flags&UPDATE_PERMISSIONS_REPLACE_ALL) != 0, 
changingPkg); 
} 
} 
} 
if (pkgInfo != null) { 
grantPermissionsLPw(pkgInfo, (flags&UPDATE_PERMISSIONS_REPLACE_PKG) != 0, changingPkg); 
} 
} 
private void grantPermissionsLPw(PackageParser.Package pkg, boolean replace, 
String packageOfInterest) { 
final PackageSetting ps = (PackageSetting) pkg.mExtras; 
if (ps == null) { 
return; 
} 
final GrantedPermissions gp = ps.sharedUser != null ? ps.sharedUser : ps; 
HashSet<String> origPermissions = gp.grantedPermissions; 
boolean changedPermission = false; 
if (replace) { 
ps.permissionsFixed = false; 
if (gp == ps) { 
origPermissions = new HashSet<String>(gp.grantedPermissions); 
gp.grantedPermissions.clear(); 
gp.gids = mGlobalGids; 
} 
} 
if (gp.gids == null) { 
gp.gids = mGlobalGids; 
} 
final int N = pkg.requestedPermissions.size(); 
for (int i=0; i<N; i++) { 
final String name = pkg.requestedPermissions.get(i); 
final boolean required = pkg.requestedPermissionsRequired.get(i); 
final BasePermission bp = mSettings.mPermissions.get(name); 
if (DEBUG_INSTALL) { 
if (gp != ps) { 
Log.i(TAG, "Package " + pkg.packageName + " checking " + name + ": " + bp); 
} 
} 
if (bp == null || bp.packageSetting == null) { 
if (packageOfInterest == null || packageOfInterest.equals(pkg.packageName)) { 
Slog.w(TAG, "Unknown permission " + name 
+ " in package " + pkg.packageName); 
} 
continue; 
} 
final String perm = bp.name; 
boolean allowed; 
boolean allowedSig = false; 
if ((bp.protectionLevel&PermissionInfo.PROTECTION_FLAG_APPOP) != 0) { 
// Keep track of app op permissions. 
ArraySet<String> pkgs = mAppOpPermissionPackages.get(bp.name); 
if (pkgs == null) { 
pkgs = new ArraySet<>(); 
mAppOpPermissionPackages.put(bp.name, pkgs); 
} 
pkgs.add(pkg.packageName); 
} 
final int level = bp.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE; 
if (level == PermissionInfo.PROTECTION_NORMAL 
|| level == PermissionInfo.PROTECTION_DANGEROUS) { 
// We grant a normal or dangerous permission if any of the following 
// are true: 
// 1) The permission is required 
// 2) The permission is optional, but was granted in the past 
// 3) The permission is optional, but was requested by an 
//    app in /system (not /data) 
// 
// Otherwise, reject the permission. 
allowed = (required || origPermissions.contains(perm) 
|| (isSystemApp(ps) && !isUpdatedSystemApp(ps))); 
} else if (bp.packageSetting == null) { 
// This permission is invalid; skip it. 
allowed = false; 
} else if (level == PermissionInfo.PROTECTION_SIGNATURE) { 
allowed = grantSignaturePermission(perm, pkg, bp, origPermissions); 
if (allowed) { 
allowedSig = true; 
} 
} else { 
allowed = false; 
} 
if (DEBUG_INSTALL) { 
if (gp != ps) { 
Log.i(TAG, "Package " + pkg.packageName + " granting " + perm); 
} 
} 
if (allowed) { 
if (!isSystemApp(ps) && ps.permissionsFixed) { 
// If this is an existing, non-system package, then 
// we can't add any new permissions to it. 
if (!allowedSig && !gp.grantedPermissions.contains(perm)) { 
// Except...  if this is a permission that was added 
// to the platform (note: need to only do this when 
// updating the platform). 
allowed = isNewPlatformPermissionForPackage(perm, pkg); 
} 
} 
if (allowed) { 
if (!gp.grantedPermissions.contains(perm)) { 
changedPermission = true; 
gp.grantedPermissions.add(perm); 
gp.gids = appendInts(gp.gids, bp.gids); 
} else if (!ps.haveGids) { 
gp.gids = appendInts(gp.gids, bp.gids); 
} 
} else { 
if (packageOfInterest == null || packageOfInterest.equals(pkg.packageName)) { 
Slog.w(TAG, "Not granting permission " + perm 
+ " to package " + pkg.packageName 
+ " because it was previously installed without"); 
} 
} 
} else { 
if (gp.grantedPermissions.remove(perm)) { 
changedPermission = true; 
gp.gids = removeInts(gp.gids, bp.gids); 
Slog.i(TAG, "Un-granting permission " + perm 
+ " from package " + pkg.packageName 
+ " (protectionLevel=" + bp.protectionLevel 
+ " flags=0x" + Integer.toHexString(pkg.applicationInfo.flags) 
+ ")"); 
} else if ((bp.protectionLevel&PermissionInfo.PROTECTION_FLAG_APPOP) == 0) { 
// Don't print warning for app op permissions, since it is fine for them 
// not to be granted, there is a UI for the user to decide. 
if (packageOfInterest == null || packageOfInterest.equals(pkg.packageName)) { 
Slog.w(TAG, "Not granting permission " + perm 
+ " to package " + pkg.packageName 
+ " (protectionLevel=" + bp.protectionLevel 
+ " flags=0x" + Integer.toHexString(pkg.applicationInfo.flags) 
+ ")"); 
} 
} 
} 
} 
if ((changedPermission || replace) && !ps.permissionsFixed && 
!isSystemApp(ps) || isUpdatedSystemApp(ps)){ 
// This is the first that we have heard about this package, so the 
// permissions we have now selected are fixed until explicitly 
// changed. 
ps.permissionsFixed = true; 
} 
ps.haveGids = true; 
}

在apk的安装时PMS会将该app的所有权限都记录下来并更新到PMS的mAppOpPermissionPackages成员变量里面,并判定是否授予该app请求的权限。

5,完成安装,发广播

processPendingInstall方法在执行installPackageLi后会执行以下语句。

 if (res.returnCode == PackageManager.INSTALL_SUCCEEDED && doRestore) { 
// Pass responsibility to the Backup Manager.  It will perform a 
// restore if appropriate, then pass responsibility back to the 
// Package Manager to run the post-install observer callbacks 
// and broadcasts. 
IBackupManager bm = IBackupManager.Stub.asInterface( 
ServiceManager.getService(Context.BACKUP_SERVICE)); 
if (bm != null) { 
if (DEBUG_INSTALL) Log.v(TAG, "token " + token 
+ " to BM for possible restore"); 
try { 
bm.restoreAtInstall(res.pkg.applicationInfo.packageName, token); 
} catch (RemoteException e) { 
// can't happen; the backup manager is local 
} catch (Exception e) { 
Slog.e(TAG, "Exception trying to enqueue restore", e); 
doRestore = false; 
} 
} else { 
Slog.e(TAG, "Backup Manager not found!"); 
doRestore = false; 
} 
}

在BackupManagerService的restoreAtInstall方法中会有以下代码:

... 
if (skip) { 
// Auto-restore disabled or no way to attempt a restore; just tell the Package 
// Manager to proceed with the post-install handling for this package. 
if (DEBUG) Slog.v(TAG, "Finishing install immediately"); 
try { 
mPackageManagerBinder.finishPackageInstall(token); 
} catch (RemoteException e) { /* can't happen */ } 
} 
...

restoreAtInstall方法最终会调用PMS的finishPackageInstall方法,而此方法最终会发送Intent.ACTION_PACKAGE_ADDED广播,apk的安装就到到此结束。

原创文章,作者:奋斗,如若转载,请注明出处:https://blog.ytso.com/5974.html

(0)
上一篇 2021年7月17日
下一篇 2021年7月17日

相关推荐

发表回复

登录后才能评论