Android PackageManagerService分析二:安装APK
来源:互联网 发布:最长公共子序列c语言 编辑:程序博客网 时间:2024/04/28 12:02
原文地址: http://www.xuebuyuan.com/2172921.html
这一章我们开始分析APK的安装过程,当我们从网上download一个APK后,点击这个APK文件,就会启动PackageInstallerActivity这个页面来parse这个APK文件,并提示一些信息给用户,当用户点击安装以后,就会开始APK的安装。在介绍安装APK之前,我们先来分析一下installd。
installd介绍
service installd /system/bin/installd class main socket installd stream 600 system system
int main(const int argc, const char *argv[]) { char buf[BUFFER_MAX]; struct sockaddr addr; socklen_t alen; int lsocket, s, count; ALOGI("installd firing up\n"); if (initialize_globals() < 0) { ALOGE("Could not initialize globals; exiting.\n"); exit(1); } if (initialize_directories() < 0) { ALOGE("Could not create directories; exiting.\n"); exit(1); } drop_privileges(); lsocket = android_get_control_socket(SOCKET_PATH); if (lsocket < 0) { ALOGE("Failed to get socket from environment: %s\n", strerror(errno)); exit(1); } if (listen(lsocket, 5)) { ALOGE("Listen on socket failed: %s\n", strerror(errno)); exit(1); } fcntl(lsocket, F_SETFD, FD_CLOEXEC); for (;;) { alen = sizeof(addr); s = accept(lsocket, &addr, &alen);
main函数中首先做一些全局化的初始化设置,然后从底层获取创建的socket号,并在这个socket上面开始listen。在这里installd就是一个server,等待client的连接。那我们再来看一下client的创建过程,也就是Installer对象的实例化,实例化Installer也是在systemServer当中:
installer = new Installer(); installer.ping();
来看一下 Installer的ping方法,它是用于建立也installd之间的socket连接,并测试是否可以正常的发送命令给installd:
public boolean ping() { if (execute("ping") < 0) { return false; } else { return true; } } private int execute(String cmd) { String res = transaction(cmd); try { return Integer.parseInt(res); } catch (NumberFormatException ex) { return -1; } } private synchronized String transaction(String cmd) { if (!connect()) { return "-1"; } if (!writeCommand(cmd)) { if (!connect() || !writeCommand(cmd)) { return "-1"; } } if (readReply()) { String s = new String(buf, 0, buflen); return s; } else { return "-1"; } }
connect函数比较简单,就是创建一个socket,并且与前面的installd进行连接,如果成功连接上,就返回true。那看一下writeCommand的实现:
private boolean writeCommand(String _cmd) { byte[] cmd = _cmd.getBytes(); int len = cmd.length; if ((len < 1) || (len > 1024)) return false; buf[0] = (byte) (len & 0xff); buf[1] = (byte) ((len >> 8) & 0xff); try { mOut.write(buf, 0, 2); mOut.write(cmd, 0, len); } catch (IOException ex) { Slog.e(TAG, "write error"); disconnect(); return false; } return true; }
发送到installd的数据格式:cmd长度+cmd本身。再到installd中看如何处理ping的命令:
for (;;) { unsigned short count; if (readx(s, &count, sizeof(count))) { ALOGE("failed to read size\n"); break; } if ((count < 1) || (count >= BUFFER_MAX)) { ALOGE("invalid size %d\n", count); break; } if (readx(s, buf, count)) { ALOGE("failed to read command\n"); break; } buf[count] = 0; if (execute(s, buf)) break; }
首先读出两字节的长度保存在count中,再从socket读出长度为count的命令保存在buf中,最后调用execute去执行命令:
static int execute(int s, char cmd[BUFFER_MAX]){ char reply[REPLY_MAX]; char *arg[TOKEN_MAX+1]; unsigned i; unsigned n = 0; unsigned short count; int ret = -1; reply[0] = 0; arg[0] = cmd; while (*cmd) { if (isspace(*cmd)) { *cmd++ = 0; n++; arg[n] = cmd; if (n == TOKEN_MAX) { ALOGE("too many arguments\n"); goto done; } } cmd++; } for (i = 0; i < sizeof(cmds) / sizeof(cmds[0]); i++) { if (!strcmp(cmds[i].name,arg[0])) { if (n != cmds[i].numargs) { ALOGE("%s requires %d arguments (%d given)\n", cmds[i].name, cmds[i].numargs, n); } else { ret = cmds[i].func(arg + 1, reply); } goto done; } } ALOGE("unsupported command '%s'\n", arg[0]);done: if (reply[0]) { n = snprintf(cmd, BUFFER_MAX, "%d %s", ret, reply); } else { n = snprintf(cmd, BUFFER_MAX, "%d", ret); } if (n > BUFFER_MAX) n = BUFFER_MAX; count = n; // ALOGI("reply: '%s'\n", cmd); if (writex(s, &count, sizeof(count))) return -1; if (writex(s, cmd, count)) return -1; return 0;}
execute函数也比较简单,首先从命令行中parse出命令和参数,然后从全局的cmds数组中获取到与要执行的命令匹配的函数并执行,最后将返回值和结果发送给client端。这里的cmds数组就表示了所有installd可以执行的命令:
struct cmdinfo cmds[] = { { "ping", 0, do_ping }, { "install", 4, do_install }, { "dexopt", 3, do_dexopt }, { "movedex", 2, do_move_dex }, { "rmdex", 1, do_rm_dex }, { "remove", 2, do_remove }, { "rename", 2, do_rename }, { "fixuid", 3, do_fixuid }, { "freecache", 1, do_free_cache }, { "rmcache", 2, do_rm_cache }, { "getsize", 6, do_get_size }, { "rmuserdata", 2, do_rm_user_data }, { "movefiles", 0, do_movefiles }, { "linklib", 3, do_linklib }, { "mkuserdata", 3, do_mk_user_data }, { "rmuser", 1, do_rm_user },};
Package Installer过程分析
pm.installPackageWithVerificationAndEncryption(mPackageURI, observer, installFlags, installerPackageName, verificationParams, null);
这个方法的第一个参数是APK所在的路径URI;第二个参数用来监控整个安装过程;第三个参数installFlags默认为0;第四个参数是APK的包名;第五个参数用于做数字签名验证。当然这里调用到PMS的真正实现的函数需要借助binder的跨进程来实现:
public void installPackageWithVerificationAndEncryption(Uri packageURI, IPackageInstallObserver observer, int flags, String installerPackageName, VerificationParams verificationParams, ContainerEncryptionParams encryptionParams) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, null); final int uid = Binder.getCallingUid(); UserHandle user; if ((flags&PackageManager.INSTALL_ALL_USERS) != 0) { user = UserHandle.ALL; } else { user = new UserHandle(UserHandle.getUserId(uid)); } final int filteredFlags; if (uid == Process.SHELL_UID || uid == 0) { if (DEBUG_INSTALL) { Slog.v(TAG, "Install from ADB"); } filteredFlags = flags | PackageManager.INSTALL_FROM_ADB; } else { filteredFlags = flags & ~PackageManager.INSTALL_FROM_ADB; } verificationParams.setInstallerUid(uid); final Message msg = mHandler.obtainMessage(INIT_COPY); msg.obj = new InstallParams(packageURI, observer, filteredFlags, installerPackageName, verificationParams, encryptionParams, user); mHandler.sendMessage(msg); }
这里首先做一些权限的检查,并判断当前安装APK的user是否具有相应的权限。在安装APK的时候分为程序开发人员通过ADB安装和user通过网上下载安装,当通过ADB安装时,往往不需要对程序做验证,这就是INSTALL_FROM_ADB这个flag的作用。最后构造一个INIT_COPY的cmd,并带有InstallParams的message发给PackageHandler处理。在分析PackageHandler处理INIT_COPY之前,先来看一下几种安装Params的关系:
再来看PackageHandler处理INIT_COPY这个命令:
case INIT_COPY: { HandlerParams params = (HandlerParams) msg.obj; int idx = mPendingInstalls.size(); if (!mBound) { if (!connectToService()) { Slog.e(TAG, "Failed to bind to media container service"); params.serviceError(); return; } else { mPendingInstalls.add(idx, params); } } else { mPendingInstalls.add(idx, params); if (idx == 0) { mHandler.sendEmptyMessage(MCS_BOUND); } } break; }
在安装APK的时候,PMS还得依赖另一个service,也就是ContainerService,它具体负责实现APK等相关资源文件在内部或外部存储器上的存储工作。如果之前我们没有绑定ContainerService,这里首先会绑定服务,并把我们前面创建的InstallParams加入到mPendingInstalls列表中,mPendingInstalls保存所有的安装请求。如果我们前面绑定过ContainerService,就再发送MCS_BOUND消息给自身。当然,在我们在绑定服务之后,同样也会发送MCS_BOUND消息。我们来看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; }
在处理MCS_BOUND消息时,首先把前面得到的ContainerService服务赋给mContainerService。然后不断的处理mPendingInstalls所有的安装请求,这里调用InstallParams的startCopy方法:
final boolean startCopy() { boolean res; try { 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; } } catch (RemoteException e) { if (DEBUG_INSTALL) Slog.i(TAG, "Posting install MCS_RECONNECT"); mHandler.sendEmptyMessage(MCS_RECONNECT); res = false; } handleReturnCode(); return res; }
这里主要调用InstallParams的handleStartCopy方法来完成资源文件的拷贝:
public void handleStartCopy() throws RemoteException { int ret = PackageManager.INSTALL_SUCCEEDED; final boolean onSd = (flags & PackageManager.INSTALL_EXTERNAL) != 0; final boolean onInt = (flags & PackageManager.INSTALL_INTERNAL) != 0; PackageInfoLite pkgLite = null; if (onInt && onSd) { } else { final long lowThreshold; final DeviceStorageMonitorService dsm = (DeviceStorageMonitorService) ServiceManager .getService(DeviceStorageMonitorService.SERVICE); if (dsm == null) { Log.w(TAG, "Couldn't get low memory threshold; no free limit imposed"); lowThreshold = 0L; } else { lowThreshold = dsm.getMemoryLowThreshold(); } try { mContext.grantUriPermission(DEFAULT_CONTAINER_PACKAGE, mPackageURI, Intent.FLAG_GRANT_READ_URI_PERMISSION); final File packageFile; if (encryptionParams != null || !"file".equals(mPackageURI.getScheme())) { //关于drm文件的安装 } else { packageFile = new File(mPackageURI.getPath()); } if (packageFile != null) { final String packageFilePath = packageFile.getAbsolutePath(); pkgLite = mContainerService.getMinimalPackageInfo(packageFilePath, flags, lowThreshold); if (pkgLite.recommendedInstallLocation == PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) { final long size = mContainerService.calculateInstalledSize( packageFilePath, isForwardLocked()); if (mInstaller.freeCache(size + lowThreshold) >= 0) { pkgLite = mContainerService.getMinimalPackageInfo(packageFilePath, flags, lowThreshold); } if (pkgLite.recommendedInstallLocation == PackageHelper.RECOMMEND_FAILED_INVALID_URI) { pkgLite.recommendedInstallLocation = PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE; } } } } finally { mContext.revokeUriPermission(mPackageURI, Intent.FLAG_GRANT_READ_URI_PERMISSION); } } if (ret == PackageManager.INSTALL_SUCCEEDED) { int loc = pkgLite.recommendedInstallLocation; if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_LOCATION) { ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION; } else if (loc == PackageHelper.RECOMMEND_FAILED_ALREADY_EXISTS) { ret = PackageManager.INSTALL_FAILED_ALREADY_EXISTS; } else if (loc == PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) { ret = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; } else if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_APK) { ret = PackageManager.INSTALL_FAILED_INVALID_APK; } else if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_URI) { ret = PackageManager.INSTALL_FAILED_INVALID_URI; } else if (loc == PackageHelper.RECOMMEND_MEDIA_UNAVAILABLE) { ret = PackageManager.INSTALL_FAILED_MEDIA_UNAVAILABLE; } else { loc = installLocationPolicy(pkgLite, flags); if (loc == PackageHelper.RECOMMEND_FAILED_VERSION_DOWNGRADE) { ret = PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE; } else if (!onSd && !onInt) { if (loc == PackageHelper.RECOMMEND_INSTALL_EXTERNAL) { // Set the flag to install on external media. flags |= PackageManager.INSTALL_EXTERNAL; flags &= ~PackageManager.INSTALL_INTERNAL; } else { flags |= PackageManager.INSTALL_INTERNAL; flags &= ~PackageManager.INSTALL_EXTERNAL; } } } }
handleStartCopy这个函数比较长,我们先来看前一部分,这里首先从installFlags中检查是否有指定安装位置,然后从DeviceStorageMonitorService中取得系统设置的内存空间的最低阀值。然后调用ContainerService去计算APK文件的大小,并检查当前内存空间是否足够,如果不足够,则调用Installd的freeCache方法尝试去释放一些cache文件。最终再来检查是否有足够的空间,并决定安装位置。接着来看handleStartCopy的实现:
final InstallArgs args = createInstallArgs(this); mArgs = args; if (ret == PackageManager.INSTALL_SUCCEEDED) { int userIdentifier = getUser().getIdentifier(); if (userIdentifier == UserHandle.USER_ALL && ((flags & PackageManager.INSTALL_FROM_ADB) != 0)) { userIdentifier = UserHandle.USER_OWNER; } final int requiredUid = mRequiredVerifierPackage == null ? -1 : getPackageUid(mRequiredVerifierPackage, userIdentifier); if (requiredUid != -1 && isVerificationEnabled(flags)) { } else { ret = args.copyApk(mContainerService, true); } } mRet = ret; }
这里首先根据安装路径的不同,创建不同的InstallArgs:
private InstallArgs createInstallArgs(InstallParams params) { if (installOnSd(params.flags) || params.isForwardLocked()) { return new AsecInstallArgs(params); } else { return new FileInstallArgs(params); } }
AsecInstallArgs就是指安装在外部存储空间上;FileInstallArgs是指安装在内部存储空间。来看一下上面的两者的类图关系:
安装在内部存储空间
int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException { if (temp) { createCopyFile(); } File codeFile = new File(codeFileName); if (!created) { } ParcelFileDescriptor out = null; try { out = ParcelFileDescriptor.open(codeFile, ParcelFileDescriptor.MODE_READ_WRITE); } catch (FileNotFoundException e) { Slog.e(TAG, "Failed to create file descriptor for : " + codeFileName); return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; } int ret = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; try { mContext.grantUriPermission(DEFAULT_CONTAINER_PACKAGE, packageURI, Intent.FLAG_GRANT_READ_URI_PERMISSION); ret = imcs.copyResource(packageURI, null, out); } finally { IoUtils.closeQuietly(out); mContext.revokeUriPermission(packageURI, Intent.FLAG_GRANT_READ_URI_PERMISSION); } if (isFwdLocked()) { } final File nativeLibraryFile = new File(getNativeLibraryPath()); if (nativeLibraryFile.exists()) { NativeLibraryHelper.removeNativeBinariesFromDirLI(nativeLibraryFile); nativeLibraryFile.delete(); } try { int copyRet = copyNativeLibrariesForInternalApp(codeFile, nativeLibraryFile); if (copyRet != PackageManager.INSTALL_SUCCEEDED) { return copyRet; } } catch (IOException e) { ret = PackageManager.INSTALL_FAILED_INTERNAL_ERROR; } return ret; }
因为前面传进来的第二个参数是true,所以这里这里首先调用createCopyFile创建一个临时文件:
void createCopyFile() { installDir = isFwdLocked() ? mDrmAppPrivateInstallDir : mAppInstallDir; codeFileName = createTempPackageFile(installDir).getPath(); resourceFileName = getResourcePathFromCodePath(); libraryPath = getLibraryPathFromCodePath(); created = true; }
installDir就是/data/app,createTempPackageFile在/data/app目录下面创建一个以"vmdl"开始,并以".tmp"结尾的临时文件。resourceFileName就是codeFileName所在的路径名。libraryPath在/data/app-lib路径下面以"vmdlxxxx"命名的文件。回到copyApk函数,接着调用ContainerService的copyResource方法将原始packageURI所指向的文件内容拷贝到我们创建的临时文件里面。最后将APK文件里面的lib文件全部拷贝到libraryPath所指定的文件目录下面。当数据拷贝完成,接着到InstallParams的startCopy方法中会调用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.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, true, res); } args.doPostInstall(res.returnCode, res.uid); }
在processPendingInstall里面,首先调用FileInstallArgs的doPreInstall方法,默认的它什么不会做。然后执行真正的安装过程installPackageLI:
private void installPackageLI(InstallArgs args, boolean newInstall, PackageInstalledInfo res) { int pFlags = args.flags; String installerPackageName = args.installerPackageName; File tmpPackageFile = new File(args.getCodePath()); boolean forwardLocked = ((pFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0); boolean onSd = ((pFlags & PackageManager.INSTALL_EXTERNAL) != 0); boolean replace = false; int scanMode = (onSd ? 0 : SCAN_MONITOR) | SCAN_FORCE_DEX | SCAN_UPDATE_SIGNATURE | (newInstall ? SCAN_NEW_INSTALL : 0); res.returnCode = PackageManager.INSTALL_SUCCEEDED; int parseFlags = mDefParseFlags | PackageParser.PARSE_CHATTY | (forwardLocked ? PackageParser.PARSE_FORWARD_LOCK : 0) | (onSd ? PackageParser.PARSE_ON_SDCARD : 0); PackageParser pp = new PackageParser(tmpPackageFile.getPath()); pp.setSeparateProcesses(mSeparateProcesses); final PackageParser.Package pkg = pp.parsePackage(tmpPackageFile, null, mMetrics, parseFlags); if (pkg == null) { res.returnCode = pp.getParseError(); return; } String pkgName = res.name = pkg.packageName; if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_TEST_ONLY) != 0) { if ((pFlags&PackageManager.INSTALL_ALLOW_TEST) == 0) { res.returnCode = PackageManager.INSTALL_FAILED_TEST_ONLY; return; } } if (GET_CERTIFICATES && !pp.collectCertificates(pkg, parseFlags)) { res.returnCode = pp.getParseError(); return; }
这里首先调用PackageParser的parsePackage方法解析AndroidManifest文件,构造一个Package对象,前面我们讲过Package对象了,大致内容如下:
pp = null; String oldCodePath = null; boolean systemApp = false; synchronized (mPackages) { if ((pFlags&PackageManager.INSTALL_REPLACE_EXISTING) != 0) { } PackageSetting ps = mSettings.mPackages.get(pkgName); if (ps != null) { } if (!args.doRename(res.returnCode, pkgName, oldCodePath)) { res.returnCode = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; return; } setApplicationInfoPaths(pkg, args.getCodePath(), args.getResourcePath()); pkg.applicationInfo.nativeLibraryDir = args.getNativeLibraryPath(); if (replace) { } else { installNewPackageLI(pkg, parseFlags, scanMode, args.user, installerPackageName, res); } synchronized (mPackages) { final PackageSetting ps = mSettings.mPackages.get(pkgName); if (ps != null) { res.newUsers = ps.queryInstalledUsers(sUserManager.getUserIds(), true); } } }
这里先不关注APK的更新,直接来看FileInstallArgs的doRename方法:
boolean doRename(int status, final String pkgName, String oldCodePath) { if (status != PackageManager.INSTALL_SUCCEEDED) { } else { final File oldCodeFile = new File(getCodePath()); final File oldResourceFile = new File(getResourcePath()); final File oldLibraryFile = new File(getNativeLibraryPath()); final String apkName = getNextCodePath(oldCodePath, pkgName, ".apk"); final File newCodeFile = new File(installDir, apkName + ".apk"); if (!oldCodeFile.renameTo(newCodeFile)) { return false; } codeFileName = newCodeFile.getPath(); final File newResFile = new File(getResourcePathFromCodePath()); if (isFwdLocked() && !oldResourceFile.renameTo(newResFile)) { return false; } resourceFileName = newResFile.getPath(); final File newLibraryFile = new File(getLibraryPathFromCodePath()); if (newLibraryFile.exists()) { NativeLibraryHelper.removeNativeBinariesFromDirLI(newLibraryFile); newLibraryFile.delete(); } if (!oldLibraryFile.renameTo(newLibraryFile)) { Slog.e(TAG, "Cannot rename native library directory " + oldLibraryFile.getPath() + " to " + newLibraryFile.getPath()); return false; } libraryPath = newLibraryFile.getPath(); if (!setPermissions()) { return false; } return true; } }
getNextCodePath返回一个类似"包名-num.apk"的文件名, 例如以新浪微博为例,apkName为"com.sina.weibo-1.apk"。然后将原来的codeFileName改为新的名字。resourceFileName和libraryPath也会做类似的改变。回到installPackageLI函数中,接着调用installNewPackageLI方法将新的package文件里面的资源加入到PMS的数据结构中,让PMS来管理这些activity、service、receiver:
private void installNewPackageLI(PackageParser.Package pkg, int parseFlags, int scanMode, UserHandle user, String installerPackageName, PackageInstalledInfo res) { String pkgName = pkg.packageName; boolean dataDirExists = getDataPathForPackage(pkg.packageName, 0).exists(); synchronized(mPackages) { if (mSettings.mRenamedPackages.containsKey(pkgName)) { res.returnCode = PackageManager.INSTALL_FAILED_ALREADY_EXISTS; return; } if (mPackages.containsKey(pkgName) || mAppDirs.containsKey(pkg.mPath)) { res.returnCode = PackageManager.INSTALL_FAILED_ALREADY_EXISTS; return; } } mLastScanError = PackageManager.INSTALL_SUCCEEDED; PackageParser.Package newPackage = scanPackageLI(pkg, parseFlags, scanMode, System.currentTimeMillis(), user); if (newPackage == null) { } else { updateSettingsLI(newPackage, installerPackageName, null, null, res); } }
在installNewPackageLI方法中首先调用scanPackageLI方法把新package的资源归入到PMS中,并创建一个PackageSettings对象,加入到Settings中的mPackages这个map中。这个函数我们在前面一章中启动PMS中大致介绍过了,这里主要来看一下与安装有关的部分:
private PackageParser.Package scanPackageLI(PackageParser.Package pkg, int parseFlags, int scanMode, long currentTime, UserHandle user) { File dataPath; if (mPlatformPackage == pkg) { } else { dataPath = getDataPathForPackage(pkg.packageName, 0); boolean uidError = false; if (dataPath.exists()) { } else { int ret = createDataDirsLI(pkgName, pkg.applicationInfo.uid, pkg.applicationInfo.seinfo); if (dataPath.exists()) { pkg.applicationInfo.dataDir = dataPath.getPath(); } else { Slog.w(TAG, "Unable to create data directory: " + dataPath); pkg.applicationInfo.dataDir = null; } } String path = scanFile.getPath(); if (pkg.applicationInfo.nativeLibraryDir != null) { try { File nativeLibraryDir = new File(pkg.applicationInfo.nativeLibraryDir); final String dataPathString = dataPath.getCanonicalPath(); if (isSystemApp(pkg) && !isUpdatedSystemApp(pkg)) { } else { if (!isForwardLocked(pkg) && !isExternal(pkg)) { if (nativeLibraryDir.getPath().startsWith(dataPathString)) { setInternalAppNativeLibraryPath(pkg, pkgSetting); nativeLibraryDir = new File(pkg.applicationInfo.nativeLibraryDir); } try { if (copyNativeLibrariesForInternalApp(scanFile, nativeLibraryDir) != PackageManager.INSTALL_SUCCEEDED) { Slog.e(TAG, "Unable to copy native libraries"); mLastScanError = PackageManager.INSTALL_FAILED_INTERNAL_ERROR; return null; } } catch (IOException e) { Slog.e(TAG, "Unable to copy native libraries", e); mLastScanError = PackageManager.INSTALL_FAILED_INTERNAL_ERROR; return null; } } if (DEBUG_INSTALL) Slog.i(TAG, "Linking native library dir for " + path); final int[] userIds = sUserManager.getUserIds(); synchronized (mInstallLock) { for (int userId : userIds) { if (mInstaller.linkNativeLibraryDirectory(pkg.packageName, pkg.applicationInfo.nativeLibraryDir, userId) < 0) { Slog.w(TAG, "Failed linking native library dir (user=" + userId + ")"); mLastScanError = PackageManager.INSTALL_FAILED_INTERNAL_ERROR; return null; } } } } } catch (IOException ioe) { Slog.e(TAG, "Unable to get canonical file " + ioe.toString()); } } pkg.mScanPath = path; if ((scanMode&SCAN_NO_DEX) == 0) { if (performDexOptLI(pkg, forceDex, (scanMode&SCAN_DEFER_DEX) != 0, false) == DEX_OPT_FAILED) { mLastScanError = PackageManager.INSTALL_FAILED_DEXOPT; return null; } }
首先取得Package的dataPath,路径为/data/data/包名,当我们第一次安装时,这个目录还没有创建,所以调用createDataDirsLI去创建data目录:
private int createDataDirsLI(String packageName, int uid, String seinfo) { int[] users = sUserManager.getUserIds(); int res = mInstaller.install(packageName, uid, uid, seinfo); if (res < 0) { return res; } for (int user : users) { if (user != 0) { } return res; }
static int do_install(char **arg, char reply[REPLY_MAX]){ return install(arg[0], atoi(arg[1]), atoi(arg[2]), arg[3]); /* pkgname, uid, gid, seinfo */}int install(const char *pkgname, uid_t uid, gid_t gid, const char *seinfo){ char pkgdir[PKG_PATH_MAX]; char libsymlink[PKG_PATH_MAX]; char applibdir[PKG_PATH_MAX]; struct stat libStat; if ((uid < AID_SYSTEM) || (gid < AID_SYSTEM)) { ALOGE("invalid uid/gid: %d %d\n", uid, gid); return -1; } if (create_pkg_path(pkgdir, pkgname, PKG_DIR_POSTFIX, 0)) { ALOGE("cannot create package path\n"); return -1; } if (create_pkg_path(libsymlink, pkgname, PKG_LIB_POSTFIX, 0)) { ALOGE("cannot create package lib symlink origin path\n"); return -1; } if (create_pkg_path_in_dir(applibdir, &android_app_lib_dir, pkgname, PKG_DIR_POSTFIX)) { ALOGE("cannot create package lib symlink dest path\n"); return -1; } if (mkdir(pkgdir, 0751) < 0) { ALOGE("cannot create dir '%s': %s\n", pkgdir, strerror(errno)); return -1; } if (chmod(pkgdir, 0751) < 0) { ALOGE("cannot chmod dir '%s': %s\n", pkgdir, strerror(errno)); unlink(pkgdir); return -1; } if (lstat(libsymlink, &libStat) < 0) { if (errno != ENOENT) { ALOGE("couldn't stat lib dir: %s\n", strerror(errno)); return -1; } } else { if (S_ISDIR(libStat.st_mode)) { if (delete_dir_contents(libsymlink, 1, 0) < 0) { ALOGE("couldn't delete lib directory during install for: %s", libsymlink); return -1; } } else if (S_ISLNK(libStat.st_mode)) { if (unlink(libsymlink) < 0) { ALOGE("couldn't unlink lib directory during install for: %s", libsymlink); return -1; } } } if (symlink(applibdir, libsymlink) < 0) { ALOGE("couldn't symlink directory '%s' -> '%s': %s\n", libsymlink, applibdir, strerror(errno)); unlink(pkgdir); return -1; } if (chown(pkgdir, uid, gid) < 0) { ALOGE("cannot chown dir '%s': %s\n", pkgdir, strerror(errno)); unlink(libsymlink); unlink(pkgdir); return -1; } return 0;}
这里首先构造几个目录名:pkgdir为/data/data/包名,libsymlink为/data/data/包名/lib,applibdir为/data/app-lib/包名。然后创建pkgdir的目录,并修改相应的权限。然后创建/data/data/包名/lib指向/data/app-lib/包名的符号链接。我们知道安装到data/app-lib下面的nativeLibrary的目录其实”包名-num“,所以我们需要重新建立符号链接,这也就是在scanPackageLI方法中mInstaller.linkNativeLibraryDirectory的作用:
static int do_linklib(char **arg, char reply[REPLY_MAX]){ return linklib(arg[0], arg[1], atoi(arg[2]));}int linklib(const char* pkgname, const char* asecLibDir, int userId){ char pkgdir[PKG_PATH_MAX]; char libsymlink[PKG_PATH_MAX]; struct stat s, libStat; int rc = 0; if (create_pkg_path(pkgdir, pkgname, PKG_DIR_POSTFIX, userId)) { ALOGE("cannot create package path\n"); return -1; } if (create_pkg_path(libsymlink, pkgname, PKG_LIB_POSTFIX, userId)) { ALOGE("cannot create package lib symlink origin path\n"); return -1; } if (stat(pkgdir, &s) < 0) return -1; if (chown(pkgdir, AID_INSTALL, AID_INSTALL) < 0) { ALOGE("failed to chown '%s': %s\n", pkgdir, strerror(errno)); return -1; } if (chmod(pkgdir, 0700) < 0) { ALOGE("linklib() 1: failed to chmod '%s': %s\n", pkgdir, strerror(errno)); rc = -1; goto out; } if (lstat(libsymlink, &libStat) < 0) { if (errno != ENOENT) { ALOGE("couldn't stat lib dir: %s\n", strerror(errno)); rc = -1; goto out; } } else { if (S_ISDIR(libStat.st_mode)) { if (delete_dir_contents(libsymlink, 1, 0) < 0) { rc = -1; goto out; } } else if (S_ISLNK(libStat.st_mode)) { if (unlink(libsymlink) < 0) { ALOGE("couldn't unlink lib dir: %s\n", strerror(errno)); rc = -1; goto out; } } } if (symlink(asecLibDir, libsymlink) < 0) { ALOGE("couldn't symlink directory '%s' -> '%s': %s\n", libsymlink, asecLibDir, strerror(errno)); rc = -errno; goto out; }
这里的第一个参数是packageName;第二个参数是nativeLibraryDir,也就是/data/app-lib/包名-num;第三个参数userID为0。在linklib中,与install一样,首先创建几个目录路径:pkgdir为/data/data/包名,libsymlink为/data/data/包名/lib。然后先把/data/data/包名/lib这个符号链接删除,然后创建一个新的符号链接/data/data/包名/lib指向/data/app-lib/包名-num。回到scanPackageLI函数中,最后调用performDexOptLI对APK安装包中的dex文件做优化,当然这里会依据当前是采用DVM还是ART的运行环境,分别用dexopt和dex2oat两种不同的命令对classes.dex文件做优化并存储在/data/dalvik-cache下面的文件里,关于dex文件优化的部分,可以参考一下Android虚拟机相关知识。接着回到installNewPackageLI函数中调用updateSettingsLI去更新Settings中的设置:
private void updateSettingsLI(PackageParser.Package newPackage, String installerPackageName, int[] allUsers, boolean[] perUserInstalled, PackageInstalledInfo res) { String pkgName = newPackage.packageName; synchronized (mPackages) { mSettings.setInstallStatus(pkgName, PackageSettingBase.PKG_INSTALL_INCOMPLETE); mSettings.writeLPr(); } if ((res.returnCode = moveDexFilesLI(newPackage)) != PackageManager.INSTALL_SUCCEEDED) { // Discontinue if moving dex files failed. return; } if (DEBUG_INSTALL) Slog.d(TAG, "New package installed in " + newPackage.mPath); synchronized (mPackages) { updatePermissionsLPw(newPackage.packageName, newPackage, UPDATE_PERMISSIONS_REPLACE_PKG | (newPackage.permissions.size() > 0 ? UPDATE_PERMISSIONS_ALL : 0)); if (isSystemApp(newPackage)) { } res.name = pkgName; res.uid = newPackage.applicationInfo.uid; res.pkg = newPackage; mSettings.setInstallStatus(pkgName, PackageSettingBase.PKG_INSTALL_COMPLETE); mSettings.setInstallerPackageName(pkgName, installerPackageName); res.returnCode = PackageManager.INSTALL_SUCCEEDED; //to update install status mSettings.writeLPr(); } }
这里首先调用Settings的writeLPr方法更新packages.xml文件,将新安装的package信息写到这个xml文件。moveDexFilesLI这里用于处理系统更新后安装目录有变化的情况,需要将原来的优化后的dex文件做重命名。updatePermissionsLPw我们在前面一章也介绍过,它用于给当前安装的APK分配权限,并把相应的gid号保存在PackageSetting或者SharedUserSetting的gids数组中。到这里installPackageLI就介绍完了,回到processPendingInstall方法中:
final boolean update = res.removedInfo.removedPackage != null; boolean doRestore = (!update && res.pkg != null && res.pkg.applicationInfo.backupAgentName != null); int token; if (mNextInstallToken < 0) mNextInstallToken = 1; token = mNextInstallToken++; PostInstallData data = new PostInstallData(args, res); mRunningInstalls.put(token, data); if (res.returnCode == PackageManager.INSTALL_SUCCEEDED && doRestore) { //与BackAgent(云备份)相关 } if (!doRestore) { Message msg = mHandler.obtainMessage(POST_INSTALL, token, 0); mHandler.sendMessage(msg); } } }); }
首先构造一个PostInstallData数据结构并添加到正在安装的列表mRunningInstalls中,这里跳过与BackAgent相关的部分。最后往PackageHandler发送一个POST_INSTALL消息,来看PackageHandler如何处理POST_INSTALL消息:
case POST_INSTALL: { if (DEBUG_INSTALL) Log.v(TAG, "Handling post-install for " + msg.arg1); PostInstallData data = mRunningInstalls.get(msg.arg1); mRunningInstalls.delete(msg.arg1); boolean deleteOld = false; if (data != null) { InstallArgs args = data.args; PackageInstalledInfo res = data.res; if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) { res.removedInfo.sendBroadcast(false, true, false); Bundle extras = new Bundle(1); extras.putInt(Intent.EXTRA_UID, res.uid); int[] firstUsers; int[] updateUsers = new int[0]; if (res.origUsers == null || res.origUsers.length == 0) { firstUsers = res.newUsers; } else { } sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, res.pkg.applicationInfo.packageName, extras, null, null, firstUsers); final boolean update = res.removedInfo.removedPackage != null; if (update) { extras.putBoolean(Intent.EXTRA_REPLACING, true); } sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, res.pkg.applicationInfo.packageName, extras, null, null, updateUsers); if (update) { } if (res.removedInfo.args != null) { deleteOld = true; } } Runtime.getRuntime().gc(); if (deleteOld) { synchronized (mInstallLock) { res.removedInfo.args.doPostDeleteLI(true); } } if (args.observer != null) { try { args.observer.packageInstalled(res.name, res.returnCode); } catch (RemoteException e) { Slog.i(TAG, "Observer no longer exists."); } } } else { Slog.e(TAG, "Bogus post-install token " + msg.arg1); } } break;
在POST_INSTALL消息中,主要就是发送一些安装成功的广播,这些广播可以被launch接收,并在桌面上面增加图标等。然后向我们注册的observer回调安装成功的callback。这样在内部存储空间上APK安装过程就介绍完了。我们来看一下大致的的流程图:
安装在外部存储空间
/data 存放的是用户的软件信息(非自带rom安装的软件);/data/app 存放用户安装的软件;/data/data 存放所有软件(包括/system/app 和 /data/app 和 /mnt/asec中装的软件)的一些lib和xml文件等数据信息;/data/dalvik-cache 缓存的dex文件,这里的文件都是可以删除的。
/sdcard 目录,这是一个软链接(相当于windows的文件夹的快捷方式),链接到/mnt/sdcard 目录,即这个目录的内容就是sdcard的内容。
在Android 2.2之后的版本允许将应用程序安装于SD卡,每一个安装在SD卡的应用程序,都可以在SD卡中的/sdcard/.android_secure 目录里找到名称中有出现它的程序名,和副文件名为asec的经过特殊加密处理后的档案。当SD卡挂载于手机时,/mnt/sdcard/.android_secure 目录会被映射到/mnt/asec 目录和 /mnt/secure 目录。其中/mnt/asec 目录中主要是程序的安装目录,包括其执行文件和lib文件等;而/mnt/secure 目录中就存放程序加密后的档案。也就是说,在/mnt路径下看到的/mnt/asec目录和/mnt/secure目录并不是真正存在在手机内存或者sd卡的分区挂载目录,它们只是/mnt/sdcard/.android_secure目录的一个影像而已。
因此,用户程序安装到到sd卡上后,其内容可能分散到:/mnt/asec , /mnt/secure , /data/data 。
int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException { if (temp) { createCopyFile(); } else { /* * Pre-emptively destroy the container since it's destroyed if * copying fails due to it existing anyway. */ PackageHelper.destroySdDir(cid); } final String newCachePath; try { mContext.grantUriPermission(DEFAULT_CONTAINER_PACKAGE, packageURI, Intent.FLAG_GRANT_READ_URI_PERMISSION); newCachePath = imcs.copyResourceToContainer(packageURI, cid, getEncryptKey(), RES_FILE_NAME, PUBLIC_RES_FILE_NAME, isExternal(), isFwdLocked()); } finally { mContext.revokeUriPermission(packageURI, Intent.FLAG_GRANT_READ_URI_PERMISSION); } if (newCachePath != null) { setCachePath(newCachePath); return PackageManager.INSTALL_SUCCEEDED; } else { return PackageManager.INSTALL_FAILED_CONTAINER_ERROR; } }
首先调用createCopyFile顺序的在SD卡上面创建目录结构,这些目录用于保存待安装的APK文件以及它们的library等资源:
void createCopyFile() { cid = getTempContainerId(); } static String getTempContainerId() { int tmpIdx = 1; String list[] = PackageHelper.getSecureContainerList(); if (list != null) { for (final String name : list) { // Ignore null and non-temporary container entries if (name == null || !name.startsWith(mTempContainerPrefix)) { continue; } String subStr = name.substring(mTempContainerPrefix.length()); try { int cid = Integer.parseInt(subStr); if (cid >= tmpIdx) { tmpIdx = cid + 1; } } catch (NumberFormatException e) { } } } return mTempContainerPrefix + tmpIdx; }
getTempContainerId调用MountService的方法去获取/mnt/asec下面的所有子目录,这些子目录都是以"smdl2tmp"开始,结尾是一个数字来命名。getTempContainerId找到一个尚未被使用的目录名赋予给cid。在copyApk方法中调用ContainService的copyResourceToContainer方法完成真正的创建目录以及拷贝文件的操作,这部分有兴趣的读者可以看看这个方法的实现。
- Android PackageManagerService分析二:安装APK
- Android PackageManagerService分析二:安装APK
- Android PackageManagerService分析二:安装APK
- Android apk安装管理(PackageManagerService 分析)
- Android -- PackageManagerService APK安装流程简要分析
- Android -- PackageManagerService APK安装流程简要分析
- Android PackageManagerService分析三:卸载APK
- PackageManagerService安装APK流程
- PackageManagerService分析之二
- PackageManagerService分析(二)
- Android7.0 PackageManagerService APK安装
- Android PackageManagerService加载apk流程
- Android PackageManagerService详细分析
- Android PackageManagerService详细分析
- Android PackageManagerService详细分析
- Android PackageManagerService详细分析
- Android PackageManagerService详细分析 .
- Android PackageManagerService详细分析
- 110. Balanced Binary Tree
- Python元编程-遗忘的远古凶兽
- quick-cocos2dx scheduler.scheduleGlobal坑
- POJ 3074 Sudoku(数独|Dancing Links精确覆盖)
- 深入安卓Package Manager和Package Installer
- Android PackageManagerService分析二:安装APK
- how to calculate the QR decomposition of a matrix
- POJ-3225 Help with Intervals-线段树/成段更新+倍增区间法
- 102. Binary Tree Level Order Traversal
- Codeforces 624A Save Luke
- 五分钟搞懂后缀数组!后缀数组解析以及应用(附详解代码)
- AIM Tech Round (Div. 2) C. Graph and String(二分图染色 | 贪心)
- 编程新手我觉得需要掌握的五个重要东西
- hdoj 1385 Minimum Transport Cost(floyd 记录最短路径)