Android性能优化 ---(6)自启动管理

自启动管理简介

Android手机上安装的很多应用都会自启动,占用资源越来越多,造成系统卡顿等现象。良好的自启动管理方案管理后台自启动和开机自启动,这样就可以节约内存、优化系统流畅性等。

自启动管理流程分析

自启动管理的实现贯穿了应用APK(AutoRun.apk)以及framework的ActivityManagerService等。实现流程比较复杂,下面分阶段地介绍整个流程。

初始化

手机开机后,会发送开机广播:android.intent.action.BOOT_COMPLETED,凡是注册该广播的应用都可以接收到开机广播。(这里设计的AutoRun.apk会有一个为之提供服务apk,取名为AutoRunCore.apk)服务AutoRunCore.apk注册开机广播,接收到开机广播后,发送自定义广播(android.intent.action.security.BOOT_COMPLETED)给AutoRun.apk。

AutoRun.apk的自定义类BootBroadcastReceiver接收到AutoRunCore.apk发过来的广播后,开启线程设置禁止自启动列表和允许自启动列表;从R.array.security_boot_run_applist数组中获取允许自启动列表,定义为白名单,白名单中保存的均是非系统应用;从R.array.security_boot_forbidrun_applist数组中获取禁止自启动的列表,定义为黑名单,黑名单中保存的均是系统应用。最终调用系统接口将禁止自启动的应用(包括黑名单中的系统应用、不在白名单中的非系统应用)全部写到/data/system/forbidden_autorun_packages.xml文件中。

关键代码如下:

1、注册广播接收器

[java]  view plain  copy
  1.     android:name="com.android.BootBroadcastReceiver"  
  2.     android:exported="true"  
  3.     android:priority="1000"  
  4.     android:process="@string/process_security" >  
  5.       
  6.         "android.intent.action.BOOT_COMPLETED" />  
  7.         "android.intent.action.security.BOOT_COMPLETED" />  
  8.       
  9.   
2、接收到广播后处理方法

[java]  view plain  copy
  1.     @Override  
  2.     public void onReceive(final Context context, Intent intent) {  
  3.         action = intent.getAction();  
  4.         if((action != null)&&(action.equals("android.intent.action.security.BOOT_COMPLETED"))){  
  5.             action = ACTION_BOOT_COMPLETED;  
  6.         }  
  7.         ......  
  8.                 if(BOOT_AUTO_RUN && ACTION_BOOT_COMPLETED.equals(action)) {  
  9.                     Log.d("forbid auto run");  
  10.                     new Thread(new Runnable() {  
  11.                         @Override  
  12.                         public void run() {  
  13.                             // TODO Auto-generated method stub  
  14.                             SharedPreferences preferences = context.getSharedPreferences(  
  15.                                     "forbidrun_appList", Context.MODE_PRIVATE);  
  16.                             boolean isInit = preferences.getBoolean("initflag"false);  
  17.                             long lastModif = preferences.getLong("fileModif"0);  
  18.                             long fileModif = 0;  
  19.                             File config = new File("data/system/seccenter/"  
  20.                                     + APPAUTORUN_CONFIG_FILE);//APPAUTORUN_CONFIG_FILE = "seccenter_appautorun_applist.xml"  
  21.                             pm = context.getPackageManager();  
  22.                             List autorun_appList = new ArrayList();  
  23.                             if (config.exists()) {  
  24.                                 fileModif = config.lastModified();  
  25.                             }  
  26.                             if (!isInit || (fileModif > lastModif)) {  
  27.                                 if (config.exists()) {  
  28.                                     try {  
  29.                                         autorun_appList = "color:#009900;">parseXML(config);  
  30.                                     } catch (Exception e) {  
  31.                                         // TODO Auto-generated catch block  
  32.                                         e.printStackTrace();  
  33.                                     }  
  34.                                 } else {  
  35.                                     autorun_appList = Arrays.asList(context.getResources()  
  36.                                             .getStringArray(R.array.security_boot_run_applist));  
  37.                                 }  
  38.   
  39.                                 List forbidrun_appList = Arrays.asList(context.getResources()  
  40.                                         .getStringArray(R.array.security_boot_forbidrun_applist));  
  41.                                 List allAppInfo = pm.getInstalledApplications(0);  
  42.                                 for (ApplicationInfo appInfo : allAppInfo) {  
  43.                                     if (!Util."color:#009900;">isSystemApp(appInfo)  
  44.                                             && !autorun_appList.contains(appInfo.packageName)) {  
  45.                                         SystemApiUtil."color:#009900;">fobidAutoRun(context,appInfo.packageName, true);  
  46.                                     } else if (Util.isSystemApp(appInfo)  
  47.                                             && forbidrun_appList.contains(appInfo.packageName)) {  
  48.                                         SystemApiUtil.fobidAutoRun(context,appInfo.packageName, true);  
  49.                                     }  
  50.                                 }  
  51.                                 SharedPreferences preference = context.getSharedPreferences("forbidrun_appList",  
  52.                                                 Context.MODE_PRIVATE);  
  53.                                 SharedPreferences.Editor editor = preference.edit();  
  54.                                 editor.putBoolean("initflag"true);  
  55.                                 editor.putLong("fileModif", fileModif);  
  56.                                 editor.commit();  
  57.                             }  
  58.                         }  
  59.                     }, "btReForbidRun").start();                         
  60. }  
在上面的处理方法中使用的几个封装的方法,下面逐一看下。先看parseXML()方法,

[java]  view plain  copy
  1. public List parseXML(File xmlFile) throws Exception {  
  2.     List appList = new ArrayList();   
  3.     DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();  
  4.     DocumentBuilder db = dbf.newDocumentBuilder();  
  5.   
  6.     Document doc = db.parse(xmlFile);  
  7.     NodeList nodeList = doc.getElementsByTagName("appname");  
  8.     Node fatherNode = nodeList.item(0);  
  9.   
  10.     NodeList childNodes = fatherNode.getChildNodes();  
  11.     for (int j = 0; j < childNodes.getLength(); j++) {  
  12.         Node childNode = childNodes.item(j);  
  13.         if (childNode instanceof Element) {  
  14.             appList.add(childNode.getFirstChild().getNodeValue());  
  15.         }  
  16.     }  
  17.     return appList;  
  18. }  
接着看下Util.isSystemApp()方法,

[java]  view plain  copy
  1. public static boolean isSystemApp(ApplicationInfo appInfo) {  
  2.     boolean flag = false;  
  3.   
  4.     if (appInfo != null  
  5.             && ((appInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0 || (appInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0)) {  
  6.         flag = true;  
  7.     }  
  8.     return flag;  
  9. }  
再接着看下forbitAutorun()方法,这里是通过反射方式调用ActivityManager中的方法。

[java]  view plain  copy
  1. private static Method mSetStopAutoStart = null;  
  2. private static Method mgetStopAutoStart = null;  
[java]  view plain  copy
  1. public static void fobidAutoRun(Context context, String pkg, boolean isFobid) {  
  2.   
  3.         if (isForceStopAutoStartMethodExist(context)) {  
  4.             try {  
  5.                 ActivityManager am = (ActivityManager) context  
  6.                         .getSystemService(Context.ACTIVITY_SERVICE);  
  7.                 mSetStopAutoStart.invoke(am, pkg,  
  8.                         isFobid);  
  9.             } catch (IllegalArgumentException e) {  
  10.                 e.printStackTrace();  
  11.             } catch (IllegalAccessException e) {  
  12.                 e.printStackTrace();  
  13.             } catch (InvocationTargetException e) {  
  14.                 e.printStackTrace();  
  15.             }  
  16.         }  
  17.     }  
  18.     public static boolean isForceStopAutoStartMethodExist(Context context) {  
  19.         synchronized (SystemApiUtil.class) {  
  20.             if (mSetStopAutoStart == null) {  
  21.                 try {  
  22.                     ActivityManager am = (ActivityManager) context  
  23.                             .getSystemService(Context.ACTIVITY_SERVICE);  
  24.                         mSetStopAutoStart = am.getClass().getMethod(  
  25.                                 "setForbiddenAutorunPackages", String.class,  
  26.                                 boolean.class);  
  27.   
  28.                     mgetStopAutoStart = am.getClass()  
  29.                             .getMethod("getForbiddenAutorunPackages");  
  30.                 } catch (SecurityException e) {  
  31.                     // TODO Auto-generated catch block  
  32.                     e.printStackTrace();  
  33.                 } catch (NoSuchMethodException e) {  
  34.                     // TODO Auto-generated catch block  
  35.                     e.printStackTrace();  
  36.                 }  
  37.             }  
  38.             if (mSetStopAutoStart == null || mgetStopAutoStart == null) {  
  39.                 return false;  
  40.             } else {  
  41.                 return true;  
  42.             }  
  43.         }  
  44.     }  

总结:在AutoRun接收到广播后,根据数组security_boot_run_applist(允许自启动白名单)和数组security_boot_forbidrun_applist(禁止自启动黑名单)设置好了禁止自启动的xml文件(forbidden_autorun_packages.xml)。

流程图如下:

AutoRun界面初始化

AutoRun在启动之前就已经设置好了黑白名单,界面初始化过程就是界面加载的过程。

(1)、AutoRun通过调用ActivityManagerService封装的getForbiddenAutoRunPackages()方法获取禁止自启动的应用列表。该方法从/data/system/forbidden_autorun_packages.xml文件中读取应用,保存到mPackagesForbiddenAutoRun全局变量中,同时也把应用包含的服务加入mServicesForbiddenAutoRun中。

(2)、security_autorun_notdisplayed_whitelist 数组表示允许自启动不显示白名单,即不显示在自启动管理界面上的应用白名单。

如果这个白名单中的应用包含getForbiddenAutoRunPackages方法返回的数组中的应用,该应用取消禁止自启动(允许自启动),并且不会显示在自启动管理界面。

取消禁止自启动的处理方法就是通过ActivityManagerService封装的setForbiddenAutoRunPackages()方法实现的。

注意:

1、第三方应用默认禁止自启动,会设置一个自启动的白名单(R.array.security_boot_run_applist),白名单里的应用就会允许自启动。

2、系统应用默认允许自启动,会设置一个禁止自启动的黑名单(R.array.security_boot_forbidrun_applist),黑名单里的应用会被禁止自启动。

具体代码如下:

[java]  view plain  copy
  1. public void onResume() {  
  2.     super.onResume();  
  3.   
  4.     checkedItemCount = 0;//设置选中的个数  
  5.     refreshList();  
  6. }  
[java]  view plain  copy
  1. private void refreshList() {  
  2.     ......  
  3.     new Thread(new InitViewRunable()).start();  
  4. }  
[java]  view plain  copy
  1. class InitViewRunable implements Runnable {  
  2.   
  3.     @Override  
  4.     public void run() {  
  5.   
  6.         initData();  
  7.         Message.obtain(mHandler, MSG_INIT_DATA_COMPLETED).sendToTarget();  
  8.   
  9.     }  
  10. }  
[java]  view plain  copy
  1. private synchronized void initData() {  
  2.     applicationList.clear();//清空列表  
  3.     forbidAutoRunList = speedLogic."color:#009900;">getForbidAutoRunList(true);//获取禁止自启动应用列表  
  4.   
  5.     SharedPreferences preferences = mContext.getSharedPreferences(  
  6.             "forbidrun_appList", Context.MODE_PRIVATE);  
  7.     boolean isInit = preferences.getBoolean("isInit"false);  
  8.     // remove the package from the forbidAutoRunList whith need to autorun  
  9.     if (!isInit) {//第一次初始化  
  10.             if (isAdded() && forbidAutoRunList != null) {  
  11.                 List autorun_whiteList = Arrays.asList(mContext  
  12.                         .getResources().getStringArray(  
  13.                             R.array."color:#009900;">security_autorun_applist));//从数组中读取允许自启动的列表  
  14.                 for (String string : autorun_whiteList) {//security_autorun_applist数组中包含禁止自启动的应用,设置为允许自启动  
  15.                     if (forbidAutoRunList.contains(string)) {  
  16.                         forbidAutoRunList.remove(string);  
  17.                         SystemApiUtil.fobidAutoRun(mContext, string, false);//更新禁止自启动数据,重新设置下xml文件  
  18.                     }  
  19.                 }  
  20.             }  
  21.         SharedPreferences.Editor editor = preferences.edit();  
  22.         editor.putBoolean("isInit"true);  
  23.         editor.commit();  
  24.     }  
  25.     canAutoRunMap = speedLogic."color:#009900;">getCanAutoRunList(false);//获得允许自启动应用列表,返回已封装的对象  
  26.     if (canAutoRunMap.size() != 0) {  
  27.         Map tmpAutoRunMap = new HashMap();  
  28.         tmpAutoRunMap.putAll(canAutoRunMap);  
  29.         Iterator iter = tmpAutoRunMap.entrySet().iterator();//将canAutoRunMap保存到临时Map对象中,并迭代  
  30.   
  31.         ApplicationInfo appInfo;  
  32.         while (iter.hasNext()) {  
  33.             Map.Entry entry = (Map.Entry) iter.next();  
  34.             String pkgName = (String) entry.getKey();//取出包名  
  35.             ListBean bean = new ListBean();  
  36.             bean.setPkgName(pkgName);//设置ListBean对象的包名  
  37.             try {  
  38.                 appInfo = pm.getApplicationInfo(pkgName, 0);//根据包名获取对应的ApplicationInfo对象  
  39.   
  40.                 String packageName = appInfo.packageName;  
  41.                 Bitmap icon = Util."color:#009900;">getIcon(mContext, packageName, null);//获取图标  
  42.                 if (isAdded()) {//设置ListBean对象的icon  
  43.                     if (null == icon) {  
  44.                         bean.setListIcon(appInfo.loadIcon(pm));  
  45.                     } else {  
  46.                         bean.setListIcon(new BitmapDrawable(icon));  
  47.                     }  
  48.                 }  
  49.   
  50.                 bean.setListTitle((String) appInfo.loadLabel(pm));  
  51.                 bean.setBoot((Boolean) entry.getValue());//设置是否禁止自启动  
  52.                 if (forbidAutoRunList != null  
  53.                         && forbidAutoRunList.contains(pkgName)) {  
  54.                     bean.setChecked(true);//如果包含在禁止自启动名单里,设置勾选状态  
  55.                 }  
  56.                 if (type == SYSTEM && Util.isSystemApp(appInfo)) {  
  57.                     if (bean.isChecked) {  
  58.                         checkedItemCount++;  
  59.                     }  
  60.                     applicationList.add(bean);  
  61.                     Log.d("forbidAutoRunList of SystemApp" + pkgName);  
  62.                 } else if (type == PERSONAL && !Util.isSystemApp(appInfo)) {  
  63.                     if (bean.isChecked) {  
  64.                         checkedItemCount++;  
  65.                     }  
  66.                     applicationList.add(bean);  
  67.                     Log.d("forbidAutoRunList of PersonalApp" + pkgName);  
  68.                 }  
  69.   
  70.             } catch (NameNotFoundException e) {  
  71.                 e.printStackTrace();  
  72.             }  
  73.         }  
  74.     }  
  75. }  
initData()完毕后,发送消息更新界面。在initData()方法中用到了几个封装的方法,分别看下。

[java]  view plain  copy
  1. public List getForbidAutoRunList(boolean forceFresh) {  
  2.     if (forbidAutoRunList == null || forceFresh) {  
  3.         forbidAutoRunList = SystemApiUtil.getForbiddenAutorunPackages(mContext);//调用系统接口  
  4.         if (forbidAutoRunList != null) {  
  5.             List autorun_whiteList = Arrays.asList(mContext.getResources()  
  6.                     .getStringArray(R.array.security_autorun_notdisplayed_whitelist));  
  7.             for (String string : autorun_whiteList) {//如果从xml文件中获取的禁止自启动列表中,包含允许自启动不显示数组中的应用,则设置该应用允许自启动  
  8.                 if (forbidAutoRunList.contains(string)) {  
  9.                     Log.d("set autorun packageName:" + string);  
  10.                     SystemApiUtil.fobidAutoRun(mContext, string, false);//设置允许自启动,并自动更新xml文件中的应用  
  11.                 }  
  12.             }  
  13.         }  
  14.     }  
  15.     return forbidAutoRunList;  
  16. }  
getAutoRunList()方法获取允许自启动列表。
[java]  view plain  copy
  1. public Map getCanAutoRunList(boolean forceFresh) {  
  2.     SharedPreferences pref = mContext.getSharedPreferences(PRE_NAME, Context.MODE_PRIVATE);  
  3.     Editor editor = pref.edit();  
  4.     boolean hasApkInstalled = pref.getBoolean("hasApkInstalled"false);  
  5.     if (autoRunMap.size() == 0 || forceFresh || hasApkInstalled) {  
  6.   
  7.         editor.putBoolean("hasApkInstalled",false);  
  8.         editor.commit();  
  9.         autoRunMap = new HashMap();  
  10.         List forbidList = getForbidAutoRunList(forceFresh);//获取最新状态的禁止自启动列表  
  11.   
  12.         List allAppInfo = null;  
  13.         try {  
  14.             allAppInfo = mPm.getInstalledApplications(0);//获取安装的第三方应用  
  15.         } catch (Exception e) {  
  16.             e.printStackTrace();  
  17.         }  
  18.   
  19.         if (allAppInfo != null) {  
  20.             for (ApplicationInfo appInfo : allAppInfo) {  
  21.                 boolean isContains = false;  
  22.                 if (forbidList != null  
  23.                         && forbidList.contains(appInfo.packageName)) {  
  24.                     // That is in forbidden list  
  25.                     isContains = true;//如果在禁止自启动列表中,设置isContains为true。  
  26.                 }  
  27.                 if ("color:#009900;">isExcludeForAutoBoot(appInfo, isContains)) {//过滤掉launcher、白名单(security_autorun_notdisplayed_whitelist)中的应用。  
  28.                     continue;  
  29.                 }  
  30.                 if ("color:#009900;">isPackageHasReceivers(appInfo.packageName)) {//判断是否注册了广播接收器  
  31.                     Boolean isBoot = false;  
  32.                     if ("color:#009900;">isPackageHasReceiverPermission(appInfo.packageName,  
  33.                             Manifest.permission.RECEIVE_BOOT_COMPLETED)) {  
  34.                         isBoot = true;//注册了开机广播  
  35.                     }  
  36.                     autoRunMap.put(appInfo.packageName, isBoot);  
  37.                 }  
  38.             }  
  39.         }  
  40.     }  
  41.     return autoRunMap;  
  42. }  
我们再看下isExcludeForAutoBoot()方法,该方法排除掉launcher应用,排除掉白名单(security_autorun_notdisplayed_whitelist)中的应用。

[java]  view plain  copy
  1. private boolean isExcludeForAutoBoot(ApplicationInfo appInfo, Boolean isContains) {  
  2.   
  3.     if (appInfo == null) {  
  4.         return true;  
  5.     }  
  6.   
  7.     if (Util.isSystemApp(appInfo)) {//系统应用  
  8.         List launcherPkgs = SpeedUtil."color:#009900;">getLauncherPkgs(mContext);//获取launcher应用  
  9.   
  10.         // not contain launcher icon ,not list it  
  11.         if (!launcherPkgs.contains(appInfo.packageName)) {  
  12.             return true;  
  13.         }//过滤掉launcher应用。  
  14.     }  
  15.   
  16.     // judge whether contain in white list,if it is ,do not list it  
  17.     if ("color:#009900;">isInAutoBootWhiteList(appInfo)) {//这里再过滤一次是否在白名单中  
  18.         if (isContains) {//如果设置了禁止自启动,则设置为允许自启动  
  19.             Log.d("i donot think will come in.");  
  20.             SystemApiUtil.fobidAutoRun(mContext, appInfo.packageName, false);  
  21.         }  
  22.         return true;  
  23.     } else {  
  24.         return false;  
  25.     }  
  26. }  
再看下isPackageHasReceivers()方法,判断是否注册了广播接收器。
[java]  view plain  copy
  1. private boolean isPackageHasReceivers(String packageName) {  
  2.   
  3.     if (mPm == null) {  
  4.         mPm = mContext.getPackageManager();  
  5.     }  
  6.     boolean hasReceivers = true;  
  7.     try {  
  8.         PackageInfo packinfo = mPm.getPackageInfo(packageName, PackageManager.GET_RECEIVERS);  
  9.         if (packinfo.receivers == null || packinfo.receivers.length <= 0) {  
  10.             hasReceivers = false;  
  11.         }  
  12.     } catch (NameNotFoundException e) {  
  13.         hasReceivers = false;  
  14.         e.printStackTrace();  
  15.     }  
  16.   
  17.     return hasReceivers;  
  18. }  
看下封装的isPackageHasReceiverPermission()方法,判断是否有接收开机广播的权限。
[java]  view plain  copy
  1. public boolean isPackageHasReceiverPermission(String packageName, String permissionName) {  
  2.   
  3.     boolean isReceiverFunctionEnable = true;  
  4.     if (mPm == null) {  
  5.         mPm = mContext.getPackageManager();  
  6.     }  
  7.     if (permissionName != null  
  8.             && PackageManager.PERMISSION_GRANTED != mPm.checkPermission(permissionName,  
  9.                     packageName)) {  
  10.         isReceiverFunctionEnable = false;  
  11.     }  
  12.     return isReceiverFunctionEnable;  
  13. }  
流程图如下:


AutoRun处理机制

1、禁止自启动

禁止自启动:调用系统接口ActivityManagerService类封装的setForbiddenAutorunPackages(final StringpackageName, boolean bAdd),此时bAdd参数为true,将指定的包名添加到禁止自启动列表mPackagesForbiddenAutoRun中,将应用包含的服务添加到禁止自启动服务列表mServicesForbiddenAutoRun中,最后将禁止自启动列表中的数据重新更新到/data/system/forbidden_autorun_packages.xml文件中。

具体代码如下:

[java]  view plain  copy
  1. /** 
  2.  * @hide 
  3.  */  
  4. public boolean setForbiddenAutorunPackages(final String packageName,  
  5.         boolean bAdd) {  
  6.     boolean bResult = false;  
  7.   
  8.     if (Feature.FEATURE_FORBID_APP_AUTORUN) {  
  9.        // Slog.v(TAG, "ctl packagename=" + packageName + "bAdd=" + bAdd);  
  10.         synchronized (this) {  
  11.             if ((null != packageName) && (null != mPackagesForbiddenAutoRun)  
  12.                   && (null != mServicesForbiddenAutoRun)) {  
  13.   
  14.                 final PackageManager pm = mContext.getPackageManager();  
  15.                 PackageInfo pi = null;  
  16.   
  17.                 try {  
  18.                     pi = pm.getPackageInfo(packageName,  
  19.                     PackageManager.GET_SERVICES);  
  20.                 } catch (NameNotFoundException e) {  
  21.                     // TODO Auto-generated catch block  
  22.                     e.printStackTrace();  
  23.                 }  
  24.   
  25.                 int temp_size = mPackagesForbiddenAutoRun.size();  
  26.                 if (bAdd) {  
  27.                     if (mPackagesForbiddenAutoRun.indexOfKey(packageName.hashCode())<0) {  
  28.                         mPackagesForbiddenAutoRun.append(packageName.hashCode(),packageName);  
  29.                         bResult = temp_size!=mPackagesForbiddenAutoRun.size();  
  30.                         if (pi != null && null != pi.services) {  
  31.                             for (ServiceInfo service : pi.services) {  
  32.                               if (0>mServicesForbiddenAutoRun.indexOfKey(service.processName.hashCode()))  {  
  33.                                   Slog.i(TAG, "ADD forbid autorun service: "+ service.processName);  
  34.                                   mServicesForbiddenAutoRun.append(service.processName.hashCode(), service.processName);  
  35.                               }  
  36.                             }  
  37.                         }  
  38.                     }  
  39.                 } else {  
  40.                     mPackagesForbiddenAutoRun.delete(packageName.hashCode());  
  41.                     bResult = mPackagesForbiddenAutoRun.size()!=temp_size;  
  42.                         if (pi != null && pi.services != null) {  
  43.                             for (ServiceInfo service : pi.services) {  
  44.                             mServicesForbiddenAutoRun.delete(service.processName.hashCode());  
  45.                         }  
  46.                     }  
  47.                 }  
  48.             }  
  49.   
  50.             writeFilterPackages();  
  51.         }  
  52.     }  
  53.   
  54.     return bResult;  
  55. }  
[java]  view plain  copy
  1. void writeFilterPackages(){  
  2.   
  3.     if ( mFileFilter == null) {  
  4.         return ;  
  5.     }  
  6.   
  7.     if (mFileFilter.exists()) {  
  8.         mFileFilter.delete();  
  9.     }  
  10.   
  11.     try {  
  12.         FileOutputStream fstr = new FileOutputStream(mFileFilter);  
  13.         BufferedOutputStream str = new BufferedOutputStream(fstr);  
  14.   
  15.         //XmlSerializer serializer = XmlUtils.serializerInstance();  
  16.         XmlSerializer serializer = new FastXmlSerializer();  
  17.         serializer.setOutput(str, "utf-8");  
  18.         serializer.startDocument(nulltrue);  
  19.         //serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);  
  20.   
  21.         serializer.startTag(null"packages");  
  22.   
  23.         int numPackages = mPackagesForbiddenAutoRun.size();  
  24.   
  25.         //Slog.i(TAG, " forbid autorun numPackages := "+ numPackages);  
  26.   
  27.         forint i = 0; i < numPackages; ++i){  
  28.             serializer.startTag(null"package");  
  29.             String apkPackageName = mPackagesForbiddenAutoRun.valueAt(i);  
  30.             //Slog.i(TAG, "forbid autorun package: "+apkPackageName);  
  31.             serializer.attribute(null"name", apkPackageName);  
  32.             serializer.endTag(null"package");  
  33.         }  
  34.   
  35.         serializer.endTag(null"packages");  
  36.   
  37.         serializer.endDocument();  
  38.   
  39.         str.flush();  
  40.         FileUtils.sync(fstr);  
  41.         str.close();  
  42.   
  43.         FileUtils.setPermissions(mFileFilter.toString(),  
  44.                 FileUtils.S_IRUSR|FileUtils.S_IWUSR  
  45.                 |FileUtils.S_IRGRP|FileUtils.S_IWGRP  
  46.                 |FileUtils.S_IROTH,  
  47.                 -1, -1);  
  48.   
  49.     } catch(java.io.IOException e) {  
  50.         Slog.w(TAG, "Unable to write broadcast filter, current changes will be lost at reboot", e);  
  51.     }  
  52. }  
[java]  view plain  copy
  1. public List getForbiddenAutorunPackages( ) {  
  2.   
  3.     if (Feature.FEATURE_FORBID_APP_AUTORUN) {  
  4.         // Slog.v(TAG, "ctl packagename YLGetForbiddenAutorunPackages" );  
  5.         synchronized (this) {  
  6.             ArrayList res = new ArrayList();  
  7.             for (int i = 0; i < mPackagesForbiddenAutoRun.size(); i++) {  
  8.                 res.add(mPackagesForbiddenAutoRun.valueAt(i));  
  9.             }  
  10.             return res;  
  11.         }  
  12.     }  
  13. }  

流程图如下:


2、允许自启动

允许自启动:调用系统接口ActivityManagerService类封装的setForbiddenAutorunPackages( final String packageName, boolean bAdd)方法,此时flag参数为false,将设置的应用的包名从全局变量mPackagesForbiddenAutoRun中移除,包括该应用包含的服务从全局变量mServicesForbiddenAutoRun中移除,最后将mPackagesForbiddenAutoRun列表中的数据重新更新到/data/system/forbidden_autorun_packages.xml文件。

流程图如下:


3、动态安装

当一个应用安装时,AppInstallReceiver接收到android.intent.action.PACKAGE_ADDED广播,如果该应用不在security_autorun_displayed_whitelist(允许自启动显示白名单)及security_autorun_notdisplayed_whitelist(允许自启动不显示白名单)中,并且该应用又是第三方应用,则将该应用加入禁止自启动列表。

4、AMS实现机制

1.开机自启动

(1)、开机启动过程中,会调用到ActivityManagerService的systemReady()方法,在该方法中读取/data/system/forbidden_autorun_packages.xml文件中的数据,并将其保存到全局数组变量mPackagesForbiddenAutoRun中。

具体代码如下:

[java]  view plain  copy
  1. if (Feature.FEATURE_FORBID_APP_AUTORUN){  
  2.         mFileFilter = new File(PATH_PACKAGES_FILTER);  
  3.         if(!mFileFilter.exists()){  
  4.              try {  
  5.                  mFileFilter.createNewFile();  
  6.                 } catch (IOException e) {  
  7.                  Slog.d(TAG, "Failed to creat black list file!!!");  
  8.                 }  
  9.          }  
  10.         readFilterPackages();  
  11.  }  
readFilterPackages()方法读取禁止自启动列表,完成mPackagesForbiddenAutoRun、mServicesForbiddenAutoRun数组的初始化。

[java]  view plain  copy
  1. private static final String PATH_PACKAGES_FILTER = "/data/system/forbidden_autorun_packages.xml";  
  2. private boolean readFilterPackages(){  
  3.     FileInputStream str = null;  
  4.   
  5.     if ( mFileFilter == null) {  
  6.         return false;  
  7.     }  
  8.   
  9.     if (!mFileFilter.exists()) {  
  10.         Log.d(TAG, PATH_PACKAGES_FILTER + "does not exist" );  
  11.         return false;  
  12.     }  
  13.   
  14.     try {  
  15.         str = new FileInputStream(mFileFilter);  
  16.         XmlPullParser parser = Xml.newPullParser();  
  17.         parser.setInput(str, null);  
  18.         int type;  
  19.         while ((type=parser.next()) != XmlPullParser.START_TAG  
  20.                    && type != XmlPullParser.END_DOCUMENT) {  
  21.             ;  
  22.         }  
  23.   
  24.         if (type != XmlPullParser.START_TAG) {  
  25.             Log.d(TAG, "No start tag found in in" + PATH_PACKAGES_FILTER  );  
  26.             return false;  
  27.         }  
  28.   
  29.         int outerDepth = parser.getDepth();  
  30.         while ((type=parser.next()) != XmlPullParser.END_DOCUMENT  
  31.                && (type != XmlPullParser.END_TAG  
  32.                        || parser.getDepth() > outerDepth)) {  
  33.             if (type == XmlPullParser.END_TAG  
  34.                     || type == XmlPullParser.TEXT) {  
  35.                 continue;  
  36.             }  
  37.             String tagName = parser.getName();  
  38.             if (tagName.equals("package")) {  
  39.                String delPackageName = parser.getAttributeValue(null,"name");  
  40.   
  41.                final PackageManager pm = mContext.getPackageManager();  
  42.                PackageInfo pi = null;  
  43.   
  44.                try {  
  45.                    pi = pm.getPackageInfo(delPackageName, PackageManager.GET_SERVICES);  
  46.                } catch (NameNotFoundException e) {  
  47.                    // TODO Auto-generated catch block  
  48.                    e.printStackTrace();  
  49.                }  
  50.   
  51.                Slog.i(TAG, "forbid autorun pakacge: "+ delPackageName);  
  52.                mPackagesForbiddenAutoRun.append(delPackageName.hashCode(),delPackageName);  
  53.   
  54.                if (pi != null && null != pi.services) {  
  55.                    for (ServiceInfo service : pi.services) {  
  56.                        if (0>mServicesForbiddenAutoRun.indexOfKey(service.processName.hashCode())) {  
  57.                           Slog.i(TAG, "forbid autorun service: "+ service.processName);  
  58.                           mServicesForbiddenAutoRun.append(service.processName.hashCode(),service.processName);  
  59.                        }  
  60.                    }  
  61.                }  
  62.   
  63.             }else {  
  64.                 Slog.w(TAG, "Unknown element under : "  
  65.                       + parser.getName());  
  66.                 XmlUtils.skipCurrentTag(parser);  
  67.             }  
  68.         }  
  69.         str.close();  
  70.     } catch(XmlPullParserException e) {  
  71.         Slog.e(TAG, "Error reading "+ PATH_PACKAGES_FILTER, e);  
  72.   
  73.     } catch(java.io.IOException e) {  
  74.         Slog.e(TAG, "Error reading "+ PATH_PACKAGES_FILTER, e);  
  75.     }  
  76.     return true;  
  77. }  

(2)、系统在启动过程中会拉起一些重要的应用,而大多数应用是在启动完成之后拉起的。这里解释下mProcessesOnHold,这是一个数组列表,保存ProcessRecord对象,表示暂时挂起的进程列表,这些进程因尝试在系统启动(systemReady)完成之前启动,而被暂时挂起,当系统启动完成之后,会启动该列表中的进程。

我们看下源码解释:

[cpp]  view plain  copy
  1. /** 
  2.  * List of records for processes that someone had tried to start before the 
  3.  * system was ready.  We don't start them at that point, but ensure they 
  4.  * are started by the time booting is complete. 
  5.  */  
  6. final ArrayList mProcessesOnHold = new ArrayList();  

这部分在启动过程中不能拉起的应用就暂存在mProcessesOnHold列表中。因此,自启动管理在系统启动完成之前将mProcessesOnHold列表中包含的mPackagesForbiddenAutoRun中的应用排除,这样在系统启动完成后,就不会启动mPackagesForbiddenAutoRun中的应用。

在startProcessLocked方法中会调用Process.start方法开启线程,我们要做的就是从mProcessesOnHold中排除禁止自启动的应用,这样就实现开机禁止自启动了。

[java]  view plain  copy
  1. // If the system is not ready yet, then hold off on starting this  
  2. // process until it is.  
  3. if (!mProcessesReady  
  4.         && !isAllowedWhileBooting(info)  
  5.         && !allowWhileBooting  
  6.         && !isInBlackList(info)) {  
  7.     if (!mProcessesOnHold.contains(app)) {  
  8.         mProcessesOnHold.add(app);  
  9.     }  
  10.     if (DEBUG_PROCESSES) Slog.v(TAG_PROCESSES,  
  11.             "System not ready, putting on hold: " + app);  
  12.     checkTime(startTime, "startProcess: returning with proc on hold");  
  13.     return app;  
  14. }  
[java]  view plain  copy
  1. public boolean isInBlackList(final ApplicationInfo info)  
  2. {  
  3.     boolean ret = (info.flags & ApplicationInfo.FLAG_PERSISTENT) != ApplicationInfo.FLAG_PERSISTENT//revert for debugging crash  
  4.                    || (info.flags & ApplicationInfo.FLAG_SYSTEM) != ApplicationInfo.FLAG_SYSTEM;  
  5.   
  6.     String packageName = info.packageName;  
  7.         ret = ((mPackagesForbiddenAutoRun.indexOfKey(packageName.hashCode())>=0 ||  
  8.                     mServicesForbiddenAutoRun.indexOfKey(packageName.hashCode())>=0) &&  
  9.                     (mPackagesHasWidgetRun.indexOfKey(packageName.hashCode())<0));  
  10.         if(ret) Slog.v(TAG, "check isInBlackList:" + packageName + " ret=" + ret);  
  11.         return ret;  
  12. }  

2、应用后台自启动

自启动管理同样包含一个应用死掉后后台重新启动的过程。Service运行在前后台都可以,即表示可以运行在当前进程也可以运行在其他进程中,Service可以为多个App共享使用,是通过binder机制来实现的。当我kill掉一个带有服务的进程(没有调用stopService()),过一会该应用会自动重启。下面是代码的调用顺序,自下往上看一下:

com.android.server.am.ActiveServices.bringDownServiceLocked(ActiveServices.java)
com.android.server.am.ActiveServices.killServicesLocked(ActiveServices.java)
com.android.server.am.ActivityManagerService.cleanUpApplicationRecordLocked(ActivityManagerService.java)
com.android.server.am.ActivityManagerService.handleAppDiedLocked(ActivityManagerService.java)
com.android.server.am.ActivityManagerService.appDiedLocked(ActivityManagerService.java)
com.android.server.am.ActivityManagerService$AppDeathRecipient.binderDied(ActivityManagerService.java)

ActivityManagerService.handleAppDiedLocked 方法中对于在黑名单中的应用调用 PackageManagerService.setPackageStoppedState方法,将应用对应的Package设置为stop状态(如果一个应用正在灭亡,或已经灭亡,就要将其对应的package置于stop状态)。

如果服务重启,则在ActiveServices.killServicesLocked方法中调用bringDownServiceLocked方法,不允许重启,直接挂掉。


具体代码如下:
[java]  view plain  copy
  1. /** 
  2.  * Main function for removing an existing process from the activity manager 
  3.  * as a result of that process going away.  Clears out all connections 
  4.  * to the process.哈哈 
  5.  */  
  6. private final void handleAppDiedLocked(ProcessRecord app,  
  7.         boolean restarting, boolean allowRestart) {  
  8.     int pid = app.pid;  
  9.     boolean kept = cleanUpApplicationRecordLocked(app, restarting, allowRestart, -1);  
  10.     if (!kept && !restarting) {  
  11.         removeLruProcessLocked(app);  
  12.         if (pid > 0) {  
  13.             ProcessList.remove(pid);  
  14.         }  
  15.     }  
  16.   
  17.     if (mProfileProc == app) {  
  18.         clearProfilerLocked();  
  19.     }  
  20.   
  21.     if (Feature.FEATURE_FORBID_APP_AUTORUN ) {  
  22.         if (app != null && app.info != null &&  
  23.             isInBlackList(app.info)) {  
  24.             IPackageManager pm = AppGlobals.getPackageManager();  
  25.             try {  
  26.                 pm.setPackageStoppedState(app.info.packageName, true, UserHandle.getUserId(app.uid));  
  27.                 Slog.i(TAG, "forbid restart this app that is contained in forbidden list: "  + app.processName + " and remove its alarms & jobs.");  
  28.                 mContext.sendBroadcastAsUser(new Intent(ACTION_APP_KILL).putExtra(Intent.EXTRA_PACKAGES, new String[]{ app.info.packageName }),  
  29.                       UserHandle.ALL, "android.permission.DEVICE_POWER");  
  30.             } catch (RemoteException e) {  
  31.                 // TODO Auto-generated catch block  
  32.                 e.printStackTrace();  
  33.             } catch (IllegalArgumentException e) {  
  34.                 Slog.w(TAG, "Failed trying to unstop package "  
  35.                         + app.info.packageName + ": " + e);  
  36.                 e.printStackTrace();  
  37.             }  
  38.         }  
  39.     }  
  40.   
  41.  ......  
  42. }  
[java]  view plain  copy
  1. /* optimize memory */  
  2. else if (Feature.FEATURE_FORBID_APP_AUTORUN &&  
  3.         app != null && app.info != null && !isCTSMode() &&  
  4.         mAm.isInBlackList(app.info)) {  
  5.     Slog.w(TAG, "Service crashed " + sr.crashCount  
  6.             + " times, stopping: " + sr + "forbid restart this app that is contained in forbidden list: " + app.processName);  
  7.     EventLog.writeEvent(EventLogTags.AM_DESTROY_SERVICE ,  
  8.             sr.crashCount, sr.shortName, app.pid,  app.processName);  
  9.     bringDownServiceLocked(sr);  
  10. }  
[java]  view plain  copy
  1. private class QuickBootReceiver extends BroadcastReceiver {//AlarmManagerService中注册该广播接收器  
  2.     static final String ACTION_APP_KILL = "org.codeaurora.quickboot.appkilled";  
  3.   
  4.     public QuickBootReceiver() {  
  5.         IntentFilter filter = new IntentFilter();  
  6.         filter.addAction(ACTION_APP_KILL);  
  7.         getContext().registerReceiver(this, filter,  
  8.                 "android.permission.DEVICE_POWER"null);  
  9.     }  
  10.   
  11.     @Override  
  12.     public void onReceive(Context context, Intent intent) {  
  13.         if (Feature.FEATURE_FORBID_APP_AUTORUN) {  
  14.             Message msg = new Message();  
  15.             msg.what = ScheduleHandler.REMOVE_DEAD_APP_ALARM_REVEIVE;  
  16.             msg.obj = intent;  
  17.             mScheduleHandler.sendMessage(msg);  
  18.             return  ;  
  19.         }  
  20.   
  21.         String action = intent.getAction();  
  22.         String pkgList[] = null;  
  23.         if (ACTION_APP_KILL.equals(action)) {  
  24.             pkgList = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);  
  25.             if (pkgList != null && (pkgList.length > 0)) {  
  26.                 for (String pkg : pkgList) {  
  27.                     removeLocked(pkg);  
  28.                     for (int i=mBroadcastStats.size()-1; i>=0; i--) {  
  29.                         ArrayMap uidStats = mBroadcastStats.valueAt(i);  
  30.                         if (uidStats.remove(pkg) != null) {  
  31.                             if (uidStats.size() <= 0) {  
  32.                                 mBroadcastStats.removeAt(i);  
  33.                             }  
  34.                         }  
  35.                     }  
  36.                 }  
  37.             }  
  38.         }  
  39.     }  
  40. }  
[java]  view plain  copy
  1. case REMOVE_DEAD_APP_ALARM_REVEIVE:  
  2.     synchronized (mLock) {  
  3.         removeDeadAppAlarmReceiveLocked((Intent)msg.obj);  
  4.     }  
  5.     break;  
[java]  view plain  copy
  1. void removeDeadAppAlarmReceiveLocked(Intent intent) {  
  2.     Slog.d(TAG, "Receive for remove dead app alarm: " + intent.getAction());  
  3.   
  4.     String pkgList[] = null;  
  5.     pkgList = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);  
  6.     if (pkgList != null && (pkgList.length > 0)) {  
  7.         for (String pkg : pkgList) {  
  8.             removeLocked(pkg);  
  9.             for (int i=mBroadcastStats.size()-1; i>=0; i--) {  
  10.                 ArrayMap uidStats = mBroadcastStats.valueAt(i);  
  11.                 if (uidStats.remove(pkg) != null) {  
  12.                     if (uidStats.size() <= 0) {  
  13.                         mBroadcastStats.removeAt(i);  
  14.                     }  
  15.                 }  
  16.             }  
  17.         }  
  18.     }  
  19. }  
有些应用进程起来后,会在native层产生一个守护进程,应用进程挂掉后,native层进程又会把应用层进程拉起来,因此在ActivityManagerS.forceStopPackage方法中调用SystemProperties.set("ctl.start","cleandaemon");执行清理脚本。

3、广播启动场景

在Android系统中,系统发出一个广播,如果应用注册了这个广播,那么都会收到这个广播。自启动管理正是采用这种广播机制来实现禁止自启动的。系统在发送广播的时候,通过判断这个广播接收者是否在禁止自启动列表中,并且该应用没有被拉起来,就不给该应用发送广播。

具体处理方法在processNextBroadcast方法中处理
[java]  view plain  copy
  1. /* For do not send broadcast to the APP in blacklist that not running */  
  2. if (Feature.FEATURE_FORBID_APP_AUTORUN &&  
  3.         info != null && info.activityInfo != null &&  
  4.         info.activityInfo.packageName!="com.yulong.android.dualmmsetting"&&  
  5.         mService.isInBlackList(info.activityInfo.applicationInfo)) {  
  6.     Slog.v(TAG, "Skipping delivery of static ["  
  7.           + mQueueName + "] " + r + " for forbiden auto run");  
  8.     r.receiver = null;  
  9.     r.curFilter = null;  
  10.     r.state = BroadcastRecord.IDLE;  
  11.     scheduleBroadcastsLocked();  
  12.     return;  
  13. }  

4、AMS垃圾回收机制

打开程序或者有程序进入后台时都会执行updateOomAdjLocked()函数。可以在该方法中进行处理。


[java]  view plain  copy
  1. if (Feature.FEATURE_FORBID_APP_AUTORUN) {  
  2.     if (isInBlackList(app.info)) {  
  3.         app.kill("Mem less than " + ProcessList.MIN_MEM_LEVEL/(1024*1024) + "M, kill background!!"true);  
  4.   
  5.     }  
  6. }  

你可能感兴趣的:(Android性能优化 ---(6)自启动管理)