赞
踩
在Android,通过发送Intent可以启动应用的安装过程,如下所示:
- Uri uri = Uri.fromFile(new File(filename));
- Intent inent = new Intent(Intent.ACTION_VIEW);
- intent.SetDataAndType(uri, application/vnd.android.package-archive);
- startActivity(intent);
在Android的系统应用PackageInstaller中有一个PackageInstallActivity会响应这个Intent。在这个Activity中有两个重要的成员变量mPm和mInstaller,分别是ApplicationPackageManger和PackageInstaller的实例对象,这两个对象也是PackageManagerService和PackageInstallerService在应用中的代理对象。
PackageInstallerAcivity中创建这两个对象的代码如下:
- protected void onCreate(Bundler icicle) {
- super.onCreate(icicle);
- mPm = getPackageManager();
- mInstaller = mPm.getPackageInstaller();
- ......
- }
PackageManagerInstallerService是Android5.0新加入的服务,主要用于管理安装会话(Installer session)。在Android5.0中,可以通过PackageManagerInstallerService来分配一个SessionId,这个系统唯一的ID代表一次安装过程,如果一个应用的安装分成几个阶段来完成,即使设备重启了,也可以通过这个ID来继续安装过程。
PackageManagerInstallerService中提供的接口createSession来创建一个Session:
- @Override
- 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方法来打开它,代码如下:
- @Override
- public IPackageInstallerSession openSession(int sessionId) {
- try {
- return openSessionInternal(sessionId);
- } catch (IOException e) {
- throw ExceptionUtils.wrap(e);
- }
- }
openSession返回一个IPackageInstallerSession对象,它是Binder服务PackageInstallerSession的IBinder对象。每个Install Session都会在SystemServer中有一个对应的PackageInstallerSession对象。在PackageInstallerService中mSessions数组保存了所有PackageInstallerSession对象,定义如下:
private final SparseArray<PackageInstallerSession> mSessions = new SparseArray<>();
当系统启动时,PackageManageService初始化时会创建PackageManagerInstallerService服务,在这个服务的初始化函数中,会读取/data/system目录下的install_sessions.xml文件,这个文件保存系统未完成的Install Session。然后PackagemanagerInstallerService会根据文件的内容创建PackageInstallerSession对象并插入mSessions中。
PackageInstallerSession中保存了应用安装相关的数据。例如,安装包路径,安装进度、中间数据保存的目录等。
应用可以调用PackageManager的installPackage方法来开始安装过程,最终会调用到PackageManagerService的installPackage或者installPackageAsUser来执行安装过程,整个安装过程比较复杂。
整个安装过程可以分成两个阶段:
1.第一阶段把需要安装的应用复制到/data/app目录下
2.第二阶段是对apk文件扫描优化,装载到内存中。
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方法:
- @Override
- 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 {
- // 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,//保存参数到InstallParamsm,发送消息
- installerPackageName, verificationParams, user, packageAbiOverride);
- mHandler.sendMessage(msg);
- }
installPackageAsUser先检查调用进程是否有安装应用的权限,再检查调用进程所属的用户是否有权限安装应用,最后检查指定的用户是否被限制安装应用。如果参数installFlags带有INSTALL_ALL_USERS,则该应用将给系统中所有用户安装,否则只给指定用户安装。
安装应用实践比较长,因此不可能在一个函数中完成。上面函数把数据保存在installParams然后发送了INIT_COPY消息。
下面我们再来看看消息处理:
- void doHandleMessage(Message msg) {
- switch (msg.what) {
- 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()) {//绑定DefaultContainerService
- Slog.e(TAG, "Failed to bind to media container service");
- params.serviceError();
- return;
- } else {//连接成功把安装信息保存到mPendingInstalls
- // 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;
- }
INIT_COPY消息的处理将绑定DefaultContainerService,因为这是一个异步的过程,要等待绑定的结果通过onServiceConnected返回,所以这里的安装参数放到了mPendingInstalls列表中。如果这个Service以前就绑定好了,现在就不需要再绑定,安装信息也会先放到mPendingInstalls。如果有多个安装请求同时到达,这里通过mPendingInstalls列表对他们进行排队。如果列表中只有一项,说明没有更多的安装请求,因此这种情况下回立即发出MCS_BOUND消息。而onServiceConnected方法同样是发出MCS_BOUND消息:
- class DefaultContainerConnection implements ServiceConnection {
- public void onServiceConnected(ComponentName name, IBinder service) {
- if (DEBUG_SD_INSTALL) Log.i(TAG, "onServiceConnected");
- IMediaContainerService imcs =
- IMediaContainerService.Stub.asInterface(service);
- mHandler.sendMessage(mHandler.obtainMessage(MCS_BOUND, imcs));
- }
-
- public void onServiceDisconnected(ComponentName name) {
- if (DEBUG_SD_INSTALL) Log.i(TAG, "onServiceDisconnected");
- }
- };
看下MCS_BOUND的消息处理
- case MCS_BOUND: {
- if (DEBUG_INSTALL) Slog.i(TAG, "mcs_bound");
- if (msg.obj != null) {
- mContainerService = (IMediaContainerService) msg.obj;
- }
- if (mContainerService == null) {//没有连接成功
- // Something seriously wrong. Bail out
- Slog.e(TAG, "Cannot bind to media container service");
- for (HandlerParams params : mPendingInstalls) {
- // Indicate service bind error
- params.serviceError();//通知出错了
- }
- mPendingInstalls.clear();
- } else if (mPendingInstalls.size() > 0) {
- HandlerParams params = mPendingInstalls.get(0);
- if (params != null) {
- if (params.startCopy()) {//执行安装
- // We are done... look for more work or to
- // go idle.
- if (DEBUG_SD_INSTALL) Log.i(TAG,
- "Checking for more work or unbind...");
- // Delete pending install
- if (mPendingInstalls.size() > 0) {
- mPendingInstalls.remove(0);//工作完成,删除第一项
- }
- if (mPendingInstalls.size() == 0) {//如果没有安装消息了,延时发送10秒MCS_UNBIND消息
- if (mBound) {
- if (DEBUG_SD_INSTALL) Log.i(TAG,
- "Posting delayed MCS_UNBIND");
- removeMessages(MCS_UNBIND);
- Message ubmsg = obtainMessage(MCS_UNBIND);
- // Unbind after a little delay, to avoid
- // continual thrashing.
- sendMessageDelayed(ubmsg, 10000);
- }
- } else {
- // There are more pending requests in queue.
- // Just post MCS_BOUND message to trigger processing
- // of next pending install.
- if (DEBUG_SD_INSTALL) Log.i(TAG,
- "Posting MCS_BOUND for next work");
- mHandler.sendEmptyMessage(MCS_BOUND);//还有消息继续发送MCS_BOUND消息
- }
- }
- }
- } else {
- // Should never happen ideally.
- Slog.w(TAG, "Empty queue");
- }
- break;
- }
如果结束了我们看看MCS_UNBIND消息的处理
- case MCS_UNBIND: {
- // If there is no actual work left, then time to unbind.
- if (DEBUG_INSTALL) Slog.i(TAG, "mcs_unbind");
-
- if (mPendingInstalls.size() == 0 && mPendingVerification.size() == 0) {
- if (mBound) {
- if (DEBUG_INSTALL) Slog.i(TAG, "calling disconnectService()");
-
- disconnectService();//断开连接
- }
- } else if (mPendingInstalls.size() > 0) {
- // There are more pending requests in queue.
- // Just post MCS_BOUND message to trigger processing
- // of next pending install.
- mHandler.sendEmptyMessage(MCS_BOUND);
- }
-
- break;
- }
MCS_UNBIND消息的处理,如果处理的时候发现mPendingInstalls又有数据了,还是发送MCS_BOUND消息继续安装,否则断开和DefaultContainerService的连接,安装结束。
下面我们看执行安装的函数startCopy:
- final boolean startCopy() {
- boolean res;
- try {
- if (DEBUG_INSTALL) Slog.i(TAG, "startCopy " + mUser + ": " + this);
-
- if (++mRetries > MAX_RETRIES) {//重试超过4次退出
- 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;
- }
handleStartCopy和copyApk代码就不分析了。
handleStartCopy函数先通过DefaultContainerService调用了getMinimallPackageInfo来确定安装位置是否有足够的空间,并在PackageInfoLite对象的recommendedIntallLocation记录错误原因。发现空间不够,会调用installer的freecache方法来释放一部分空间。
再接下来handleStartCopy有很长一段都在处理apk的校验,这个校验过程是通过发送Intent ACTION_PACKAGE_NEEDS_VERIFICATION给系统中所有接受该Intent的应用来完成。如果无需校验,直接调用InstallArgs对象的copyApk方法。
而copyApk方法同样是调用DefaultContainerService的copyPackage将应用的文件复制到/data/app下,如果还有native动态库,也会把包在apk文件中的动态库提取出来。
执行完copyApk后,应用安装到了data/app目录下了。
接下来是第二阶段的工作,把应用的格式装换成oat格式,为应用创建数据目录。最后把应用信息装载进PackageManagerService的数据结构中。
接着上面startCopy方法最后会调用handleReturnCode方法,代码如下:
- @Override
- void handleReturnCode() {
- // If mArgs is null, then MCS couldn't be reached. When it
- // reconnects, it will try again to install. At that point, this
- // will succeed.
- 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);//防止重复调用
- // 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);//装载安装的应用
- }
- args.doPostInstall(res.returnCode, res.uid);
- }
- ..........
-
- if (!doRestore) {
- // 发送POST_INSTALL消息
- Message msg = mHandler.obtainMessage(POST_INSTALL, token, 0);
- mHandler.sendMessage(msg);
- }
- }
- });
- }
processPendingInstall方法post了一个消息,这样安装过程以异步的方式继续执行。在post消息的处理中,首先调用installPackageLI来装载应用,然后很大的代码在执行备份,备份是通过BackupManagerService来完成的。备份完成后,通过发送POST_INSTALL消息来继续处理。而这个消息的处理主要就是在发送广播,应用安装完成后要通知系统中其他的应用开始处理,比如Launcher中需要增加应用的图标等。
我们来分析下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);//解析apk文件
- } catch (PackageParserException e) {
- res.setError("Failed parse during installPackageLI", e);
- return;
- }
这里先调用parsePackage解析apk文件,这个之前分析过,我们就不再分析了。
继续分析processPendingInstall函数
- 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);
- }
- }
继续分析
- if (systemApp && onSd) {//不能将系统应用装载sd卡
- // 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
- replacePackageLI(pkg, parseFlags, scanFlags | SCAN_REPLACING, args.user,
- installerPackageName, res);
- } else {//如果是新应用,调用installNewPackageLI继续处理
- 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);
- }
- }
下面我们分析下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,//调用scanPackageLI
- 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);
- }
- }
这里和上篇博客分析扫描apk文件类似,我们来看下这个函数scanPackageLI
- private PackageParser.Package scanPackageLI(PackageParser.Package pkg, int parseFlags,
- int scanFlags, long currentTime, UserHandle user) throws PackageManagerException {
- boolean success = false;
- try {
- final PackageParser.Package res = scanPackageDirtyLI(pkg, parseFlags, scanFlags,
- currentTime, user);
- success = true;
- return res;
- } finally {
- if (!success && (scanFlags & SCAN_DELETE_DATA_ON_FAILURES) != 0) {
- removeDataDirsLI(pkg.packageName);
- }
- }
- }
scanPackageLI函数主要调用了scanPackageDirtyLI函数,这个函数前面分析过了就不分析了。
我们再来看下在processPendingInstall函数中调用完installPackageLI函数之后,发送了一个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) {//安装成功
- final String packageName = res.pkg.applicationInfo.packageName;
- res.removedInfo.sendBroadcast(false, true, false);
- Bundle extras = new Bundle(1);
- extras.putInt(Intent.EXTRA_UID, res.uid);
-
- // Now that we successfully installed the package, grant runtime
- // permissions if requested before broadcasting the install.
- if ((args.installFlags
- & PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS) != 0) {
- grantRequestedRuntimePermissions(res.pkg, args.user.getIdentifier(),
- args.installGrantPermissions);
- }
-
- // Determine the set of users who are adding this
- // package for the first time vs. those who are seeing
- // an update.
- int[] firstUsers;
- int[] updateUsers = new int[0];
- if (res.origUsers == null || res.origUsers.length == 0) {
- firstUsers = res.newUsers;
- } else {
- firstUsers = new int[0];
- for (int i=0; i<res.newUsers.length; i++) {
- int user = res.newUsers[i];
- boolean isNew = true;
- for (int j=0; j<res.origUsers.length; j++) {
- if (res.origUsers[j] == user) {
- isNew = false;
- break;
- }
- }
- if (isNew) {
- int[] newFirst = new int[firstUsers.length+1];
- System.arraycopy(firstUsers, 0, newFirst, 0,
- firstUsers.length);
- newFirst[firstUsers.length] = user;
- firstUsers = newFirst;
- } else {
- int[] newUpdate = new int[updateUsers.length+1];
- System.arraycopy(updateUsers, 0, newUpdate, 0,
- updateUsers.length);
- newUpdate[updateUsers.length] = user;
- updateUsers = newUpdate;
- }
- }
- }
- sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED,//发送ACTION_PACKAGE_ADDED广播
- 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,
- packageName, extras, null, null, updateUsers);
- if (update) {//如果是升级,发送更多广播
- sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED,
- packageName, extras, null, null, updateUsers);
- sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED,
- null, null, packageName, null, updateUsers);
-
- // treat asec-hosted packages like removable media on upgrade
- if (res.pkg.isForwardLocked() || isExternal(res.pkg)) {
- if (DEBUG_INSTALL) {
- Slog.i(TAG, "upgrading pkg " + res.pkg
- + " is ASEC-hosted -> AVAILABLE");
- }
- int[] uidArray = new int[] { res.pkg.applicationInfo.uid };
- ArrayList<String> pkgList = new ArrayList<String>(1);
- pkgList.add(packageName);
- sendResourcesChangedBroadcast(true, true,
- pkgList,uidArray, null);
- }
- }
- if (res.removedInfo.args != null) {
- // Remove the replaced package's older resources safely now
- deleteOld = true;
- }
-
- // If this app is a browser and it's newly-installed for some
- // users, clear any default-browser state in those users
- if (firstUsers.length > 0) {
- // the app's nature doesn't depend on the user, so we can just
- // check its browser nature in any user and generalize.
- if (packageIsBrowser(packageName, firstUsers[0])) {
- synchronized (mPackages) {
- for (int userId : firstUsers) {
- mSettings.setDefaultBrowserPackageNameLPw(null, userId);
- }
- }
- }
- }
- // Log current value of "unknown sources" setting
- EventLog.writeEvent(EventLogTags.UNKNOWN_SOURCES_ENABLED,
- getUnknownSourcesSettings());
- }
- // Force a gc to clear up things
- Runtime.getRuntime().gc();
- // We delete after a gc for applications on sdcard.
- if (deleteOld) {
- synchronized (mInstallLock) {
- res.removedInfo.args.doPostDeleteLI(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.");
- }
- }
- } else {
- Slog.e(TAG, "Bogus post-install token " + msg.arg1);
- }
- } break;
这样安装应用的流程就讲完了。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。