1. 程式人生 > >Android原始碼筆記--APK解除安裝過程

Android原始碼筆記--APK解除安裝過程

            Android中應用的解除安裝主要是通過PackageManager中提供的deletePackage()函式來解除安裝,該函式通過IPC呼叫到Pms的deletePackage()函式,繼而呼叫到deletePackageX();

           當在設定中的應用列表中點選一個安裝的應用,點選解除安裝後,會發送一個Intent給UninstallerActivity,在UninstallerActivity最後會啟動UninstallAppProgress的initView方法,如下:

UninstallerActivity.java
 
private void startUninstallProgress() {
        Intent newIntent = new Intent(Intent.ACTION_VIEW);
        newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO, 
                                                  mAppInfo);
        newIntent.putExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, mAllUsers);
        if (getIntent().getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false)) {
            newIntent.putExtra(Intent.EXTRA_RETURN_RESULT, true);
            newIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
        }
        newIntent.setClass(this, UninstallAppProgress.class);
        startActivity(newIntent);
        finish();
    }
UninstallAppProgress.java
 
public void initView() {
  ...
PackageDeleteObserver observer = new PackageDeleteObserver(); getPackageManager().deletePackage(mAppInfo.packageName, observer,
                mAllUsers ? PackageManager.DELETE_ALL_USERS : 0);
}

分析:mAllUsers預設是false。getPackageManager()函式的實現在ContextImpl.java,它最後會呼叫到ApplicantPackageManger.java的deletePackage方法:

ApplicationPackageManager.java

@Override
    public void deletePackage(String packageName, IPackageDeleteObserver observer, int flags) {
        try {
            mPM.deletePackageAsUser(packageName, observer, UserHandle.myUserId(), flags);
        } catch (RemoteException e) {
            // Should never happen!
        }
    }

通過Binde呼叫到PMS的

PackageManagerService.java

@Override
    public void deletePackageAsUser(String packageName, IPackageDeleteObserver observer, int userId,
            int flags) {
        deletePackage(packageName, new LegacyPackageDeleteObserver(observer).getBinder(), userId,
                flags);
    }

    @Override
    public void deletePackage(final String packageName,
            final IPackageDeleteObserver2 observer, final int userId, final int flags) {
        mContext.enforceCallingOrSelfPermission(
                android.Manifest.permission.DELETE_PACKAGES, null);
        final int uid = Binder.getCallingUid();
        if (UserHandle.getUserId(uid) != userId) {
            mContext.enforceCallingPermission(
                    android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
                    "deletePackage for user " + userId);
        }
        if (isUserRestricted(userId, UserManager.DISALLOW_UNINSTALL_APPS)) {
            try {
                observer.onPackageDeleted(packageName,
                        PackageManager.DELETE_FAILED_USER_RESTRICTED, null);
            } catch (RemoteException re) {
            }
            return;
        }

        boolean uninstallBlocked = false;
        if ((flags & PackageManager.DELETE_ALL_USERS) != 0) {
            int[] users = sUserManager.getUserIds();
            for (int i = 0; i < users.length; ++i) {
                if (getBlockUninstallForUser(packageName, users[i])) {
                    uninstallBlocked = true;
                    break;
                }
            }
        } else {
            uninstallBlocked = getBlockUninstallForUser(packageName, userId);
        }
        if (uninstallBlocked) {
            try {
                observer.onPackageDeleted(packageName, PackageManager.DELETE_FAILED_OWNER_BLOCKED,
                        null);
            } catch (RemoteException re) {
            }
            return;
        }

        if (DEBUG_REMOVE) {
            Slog.d(TAG, "deletePackageAsUser: pkg=" + packageName + " user=" + userId);
        }
        // Queue up an async operation since the package deletion may take a little while.
        mHandler.post(new Runnable() {
            public void run() {
                mHandler.removeCallbacks(this);
                final int returnCode = deletePackageX(packageName, userId, flags);
                if (observer != null) {
                    try {
                        observer.onPackageDeleted(packageName, returnCode, null);
                    } catch (RemoteException e) {
                        Log.i(TAG, "Observer no longer exists.");
                    } //end catch
                } //end if
            } //end run
        });
    }

在deletePackageAsUser方法中,首先做許可權檢查,然後就呼叫deletePackageX方法去執行解除安裝任務:

PMS

private int deletePackageX(String packageName, int userId, int flags) {
        final PackageRemovedInfo info = new PackageRemovedInfo();
        final boolean res;

        final UserHandle removeForUser = (flags & PackageManager.DELETE_ALL_USERS) != 0
                ? UserHandle.ALL : new UserHandle(userId);

        if (isPackageDeviceAdmin(packageName, removeForUser.getIdentifier())) {
            Slog.w(TAG, "Not removing package " + packageName + ": has active device admin");
            return PackageManager.DELETE_FAILED_DEVICE_POLICY_MANAGER;
        }

        boolean removedForAllUsers = false;
        boolean systemUpdate = false;

        // for the uninstall-updates case and restricted profiles, remember the per-
        // userhandle installed state
        int[] allUsers;
        boolean[] perUserInstalled;
        synchronized (mPackages) {
            PackageSetting ps = mSettings.mPackages.get(packageName);
            allUsers = sUserManager.getUserIds();
            perUserInstalled = new boolean[allUsers.length];
            for (int i = 0; i < allUsers.length; i++) {
                perUserInstalled[i] = ps != null ? ps.getInstalled(allUsers[i]) : false;
            }
        }

        synchronized (mInstallLock) {
            if (DEBUG_REMOVE) Slog.d(TAG, "deletePackageX: pkg=" + packageName + " user=" + userId);
            res = deletePackageLI(packageName, removeForUser,
                    true, allUsers, perUserInstalled,
                    flags | REMOVE_CHATTY, info, true);
            systemUpdate = info.isRemovedPackageSystemUpdate;
            if (res && !systemUpdate && mPackages.get(packageName) == null) {
                removedForAllUsers = true;
            }
            if (DEBUG_REMOVE) Slog.d(TAG, "delete res: systemUpdate=" + systemUpdate
                    + " removedForAllUsers=" + removedForAllUsers);
        }

        if (res) {
            info.sendBroadcast(true, systemUpdate, removedForAllUsers);

            // If the removed package was a system update, the old system package
            // was re-enabled; we need to broadcast this information
            if (systemUpdate) {
                Bundle extras = new Bundle(1);
                extras.putInt(Intent.EXTRA_UID, info.removedAppId >= 0
                        ? info.removedAppId : info.uid);
                extras.putBoolean(Intent.EXTRA_REPLACING, true);

                sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
                        extras, null, null, null);
                sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, packageName,
                        extras, null, null, null);
                sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED, null,
                        null, packageName, null, null);
            }
        }
        // Force a gc here.
        Runtime.getRuntime().gc();
        // Delete the resources here after sending the broadcast to let
        // other processes clean up before deleting resources.
        if (info.args != null) {
            synchronized (mInstallLock) {
                info.args.doPostDeleteLI(true);
            }
        }

        return res ? PackageManager.DELETE_SUCCEEDED : PackageManager.DELETE_FAILED_INTERNAL_ERROR;
    }
 /*
     * This method handles package deletion in general
     */
    private boolean deletePackageLI(String packageName, UserHandle user,
            boolean deleteCodeAndResources, int[] allUserHandles, boolean[] perUserInstalled,
            int flags, PackageRemovedInfo outInfo,
            boolean writeSettings) {
        if (packageName == null) {
            Slog.w(TAG, "Attempt to delete null packageName.");
            return false;
        }
       
        PackageSetting ps;
        boolean dataOnly = false;
        int removeUser = -1;
        int appId = -1;
        synchronized (mPackages) {
            ps = mSettings.mPackages.get(packageName);
            if (ps == null) {
                Slog.w(TAG, "Package named '" + packageName + "' doesn't exist.");
                return false;
            }
            if ((!isSystemApp(ps) || (flags&PackageManager.DELETE_SYSTEM_APP) != 0) && user != null
                    && user.getIdentifier() != UserHandle.USER_ALL) {
                // The caller is asking that the package only be deleted for a single
                // user.  To do this, we just mark its uninstalled state and delete
                // its data.  If this is a system app, we only allow this to happen if
                // they have set the special DELETE_SYSTEM_APP which requests different
                // semantics than normal for uninstalling system apps.
                if (DEBUG_REMOVE) Slog.d(TAG, "Only deleting for single user");
                ps.setUserState(user.getIdentifier(),
                        COMPONENT_ENABLED_STATE_DEFAULT,
                        false, //installed
                        true,  //stopped
                        true,  //notLaunched
                        false, //hidden
                        null, null, null,
                        false // blockUninstall
                        );
                if (!isSystemApp(ps)) {
                    if (ps.isAnyInstalled(sUserManager.getUserIds())) {
                        // Other user still have this package installed, so all
                        // we need to do is clear this user's data and save that
                        // it is uninstalled.
                        if (DEBUG_REMOVE) Slog.d(TAG, "Still installed by other users");
                        removeUser = user.getIdentifier();
                        appId = ps.appId;
                        mSettings.writePackageRestrictionsLPr(removeUser);
                    } else {
                        // We need to set it back to 'installed' so the uninstall
                        // broadcasts will be sent correctly.
                        if (DEBUG_REMOVE) Slog.d(TAG, "Not installed by other users, full delete");
                        ps.setInstalled(true, user.getIdentifier());
                    }
                } else {
                    // This is a system app, so we assume that the
                    // other users still have this package installed, so all
                    // we need to do is clear this user's data and save that
                    // it is uninstalled.
                    if (DEBUG_REMOVE) Slog.d(TAG, "Deleting system app");
                    removeUser = user.getIdentifier();
                    appId = ps.appId;
                    mSettings.writePackageRestrictionsLPr(removeUser);
                }
            }
        }

        if (removeUser >= 0) {
            // From above, we determined that we are deleting this only
            // for a single user.  Continue the work here.
            if (DEBUG_REMOVE) Slog.d(TAG, "Updating install state for user: " + removeUser);
            if (outInfo != null) {
                outInfo.removedPackage = packageName;
                outInfo.removedAppId = appId;
                outInfo.removedUsers = new int[] {removeUser};
            }
            mInstaller.clearUserData(packageName, removeUser);
            removeKeystoreDataIfNeeded(removeUser, appId);
            schedulePackageCleaning(packageName, removeUser, false);
            return true;
        }

       ...

        boolean ret = false;
        if (isSystemApp(ps)) {
            if (DEBUG_REMOVE) Slog.d(TAG, "Removing system package:" + ps.name);
            // When an updated system application is deleted we delete the existing resources as well and
            // fall back to existing code in system partition
            ret = deleteSystemPackageLI(ps, allUserHandles, perUserInstalled,
                    flags, outInfo, writeSettings);
        } else {
            if (DEBUG_REMOVE) Slog.d(TAG, "Removing non-system package:" + ps.name);
            // Kill application pre-emptively especially for apps on sd.
            killApplication(packageName, ps.appId, "uninstall pkg");
            ret = deleteInstalledPackageLI(ps, deleteCodeAndResources, flags,
                    allUserHandles, perUserInstalled,
                    outInfo, writeSettings);
        }

        return ret;
    }

在deletePackageLI函式中根據是否是systemApp呼叫不同的流程,如果是systemApp,則呼叫deleteSystemPackageLI完成解除安裝;如果非systemApp,則呼叫deleteInstalledPackageLI完成解除安裝; 在解除安裝之前,首先會呼叫AMS的killApplication方法先讓這個APP停止執行。

主要看一下非系統App的解除安裝流程:

private boolean deleteInstalledPackageLI(PackageSetting ps,  
        boolean deleteCodeAndResources, int flags,  
        int[] allUserHandles, boolean[] perUserInstalled,  
        PackageRemovedInfo outInfo, boolean writeSettings) {  
    if (outInfo != null) {  
        outInfo.uid = ps.appId;  
    }  
  
    removePackageDataLI(ps, allUserHandles, perUserInstalled, outInfo, flags, writeSettings);  
  
    if (deleteCodeAndResources && (outInfo != null)) {  
        outInfo.args = createInstallArgs(packageFlagsToInstallFlags(ps), ps.codePathString,  
                ps.resourcePathString, ps.nativeLibraryPathString);  
    }  
    return true;  
}  

         在deleteInstalledPackageLI方法中,分為兩步去解除安裝應用:第一步刪除/data/data下面的資料目錄,並從PMS的內部資料結構上清除當前解除安裝的package資訊;第二步就刪除code和resource檔案。我們先來看第一步:

private void removePackageDataLI(PackageSetting ps,  
        int[] allUserHandles, boolean[] perUserInstalled,  
        PackageRemovedInfo outInfo, int flags, boolean writeSettings) {  
    String packageName = ps.name;  
    removePackageLI(ps, (flags&REMOVE_CHATTY) != 0);  
    final PackageSetting deletedPs;  
  
    synchronized (mPackages) {  
        deletedPs = mSettings.mPackages.get(packageName);  
        if (outInfo != null) {  
            outInfo.removedPackage = packageName;  
            outInfo.removedUsers = deletedPs != null  
                    ? deletedPs.queryInstalledUsers(sUserManager.getUserIds(), true)  
                    : null;  
        }  
    }  
    if ((flags&PackageManager.DELETE_KEEP_DATA) == 0) {  
        removeDataDirsLI(packageName);  
        schedulePackageCleaning(packageName, UserHandle.USER_ALL, true);  
    }

           removePackageDataLI用於刪除應用的/data/data資料目錄,並且從PMS內部資料結構裡面清除package的資訊。首先呼叫removePackageLI從PMS內部的資料結構上刪除要解除安裝的package資訊:

void removePackageLI(PackageSetting ps, boolean chatty) {  
    synchronized (mPackages) {  
        mPackages.remove(ps.name);  
        if (ps.codePathString != null) {  
            mAppDirs.remove(ps.codePathString);  
        }  
  
        final PackageParser.Package pkg = ps.pkg;  
        if (pkg != null) {  
            cleanPackageDataStructuresLILPw(pkg, chatty);  
        }  
    }  
}  

           cleanPackageDataStructuresLILPw用於將package的providers、services、receivers、activities等資訊去PMS的全域性資料結構上移除,這部分程式碼比較簡單。如果沒有設定DELETE_KEEP_DATA這個flag,就會首先呼叫removeDataDirsLI去刪除/data/data下面的目錄:

private int removeDataDirsLI(String packageName) {  
    int[] users = sUserManager.getUserIds();  
    int res = 0;  
    for (int user : users) {  
        int resInner = mInstaller.remove(packageName, user);  
        if (resInner < 0) {  
            res = resInner;  
        }  
    }  
  
    final File nativeLibraryFile = new File(mAppLibInstallDir, packageName);  
    NativeLibraryHelper.removeNativeBinariesFromDirLI(nativeLibraryFile);  
    if (!nativeLibraryFile.delete()) {  
        Slog.w(TAG, "Couldn't delete native library directory " + nativeLibraryFile.getPath());  
    }  
  
    return res;  
}  

         這裡首先呼叫installd的remove方法去刪除/data/data下面的目錄。然後去刪除/data/app-lib下面的應用程式的library資訊,但因為這裡的nativeLibraryFile為/data/app-lib/packageName,和前面介紹的APK安裝過程中的目錄/data/app-lib/packageName-num不一樣,所以實際上,這裡並沒有真正的去刪除library目錄。
先來看installd的remove方法:

static int do_remove(char **arg, char reply[REPLY_MAX])  
{  
    return uninstall(arg[0], atoi(arg[1])); /* pkgname, userid */  
}  
  
int uninstall(const char *pkgname, userid_t userid)  
{  
    char pkgdir[PKG_PATH_MAX];  
  
    if (create_pkg_path(pkgdir, pkgname, PKG_DIR_POSTFIX, userid))  
        return -1;  
  
    return delete_dir_contents(pkgdir, 1, NULL);  
}  
  
int delete_dir_contents(const char *pathname,  
                        int also_delete_dir,  
                        const char *ignore)  
{  
    int res = 0;  
    DIR *d;  
  
    d = opendir(pathname);  
    if (d == NULL) {  
        ALOGE("Couldn't opendir %s: %s\n", pathname, strerror(errno));  
        return -errno;  
    }  
    res = _delete_dir_contents(d, ignore);  
    closedir(d);  
    if (also_delete_dir) {  
        if (rmdir(pathname)) {  
            ALOGE("Couldn't rmdir %s: %s\n", pathname, strerror(errno));  
            res = -1;  
        }  
    }  
    return res;  
}  

         create_pkg_path方法構造/data/data/packageName的檔案路徑名,然後呼叫delete_dir_contents來刪除檔案內容以及目錄,前面介紹過,/data/data/packageName的檔案其實都是符號連結,所以_delete_dir_contents的實現中都是呼叫unlinkat去刪除這些符號連結。回到removePackageDataLI中,接著呼叫schedulePackageCleaning來安排清理動作:

void schedulePackageCleaning(String packageName, int userId, boolean andCode) {  
    mHandler.sendMessage(mHandler.obtainMessage(START_CLEANING_PACKAGE,  
            userId, andCode ? 1 : 0, packageName));  
}  

          這裡向PackageHandler傳送START_CLEANING_PACKAGE訊息,PMS會呼叫ContainService的函式去刪除/storage/sdcard0/Android/data和/storage/sdcard0/Android/media下面與package相關的檔案,接著來看removePackageDataLI方法:

synchronized (mPackages) {  
    if (deletedPs != null) {  
        if ((flags&PackageManager.DELETE_KEEP_DATA) == 0) {  
            if (outInfo != null) {  
                outInfo.removedAppId = mSettings.removePackageLPw(packageName);  
            }  
            if (deletedPs != null) {  
                updatePermissionsLPw(deletedPs.name, null, 0);  
                if (deletedPs.sharedUser != null) {  
                    // remove permissions associated with package  
                    mSettings.updateSharedUserPermsLPw(deletedPs, mGlobalGids);  
                }  
            }  
            clearPackagePreferredActivitiesLPw(deletedPs.name, UserHandle.USER_ALL);  
        }  
    }  
  
    if (writeSettings) {  
        mSettings.writeLPr();  
    }  
}  
if (outInfo != null) {  
    removeKeystoreDataIfNeeded(UserHandle.USER_ALL, outInfo.removedAppId);  
}  

這裡首先從Settings中刪除PackageSettings的資訊:

int removePackageLPw(String name) {  
    final PackageSetting p = mPackages.get(name);  
    if (p != null) {  
        mPackages.remove(name);  
        if (p.sharedUser != null) {  
            p.sharedUser.removePackage(p);  
            if (p.sharedUser.packages.size() == 0) {  
                mSharedUsers.remove(p.sharedUser.name);  
                removeUserIdLPw(p.sharedUser.userId);  
                return p.sharedUser.userId;  
            }  
        } else {  
            removeUserIdLPw(p.appId);  
            return p.appId;  
        }  
    }  
    return -1;  
}  
void updateSharedUserPermsLPw(PackageSetting deletedPs, int[] globalGids) {  
    SharedUserSetting sus = deletedPs.sharedUser;  
  
    for (String eachPerm : deletedPs.pkg.requestedPermissions) {  
        boolean used = false;  
        if (!sus.grantedPermissions.contains(eachPerm)) {  
            continue;  
        }  
        for (PackageSetting pkg:sus.packages) {  
            if (pkg.pkg != null &&  
                    !pkg.pkg.packageName.equals(deletedPs.pkg.packageName) &&  
                    pkg.pkg.requestedPermissions.contains(eachPerm)) {  
                used = true;  
                break;  
            }  
        }  
        if (!used) {  
            sus.grantedPermissions.remove(eachPerm);  
        }  
    }  
    int newGids[] = globalGids;  
    for (String eachPerm : sus.grantedPermissions) {  
        BasePermission bp = mPermissions.get(eachPerm);  
        if (bp != null) {  
            newGids = PackageManagerService.appendInts(newGids, bp.gids);  
        }  
    }  
    sus.gids = newGids;  
}  

            迴圈的從要被解除安裝的Package所在的sharedUser組中找被申請的許可權是否還被同一組的其它package使用,如果沒有使用者,就從sharedUser的grantedPermissions刪除。clearPackagePreferredActivitiesLPw與AMS相關,我們留到以後再來介紹。在removePackageDataLI方法最好呼叫Settings.writeLPr()方法將改動的資訊寫到Package.xml中。到這裡,我們前面所說的deleteInstalledPackageLI方法中的第一步已經完成,來看第二部分:

if (deleteCodeAndResources && (outInfo != null)) {  
        outInfo.args = createInstallArgs(packageFlagsToInstallFlags(ps), ps.codePathString,  
                ps.resourcePathString, ps.nativeLibraryPathString);  
    }  
  
private InstallArgs createInstallArgs(int flags, String fullCodePath, String fullResourcePath,  
        String nativeLibraryPath) {  
    final boolean isInAsec;  
    if (installOnSd(flags)) {  
        isInAsec = true;  
    } else if (installForwardLocked(flags)  
            && !fullCodePath.startsWith(mDrmAppPrivateInstallDir.getAbsolutePath())) {  
        isInAsec = true;  
    } else {  
        isInAsec = false;  
    }  
  
    if (isInAsec) {  
        return new AsecInstallArgs(fullCodePath, fullResourcePath, nativeLibraryPath,  
                installOnSd(flags), installForwardLocked(flags));  
    } else {  
        return new FileInstallArgs(fullCodePath, fullResourcePath, nativeLibraryPath);  
    }  
}  

        這裡根據安裝目錄的不同,分別構造FileInstallArgs和AsecInstallArgs來完成code和resource資源的清除。這裡我們主要介紹解除安裝內部儲存空間上面的APK,來看FileInstallArgs的doPostDeleteLI方法:

boolean doPostDeleteLI(boolean delete) {  
    cleanUpResourcesLI();  
    return true;  
}  
  
void cleanUpResourcesLI() {  
    String sourceDir = getCodePath();  
    if (cleanUp()) {  
        int retCode = mInstaller.rmdex(sourceDir);  
        if (retCode < 0) {  
            Slog.w(TAG, "Couldn't remove dex file for package: "  
                    +  " at location "  
                    + sourceDir + ", retcode=" + retCode);  
            // we don't consider this to be a failure of the core package deletion  
        }  
    }  
}  
private boolean cleanUp() {  
    boolean ret = true;  
    String sourceDir = getCodePath();  
    String publicSourceDir = getResourcePath();  
    if (sourceDir != null) {  
        File sourceFile = new File(sourceDir);  
        if (!sourceFile.exists()) {  
            Slog.w(TAG, "Package source " + sourceDir + " does not exist.");  
            ret = false;  
        }  
  
        sourceFile.delete();  
    }  
    if (publicSourceDir != null && !publicSourceDir.equals(sourceDir)) {  
        final File publicSourceFile = new File(publicSourceDir);  
        if (!publicSourceFile.exists()) {  
            Slog.w(TAG, "Package public source " + publicSourceFile + " does not exist.");  
        }  
        if (publicSourceFile.exists()) {  
            publicSourceFile.delete();  
        }  
    }  
  
    if (libraryPath != null) {  
        File nativeLibraryFile = new File(libraryPath);  
        NativeLibraryHelper.removeNativeBinariesFromDirLI(nativeLibraryFile);  
        if (!nativeLibraryFile.delete()) {  
            Slog.w(TAG, "Couldn't delete native library directory " + libraryPath);  
        }  
    }  
  
    return ret;  
}  

然後cleanUpResourcesLI呼叫installd的rmdex方法去刪除存在/data/dalvik-cache檔案:

static int do_rm_dex(char **arg, char reply[REPLY_MAX])  
{  
    return rm_dex(arg[0]); /* pkgname */  
}  
  
int rm_dex(const char *path)  
{  
    char dex_path[PKG_PATH_MAX];  
  
    if (validate_apk_path(path)) return -1;  
    if (create_cache_path(dex_path, path)) return -1;  
  
    ALOGV("unlink %s\n", dex_path);  
    if (unlink(dex_path) < 0) {  
        ALOGE("Couldn't unlink %s: %s\n", dex_path, strerror(errno));  
        return -1;  
    } else {  
        return 0;  
    }  
}