1. 程式人生 > >Android效能優化 -- 自啟動管理

Android效能優化 -- 自啟動管理

(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 {

        @Override
        public 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 autorun
        if (!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物件的icon
                        if (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 list
                        isContains = 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 it
            if (!launcherPkgs.contains(appInfo.packageName)) {
                return true;
            }//過濾掉launcher應用。
        }

        // judge whether contain in white list,if it is ,do not list it
        if (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 block
                        e.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 block
                       e.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 block
                    e.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);
        }

        @Override
        public 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);

                        }
                    }