自启动管理简介

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、注册广播接收器

        <receiverandroid:name="com.android.BootBroadcastReceiver"android:exported="true"android:priority="1000"android:process="@string/process_security" ><intent-filter><action android:name="android.intent.action.BOOT_COMPLETED" /><action android:name="android.intent.action.security.BOOT_COMPLETED" /></intent-filter></receiver>

2、接收到广播后处理方法

    @Overridepublic void onReceive(final Context context, Intent intent) {action = intent.getAction();if((action != null)&&(action.equals("android.intent.action.security.BOOT_COMPLETED"))){action = ACTION_BOOT_COMPLETED;}......if(BOOT_AUTO_RUN && ACTION_BOOT_COMPLETED.equals(action)) {Log.d("forbid auto run");new Thread(new Runnable() {@Overridepublic void run() {// TODO Auto-generated method stubSharedPreferences preferences = context.getSharedPreferences("forbidrun_appList", Context.MODE_PRIVATE);boolean isInit = preferences.getBoolean("initflag", false);long lastModif = preferences.getLong("fileModif", 0);long fileModif = 0;File config = new File("data/system/seccenter/"+ APPAUTORUN_CONFIG_FILE);//APPAUTORUN_CONFIG_FILE = "seccenter_appautorun_applist.xml"pm = context.getPackageManager();List<String> autorun_appList = new ArrayList<String>();if (config.exists()) {fileModif = config.lastModified();}if (!isInit || (fileModif > lastModif)) {if (config.exists()) {try {autorun_appList = parseXML(config);} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();}} else {autorun_appList = Arrays.asList(context.getResources().getStringArray(R.array.security_boot_run_applist));}List<String> forbidrun_appList = Arrays.asList(context.getResources().getStringArray(R.array.security_boot_forbidrun_applist));List<ApplicationInfo> allAppInfo = pm.getInstalledApplications(0);for (ApplicationInfo appInfo : allAppInfo) {if (!Util.isSystemApp(appInfo)&& !autorun_appList.contains(appInfo.packageName)) {SystemApiUtil.fobidAutoRun(context,appInfo.packageName, true);} else if (Util.isSystemApp(appInfo)&& forbidrun_appList.contains(appInfo.packageName)) {SystemApiUtil.fobidAutoRun(context,appInfo.packageName, true);}}SharedPreferences preference = context.getSharedPreferences("forbidrun_appList",Context.MODE_PRIVATE);SharedPreferences.Editor editor = preference.edit();editor.putBoolean("initflag", true);editor.putLong("fileModif", fileModif);editor.commit();}}}, "btReForbidRun").start();
}

在上面的处理方法中使用的几个封装的方法,下面逐一看下。先看parseXML()方法,

    public List<String> parseXML(File xmlFile) throws Exception {List<String> appList = new ArrayList<String>(); DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();DocumentBuilder db = dbf.newDocumentBuilder();Document doc = db.parse(xmlFile);NodeList nodeList = doc.getElementsByTagName("appname");Node fatherNode = nodeList.item(0);NodeList childNodes = fatherNode.getChildNodes();for (int j = 0; j < childNodes.getLength(); j++) {Node childNode = childNodes.item(j);if (childNode instanceof Element) {appList.add(childNode.getFirstChild().getNodeValue());}}return appList;}

接着看下Util.isSystemApp()方法,

    public static boolean isSystemApp(ApplicationInfo appInfo) {boolean flag = false;if (appInfo != null&& ((appInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0 || (appInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0)) {flag = true;}return flag;}

再接着看下forbitAutorun()方法,这里是通过反射方式调用ActivityManager中的方法。

private static Method mSetStopAutoStart = null;
private static Method mgetStopAutoStart = null;
public static void fobidAutoRun(Context context, String pkg, boolean isFobid) {if (isForceStopAutoStartMethodExist(context)) {try {ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);mSetStopAutoStart.invoke(am, pkg,isFobid);} catch (IllegalArgumentException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();} catch (InvocationTargetException e) {e.printStackTrace();}}}public static boolean isForceStopAutoStartMethodExist(Context context) {synchronized (SystemApiUtil.class) {if (mSetStopAutoStart == null) {try {ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);mSetStopAutoStart = am.getClass().getMethod("setForbiddenAutorunPackages", String.class,boolean.class);mgetStopAutoStart = am.getClass().getMethod("getForbiddenAutorunPackages");} catch (SecurityException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (NoSuchMethodException e) {// TODO Auto-generated catch blocke.printStackTrace();}}if (mSetStopAutoStart == null || mgetStopAutoStart == null) {return false;} else {return true;}}}

总结:在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),黑名单里的应用会被禁止自启动。

具体代码如下:

    public void onResume() {super.onResume();checkedItemCount = 0;//设置选中的个数refreshList();}
    private void refreshList() {......new Thread(new InitViewRunable()).start();}
    class InitViewRunable implements Runnable {@Overridepublic void run() {initData();Message.obtain(mHandler, MSG_INIT_DATA_COMPLETED).sendToTarget();}}
    private synchronized void initData() {applicationList.clear();//清空列表forbidAutoRunList = speedLogic.getForbidAutoRunList(true);//获取禁止自启动应用列表SharedPreferences preferences = mContext.getSharedPreferences("forbidrun_appList", Context.MODE_PRIVATE);boolean isInit = preferences.getBoolean("isInit", false);// remove the package from the forbidAutoRunList whith need to autorunif (!isInit) {//第一次初始化if (isAdded() && forbidAutoRunList != null) {List<String> autorun_whiteList = Arrays.asList(mContext.getResources().getStringArray(R.array.security_autorun_applist));//从数组中读取允许自启动的列表for (String string : autorun_whiteList) {//security_autorun_applist数组中包含禁止自启动的应用,设置为允许自启动if (forbidAutoRunList.contains(string)) {forbidAutoRunList.remove(string);SystemApiUtil.fobidAutoRun(mContext, string, false);//更新禁止自启动数据,重新设置下xml文件}}}SharedPreferences.Editor editor = preferences.edit();editor.putBoolean("isInit", true);editor.commit();}canAutoRunMap = speedLogic.getCanAutoRunList(false);//获得允许自启动应用列表,返回已封装的对象if (canAutoRunMap.size() != 0) {Map<String, Boolean> tmpAutoRunMap = new HashMap<String, Boolean>();tmpAutoRunMap.putAll(canAutoRunMap);Iterator iter = tmpAutoRunMap.entrySet().iterator();//将canAutoRunMap保存到临时Map对象中,并迭代ApplicationInfo appInfo;while (iter.hasNext()) {Map.Entry entry = (Map.Entry) iter.next();String pkgName = (String) entry.getKey();//取出包名ListBean bean = new ListBean();bean.setPkgName(pkgName);//设置ListBean对象的包名try {appInfo = pm.getApplicationInfo(pkgName, 0);//根据包名获取对应的ApplicationInfo对象String packageName = appInfo.packageName;Bitmap icon = Util.getIcon(mContext, packageName, null);//获取图标if (isAdded()) {//设置ListBean对象的iconif (null == icon) {bean.setListIcon(appInfo.loadIcon(pm));} else {bean.setListIcon(new BitmapDrawable(icon));}}bean.setListTitle((String) appInfo.loadLabel(pm));bean.setBoot((Boolean) entry.getValue());//设置是否禁止自启动if (forbidAutoRunList != null&& forbidAutoRunList.contains(pkgName)) {bean.setChecked(true);//如果包含在禁止自启动名单里,设置勾选状态}if (type == SYSTEM && Util.isSystemApp(appInfo)) {if (bean.isChecked) {checkedItemCount++;}applicationList.add(bean);Log.d("forbidAutoRunList of SystemApp" + pkgName);} else if (type == PERSONAL && !Util.isSystemApp(appInfo)) {if (bean.isChecked) {checkedItemCount++;}applicationList.add(bean);Log.d("forbidAutoRunList of PersonalApp" + pkgName);}} catch (NameNotFoundException e) {e.printStackTrace();}}}}

initData()完毕后,发送消息更新界面。在initData()方法中用到了几个封装的方法,分别看下。

    public List<String> getForbidAutoRunList(boolean forceFresh) {if (forbidAutoRunList == null || forceFresh) {forbidAutoRunList = SystemApiUtil.getForbiddenAutorunPackages(mContext);//调用系统接口if (forbidAutoRunList != null) {List<String> autorun_whiteList = Arrays.asList(mContext.getResources().getStringArray(R.array.security_autorun_notdisplayed_whitelist));for (String string : autorun_whiteList) {//如果从xml文件中获取的禁止自启动列表中,包含允许自启动不显示数组中的应用,则设置该应用允许自启动if (forbidAutoRunList.contains(string)) {Log.d("set autorun packageName:" + string);SystemApiUtil.fobidAutoRun(mContext, string, false);//设置允许自启动,并自动更新xml文件中的应用}}}}return forbidAutoRunList;}

getAutoRunList()方法获取允许自启动列表。

    public Map<String, Boolean> getCanAutoRunList(boolean forceFresh) {SharedPreferences pref = mContext.getSharedPreferences(PRE_NAME, Context.MODE_PRIVATE);Editor editor = pref.edit();boolean hasApkInstalled = pref.getBoolean("hasApkInstalled", false);if (autoRunMap.size() == 0 || forceFresh || hasApkInstalled) {editor.putBoolean("hasApkInstalled",false);editor.commit();autoRunMap = new HashMap<String, Boolean>();List<String> forbidList = getForbidAutoRunList(forceFresh);//获取最新状态的禁止自启动列表List<ApplicationInfo> allAppInfo = null;try {allAppInfo = mPm.getInstalledApplications(0);//获取安装的第三方应用} catch (Exception e) {e.printStackTrace();}if (allAppInfo != null) {for (ApplicationInfo appInfo : allAppInfo) {boolean isContains = false;if (forbidList != null&& forbidList.contains(appInfo.packageName)) {// That is in forbidden listisContains = true;//如果在禁止自启动列表中,设置isContains为true。}if (isExcludeForAutoBoot(appInfo, isContains)) {//过滤掉launcher、白名单(security_autorun_notdisplayed_whitelist)中的应用。continue;}if (isPackageHasReceivers(appInfo.packageName)) {//判断是否注册了广播接收器Boolean isBoot = false;if (isPackageHasReceiverPermission(appInfo.packageName,Manifest.permission.RECEIVE_BOOT_COMPLETED)) {isBoot = true;//注册了开机广播}autoRunMap.put(appInfo.packageName, isBoot);}}}}return autoRunMap;}

我们再看下isExcludeForAutoBoot()方法,该方法排除掉launcher应用,排除掉白名单(security_autorun_notdisplayed_whitelist)中的应用。

    private boolean isExcludeForAutoBoot(ApplicationInfo appInfo, Boolean isContains) {if (appInfo == null) {return true;}if (Util.isSystemApp(appInfo)) {//系统应用List<String> launcherPkgs = SpeedUtil.getLauncherPkgs(mContext);//获取launcher应用// not contain launcher icon ,not list itif (!launcherPkgs.contains(appInfo.packageName)) {return true;}//过滤掉launcher应用。}// judge whether contain in white list,if it is ,do not list itif (isInAutoBootWhiteList(appInfo)) {//这里再过滤一次是否在白名单中if (isContains) {//如果设置了禁止自启动,则设置为允许自启动Log.d("i donot think will come in.");SystemApiUtil.fobidAutoRun(mContext, appInfo.packageName, false);}return true;} else {return false;}}

再看下isPackageHasReceivers()方法,判断是否注册了广播接收器。

    private boolean isPackageHasReceivers(String packageName) {if (mPm == null) {mPm = mContext.getPackageManager();}boolean hasReceivers = true;try {PackageInfo packinfo = mPm.getPackageInfo(packageName, PackageManager.GET_RECEIVERS);if (packinfo.receivers == null || packinfo.receivers.length <= 0) {hasReceivers = false;}} catch (NameNotFoundException e) {hasReceivers = false;e.printStackTrace();}return hasReceivers;}

看下封装的isPackageHasReceiverPermission()方法,判断是否有接收开机广播的权限。

    public boolean isPackageHasReceiverPermission(String packageName, String permissionName) {boolean isReceiverFunctionEnable = true;if (mPm == null) {mPm = mContext.getPackageManager();}if (permissionName != null&& PackageManager.PERMISSION_GRANTED != mPm.checkPermission(permissionName,packageName)) {isReceiverFunctionEnable = false;}return isReceiverFunctionEnable;}

流程图如下:

AutoRun处理机制

1、禁止自启动

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

具体代码如下:

    /*** @hide*/public boolean setForbiddenAutorunPackages(final String packageName,boolean bAdd) {boolean bResult = false;if (Feature.FEATURE_FORBID_APP_AUTORUN) {// Slog.v(TAG, "ctl packagename=" + packageName + "bAdd=" + bAdd);synchronized (this) {if ((null != packageName) && (null != mPackagesForbiddenAutoRun)&& (null != mServicesForbiddenAutoRun)) {final PackageManager pm = mContext.getPackageManager();PackageInfo pi = null;try {pi = pm.getPackageInfo(packageName,PackageManager.GET_SERVICES);} catch (NameNotFoundException e) {// TODO Auto-generated catch blocke.printStackTrace();}int temp_size = mPackagesForbiddenAutoRun.size();if (bAdd) {if (mPackagesForbiddenAutoRun.indexOfKey(packageName.hashCode())<0) {mPackagesForbiddenAutoRun.append(packageName.hashCode(),packageName);bResult = temp_size!=mPackagesForbiddenAutoRun.size();if (pi != null && null != pi.services) {for (ServiceInfo service : pi.services) {if (0>mServicesForbiddenAutoRun.indexOfKey(service.processName.hashCode()))  {Slog.i(TAG, "ADD forbid autorun service: "+ service.processName);mServicesForbiddenAutoRun.append(service.processName.hashCode(), service.processName);}}}}} else {mPackagesForbiddenAutoRun.delete(packageName.hashCode());bResult = mPackagesForbiddenAutoRun.size()!=temp_size;if (pi != null && pi.services != null) {for (ServiceInfo service : pi.services) {mServicesForbiddenAutoRun.delete(service.processName.hashCode());}}}}writeFilterPackages();}}return bResult;}
    void writeFilterPackages(){if ( mFileFilter == null) {return ;}if (mFileFilter.exists()) {mFileFilter.delete();}try {FileOutputStream fstr = new FileOutputStream(mFileFilter);BufferedOutputStream str = new BufferedOutputStream(fstr);//XmlSerializer serializer = XmlUtils.serializerInstance();XmlSerializer serializer = new FastXmlSerializer();serializer.setOutput(str, "utf-8");serializer.startDocument(null, true);//serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);serializer.startTag(null, "packages");int numPackages = mPackagesForbiddenAutoRun.size();//Slog.i(TAG, " forbid autorun numPackages := "+ numPackages);for( int i = 0; i < numPackages; ++i){serializer.startTag(null, "package");String apkPackageName = mPackagesForbiddenAutoRun.valueAt(i);//Slog.i(TAG, "forbid autorun package: "+apkPackageName);serializer.attribute(null, "name", apkPackageName);serializer.endTag(null, "package");}serializer.endTag(null, "packages");serializer.endDocument();str.flush();FileUtils.sync(fstr);str.close();FileUtils.setPermissions(mFileFilter.toString(),FileUtils.S_IRUSR|FileUtils.S_IWUSR|FileUtils.S_IRGRP|FileUtils.S_IWGRP|FileUtils.S_IROTH,-1, -1);} catch(java.io.IOException e) {Slog.w(TAG, "Unable to write broadcast filter, current changes will be lost at reboot", e);}}
    public List<String> getForbiddenAutorunPackages( ) {if (Feature.FEATURE_FORBID_APP_AUTORUN) {// Slog.v(TAG, "ctl packagename YLGetForbiddenAutorunPackages" );synchronized (this) {ArrayList<String> res = new ArrayList<String>();for (int i = 0; i < mPackagesForbiddenAutoRun.size(); i++) {res.add(mPackagesForbiddenAutoRun.valueAt(i));}return res;}}}

流程图如下:

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中。

具体代码如下:

            if (Feature.FEATURE_FORBID_APP_AUTORUN){mFileFilter = new File(PATH_PACKAGES_FILTER);if(!mFileFilter.exists()){try {mFileFilter.createNewFile();} catch (IOException e) {Slog.d(TAG, "Failed to creat black list file!!!");}}readFilterPackages();}

readFilterPackages()方法读取禁止自启动列表,完成mPackagesForbiddenAutoRun、mServicesForbiddenAutoRun数组的初始化。

    private static final String PATH_PACKAGES_FILTER = "/data/system/forbidden_autorun_packages.xml";private boolean readFilterPackages(){FileInputStream str = null;if ( mFileFilter == null) {return false;}if (!mFileFilter.exists()) {Log.d(TAG, PATH_PACKAGES_FILTER + "does not exist" );return false;}try {str = new FileInputStream(mFileFilter);XmlPullParser parser = Xml.newPullParser();parser.setInput(str, null);int type;while ((type=parser.next()) != XmlPullParser.START_TAG&& type != XmlPullParser.END_DOCUMENT) {;}if (type != XmlPullParser.START_TAG) {Log.d(TAG, "No start tag found in in" + PATH_PACKAGES_FILTER  );return false;}int outerDepth = parser.getDepth();while ((type=parser.next()) != XmlPullParser.END_DOCUMENT&& (type != XmlPullParser.END_TAG|| parser.getDepth() > outerDepth)) {if (type == XmlPullParser.END_TAG|| type == XmlPullParser.TEXT) {continue;}String tagName = parser.getName();if (tagName.equals("package")) {String delPackageName = parser.getAttributeValue(null,"name");final PackageManager pm = mContext.getPackageManager();PackageInfo pi = null;try {pi = pm.getPackageInfo(delPackageName, PackageManager.GET_SERVICES);} catch (NameNotFoundException e) {// TODO Auto-generated catch blocke.printStackTrace();}Slog.i(TAG, "forbid autorun pakacge: "+ delPackageName);mPackagesForbiddenAutoRun.append(delPackageName.hashCode(),delPackageName);if (pi != null && null != pi.services) {for (ServiceInfo service : pi.services) {if (0>mServicesForbiddenAutoRun.indexOfKey(service.processName.hashCode())) {Slog.i(TAG, "forbid autorun service: "+ service.processName);mServicesForbiddenAutoRun.append(service.processName.hashCode(),service.processName);}}}}else {Slog.w(TAG, "Unknown element under <packages>: "+ parser.getName());XmlUtils.skipCurrentTag(parser);}}str.close();} catch(XmlPullParserException e) {Slog.e(TAG, "Error reading "+ PATH_PACKAGES_FILTER, e);} catch(java.io.IOException e) {Slog.e(TAG, "Error reading "+ PATH_PACKAGES_FILTER, e);}return true;}

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

我们看下源码解释:

    /*** List of records for processes that someone had tried to start before the* system was ready.  We don't start them at that point, but ensure they* are started by the time booting is complete.*/final ArrayList<ProcessRecord> mProcessesOnHold = new ArrayList<ProcessRecord>();

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

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

        // If the system is not ready yet, then hold off on starting this// process until it is.if (!mProcessesReady&& !isAllowedWhileBooting(info)&& !allowWhileBooting&& !isInBlackList(info)) {if (!mProcessesOnHold.contains(app)) {mProcessesOnHold.add(app);}if (DEBUG_PROCESSES) Slog.v(TAG_PROCESSES,"System not ready, putting on hold: " + app);checkTime(startTime, "startProcess: returning with proc on hold");return app;}
    public boolean isInBlackList(final ApplicationInfo info){boolean ret = (info.flags & ApplicationInfo.FLAG_PERSISTENT) != ApplicationInfo.FLAG_PERSISTENT//revert for debugging crash|| (info.flags & ApplicationInfo.FLAG_SYSTEM) != ApplicationInfo.FLAG_SYSTEM;String packageName = info.packageName;ret = ((mPackagesForbiddenAutoRun.indexOfKey(packageName.hashCode())>=0 ||mServicesForbiddenAutoRun.indexOfKey(packageName.hashCode())>=0) &&(mPackagesHasWidgetRun.indexOfKey(packageName.hashCode())<0));if(ret) Slog.v(TAG, "check isInBlackList:" + packageName + " ret=" + ret);return ret;}

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方法,不允许重启,直接挂掉。

具体代码如下:

    /*** Main function for removing an existing process from the activity manager* as a result of that process going away.  Clears out all connections* to the process.哈哈*/private final void handleAppDiedLocked(ProcessRecord app,boolean restarting, boolean allowRestart) {int pid = app.pid;boolean kept = cleanUpApplicationRecordLocked(app, restarting, allowRestart, -1);if (!kept && !restarting) {removeLruProcessLocked(app);if (pid > 0) {ProcessList.remove(pid);}}if (mProfileProc == app) {clearProfilerLocked();}if (Feature.FEATURE_FORBID_APP_AUTORUN ) {if (app != null && app.info != null &&isInBlackList(app.info)) {IPackageManager pm = AppGlobals.getPackageManager();try {pm.setPackageStoppedState(app.info.packageName, true, UserHandle.getUserId(app.uid));Slog.i(TAG, "forbid restart this app that is contained in forbidden list: "  + app.processName + " and remove its alarms & jobs.");mContext.sendBroadcastAsUser(new Intent(ACTION_APP_KILL).putExtra(Intent.EXTRA_PACKAGES, new String[]{ app.info.packageName }),UserHandle.ALL, "android.permission.DEVICE_POWER");} catch (RemoteException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (IllegalArgumentException e) {Slog.w(TAG, "Failed trying to unstop package "+ app.info.packageName + ": " + e);e.printStackTrace();}}}......}
            /* optimize memory */else if (Feature.FEATURE_FORBID_APP_AUTORUN &&app != null && app.info != null && !isCTSMode() &&mAm.isInBlackList(app.info)) {Slog.w(TAG, "Service crashed " + sr.crashCount+ " times, stopping: " + sr + "forbid restart this app that is contained in forbidden list: " + app.processName);EventLog.writeEvent(EventLogTags.AM_DESTROY_SERVICE ,sr.crashCount, sr.shortName, app.pid,  app.processName);bringDownServiceLocked(sr);}

    private class QuickBootReceiver extends BroadcastReceiver {//AlarmManagerService中注册该广播接收器static final String ACTION_APP_KILL = "org.codeaurora.quickboot.appkilled";public QuickBootReceiver() {IntentFilter filter = new IntentFilter();filter.addAction(ACTION_APP_KILL);getContext().registerReceiver(this, filter,"android.permission.DEVICE_POWER", null);}@Overridepublic void onReceive(Context context, Intent intent) {if (Feature.FEATURE_FORBID_APP_AUTORUN) {Message msg = new Message();msg.what = ScheduleHandler.REMOVE_DEAD_APP_ALARM_REVEIVE;msg.obj = intent;mScheduleHandler.sendMessage(msg);return  ;}String action = intent.getAction();String pkgList[] = null;if (ACTION_APP_KILL.equals(action)) {pkgList = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);if (pkgList != null && (pkgList.length > 0)) {for (String pkg : pkgList) {removeLocked(pkg);for (int i=mBroadcastStats.size()-1; i>=0; i--) {ArrayMap<String, BroadcastStats> uidStats = mBroadcastStats.valueAt(i);if (uidStats.remove(pkg) != null) {if (uidStats.size() <= 0) {mBroadcastStats.removeAt(i);}}}}}}}}
            case REMOVE_DEAD_APP_ALARM_REVEIVE:synchronized (mLock) {removeDeadAppAlarmReceiveLocked((Intent)msg.obj);}break;
    void removeDeadAppAlarmReceiveLocked(Intent intent) {Slog.d(TAG, "Receive for remove dead app alarm: " + intent.getAction());String pkgList[] = null;pkgList = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);if (pkgList != null && (pkgList.length > 0)) {for (String pkg : pkgList) {removeLocked(pkg);for (int i=mBroadcastStats.size()-1; i>=0; i--) {ArrayMap<String, BroadcastStats> uidStats = mBroadcastStats.valueAt(i);if (uidStats.remove(pkg) != null) {if (uidStats.size() <= 0) {mBroadcastStats.removeAt(i);}}}}}}

有些应用进程起来后,会在native层产生一个守护进程,应用进程挂掉后,native层进程又会把应用层进程拉起来,因此在ActivityManagerS.forceStopPackage方法中调用SystemProperties.set("ctl.start","cleandaemon");执行清理脚本。

3、广播启动场景

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

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

4、AMS垃圾回收机制

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

                    if (Feature.FEATURE_FORBID_APP_AUTORUN) {if (isInBlackList(app.info)) {app.kill("Mem less than " + ProcessList.MIN_MEM_LEVEL/(1024*1024) + "M, kill background!!", true);}}

Android性能优化 -- 自启动管理相关推荐

  1. Android性能优化OOM内存管理——ADJ

    前言 OOM_ADJ (Out of Memory Adjustment)是android系统在内存不足情况下进行内存调整的重要参数.在处理app启动速度的时候,可以设置主线程的优先级,保证主线程占用 ...

  2. Android 性能优化

    为什么80%的码农都做不了架构师?>>>    原文作者:鸿洋 原文地址:点我跳转原文 一般情况下,我们谈性能优化基本上会从以下几个方面: App启动速度优化 UI流畅度优化 内存优 ...

  3. Android性能优化系列 + Android官方培训课程中文版

    Android性能优化典范 - 第6季 http://hukai.me/android-performance-patterns-season-6/ Android性能优化典范 - 第5季 http: ...

  4. Android性能优化典范第二季

    原文链接:http://hukai.me/android-performance-patterns-season-2/ 1)Battery Drain and Networking 对于手机程序,网络 ...

  5. Android性能优化:手把手教你如何让App更快、更稳、更省(含内存、布局优化等)...

    2019独角兽企业重金招聘Python工程师标准>>> 前言 在 Android开发中,性能优化策略十分重要 因为其决定了应用程序的开发质量:可用性.流畅性.稳定性等,是提高用户留存 ...

  6. Android性能优化 - 消除卡顿

    性能优化系列阅读 Android性能优化 性能优化 - 消除卡顿 性能优化 - 内存优化 性能分析工具 - TraceView Android性能分析工具 消除卡顿 什么是卡顿及卡顿的衡量标准 产生卡 ...

  7. Android性能优化 - 内存优化

    性能优化系列阅读 Android性能优化 性能优化 - 消除卡顿 性能优化- 内存优化 性能分析工具 - TraceView Android性能分析工具 为什么内存优化? 在一个商业项目中,很有可能因 ...

  8. Android性能优化(3)

    Android性能优化(3) 布局优化 在Android 开发过程中,其实有很大一部分工作量是在写布局上,布局写的好不好,直接影响用户的直观感受.如何做一个运行流畅,可靠,ui美观高效的app,狠重要 ...

  9. Android 性能优化 之谈谈Java内存区域

    最近一年副业主要在学习投资和技能学习,把以前学习内存分析的一些笔记总结发出来,写了很多笔记总结都没有写完就又忙着了,最近再次总结复习学习一遍,还有提醒各位同学一定要学会投资.. 了解Android 内 ...

  10. android包内存放视频,Android性能优化:手把手教你如何让App更快、更稳、更省(含内存、布局优化等)...

    为其决定了应用程序的开发质量:可用性.流畅性.稳定性等,是提高用户留存率的关键 本文全面讲解性能优化中的所有知识,献上一份 Android性能优化的详细攻略, 含:优化方向.原因 & 具体优化 ...

最新文章

  1. GVINS:基于GNSS视觉惯性紧耦合的平滑状态估计方法
  2. 从零开始一起学习SLAM | 三维空间刚体的旋转
  3. python超时退出进程_Python如何实现让一个函数超时退出?
  4. nginx动静分离和负载均衡
  5. Ubuntu linux 关机、重启、注销 命令
  6. mysql-日志浅析_mysql日志使用浅析
  7. 《非暴力沟通》听书心得
  8. Netty短时间内大量写消息导致的内存问题
  9. 计算机语言疾病矫治操作流程,言语矫治专家决策系统的构建.doc
  10. Ajax无刷新数据绑定
  11. 华大智造与Illumina测序平台比较:全基因组测序性能具有高度一致性
  12. 化工企业ERP建设中设备管理的重要性
  13. 云计算与大数据技术应用 第二章
  14. [原]极域电子教室3个没被发现的bug(V6 2007)
  15. 网络流24题 最小路径覆盖(DCOJ8002)
  16. 麒麟芯片鸿蒙芯片高通骁龙,麒麟和骁龙两款处理器,选哪一款比较好,看完这三点你就明白了...
  17. 在SQLBolt网站上学习入门SQL
  18. 资源、角色、用户、岗位的关系(工作中用到的)
  19. 一意孤行亚马逊----一个钓鱼疯子的巴西亚马逊之行( 5.九月21日 亚马逊的鱼并不好钓) 作者:咸水鱼...
  20. 一个困扰程序员很多年的问题:微服务领域SpringCloud这么火,为何还要学习SpringCloud Alibaba?

热门文章

  1. 中文分词词库汇总(一)
  2. linux把大文件分成小文件,linux文件分割(将大的日志文件分割成小的)
  3. 六年如逆旅,我亦是行人
  4. SAMBA+LDAP ERROR“smbd dead but pid file exists”
  5. 64位linux默认内存对齐,Linux内存初始化(汇编部分)
  6. python3爬虫(5):财务报表爬取入库
  7. Java基本语法笔记
  8. 【c语言】判断整数x是否是同构数。若是同构数,函数返回1;否则返回0.
  9. html缩放动画效果,CSS3鼠标hover背景图片缩放动画效果
  10. 如何使用EasyNVR+CDN突破萤石云在直播客户端数量上的限制,做到低成本高性价比的直播