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)
如果服務重啟,則在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);
}
}