转自:http://blog.csdn.net/lilian0118/article/details/25792601
这一章我们开始分析APK的安装过程,当我们从网上download一个APK后,点击这个APK文件,就会启动PackageInstallerActivity这个页面来parse这个APK文件,并提示一些信息给用户,当用户点击安装以后,就会开始APK的安装。在介绍安装APK之前,我们先来分析一下installd。
installd介绍
首先从installd的启动开始介绍,installd是在init.rc脚本中启动的:
- service installd /system/bin/installd
- class main
- socket installd stream 600 system system
上面的脚本文件会创建一个/dev/socket/installd的unix domain socket,并把socket文件描述符返回给installd。我们简单的来看看installd的main函数:
- 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();
- 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;
- }
- 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过程分析
当我们点击一个APK文件后,就会发送一个Intent给PackageInstallerActivity,PackageInstallerActivity首先会去检查系统是否支持安装未知来源的APK。然后会去解析这个APK的一些信息显示出来给用户,例如是否需要读取通讯录、是否要访问网络等等。当用户点击这个activity的安装按钮后,就会开始真正的执行安装,它对应的函数如下:- pm.installPackageWithVerificationAndEncryption(mPackageURI, observer, installFlags,
- installerPackageName, verificationParams, null);
- 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);
- }
再来看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是指安装在内部存储空间。来看一下上面的两者的类图关系:
接下来以分安装在内部存储空间和外部存储空间两种情况来分析:
安装在内部存储空间
首先来看FileInstallArgs的copyApk方法:- 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;
- }
- void handleReturnCode() {
- if (mArgs != null) {
- processPendingInstall(mArgs, mRet);
- }
- }
- ate 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对象了,大致内容如下:
然后手机APK文件中的数字签名信息。接着来看installPackageLI方法:
- 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;
- }
- 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;
- }
- 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安装过程就介绍完了。我们来看一下大致的的流程图:
从前面的介绍,我们可以把安装APK的过程简化为以下几步: 1.复制文件到code、library和resource等文件 2.解析待安装的APK的manifest文件,并把activity、service、provider等信息更新到PMS的全局数据结构中 3.更新Settings中的PackageSetting等信息 4.调用installd在/data/data和/data/dalvik-cache中新建package的文件目录,并link相应的文件
安装在外部存储空间
在介绍AsecInstallArgs的copyApk之前,我们先来简单的了解一下Android上面的目录结构:/system 存放的是rom的信息;/system/app 存放rom本身附带的软件即系统软件;/system/data 存放/system/app 中核心系统软件的数据文件信息。/data 存放的是用户的软件信息(非自带rom安装的软件);/data/app 存放用户安装的软件;/data/data 存放所有软件(包括/system/app 和 /data/app 和 /mnt/asec中装的软件)的一些lib和xml文件等数据信息;/data/dalvik-cache 缓存的dex文件,这里的文件都是可以删除的。 /mnt 目录,熟悉linux的人都清楚,linux默认挂载外部设备都会挂到这个目录下面去,如将sd卡挂载上去后,会生成一个/mnt/sdcard 目录。
/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 。
我们来看AsecInstallArgs的copyApk方法,通过前面的介绍,copyApk就是将原来的安装文件以及library复制到SD卡中的android_secure目录中,这里会借助ContainerService和MountService。从前面安装到内部存储空间的流程上我们知道,FileInstallArgs和AsecInstallArgs主要区别就是在copyApk和doRename两个方法上面:
- 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();
- }
- ic 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方法完成真正的创建目录以及拷贝文件的操作,这部分有兴趣的读者可以看看这个方法的实现。