Android Framework 包管理子系统(03)应用安装

123 篇文章 94 订阅

该系列文章总纲链接:专题分纲目录 Android Framework 包管理子系统


本章关键点总结 & 说明:

导图是不断迭代的,这里主要关注➕ PkgMS安装应用部分。主要是三个步骤:从执行adb install 到复制文件,再到装载文件,最后到安装成功。

PackagerInstallerService特殊说明:PackagerInstallerService(PkgMIS)是android5.0中新加入的服务,一般通过PackagerInstallerService来分配一个SessinId,这个系统唯一的ID代表一次安装过程,如果一个应用的安装被中断了,比如设备重启,那么也可以通过这个ID来继续安装。Session的创建如下所示:

public int createSession(SessionParams params, String installerPackageName, int userId) {
    try {
        return createSessionInternal(params, installerPackageName, userId);
    } catch (IOException e) {
        throw ExceptionUtils.wrap(e);
    }
}

该方法会返回个系统唯一值作为SessionID,如果希望再次使用Session,可以通过接口openSession打开,代码实现如下:

public IPackageInstallerSession openSession(int sessionId) {
    try {
        return openSessionInternal(sessionId);
    } catch (IOException e) {
        throw ExceptionUtils.wrap(e);
    }
}

这里将返回一个IPackageInstallerSession(PackageInstallerSession的IBinder对象),在PackagerManagerService中用变量mSession来存所有的PackageInstallerSession对象。这些PackageInstallerSession对象保存了应用安装的相关数据,比如安装路径、安装进度等。。。

1 安装应用程序(从 adb install 到PkgMS)

一般我们会使用adb install 来安装应用,因此这里就从adb install开始分析,adb命令,install参数。直接看adb命令 处理install参数的代码,如下所示:

int adb_commandline(int argc, char **argv)
{
    char buf[4096];
    //...
    if (!strcmp(argv[0], "install")) {
        if (argc < 2) return usage();
        return install_app(ttype, serial, argc, argv);
    }
	//...
    if (!strcmp(argv[0], "uninstall")) {
        if (argc < 2) return usage();
        return uninstall_app(ttype, serial, argc, argv);
    }
	//...
    usage();
    return 1;
}

这里继续分析install_app实现,代码如下:

int install_app(transport_type transport, char* serial, int argc, char** argv)
{
    //这里需要设置复制目标的目录,如果安装在内部存储中,则目标目录为/data/local/tmp。
    static const char *const DATA_DEST = "/data/local/tmp/%s";
    //如果安装在SD卡上,则目标目录为/sdcard/tmp。
    static const char *const SD_DEST = "/sdcard/tmp/%s";
    //...
    for (i = 1; i < argc; i++) {
        if (!strcmp(argv[i], "-s")) {
            where = SD_DEST; //-s参数指明该APK安装到SD卡上
        }
    }
    //...
    char* apk_file = argv[last_apk];
    char apk_dest[PATH_MAX];
    snprintf(apk_dest, sizeof apk_dest, where, get_basename(apk_file));
    //将此APK传送到手机的目标路径
    int err = do_sync_push(apk_file, apk_dest, 0 /* no show progress */);
    if (err) {
        goto cleanup_apk;
    } else {
        argv[last_apk] = apk_dest; /* destination name, not source location */
    }
    //执行pm命令
    pm_command(transport, serial, argc, argv);

cleanup_apk:
    //手机中执行shell rm 命令,删除刚才传送过去的目标APK文件
    delete_file(transport, serial, apk_dest);
    return err;
}

这里关注pm_command方法,代码如下:

static int pm_command(transport_type transport, char* serial,
                      int argc, char** argv)
{
    char buf[4096];

    snprintf(buf, sizeof(buf), "shell:pm");

    while(argc-- > 0) {
        char *quoted = escape_arg(*argv++);
        strncat(buf, " ", sizeof(buf) - 1);
        strncat(buf, quoted, sizeof(buf) - 1);
        free(quoted);
    }

    send_shellcommand(transport, serial, buf);
    return 0;
}

这里调用了send_shellcommand 发送"shell:pm install 参数"给手机端的adbd,这里继续分析pm命令,pm是脚本文件,构成如下:

# Script to start "pm" on the device,which has a very rudimentary
# shell.
#

base=/system
export CLASSPATH=$base/frameworks/pm.jar
exec app_process $base/bincom.android.commands.pm.Pm "$@"

在编译system镜像时,Android.mk中会将该脚本复制到system/bin目录下。从pm脚本的内容来看,它就是通过app_process执行pm.jar包的main函数。pm的main函数实现如下:

public static void main(String[] args) {
    int exitCode = 1;
    exitCode = new Pm().run(args);
    //...
    System.exit(exitCode);
}

这里的run方法实现如下:

public int run(String[] args) throws IOException, RemoteException {
    //...
    //获取UserM 和 PkgMS的binder客户端
    mUm = IUserManager.Stub.asInterface(ServiceManager.getService("user"));
    mPm = IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
    //...
    mInstaller = mPm.getPackageInstaller();
    //...
    //处理其他命令,这里仅考虑install的处理
    if ("install".equals(op)) {
        return runInstall();
    }
    //...
}

这里仅仅关注install部分相关代码,runInstall代码实现如下:

private int runInstall() {
    int installFlags = 0;
    //...
    while ((opt=nextOption()) != null) {
        if (opt.equals("-l")) {
            installFlags |= PackageManager.INSTALL_FORWARD_LOCK;
        } else if (opt.equals("-r")) {
            installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
        //...各种参数解析
        } else {
            System.err.println("Error: Unknown option: " + opt);
            return 1;
        }
    }
    //...
    //获取Verification Package的文件位置
    final String verificationFilePath = nextArg();
    if (verificationFilePath != null) {
        System.err.println("\tver: " + verificationFilePath);
        verificationURI = Uri.fromFile(new File(verificationFilePath));
    } else {
        verificationURI = null;
    }
    //创建PackageInstallObserver,用于接收PkgMS的安装结果
    LocalPackageInstallObserver obs = new LocalPackageInstallObserver();
    try {
        VerificationParams verificationParams = new VerificationParams(verificationURI,
                originatingURI, referrerURI, VerificationParams.NO_UID, null);
        //这里调用PkgMS来安装应用程序
        mPm.installPackageAsUser(apkFilePath, obs.getBinder(), installFlags,
                installerPackageName, verificationParams, abi, userId);

        synchronized (obs) {
            while (!obs.finished) {
                try {
                    obs.wait();//等待安装结果
                } catch (InterruptedException e) {
                    //...
                }
            }
            //...
        }
    } catch (RemoteException e) {
        //...
    }
}

这里调用了mPm.installPackageAsUser方法来处理“安装”,从 adb install 到PkgMS到此分析结束,接下来从这里开始便正式进入PkgMS来安装应用程序。

同时这里介绍另一种调用方式,给指定用户安装应用,安装应用程序也可以通过调用installPackage来实现,代码如下:

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方法。因此后面的安装应用程序步骤会从installPackageAsUser开始分析。

2 安装应用程序(复制文件)

2.1 流程分析

从installPackageAsUser开始分析 安装应用程序的步骤,代码如下所示:

public void installPackageAsUser(String originPath, IPackageInstallObserver2 observer,
        int installFlags, String installerPackageName, VerificationParams verificationParams,
        String packageAbiOverride, int userId) {
    //检查调用进程的权限
    mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, null);
    //检查调用进程的用户是否有安装权限
    final int callingUid = Binder.getCallingUid();
    enforceCrossUserPermission(callingUid, userId, true, true, "installPackageAsUser");
    //检查用户是否被限制 安装应用
    if (isUserRestricted(userId, UserManager.DISALLOW_INSTALL_APPS)) {
        try {
            if (observer != null) {
                //用户被限制,通知给监听者
                observer.onPackageInstalled("", INSTALL_FAILED_USER_RESTRICTED, null, null);
            }
        } catch (RemoteException re) {
        }
        return;
    }

    if ((callingUid == Process.SHELL_UID) || (callingUid == Process.ROOT_UID)) {
        installFlags |= PackageManager.INSTALL_FROM_ADB;
    } else {
        installFlags &= ~PackageManager.INSTALL_FROM_ADB;
        installFlags &= ~PackageManager.INSTALL_ALL_USERS;
    }

    UserHandle user;
    //如果有INSTALL_ALL_USERS标志,则给所有用户安装
    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);
    //保存参数到InstallParams对象
    final Message msg = mHandler.obtainMessage(INIT_COPY);
    msg.obj = new InstallParams(origin, observer, installFlags,
            installerPackageName, verificationParams, user, packageAbiOverride);
    mHandler.sendMessage(msg);//发送消息
}

这里最后把调用的参数Installarams对象中,然后发送INIT_COPY消息。mHandler被绑定到另外一个工作线程(借助ThreadHandler对象的Looper)中,所以该INIT_COPY消息也将在那个工作线程中进行处理。接下来看handleMessage如何处理消息,代码如下:

public void handleMessage(Message msg) {
    try {
        doHandleMessage(msg);
    } finally {
        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
    }
}

继续分析doHandleMessage,这里只关注INIT_COPY相关处理,代码如下:

              case INIT_COPY: {
                    HandlerParams params = (HandlerParams) msg.obj;
                    int idx = mPendingInstalls.size();
                    if (!mBound) {
                        //绑定defaultContainerService服务
                        if (!connectToService()) {
                            //错误处理
                        } else {
                            //连接成功,把安装信息保存到mPendingInstalls
                            mPendingInstalls.add(idx, params);
                        }
                    } else {
                        //插入安装信息
                        mPendingInstalls.add(idx, params);
                        //如果安装请求队列之前的状态为空,则表明要启动安装
                        if (idx == 0) {
                            mHandler.sendEmptyMessage(MCS_BOUND);
                        }
                    }
                    break;
                }

这里有一个前提,成功启动了DefaultContainerService(简称DCS)且idx为零,所以这是PkgMS首次处理安装请求,即将要处理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) {
                                        //安装请求都处理完了,则需要和Service断绝联系,
                                        //通过发送MSC_UNB消息处理断交请求
                                        removeMessages(MCS_UNBIND);
                                        Message ubmsg = obtainMessage(MCS_UNBIND);
                                        sendMessageDelayed(ubmsg, 10000);
                                    }
                                } else {
                                    //还有未处理的请求,则继续发送MCS_BOUND
                                    mHandler.sendEmptyMessage(MCS_BOUND);
                                }
                            }
                        }
                    }
                    break;
                }

这里先分析MCS_UNBIND的处理流程,代码如下所示:

                case MCS_UNBIND: {
                    if (mPendingInstalls.size() == 0 && mPendingVerification.size() == 0) {
                        if (mBound) {
                            disconnectService();
                        }
                    } else if (mPendingInstalls.size() > 0) {
                        mHandler.sendEmptyMessage(MCS_BOUND);
                    }
                    break;
                }

可以看到,如果没有请求则与服务断开联系,只要还有请求,就会继续发送MCS_BOUND消息。接下来继续分析startCopy,代码实现如下:

final boolean startCopy() {
    boolean res;
    try {
        if (++mRetries > MAX_RETRIES) {
            mHandler.sendEmptyMessage(MCS_GIVE_UP);
            handleServiceError();
            return false;
        } else {
            handleStartCopy();//调用派生类InstallParams的handleStartCopy函数
            res = true;
        }
    } catch (RemoteException e) {
        mHandler.sendEmptyMessage(MCS_RECONNECT);
        res = false;
    }
    //调用派生类InstallParams的handleReturnCode,返回处理结果
    handleReturnCode();
    return res;
}

这里继续分析关键方法InstallParams类的handleStartCopy,代码如下:

public void handleStartCopy() throws RemoteException {
    //...
    if (onInt && onSd) {
        // Check if both bits are set
        ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
    } else {
        pkgLite = mContainerService.getMinimalPackageInfo(origin.resolvedPath, installFlags,
                packageAbiOverride);
        if (!origin.staged && pkgLite.recommendedInstallLocation
                == PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) {
            //如果安装空间不够,释放cache空间
            final StorageManager storage = StorageManager.from(mContext);
            final long lowThreshold = storage.getStorageLowBytes(
                    Environment.getDataDirectory());

            final long sizeBytes = mContainerService.calculateInstalledSize(
                    origin.resolvedPath, isForwardLocked(), packageAbiOverride);

            if (mInstaller.freeCache(sizeBytes + lowThreshold) >= 0) {
                pkgLite = mContainerService.getMinimalPackageInfo(origin.resolvedPath,
                        installFlags, packageAbiOverride);
            }
            if (pkgLite.recommendedInstallLocation
                    == PackageHelper.RECOMMEND_FAILED_INVALID_URI) {
                pkgLite.recommendedInstallLocation
                    = PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE;
            }
        }
    }
    //...
    //创建InstallArgs对象,如果应用要求安装在SD卡或forward lock的应用
    //则创建AsecInstallArgs,否则创建FileInstallArgs
    final InstallArgs args = createInstallArgs(this);
    mArgs = args;

    if (ret == PackageManager.INSTALL_SUCCEEDED) {
        //...
        final int requiredUid = mRequiredVerifierPackage == null ? -1
                : getPackageUid(mRequiredVerifierPackage, userIdentifier);
        if (!origin.existing && requiredUid != -1
                && isVerificationEnabled(userIdentifier, installFlags)) {
            //...校验相关
        } else {//无需校验则直接处理
            ret = args.copyApk(mContainerService, true);
        }
    }
    mRet = ret;
}

这里关注无需校验环节的copyApk(依赖于args的类,这里只从FileInstallArgs类开始分析)方法实现,代码如下:

int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException {
    if (origin.staged) {
        codeFile = origin.file;
        resourceFile = origin.file;
        return PackageManager.INSTALL_SUCCEEDED;
    }

    try {
        //在data/app下生成临时文件
        final File tempDir = mInstallerService.allocateInternalStageDirLegacy();
        codeFile = tempDir;
        resourceFile = tempDir;
    } catch (IOException e) {
        return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
    }

    final IParcelFileDescriptorFactory target = new IParcelFileDescriptorFactory.Stub() {
        //为临时文件创建文件描述符ParcelFileDescriptor,能够通过Binder传递
        @Override
        public ParcelFileDescriptor open(String name, int mode) throws RemoteException {
            if (!FileUtils.isValidExtFilename(name)) {
                throw new IllegalArgumentException("Invalid filename: " + name);
            }
            try {
                final File file = new File(codeFile, name);
                final FileDescriptor fd = Os.open(file.getAbsolutePath(),
                        O_RDWR | O_CREAT, 0644);
                Os.chmod(file.getAbsolutePath(), 0644);
                return new ParcelFileDescriptor(fd);
            } catch (ErrnoException e) {
                throw new RemoteException("Failed to open: " + e.getMessage());
            }
        }
    };

    int ret = PackageManager.INSTALL_SUCCEEDED;
    //复制文件
    ret = imcs.copyPackage(origin.file.getAbsolutePath(), target);
    if (ret != PackageManager.INSTALL_SUCCEEDED) {
        return ret;
    }

    //安装系统自带的native动态库
    final File libraryRoot = new File(codeFile, LIB_DIR_NAME);
    NativeLibraryHelper.Handle handle = null;
    try {
        handle = NativeLibraryHelper.Handle.create(codeFile);
        ret = NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libraryRoot,
                abiOverride);
    } catch (IOException e) {
        ret = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
    } finally {
        IoUtils.closeQuietly(handle);
    }
    return ret;
}

至此,安装的第一部份工作完成,应用安装到/data/app目录下。

2.2 关键类说明

HandlerParams和InstallArgs介绍,关系如下所示:

HandlerParams和InstallArgs均为抽象类。HandlerParams有三个子类:InstallParams、MoveParams和MeasureParams:

  1. InstallParams 处理APK的安装。
  2. MoveParams 处理某个已安装APK的搬家请求(例如从内部存储移动到SD卡上)。
  3. MeasureParams 查询某个已安装的APK占据存储空间的大小(例如在设置程序中得到的某个APK使用的缓存文件的大小)。

对于InstallParams来说,它还有两个伴儿,即InstallArgs的派生类FileInstallArgs和AsecInstallArgs。FileInstallArgs针对的是安装在内部存储的APK,而AsecInstallArgs针对的是那些安装在SD卡上的APK。(整个流程的分析,包括后面,都是针对FileInstallArgs来做,AsecInstallArgs分析方式类似)

3 应用安装第二阶段:装载应用

本阶段是将应用格式转换成oat格式,为应用创建数据目录,最后把应用的信息装载到PkgMS的数据结构中,在前面startCopy函数中最后调用了handleReturnCode方法,代码如下:

void handleReturnCode() {
    if (mArgs != null) {
        processPendingInstall(mArgs, mRet);
    }
}

继续分析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);//防止重复调用
            PackageInstalledInfo res = new PackageInstalledInfo();
            res.returnCode = currentStatus;
            res.uid = -1;
            res.pkg = null;
            res.removedInfo = new PackageRemovedInfo();
            //如果应用安装成功,则装载安装的应用
            if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
                //InstallArgs的doPreInstall函数
                args.doPreInstall(res.returnCode);
                synchronized (mInstallLock) {
                    //关键函数,进行APK安装
                    installPackageLI(args, res);
                }
                //FileInstallArgs的doPostInstall函数
                args.doPostInstall(res.returnCode, res.uid);
            }
            //执行备份相关代码(通过BackupManagerService来备份处理)...
            if (!doRestore) {
                //发送POST_INSTALL消息
                Message msg = mHandler.obtainMessage(POST_INSTALL, token, 0);
                mHandler.sendMessage(msg);
            }
        }
    });
}

该方法总结如下:

  1. 这里的InstallArgs的doPreInstall函数在本例中是FileInstallArgs的doPreInstall函数。
  2. 调用PkgMS的installPackageLI函数进行APK安装,接下来将深度分析该方法。
  3. 这里的InstallArgs的doPostInstall函数在本例中是FileInstallArgs的doPostInstall函数。
  4. 此时,该APK已经安装完成(不论失败还是成功),继续向mHandler抛送一个POST_INSTALL消息,该消息携带一个token,通过它可从mRunningInstalls数组中取得一个PostInstallData对象。后面也将继续分析handler对POST_INSTALL的处理流程。

接下来将针对{步骤2}的installPackageLI和{步骤4}的handler消息处理进行详细分析。

@1 installPackageLI实现

private void installPackageLI(InstallArgs args, PackageInstalledInfo res) {
    //...
    final int parseFlags = mDefParseFlags | PackageParser.PARSE_CHATTY
            | (forwardLocked ? PackageParser.PARSE_FORWARD_LOCK : 0)
            | (onSd ? PackageParser.PARSE_ON_SDCARD : 0);
    //创建apk文件的解析器
    PackageParser pp = new PackageParser();
    pp.setSeparateProcesses(mSeparateProcesses);
    pp.setDisplayMetrics(mMetrics);

    final PackageParser.Package pkg;
    try {
        //解析apk文件
        pkg = pp.parsePackage(tmpPackageFile, parseFlags);
    } catch (PackageParserException e) {
        res.setError("Failed parse during installPackageLI", e);
        return;
    }
    //收集应用的签名
    try {
        pp.collectCertificates(pkg, parseFlags);
        pp.collectManifestDigest(pkg);
    } catch (PackageParserException e) {
        res.setError("Failed collect during installPackageLI", e);
        return;
    }
    //...
    pp = null;
    String oldCodePath = null;
    boolean systemApp = false;
    synchronized (mPackages) {
        //升级判定及相关处理
        int N = pkg.permissions.size();
        //permission相关处理
    }

    //有系统标志的应用要求装在sd卡上,报错退出
    if (systemApp && onSd) {
        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);
        }
    }
}

installPackageLI首先解析了安装的应用文件,得到解析的结果后,主要是判断新安装应用是否和应用系统中已安装应用构成升级关系,是则调用replacePackageLI来处理,否则就用installNewPackageLI来处理。

@2 POST_INSTALL消息处理

相关代码如下:

case POST_INSTALL: {
    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);
            //...
            //发送广播ACTION_PACKAGE_ADDED
            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) {
                //如果是升级包,则发送更多的广播
                sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED,
                        res.pkg.applicationInfo.packageName,
                        extras, null, null, updateUsers);
                sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED,
                        null, null,
                        res.pkg.applicationInfo.packageName, null, updateUsers);

                //如果是forward lock的应用,或者安装在sd卡上的应用,发送广播
                if (isForwardLocked(res.pkg) || isExternal(res.pkg)) {
                    int[] uidArray = new int[] { res.pkg.applicationInfo.uid };
                    ArrayList<String> pkgList = new ArrayList<String>(1);
                    pkgList.add(res.pkg.applicationInfo.packageName);
                    sendResourcesChangedBroadcast(true, true,
                            pkgList,uidArray, null);
                }
            }
            if (res.removedInfo.args != null) {
                deleteOld = true;
            }
        }
        //...
        if (args.observer != null) {
            try {//通知调用者 安装的结果
                Bundle extras = extrasForInstallResult(res);
                args.observer.onPackageInstalled(res.name, res.returnCode,
                        res.returnMsg, extras);
            } catch (RemoteException e) {
                Slog.i(TAG, "Observer no longer exists.");
            }
        }
    }
} break;

整个消息处理主要是发送广播,应用安装成功后通知其他的应用开始处理,比如Launcher中需要添加的图标等。。。最后通过回调函数通知 安装者 安装的结果。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

图王大胜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值