Android O :清除应用数据

来源:互联网 发布:沙盘软件多开器 编辑:程序博客网 时间:2024/05/18 00:52

本篇博客主要以Android O的代码为例,分析一下清除应用数据的流程。


一、UI部分
从手机的设置界面进入到应用管理界面,选中某个应用后,可以看到清除数据的按键。
按键对应的代码定义于AppStorageSettings.java中。

界面加载时的代码如下:

private void setupViews() {    ...........    //初始化对应的按键    mClearDataButton = (Button) ((LayoutPreference) findPreference(KEY_CLEAR_DATA)).findViewById(R.id.button);    ...........}

点击mClearDataButton后的处理逻辑如下:

@Overridepublic void onClick(View v) {    ............    } else if (v == mClearDataButton) {        if (mAppsControlDisallowedAdmin != null && !mAppsControlDisallowedBySystem) {            ............        }  else if (mAppEntry.info.manageSpaceActivityName != null) {            ............        } else {            //弹出清除数据的对话框            showDialogInner(DLG_CLEAR_DATA, 0);        }    }...........    ............}

接着看看创建对话框的部分:

@Overrideprotected AlertDialog createDialog(int id, int errorCode) {    switch (id) {        case DLG_CLEAR_DATA:            return new AlertDialog.Builder(getActivity())                        .setTitle(getActivity().getText(R.string.clear_data_dlg_title))                        .setMessage(getActivity().getText(R.string.clear_data_dlg_text))                        .setPositiveButton(R.string.dlg_ok, new DialogInterface.OnClickListener() {                            public void onClick(DialogInterface dialog, int which) {                                // Clear user data here                                initiateClearUserData();                            }                        })                        .setNegativeButton(R.string.dlg_cancel, null)                        .create();        case DLG_CANNOT_CLEAR_DATA:            ............    }    return null;}

容易看出,一旦点击确定按键后,将会调用initiateClearUserData函数:

private void initiateClearUserData() {    mClearDataButton.setEnabled(false);    // Invoke uninstall or clear user data based on sysPackage    String packageName = mAppEntry.info.packageName;    Log.i(TAG, "Clearing user data for package : " + packageName);    if (mClearDataObserver == null) {        //这里创建的是AppStorageSettings的内部类,用于清除数据后回调        mClearDataObserver = new ClearUserDataObserver();    }    ActivityManager am = (ActivityManager)            getActivity().getSystemService(Context.ACTIVITY_SERVICE);    //调用ActivityManager的clearApplicationUserData函数    boolean res = am.clearApplicationUserData(packageName, mClearDataObserver);    if (!res) {        // Clearing data failed for some obscure reason. Just log error for now        Log.i(TAG, "Couldnt clear application user data for package:"+packageName);        showDialogInner(DLG_CANNOT_CLEAR_DATA, 0);    } else {        mClearDataButton.setText(R.string.recompute_size);    }}

二、AMS部分
UI部分结束后,我们看看AMS相关的逻辑:

ActivityManager中的clearApplicationUserData函数:

public boolean clearApplicationUserData(String packageName, IPackageDataObserver observer) {    try {        //最终调用到AMS的clearApplicationUserData        return getService().clearApplicationUserData(packageName,                observer, UserHandle.myUserId());    } catch (RemoteException e) {        throw e.rethrowFromSystemServer();    }}............public static IActivityManager getService() {    return IActivityManagerSingleton.get();}//AMS的单例客户端private static final Singleton<IActivityManager> IActivityManagerSingleton =        new Singleton<IActivityManager>() {            @Override            protected IActivityManager create() {                final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);                final IActivityManager am = IActivityManager.Stub.asInterface(b);                return am;            }        };......

顺着上面的代码,我们跟进AMS的clearApplicationUserData函数:

@Overridepublic boolean clearApplicationUserData(final String packageName,        final IPackageDataObserver observer, int userId) {    ..............    int uid = Binder.getCallingUid();    int pid = Binder.getCallingPid();    //得到用户ID(Android毕竟是个多用户系统,需要该信息)    userId = mUserController.handleIncomingUser(pid, uid, userId, false,            ALLOW_FULL_ONLY, "clearApplicationUserData", null);    long callingId = Binder.clearCallingIdentity();    try {        IPackageManager pm = AppGlobals.getPackageManager();        int pkgUid = -1;        synchronized(this) {            //判断数据是否受保护            if (getPackageManagerInternalLocked().isPackageDataProtected(                    userId, packageName)) {                throw new SecurityException(                        "Cannot clear data for a protected package: " + packageName);            }            try {                //根据packageName获取一次uid                pkgUid = pm.getPackageUid(packageName, MATCH_UNINSTALLED_PACKAGES, userId);            } catch (RemoteException e) {            }            //packageName存在问题时            if (pkgUid == -1) {                ........                if (observer != null) {                    try {                        //直接回调,不进行后续工作                        observer.onRemoveCompleted(packageName, false);                    } catch (RemoteException e) {                        Slog.i(TAG, "Observer no longer exists.");                    }                }                return false;            }            //pkg有效且应用具有删除数据的权限(设置有这个权限)            if (uid == pkgUid || checkComponentPermission(                    android.Manifest.permission.CLEAR_APP_USER_DATA,                    pid, uid, -1, true)                    == PackageManager.PERMISSION_GRANTED) {                //调用forceStopPackageLocked函数                forceStopPackageLocked(packageName, pkgUid, "clear data");            } else {                throw new SecurityException("PID " + pid + " does not have permission "                        + android.Manifest.permission.CLEAR_APP_USER_DATA + " to clear data"                                + " of package " + packageName);            }            // Remove all tasks match the cleared application package and user            for (int i = mRecentTasks.size() - 1; i >= 0; i--) {                final TaskRecord tr = mRecentTasks.get(i);                final String taskPackageName =                        tr.getBaseIntent().getComponent().getPackageName();                if (tr.userId != userId) continue;                if (!taskPackageName.equals(packageName)) continue;                //清除Task栈中与被清除应用相关的对象                mStackSupervisor.removeTaskByIdLocked(tr.taskId, false, REMOVE_FROM_RECENTS);            }        }        final int pkgUidF = pkgUid;        final int userIdF = userId;        //这里也是创建一个回调对象        final IPackageDataObserver localObserver = new IPackageDataObserver.Stub() {            @Override            public void onRemoveCompleted(String packageName, boolean succeeded)                    throws RemoteException {                synchronized (ActivityManagerService.this) {                    finishForceStopPackageLocked(packageName, pkgUidF);                }                //PKMS清除完数据,回调后会发送Intent.ACTION_PACKAGE_DATA_CLEARED广播                //这里需要注意的是:这个广播不会发送给被清除数据的应用                //即packageName对应的应用不会收到该广播                final Intent intent = new Intent(Intent.ACTION_PACKAGE_DATA_CLEARED,                        Uri.fromParts("package", packageName, null));                intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);                intent.putExtra(Intent.EXTRA_UID, pkgUidF);                intent.putExtra(Intent.EXTRA_USER_HANDLE, UserHandle.getUserId(pkgUidF));                broadcastIntentInPackage("android", SYSTEM_UID, intent,                        null, null, 0, null, null, null, null, false, false, userIdF);                if (observer != null) {                    //这里是回调设置界面传入的观察者                    observer.onRemoveCompleted(packageName, succeeded);                }            }        };        try {            // Clear application user data            // 进入PKMS的清除流程            pm.clearApplicationUserData(packageName, localObserver, userId);            synchronized(this) {                // Remove all permissions granted from/to this package                // 移除Uri权限,最后一个参数为true,即删除临时和持久的Uri访问权限                // 可参考Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION的描述                removeUriPermissionsForPackageLocked(packageName, userId, true);            }            // Reset notification settings.            // 清除掉对应的通知            INotificationManager inm = NotificationManager.getService();            inm.clearData(packageName, pkgUidF, uid == pkgUidF);        } catch (RemoteException e) {        }    } finally {        Binder.restoreCallingIdentity(callingId);    }    return true;}

以上就是AMS clearApplicationUserData的逻辑,该函数看起来很长,但主要功能按顺序有以下几点:
1、在权限等条件满足的情况下,调用forceStopPackageLocked函数,应该是强制结束进程;
2、清除Task栈中与被清除应用相关的对象;
3、调用PKMS的clearApplicationUserData函数,清除应用的数据;
4、移除应用的Uri权限和通知;
5、PKMS处理完毕后,在回调接口中发送广播,并通知设置(或其它清除数据功能的调用者)。

2.1 forceStopPackageLocked
大概了解清除应用数据的主要流程后,我们先深入看看forceStopPackageLocked函数。

private void forceStopPackageLocked(final String packageName, int uid, String reason) {    forceStopPackageLocked(packageName, UserHandle.getAppId(uid), false,            false, true, false, false, UserHandle.getUserId(uid), reason);}//callerWillRestart = false, purgeCache = false, doit = true, evenPersistent = false, uninstalling = falsefinal boolean forceStopPackageLocked(String packageName, int appId,        boolean callerWillRestart, boolean purgeCache, boolean doit,        boolean evenPersistent, boolean uninstalling, int userId, String reason) {    ..............    if (doit) {        ...........        //移除应用的crash计数        mAppErrors.resetProcessCrashTimeLocked(packageName == null, appId, userId);    }    //kill process    boolean didSomething = killPackageProcessesLocked(packageName, appId, userId,            ProcessList.INVALID_ADJ, callerWillRestart, true, doit, evenPersistent,            packageName == null ? ("stop user " + userId) : ("stop " + packageName));    //被杀死的进程,如果有待启动的Activity,将被移除    didSomething |= mActivityStarter.clearPendingActivityLaunchesLocked(packageName);    //清除进程包含的Activity    if (mStackSupervisor.finishDisabledPackageActivitiesLocked(            packageName, null, doit, evenPersistent, userId)) {        if (!doit) {            //此处不返回            return true;        }        didSomething = true;    }    //清除进程包含的Service    if (mServices.bringDownDisabledPackageServicesLocked(            packageName, null, userId, evenPersistent, true, doit)) {        if (!doit) {            return true;        }        didSomething = true;    }    //packageName为null时,才会清除粘性广播的记录    if (packageName == null) {        // Remove all sticky broadcasts from this user.        mStickyBroadcasts.remove(userId);    }    //搜集进程对应的Provider    ArrayList<ContentProviderRecord> providers = new ArrayList<>();    if (mProviderMap.collectPackageProvidersLocked(packageName, null, doit, evenPersistent,            userId, providers)) {        if (!doit) {            return true;        }        didSomething = true;    }    //移除这些Provider    for (i = providers.size() - 1; i >= 0; i--) {        removeDyingProviderLocked(null, providers.get(i), true);    }    // Remove transient permissions granted from/to this package/user    // 此时第三个参数为false,表示仅清除临时的Uri权限    removeUriPermissionsForPackageLocked(packageName, userId, false);    //清除进程广播相关的组件    if (doit) {        for (i = mBroadcastQueues.length - 1; i >= 0; i--) {            didSomething |= mBroadcastQueues[i].cleanupDisabledPackageReceiversLocked(                    packageName, null, userId, doit);        }    }    // 这部分是清除UserId对应的pending intent    // 从设置界面清除应用数据时,不会进入这里    if (packageName == null || uninstalling) {        // Remove pending intents.  For now we only do this when force        // stopping users, because we have some problems when doing this        // for packages -- app widgets are not currently cleaned up for        // such packages, so they can be left with bad pending intents.        ....................    }    .....................    return didSomething;}

至此,forceStopPackageLocked函数分析完毕,我仅截取了从设置界面清除应用数据涉及的流程。
这部分代码主要的工作可以概括为以下几点:
1、杀死进程;
2、清除进程相关的Activity、Service、Provider、Broadcast等;
3、清除进程临时获取的Uri访问权限。

2.2 killPackageProcessesLocked
现在我们再来看看杀死进程的函数逻辑:

//callerWillRestart = false,  allowRestart = true, doit = true, evenPersistent = falseprivate final boolean killPackageProcessesLocked(String packageName, int appId,        int userId, int minOomAdj, boolean callerWillRestart, boolean allowRestart,        boolean doit, boolean evenPersistent, String reason) {    // 将所有需要结束的进程加入到procs中    ArrayList<ProcessRecord> procs = new ArrayList<>();    // Remove all processes this package may have touched: all with the    // same UID (except for the system or root user), and all whose name    // matches the package name.    // 轮询找出待清理的进程    final int NP = mProcessNames.getMap().size();    for (int ip=0; ip<NP; ip++) {        SparseArray<ProcessRecord> apps = mProcessNames.getMap().valueAt(ip);        final int NA = apps.size();        for (int ia=0; ia<NA; ia++) {            ProcessRecord app = apps.valueAt(ia);            //注意到从设置界面清除系统应用的数据时,persistent应用不会被加入到procs中,即不会被杀死            if (app.persistent && !evenPersistent) {                // we don't kill persistent processes                continue;            }            if (app.removed) {                if (doit) {                    procs.add(app);                }                continue;            }            ..............            // If no package is specified, we call all processes under the            // give user id.            if (packageName == null) {                // 主要判断processRecord的userid是否满足要求                ..........            } else {                // Package has been specified, we want to hit all processes                // that match it.  We need to qualify this by the processes                // that are running under the specified app and user ID.                // 这里主要判断processRecord的userid及packageName是否满足要求                .........            }            ......            app.removed = true;            // 满足要求的processRecord被加入到procs里            procs.add(app);        }    }    int N = procs.size();    for (int i=0; i<N; i++) {        removeProcessLocked(procs.get(i), callerWillRestart, allowRestart, reason);    }    updateOomAdjLocked();    return N > 0;}

容易看出killPackageProcessesLocked的主要逻辑是筛选出满足条件的processRecord,
然后调用removeProcessLocked进行处理。
removeProcessLocked会进行:
清除系统中与进程相关的记录信息,
通知电量服务结束对进程的耗电统计等操作,
最终通过ProcessRecord的kill函数结束进程(即 kill -9 pid)。

三、PKMS部分
根据前文的代码,我们知道清除应用数据时,有一部分工作交给PKMS来完成。
我们来看看PKMS中的clearApplicationUserData函数:

public void clearApplicationUserData(final String packageName,        final IPackageDataObserver observer, final int userId) {    ...........    // Queue up an async operation since the package deletion may take a little while.    mHandler.post(new Runnable() {        public void run() {            //刚run就removeCallbacks,这个操作有点飘逸....暂时有点无法体会            mHandler.removeCallbacks(this);            final boolean succeeded;            // try后接括号,java 7新特性            // try块中逻辑执行完毕后,括号内的资源自动释放            // 这里是创建出内部类PackageFreezer, 实现AutoCloseable接口            // 让人无语的是:在其构造函数中会调用killApplication杀死应用....            // 这代码写的让人心醉            try (PackageFreezer freezer = freezePackage(packageName,                    "clearApplicationUserData")) {                synchronized (mInstallLock) {                    //1、清除应用用户数据                    succeeded = clearApplicationUserDataLIF(packageName, userId);                }                //2、清除SD card数据                clearExternalStorageDataSync(packageName, userId, true);                synchronized (mPackages) {                    //3、清除instant app的数据                    //关于instant app, 大家可以参考下网上资料                    //号称不用安装即可运行的app, N及以上版本支持,非常牛逼                    mInstantAppRegistry.deleteInstantApplicationMetadataLPw(                            packageName, userId);                }            }            .............            if(observer != null) {                try {                    //回调AMS中的观察者,发送广播                    observer.onRemoveCompleted(packageName, succeeded);                } catch (RemoteException e) {                    Log.i(TAG, "Observer no longer exists.");                }            }        }    }}

从上述代码来看,PKMS清除应用数据的工作,将异步地交给三个函数来完成。
这里我们主要看看clearApplicationUserDataLIF的逻辑,
其它两个函数虽然也很繁琐,但思想比较直白。

从函数名来看,clearApplicationUserDataLIF函数的主要作用是清除应用的用户数据。

private boolean clearApplicationUserDataLIF(String packageName, int userId) {    .............    // Try finding details about the requested package    // 得到Package对应的数据    PackageParser.Package pkg;    synchronized (mPackages) {        pkg = mPackages.get(packageName);        if (pkg == null) {            final PackageSetting ps = mSettings.mPackages.get(packageName);            if (ps != null) {                pkg = ps.pkg;            }        }        if (pkg == null) {            Slog.w(TAG, "Package named '" + packageName + "' doesn't exist.");            return false;        }        PackageSetting ps = (PackageSetting) pkg.mExtras;        //清除权限相关的内容,这里面我看的不太懂......功力还不深厚        resetUserChangesToRuntimePermissionsAndFlagsLPw(ps, userId);    }    //这里将调用Installer的clearAppData接口,最终应该是通过native实现    clearAppDataLIF(pkg, userId,            StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE);    //这里是清除keystore daemon中进程对应的签名记录    final int appId = UserHandle.getAppId(pkg.applicationInfo.uid);    removeKeystoreDataIfNeeded(userId, appId);    //根据user的状态,定义flag    UserManagerInternal umInternal = getUserManagerInternal();    final int flags;    if (umInternal.isUserUnlockingOrUnlocked(userId)) {        flags = StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE;    } else if (umInternal.isUserRunning(userId)) {        flags = StorageManager.FLAG_STORAGE_DE;    } else {        flags = 0;    }    //最终调用prepareAppDataContentsLeafLIF    //若上述flags包含FLAG_STORAGE_CE且应用包含32位的library时,创建一个native library symlink    //说实话,完全不知道清除应用数据时,在这里调用这个干啥.........    prepareAppDataContentsLIF(pkg, userId, flags);}

从代码来看,clearApplicationUserDataLIF主要通过clearAppDataLIF函数来进行实际的数据清理工作。
clearAppDataLIF函数将通过native函数来完成实际的工作,这里我们不继续深入了。
除此之外,clearApplicationUserDataLIF还涉及权限、签名的清理等工作。

个人感觉,PKMS的代码涉及许多数据结构,读起来太晦涩了。
例如上面的resetUserChangesToRuntimePermissionsAndFlagsLPw、prepareAppDataContentsLIF函数,真心有种读不懂的感觉。
要完全搞清这部分内容,可能需要加log,编译rom跑一下了。

三、总结
至此,除了部分细节外,我们大概了解从设置界面清除应用数据的大体流程。
从代码上容易看出:
对于一个非persistent进程来说,一旦清除它的数据,将导致进程被杀死。
对于persistent进程而言,即使清除了它的数据,它也不会被杀掉。
此外,进程被清理数据后,AMS将发送广播,但被清理掉的进程不会收到该广播
(从代码来看,应该是进程广播接收相关的组件被清理掉了)。
自己测试过,即使静态注册的广播接收器,也收不到该广播。

这种设计其实就带来了问题:
一个persistent进程的数据被清理后,整个进程的状态其实就不正常了,
但它似乎并没有手段得知自己被清理,这是否会引起persistent进程不必要的crash?
自己碰到了一个问题,persistent进程的数据被清理后没有被杀掉,
但获取不到之前申请的权限了。
好在persistent进程crash后会重新启动,让一切从头开始。
目前,还不知道android这一块的逻辑为什么这么设计。

看到有些厂商的persistent应用禁止被清理数据,不知道是否与这部分流程有关?

自己现在暂时离开了厂商,以前也没接触这部分逻辑,
实在没有手段来解答了,希望能碰到高手不吝赐教。

原创粉丝点击