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应用禁止被清理数据,不知道是否与这部分流程有关?
自己现在暂时离开了厂商,以前也没接触这部分逻辑,
实在没有手段来解答了,希望能碰到高手不吝赐教。
- Android O :清除应用数据
- android 清除其他应用数据
- Android清除应用缓存数据
- android清除应用缓存数据
- android应用数据清除管理器
- android 中清除应用的数据会清除哪些数据
- android 中清除应用的数据会清除哪些数据
- android退出应用时候清除数据
- Android本应用数据清除管理器
- Android应用将"清除数据"项变为"管理空间",自定义数据清除
- Android一键清除某个应用下的数据
- android 在清除应用数据后,会重复生成shortcut
- android 清除data/data/ 下其他应用的数据
- Android应用数据、缓存的清除和获取缓存大小
- android 设置中清除数据 重启应用
- Android 清除应用缓存
- Android清除应用缓存
- Android存储扩展学习-----应用的清除数据和清除缓存
- Python入门——字典
- java学习笔记(六)IO
- 购物车
- ucore lab1
- c++类实现二叉树的基本操作a
- Android O :清除应用数据
- odoo10 report添加自定义CSS
- linux下创建、删除文件夹
- 动态规划
- Python之函数
- @ResponseBody的作用
- 高亮显示
- javaScript -throw作用
- 算法笔记 //11_旅行售货员问题