Android开发之如何保证Service不被杀掉(broadcast+system/app)

确保Service后台持久运行
本文探讨了多种确保Android Service在后台稳定运行的方法,包括设置START_STICKY、提升Service及进程优先级、监听系统广播、设置Application Persistent属性等。

转自:http://blog.youkuaiyun.com/mad1989/article/details/22492519


序言

最近项目要实现这样一个效果:运行后,要有一个service始终保持在后台运行,不管用户作出什么操作,都要保证service不被kill,这可真是一个难题。参考了现今各种定制版的系统和安全厂商牛虻软件,如何能保证自己的Service不被杀死呢?

其实除了常规的手段,我们可以参考一下微信和360,设置-程序-正在运行,可以看到微信是同时开启了两个进程和服务:

【有兴趣可以研究一下 守护进程 和 AIDL 


我猜想它应该是相互监听,如果有一方被kill掉,另一个捕获到立即启动,以达到service永远都在运行的状态,貌似360也是这个原理,具体是不是这个样子,还有待参考,目前我还没有参透它们是如何实现的,先简单说一下我自己的防控措施吧,首先介绍一下Service概念,记性不好,重复造一下车轮,高手可以直接看最后。



Service简介

Service是在一段不定的时间运行在后台,不和用户交互应用组件。每个Service必须在manifest中 通过<service>来声明。可以通过contect.startservice和contect.bindserverice来启动。和其他的应用组件一样,运行在进程的主线程中。这就是说如果service需要很多耗时或者阻塞的操作,需要在其子线程中实现(或者用系统提供的IntentService,它继承了Service,它处理数据是用自身新开的线程)。【当然你也可以在新的线程中startService,这样Service就不是在MainThread了


本地服务 Local Service 用于应用程序内部

它可以启动并运行,直至有人停止了它或它自己停止。在这种方式下,它以调用Context.startService()启动,而以调用Context.stopService()结束。它可以调用Service.stopSelf() 或 Service.stopSelfResult()来自己停止。不论调用了多少次startService()方法,你只需要调用一次stopService()来停止服务。

【用于实现应用程序自己的一些耗时任务,比如查询升级信息,并不占用应用程序比如Activity所属线程,而是单开线程后台执行,这样用户体验比较好】


远程服务 Remote Service 用于android系统内部的应用程序之间

它可以通过自己定义并暴露出来的接口进行程序操作。客户端建立一个到服务对象的连接,并通过那个连接来调用服务。连接以调用Context.bindService()方法建立,以调用 Context.unbindService()关闭。多个客户端可以绑定至同一个服务。如果服务此时还没有加载,bindService()会先加载它。

【可被其他应用程序复用,比如天气预报服务,其他应用程序不需要再写这样的服务,调用已有的即可】


1,Service的生命周期



2,Service运行方式

以startService()启动服务,系统将通过传入的Intent在底层搜索相关符合Intent里面信息的service。如果服务没有启动则先运行onCreate,然后运行onStartCommand (可在里面处理启动时传过来的Intent和其他参数),直到明显调用stopService或者stopSelf才将停止Service。无论运行startService多少次,只要调用一次stopService或者stopSelf,Service都会停止。使用stopSelf(int)方法可以保证在处理好intent后再停止。onStartCommand ,在2.0后被引入用于service的启动函数,2.0之前为public void onStart(Intent intent, int startId) 。

以bindService()方法启用服务,调用者与服务绑定在了一起,调用者一旦退出,服务也就终止。onBind()只有采用Context.bindService()方法启动服务时才会回调该方法。该方法在调用者与服务绑定时被调用,当调用者与服务已经绑定,多次调用Context.bindService()方法并不会导致该方法被多次调用。采用Context.bindService()方法启动服务时只能调用onUnbind()方法解除调用者与服务解除,服务结束时会调用onDestroy()方法。


3,拥有service的进程具有较高的优先级

官方文档告诉我们,Android系统会尽量保持拥有service的进程运行,只要在该service已经被启动(start)或者客户端连接(bindService)到它。当内存不足时,需要保持,拥有service的进程具有较高的优先级。

1. 如果service正在调用onCreate,onStartCommand或者onDestory方法,那么用于当前service的进程则变为前台进程以避免被killed。
2. 如果当前service已经被启动(start),拥有它的进程则比那些用户可见的进程优先级低一些,但是比那些不可见的进程更重要,这就意味着service一般不会被killed.
3. 如果客户端已经连接到service (bindService),那么拥有Service的进程则拥有最高的优先级,可以认为service是可见的。
4. 如果service可以使用startForeground(int, Notification)方法来将service设置为前台状态,那么系统就认为是对用户可见的,并不会在内存不足时killed。
5. 如果有其他的应用组件作为Service,Activity等运行在相同的进程中,那么将会增加该进程的重要性。


保证service不被杀掉


onStartCommand方法,返回START_STICKY


StartCommond几个常量参数简介:

1、START_STICKY

在运行onStartCommand后service进程被kill后,那将保留在开始状态,但是不保留那些传入的intent。不久后service就会再次尝试重新创建,因为保留在开始状态,在创建     service后将保证调用onstartCommand。如果没有传递任何开始命令给service,那将获取到null的intent。

2、START_NOT_STICKY

在运行onStartCommand后service进程被kill后,并且没有新的intent传递给它。Service将移出开始状态,并且直到新的明显的方法(startService)调用才重新创建。因为如果没有传递任何未决定的intent那么service是不会启动,也就是期间onstartCommand不会接收到任何null的intent。

3、START_REDELIVER_INTENT

在运行onStartCommand后service进程被kill后,系统将会再次启动service,并传入最后一个intent给onstartCommand。直到调用stopSelf(int)才停止传递intent。如果在被kill后还有未处理好的intent,那被kill后服务还是会自动启动。因此onstartCommand不会接收到任何null的intent。

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. @Override  
  2. public int onStartCommand(Intent intent, int flags, int startId) {  
  3.     flags = START_STICKY;  
  4.     return super.onStartCommand(intent, flags, startId);  
  5. }  

【结论】 手动返回START_STICKY,亲测当service因内存不足被kill,当内存又有的时候,service又被重新创建,比较不错,但是不能保证任何情况下都被重建,比如进程被干掉了....



提升service优先级


在AndroidManifest.xml文件中对于intent-filter可以通过android:priority = "1000"这个属性设置最高优先级,1000是最高值,如果数字越小则优先级越低,同时适用于广播。

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. <service  
  2.     android:name="com.dbjtech.acbxt.waiqin.UploadService"  
  3.     android:enabled="true" >  
  4.     <intent-filter android:priority="1000" >  
  5.         <action android:name="com.dbjtech.myservice" />  
  6.     </intent-filter>  
  7. </service>  

【结论】目前看来,priority这个属性貌似只适用于broadcast,对于Service来说可能无效


提升service进程优先级


Android中的进程是托管的,当系统进程空间紧张的时候,会依照优先级自动进行进程的回收。Android将进程分为6个等级,它们按优先级顺序由高到低依次是:

   1.前台进程( FOREGROUND_APP)
   2.可视进程(VISIBLE_APP )
   3. 次要服务进程(SECONDARY_SERVER )
   4.后台进程 (HIDDEN_APP)
   5.内容供应节点(CONTENT_PROVIDER)
   6.空进程(EMPTY_APP)

当service运行在低内存的环境时,将会kill掉一些存在的进程。因此进程的优先级将会很重要,可以使用startForeground 将service放到前台状态。这样在低内存时被kill的几率会低一些。


在onStartCommand方法内添加如下代码:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1.  Notification notification = new Notification(R.drawable.ic_launcher,  
  2.  getString(R.string.app_name), System.currentTimeMillis());  
  3.   
  4.  PendingIntent pendingintent = PendingIntent.getActivity(this0,  
  5.  new Intent(this, AppMain.class), 0);  
  6.  notification.setLatestEventInfo(this"uploadservice""请保持程序在后台运行",  
  7.  pendingintent);  
  8. <span style="color:#ff0000;"> startForeground(0x111, notification);</span>  

注意在onDestroy里还需要stopForeground(true),运行时在下拉列表会看到自己的APP在:



【结论】如果在极度极度低内存的压力下,该service还是会被kill掉,并且不一定会restart



onDestroy方法里重启service


service +broadcast  方式,就是当service走ondestory的时候,发送一个自定义的广播,当收到广播的时候,重新启动service;

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. <receiver android:name="com.dbjtech.acbxt.waiqin.BootReceiver" >  
  2.     <intent-filter>  
  3.         <action android:name="android.intent.action.BOOT_COMPLETED" />  
  4.         <action android:name="android.intent.action.USER_PRESENT" />  
  5.         <action android:name="com.dbjtech.waiqin.destroy" />//这个就是自定义的action  
  6.     </intent-filter>  
  7. </receiver>  
在onDestroy时:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. @Override  
  2. public void onDestroy() {  
  3.     stopForeground(true);  
  4.     Intent intent = new Intent("com.dbjtech.waiqin.destroy");  
  5.     sendBroadcast(intent);  
  6.     super.onDestroy();  
  7. }  
在BootReceiver里

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public class BootReceiver extends BroadcastReceiver {  
  2.   
  3.     @Override  
  4.     public void onReceive(Context context, Intent intent) {  
  5.         if (intent.getAction().equals("com.dbjtech.waiqin.destroy")) {  
  6.             //TODO  
  7.             //在这里写重新启动service的相关操作  
  8.                 startUploadService(context);  
  9.         }  
  10.   
  11.     }  
  12.   
  13. }  

也可以直接在onDestroy()里startService

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. @Override  
  2. public void onDestroy() {  
  3.   
  4.      Intent sevice = new Intent(this, MainService.class);  
  5.      this.startService(sevice);  
  6.   
  7.     super.onDestroy();  
  8. }  

【结论】当使用类似口口管家等第三方应用或是在setting里-应用-强制停止时,APP进程可能就直接被干掉了,onDestroy方法都进不来,所以还是无法保证~.~



Application加上Persistent属性


看Android的文档知道,当进程长期不活动,或系统需要资源时,会自动清理门户,杀死一些Service,和不可见的Activity等所在的进程。但是如果某个进程不想被杀死(如数据缓存进程,或状态监控进程,或远程服务进程),可以这么做:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. <application  
  2.     android:name="com.test.Application"  
  3.     android:allowBackup="true"  
  4.     android:icon="@drawable/ic_launcher"  
  5.     android:label="@string/app_name"  
  6.    <span style="color:#ff0000;"> android:persistent="true"</span>  
  7.     android:theme="@style/AppTheme" >  
  8. </application>  


【结论】据说这个属性不能乱设置,不过设置后,的确发现优先级提高不少,或许是相当于系统级的进程,但是还是无法保证存活



监听系统广播判断Service状态


通过系统的一些广播,比如:手机重启、界面唤醒、应用状态改变等等监听并捕获到,然后判断我们的Service是否还存活,别忘记加权限啊。

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. <receiver android:name="com.dbjtech.acbxt.waiqin.BootReceiver" >  
  2.     <intent-filter>  
  3.         <action android:name="android.intent.action.BOOT_COMPLETED" />  
  4.         <action android:name="android.intent.action.USER_PRESENT" />  
  5.         <action android:name="android.intent.action.PACKAGE_RESTARTED" />  
  6.         <action android:name="com.dbjtech.waiqin.destroy" />  
  7.     </intent-filter>  
  8. </receiver>  
BroadcastReceiver中:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. @Override  
  2. public void onReceive(Context context, Intent intent) {  
  3.     if (Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) {  
  4.         System.out.println("手机开机了....");  
  5.         startUploadService(context);  
  6.     }  
  7.     if (Intent.ACTION_USER_PRESENT.equals(intent.getAction())) {  
  8.             startUploadService(context);  
  9.     }  
  10. }  

【结论】这也能算是一种措施,不过感觉监听多了会导致Service很混乱,带来诸多不便


将APK安装到/system/app,变身系统级应用

这个办法不推荐使用,因为如果你的APP如果是给用户使用的,那就不合适了,我是为了给测试的妹子来用,这个APP的目的也是很简单,打开后开启Service并且能保证一直在后台驻留,开机自启动。但是昨天发现如果她的HuaWei手机长时间关闭, 再重新打开时,我们应用的Service不会自启动,貌似广播收不到了~一怒之下,打算搞成系统应用。

前提:
ROOT过的手机
1,把代码编写好后,打包导出apk,copy到手机SD卡根目录下。
2,手机连接eclipse,cmd: adb shell
3,切换root模式,输入:su     (如果root过就不会有错误)
4,设置System为读写权限:mount –o remount rw /system (System默认为只读,无法写入,这一步很关键)

5,cd到sd卡跟目录下,确认是否有我们拷贝到sd卡根目录下的apk(一般都是 storage/sdcard0)
shell@android:/ # cd storage/sdcard0
6,最关键的一步,我们要把apk拷贝到 /System/app中:

发现copy命令无效~那么我们就用push把:

如果有错误:device not found,那么手机下载一个Root Explorer把,找到apk,copy到System/app下,通过这个APP要更容易一些。
7,system/app 确定拥有我们的APK后,重启手机把:

设置-应用程序管理,查看一下:
可以看到我们的APP已经无法卸载了,只能停用

这个时候,就算强制停止,或是关闭Service,重启手机后照样可以起来Service~!


系统级的APP,这样一些第三方的管家软件,就无法杀掉我们,除非自己把APP停用掉,或是强制停止(但是我的APP可以开机自启动)。

【结论】 这种方式适合调试来用,并不算是一种解决办法,大家可以尝试在正在运行的界面:强制关闭搜狐视频的两个进程,重启手机,发现他又可以自启动,但是如果换成我们的APP,强制停止,进程挂了,再重启手机,无法自启动了~



大家一起研究,怎么样才能像上图搜狐视频一样,开启两个进程,相互监听,做到最大程度的存活,如果这个能实现了,那就和微信、360等一样的效果了。

/media/fangqing/95b0e9e6-31e0-48d0-96fa-46599f44a68c/codessi/QSSI12/frameworks/base/services/core/java/com/android/server/am/OomAdjuster.java private final boolean computeOomAdjLocked(ProcessRecord app, int cachedAdj, ProcessRecord topApp, boolean doingAll, long now, boolean cycleReEval, boolean computeClients) { if (mAdjSeq == app.adjSeq) { if (app.adjSeq == app.completedAdjSeq) { // This adjustment has already been computed successfully. return false; } else { // The process is being computed, so there is a cycle. We cannot // rely on this process's state. app.containsCycle = true; return false; } } if (app.thread == null) { app.adjSeq = mAdjSeq; app.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_BACKGROUND); app.setCurProcState(PROCESS_STATE_CACHED_EMPTY); app.curAdj = ProcessList.CACHED_APP_MAX_ADJ; app.setCurRawAdj(ProcessList.CACHED_APP_MAX_ADJ); app.completedAdjSeq = app.adjSeq; app.curCapability = PROCESS_CAPABILITY_NONE; return false; } app.adjTypeCode = ActivityManager.RunningAppProcessInfo.REASON_UNKNOWN; app.adjSource = null; app.adjTarget = null; app.empty = false; app.setCached(false); app.shouldNotFreeze = false; final int appUid = app.info.uid; final int logUid = mService.mCurOomAdjUid; int prevAppAdj = app.curAdj; int prevProcState = app.getCurProcState(); int prevCapability = app.curCapability; if (app.maxAdj <= ProcessList.FOREGROUND_APP_ADJ) { // The max adjustment doesn't allow this app to be anything // below foreground, so it is not worth doing work for it. if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { mService.reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making fixed: " + app); } app.adjType = "fixed"; app.adjSeq = mAdjSeq; app.setCurRawAdj(app.maxAdj); app.setHasForegroundActivities(false); app.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_DEFAULT); app.curCapability = PROCESS_CAPABILITY_ALL; app.setCurProcState(ActivityManager.PROCESS_STATE_PERSISTENT); // System processes can do UI, and when they do we want to have // them trim their memory after the user leaves the UI. To // facilitate this, here we need to determine whether or not it // is currently showing UI. app.systemNoUi = true; if (app == topApp) { app.systemNoUi = false; app.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_TOP_APP); app.adjType = "pers-top-activity"; } else if (app.hasTopUi()) { // sched group/proc state adjustment is below app.systemNoUi = false; app.adjType = "pers-top-ui"; } else if (app.getCachedHasVisibleActivities()) { app.systemNoUi = false; } if (!app.systemNoUi) { if (mService.mWakefulness == PowerManagerInternal.WAKEFULNESS_AWAKE) { // screen on, promote UI app.setCurProcState(ActivityManager.PROCESS_STATE_PERSISTENT_UI); app.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_TOP_APP); } else { // screen off, restrict UI scheduling app.setCurProcState(PROCESS_STATE_BOUND_FOREGROUND_SERVICE); app.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_RESTRICTED); } } app.setCurRawProcState(app.getCurProcState()); app.curAdj = app.maxAdj; app.completedAdjSeq = app.adjSeq; // if curAdj is less than prevAppAdj, then this process was promoted return app.curAdj < prevAppAdj || app.getCurProcState() < prevProcState; } app.systemNoUi = false; final int PROCESS_STATE_CUR_TOP = mService.mAtmInternal.getTopProcessState(); // Determine the importance of the process, starting with most // important to least, and assign an appropriate OOM adjustment. int adj; int schedGroup; int procState; int cachedAdjSeq; int capability = 0; boolean foregroundActivities = false; if (PROCESS_STATE_CUR_TOP == PROCESS_STATE_TOP && app == topApp) { // The last app on the list is the foreground app. adj = ProcessList.FOREGROUND_APP_ADJ; schedGroup = ProcessList.SCHED_GROUP_TOP_APP; app.adjType = "top-activity"; foregroundActivities = true; procState = PROCESS_STATE_CUR_TOP; if(mIsTopAppRenderThreadBoostEnabled) { if(mCurRenderThreadTid != app.renderThreadTid && app.renderThreadTid > 0) { mCurRenderThreadTid = app.renderThreadTid; if (mPerfBoost != null) { Slog.d(TAG, "TOP-APP: pid:" + app.pid + ", processName: " + app.processName + ", renderThreadTid: " + app.renderThreadTid); if (mPerfHandle >= 0) { mPerfBoost.perfLockRelease(); mPerfHandle = -1; } mPerfHandle = mPerfBoost.perfHint(BoostFramework.VENDOR_HINT_BOOST_RENDERTHREAD, app.processName, app.renderThreadTid, 1); Slog.d(TAG, "VENDOR_HINT_BOOST_RENDERTHREAD perfHint was called. mPerfHandle: " + mPerfHandle); } } } if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making top: " + app); } } else if (app.runningRemoteAnimation) { adj = ProcessList.VISIBLE_APP_ADJ; schedGroup = ProcessList.SCHED_GROUP_TOP_APP; app.adjType = "running-remote-anim"; procState = PROCESS_STATE_CUR_TOP; if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making running remote anim: " + app); } } else if (app.getActiveInstrumentation() != null) { // Don't want to kill running instrumentation. adj = ProcessList.FOREGROUND_APP_ADJ; schedGroup = ProcessList.SCHED_GROUP_DEFAULT; app.adjType = "instrumentation"; procState = PROCESS_STATE_FOREGROUND_SERVICE; if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making instrumentation: " + app); } } else if (app.getCachedIsReceivingBroadcast(mTmpBroadcastQueue)) { // An app that is currently receiving a broadcast also // counts as being in the foreground for OOM killer purposes. // It's placed in a sched group based on the nature of the // broadcast as reflected by which queue it's active in. adj = ProcessList.FOREGROUND_APP_ADJ; schedGroup = (mTmpBroadcastQueue.contains(mService.mFgBroadcastQueue)) ? ProcessList.SCHED_GROUP_DEFAULT : ProcessList.SCHED_GROUP_BACKGROUND; app.adjType = "broadcast"; procState = ActivityManager.PROCESS_STATE_RECEIVER; if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making broadcast: " + app); } } else if (app.executingServices.size() > 0) { // An app that is currently executing a service callback also // counts as being in the foreground. adj = ProcessList.FOREGROUND_APP_ADJ; schedGroup = app.execServicesFg ? ProcessList.SCHED_GROUP_DEFAULT : ProcessList.SCHED_GROUP_BACKGROUND; app.adjType = "exec-service"; procState = PROCESS_STATE_SERVICE; if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making exec-service: " + app); } //Slog.i(TAG, "EXEC " + (app.execServicesFg ? "FG" : "BG") + ": " + app); } else if (app == topApp) { adj = ProcessList.FOREGROUND_APP_ADJ; schedGroup = ProcessList.SCHED_GROUP_BACKGROUND; app.adjType = "top-sleeping"; foregroundActivities = true; procState = PROCESS_STATE_CUR_TOP; if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making top (sleeping): " + app); } } else { // As far as we know the process is empty. We may change our mind later. schedGroup = ProcessList.SCHED_GROUP_BACKGROUND; // At this point we don't actually know the adjustment. Use the cached adj // value that the caller wants us to. adj = cachedAdj; procState = PROCESS_STATE_CACHED_EMPTY; if (!app.containsCycle) { app.setCached(true); app.empty = true; app.adjType = "cch-empty"; } if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making empty: " + app); } } // Examine all activities if not already foreground. if (!foregroundActivities && app.getCachedHasActivities()) { app.computeOomAdjFromActivitiesIfNecessary(mTmpComputeOomAdjWindowCallback, adj, foregroundActivities, procState, schedGroup, appUid, logUid, PROCESS_STATE_CUR_TOP); adj = app.mCachedAdj; foregroundActivities = app.mCachedForegroundActivities; procState = app.mCachedProcState; schedGroup = app.mCachedSchedGroup; } if (procState > PROCESS_STATE_CACHED_RECENT && app.getCachedHasRecentTasks()) { procState = PROCESS_STATE_CACHED_RECENT; app.adjType = "cch-rec"; if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise procstate to cached recent: " + app); } } if (adj > ProcessList.PERCEPTIBLE_APP_ADJ || procState > PROCESS_STATE_FOREGROUND_SERVICE) { if (app.hasForegroundServices()) { // The user is aware of this app, so make it visible. adj = ProcessList.PERCEPTIBLE_APP_ADJ; procState = PROCESS_STATE_FOREGROUND_SERVICE; app.adjType = "fg-service"; app.setCached(false); schedGroup = ProcessList.SCHED_GROUP_DEFAULT; if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise to " + app.adjType + ": " + app + " "); } } else if (app.hasOverlayUi()) { // The process is display an overlay UI. adj = ProcessList.PERCEPTIBLE_APP_ADJ; procState = PROCESS_STATE_IMPORTANT_FOREGROUND; app.setCached(false); app.adjType = "has-overlay-ui"; schedGroup = ProcessList.SCHED_GROUP_DEFAULT; if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise to overlay ui: " + app); } } } // If the app was recently in the foreground and moved to a foreground service status, // allow it to get a higher rank in memory for some time, compared to other foreground // services so that it can finish performing any persistence/processing of in-memory state. if (app.hasForegroundServices() && adj > ProcessList.PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ && (app.lastTopTime + mConstants.TOP_TO_FGS_GRACE_DURATION > now || app.setProcState <= PROCESS_STATE_TOP)) { adj = ProcessList.PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ; app.adjType = "fg-service-act"; if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise to recent fg: " + app); } } if (adj > ProcessList.PERCEPTIBLE_APP_ADJ || procState > PROCESS_STATE_TRANSIENT_BACKGROUND) { if (app.forcingToImportant != null) { // This is currently used for toasts... they are not interactive, and // we don't want them to cause the app to become fully foreground (and // thus out of background check), so we yes the best background level we can. adj = ProcessList.PERCEPTIBLE_APP_ADJ; procState = PROCESS_STATE_TRANSIENT_BACKGROUND; app.setCached(false); app.adjType = "force-imp"; app.adjSource = app.forcingToImportant; schedGroup = ProcessList.SCHED_GROUP_DEFAULT; if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise to force imp: " + app); } } } if (app.getCachedIsHeavyWeight()) { if (adj > ProcessList.HEAVY_WEIGHT_APP_ADJ) { // We don't want to kill the current heavy-weight process. adj = ProcessList.HEAVY_WEIGHT_APP_ADJ; schedGroup = ProcessList.SCHED_GROUP_BACKGROUND; app.setCached(false); app.adjType = "heavy"; if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise adj to heavy: " + app); } } if (procState > ActivityManager.PROCESS_STATE_HEAVY_WEIGHT) { procState = ActivityManager.PROCESS_STATE_HEAVY_WEIGHT; app.adjType = "heavy"; if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise procstate to heavy: " + app); } } } if (app.getCachedIsHomeProcess()) { if (adj > ProcessList.HOME_APP_ADJ) { // This process is hosting what we currently consider to be the // home app, so we don't want to let it go into the background. adj = ProcessList.HOME_APP_ADJ; schedGroup = ProcessList.SCHED_GROUP_BACKGROUND; app.setCached(false); app.adjType = "home"; if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise adj to home: " + app); } } if (procState > ActivityManager.PROCESS_STATE_HOME) { procState = ActivityManager.PROCESS_STATE_HOME; app.adjType = "home"; if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise procstate to home: " + app); } } } if (app.getCachedIsPreviousProcess() && app.getCachedHasActivities()) { if (adj > ProcessList.PREVIOUS_APP_ADJ) { // This was the previous process that showed UI to the user. // We want to try to keep it around more aggressively, to give // a good experience around switching between two apps. adj = ProcessList.PREVIOUS_APP_ADJ; schedGroup = ProcessList.SCHED_GROUP_BACKGROUND; app.setCached(false); app.adjType = "previous"; if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise adj to prev: " + app); } } if (procState > PROCESS_STATE_LAST_ACTIVITY) { procState = PROCESS_STATE_LAST_ACTIVITY; app.adjType = "previous"; if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise procstate to prev: " + app); } } } if (false) Slog.i(TAG, "OOM " + app + ": initial adj=" + adj + " reason=" + app.adjType); // By default, we use the computed adjustment. It may be changed if // there are applications dependent on our services or providers, but // this gives us a baseline and makes sure we don't get into an // infinite recursion. If we're re-evaluating due to cycles, use the previously computed // values. if (cycleReEval) { procState = Math.min(procState, app.getCurRawProcState()); adj = Math.min(adj, app.getCurRawAdj()); schedGroup = Math.max(schedGroup, app.getCurrentSchedulingGroup()); } app.setCurRawAdj(adj); app.setCurRawProcState(procState); app.hasStartedServices = false; app.adjSeq = mAdjSeq; final BackupRecord backupTarget = mService.mBackupTargets.get(app.userId); if (backupTarget != null && app == backupTarget.app) { // If possible we want to avoid killing apps while they're being backed up if (adj > ProcessList.BACKUP_APP_ADJ) { if (DEBUG_BACKUP) Slog.v(TAG_BACKUP, "oom BACKUP_APP_ADJ for " + app); adj = ProcessList.BACKUP_APP_ADJ; if (procState > PROCESS_STATE_TRANSIENT_BACKGROUND) { procState = PROCESS_STATE_TRANSIENT_BACKGROUND; } app.adjType = "backup"; if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise adj to backup: " + app); } app.setCached(false); } if (procState > ActivityManager.PROCESS_STATE_BACKUP) { procState = ActivityManager.PROCESS_STATE_BACKUP; app.adjType = "backup"; if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise procstate to backup: " + app); } } } int capabilityFromFGS = 0; // capability from foreground service. for (int is = app.numberOfRunningServices() - 1; is >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ || schedGroup == ProcessList.SCHED_GROUP_BACKGROUND || procState > PROCESS_STATE_TOP); is--) { ServiceRecord s = app.getRunningServiceAt(is); if (s.startRequested) { app.hasStartedServices = true; if (procState > PROCESS_STATE_SERVICE) { procState = PROCESS_STATE_SERVICE; app.adjType = "started-services"; if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise procstate to started service: " + app); } } if (!s.mKeepWarming && app.hasShownUi && !app.getCachedIsHomeProcess()) { // If this process has shown some UI, let it immediately // go to the LRU list because it may be pretty heavy with // UI stuff. We'll tag it with a label just to help // debug and understand what is going on. if (adj > ProcessList.SERVICE_ADJ) { app.adjType = "cch-started-ui-services"; } } else { if (s.mKeepWarming || now < (s.lastActivity + mConstants.MAX_SERVICE_INACTIVITY)) { // This service has seen some activity within // recent memory, so we will keep its process ahead // of the background processes. if (adj > ProcessList.SERVICE_ADJ) { adj = ProcessList.SERVICE_ADJ; app.adjType = "started-services"; if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise adj to started service: " + app); } app.setCached(false); } } // If we have let the service slide into the background // state, still have some text describing what it is doing // even though the service no longer has an impact. if (adj > ProcessList.SERVICE_ADJ) { app.adjType = "cch-started-services"; } } } if (s.isForeground) { final int fgsType = s.foregroundServiceType; if (s.mAllowWhileInUsePermissionInFgs) { capabilityFromFGS |= (fgsType & FOREGROUND_SERVICE_TYPE_LOCATION) != 0 ? PROCESS_CAPABILITY_FOREGROUND_LOCATION : 0; boolean enabled = false; try { enabled = mPlatformCompat.isChangeEnabled( CAMERA_MICROPHONE_CAPABILITY_CHANGE_ID, s.appInfo); } catch (RemoteException e) { } if (enabled) { capabilityFromFGS |= (fgsType & FOREGROUND_SERVICE_TYPE_CAMERA) != 0 ? PROCESS_CAPABILITY_FOREGROUND_CAMERA : 0; capabilityFromFGS |= (fgsType & FOREGROUND_SERVICE_TYPE_MICROPHONE) != 0 ? PROCESS_CAPABILITY_FOREGROUND_MICROPHONE : 0; } else { capabilityFromFGS |= PROCESS_CAPABILITY_FOREGROUND_CAMERA | PROCESS_CAPABILITY_FOREGROUND_MICROPHONE; } } } ArrayMap<IBinder, ArrayList<ConnectionRecord>> serviceConnections = s.getConnections(); for (int conni = serviceConnections.size() - 1; conni >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ || schedGroup == ProcessList.SCHED_GROUP_BACKGROUND || procState > PROCESS_STATE_TOP); conni--) { ArrayList<ConnectionRecord> clist = serviceConnections.valueAt(conni); for (int i = 0; i < clist.size() && (adj > ProcessList.FOREGROUND_APP_ADJ || schedGroup == ProcessList.SCHED_GROUP_BACKGROUND || procState > PROCESS_STATE_TOP); i++) { // XXX should compute this based on the max of // all connected clients. ConnectionRecord cr = clist.get(i); if (cr.binding.client == app) { // Binding to oneself is not interesting. continue; } boolean trackedProcState = false; ProcessRecord client = cr.binding.client; if (computeClients) { computeOomAdjLocked(client, cachedAdj, topApp, doingAll, now, cycleReEval, true); } else { client.setCurRawAdj(client.setAdj); client.setCurRawProcState(client.setProcState); } int clientAdj = client.getCurRawAdj(); int clientProcState = client.getCurRawProcState(); if ((cr.flags & Context.BIND_WAIVE_PRIORITY) == 0) { if (shouldSkipDueToCycle(app, client, procState, adj, cycleReEval)) { continue; } if (cr.hasFlag(Context.BIND_INCLUDE_CAPABILITIES)) { capability |= client.curCapability; } if (clientProcState >= PROCESS_STATE_CACHED_ACTIVITY) { // If the other app is cached for any reason, for purposes here // we are going to consider it empty. The specific cached state // doesn't propagate except under certain conditions. clientProcState = PROCESS_STATE_CACHED_EMPTY; } String adjType = null; if ((cr.flags&Context.BIND_ALLOW_OOM_MANAGEMENT) != 0) { // Not doing bind OOM management, so treat // this guy more like a started service. if (app.hasShownUi && !app.getCachedIsHomeProcess()) { // If this process has shown some UI, let it immediately // go to the LRU list because it may be pretty heavy with // UI stuff. We'll tag it with a label just to help // debug and understand what is going on. if (adj > clientAdj) { adjType = "cch-bound-ui-services"; } app.setCached(false); clientAdj = adj; clientProcState = procState; } else { if (now >= (s.lastActivity + mConstants.MAX_SERVICE_INACTIVITY)) { // This service has not seen activity within // recent memory, so allow it to drop to the // LRU list if there is no other reason to keep // it around. We'll also tag it with a label just // to help debug and undertand what is going on. if (adj > clientAdj) { adjType = "cch-bound-services"; } clientAdj = adj; } } } if (adj > clientAdj) { // If this process has recently shown UI, and // the process that is binding to it is less // important than being visible, then we don't // care about the binding as much as we care // about letting this process get into the LRU // list to be killed and restarted if needed for // memory. if (app.hasShownUi && !app.getCachedIsHomeProcess() && clientAdj > ProcessList.PERCEPTIBLE_APP_ADJ) { if (adj >= ProcessList.CACHED_APP_MIN_ADJ) { adjType = "cch-bound-ui-services"; } } else { int newAdj; if ((cr.flags&(Context.BIND_ABOVE_CLIENT |Context.BIND_IMPORTANT)) != 0) { if (clientAdj >= ProcessList.PERSISTENT_SERVICE_ADJ) { newAdj = clientAdj; } else { // make this service persistent newAdj = ProcessList.PERSISTENT_SERVICE_ADJ; schedGroup = ProcessList.SCHED_GROUP_DEFAULT; procState = ActivityManager.PROCESS_STATE_PERSISTENT; cr.trackProcState(procState, mAdjSeq, now); trackedProcState = true; } } else if ((cr.flags & Context.BIND_NOT_PERCEPTIBLE) != 0 && clientAdj <= ProcessList.PERCEPTIBLE_APP_ADJ && adj >= ProcessList.PERCEPTIBLE_LOW_APP_ADJ) { newAdj = ProcessList.PERCEPTIBLE_LOW_APP_ADJ; } else if ((cr.flags&Context.BIND_NOT_VISIBLE) != 0 && clientAdj < ProcessList.PERCEPTIBLE_APP_ADJ && adj >= ProcessList.PERCEPTIBLE_APP_ADJ) { newAdj = ProcessList.PERCEPTIBLE_APP_ADJ; } else if (clientAdj >= ProcessList.PERCEPTIBLE_APP_ADJ) { newAdj = clientAdj; } else { if (adj > ProcessList.VISIBLE_APP_ADJ) { // TODO: Is this too limiting for apps bound from TOP? newAdj = Math.max(clientAdj, ProcessList.VISIBLE_APP_ADJ); } else { newAdj = adj; } } if (!client.isCached()) { app.setCached(false); } if (adj > newAdj) { adj = newAdj; app.setCurRawAdj(adj); adjType = "service"; } } } if ((cr.flags & (Context.BIND_NOT_FOREGROUND | Context.BIND_IMPORTANT_BACKGROUND)) == 0) { // This will treat important bound services identically to // the top app, which may behave differently than generic // foreground work. final int curSchedGroup = client.getCurrentSchedulingGroup(); if (curSchedGroup > schedGroup) { if ((cr.flags&Context.BIND_IMPORTANT) != 0) { schedGroup = curSchedGroup; } else { schedGroup = ProcessList.SCHED_GROUP_DEFAULT; } } if (clientProcState < PROCESS_STATE_TOP) { // Special handling for above-top states (persistent // processes). These should not bring the current process // into the top state, since they are not on top. Instead // give them the best bound state after that. if (cr.hasFlag(Context.BIND_FOREGROUND_SERVICE)) { clientProcState = PROCESS_STATE_BOUND_FOREGROUND_SERVICE; ; } else if (mService.mWakefulness == PowerManagerInternal.WAKEFULNESS_AWAKE && (cr.flags & Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE) != 0) { clientProcState = PROCESS_STATE_BOUND_FOREGROUND_SERVICE; } else { clientProcState = PROCESS_STATE_IMPORTANT_FOREGROUND; } } else if (clientProcState == PROCESS_STATE_TOP) { // Go at most to BOUND_TOP, unless requested to elevate // to client's state. clientProcState = PROCESS_STATE_BOUND_TOP; boolean enabled = false; try { enabled = mPlatformCompat.isChangeEnabled( PROCESS_CAPABILITY_CHANGE_ID, client.info); } catch (RemoteException e) { } if (enabled) { if (cr.hasFlag(Context.BIND_INCLUDE_CAPABILITIES)) { // TOP process passes all capabilities to the service. capability |= PROCESS_CAPABILITY_ALL; } else { // TOP process passes no capability to the service. } } else { // TOP process passes all capabilities to the service. capability |= PROCESS_CAPABILITY_ALL; } } } else if ((cr.flags & Context.BIND_IMPORTANT_BACKGROUND) == 0) { if (clientProcState < PROCESS_STATE_TRANSIENT_BACKGROUND) { clientProcState = PROCESS_STATE_TRANSIENT_BACKGROUND; } } else { if (clientProcState < PROCESS_STATE_IMPORTANT_BACKGROUND) { clientProcState = PROCESS_STATE_IMPORTANT_BACKGROUND; } } if (schedGroup < ProcessList.SCHED_GROUP_TOP_APP && (cr.flags & Context.BIND_SCHEDULE_LIKE_TOP_APP) != 0) { schedGroup = ProcessList.SCHED_GROUP_TOP_APP; } if (!trackedProcState) { cr.trackProcState(clientProcState, mAdjSeq, now); } if (procState > clientProcState) { procState = clientProcState; app.setCurRawProcState(procState); if (adjType == null) { adjType = "service"; } } if (procState < PROCESS_STATE_IMPORTANT_BACKGROUND && (cr.flags & Context.BIND_SHOWING_UI) != 0) { app.setPendingUiClean(true); } if (adjType != null) { app.adjType = adjType; app.adjTypeCode = ActivityManager.RunningAppProcessInfo .REASON_SERVICE_IN_USE; app.adjSource = cr.binding.client; app.adjSourceProcState = clientProcState; app.adjTarget = s.instanceName; if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise to " + adjType + ": " + app + ", due to " + cr.binding.client + " adj=" + adj + " procState=" + ProcessList.makeProcStateString(procState)); } } } else { // BIND_WAIVE_PRIORITY == true // BIND_WAIVE_PRIORITY bindings are special when it comes to the // freezer. Processes bound via WPRI are expected to be running, // but they are not promoted in the LRU list to keep them out of // cached. As a result, they can freeze based on oom_adj alone. // Normally, bindToDeath would fire when a cached app would die // in the background, but nothing will fire when a running process // pings a frozen process. Accordingly, any cached app that is // bound by an unfrozen app via a WPRI binding has to remain // unfrozen. if (clientAdj < ProcessList.CACHED_APP_MIN_ADJ) { app.shouldNotFreeze = true; } } if ((cr.flags&Context.BIND_TREAT_LIKE_ACTIVITY) != 0) { app.treatLikeActivity = true; } final ActivityServiceConnectionsHolder a = cr.activity; if ((cr.flags&Context.BIND_ADJUST_WITH_ACTIVITY) != 0) { if (a != null && adj > ProcessList.FOREGROUND_APP_ADJ && a.isActivityVisible()) { adj = ProcessList.FOREGROUND_APP_ADJ; app.setCurRawAdj(adj); if ((cr.flags&Context.BIND_NOT_FOREGROUND) == 0) { if ((cr.flags&Context.BIND_IMPORTANT) != 0) { schedGroup = ProcessList.SCHED_GROUP_TOP_APP_BOUND; } else { schedGroup = ProcessList.SCHED_GROUP_DEFAULT; } } app.setCached(false); app.adjType = "service"; app.adjTypeCode = ActivityManager.RunningAppProcessInfo .REASON_SERVICE_IN_USE; app.adjSource = a; app.adjSourceProcState = procState; app.adjTarget = s.instanceName; if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise to service w/activity: " + app); } } } } } } for (int provi = app.pubProviders.size() - 1; provi >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ || schedGroup == ProcessList.SCHED_GROUP_BACKGROUND || procState > PROCESS_STATE_TOP); provi--) { ContentProviderRecord cpr = app.pubProviders.valueAt(provi); for (int i = cpr.connections.size() - 1; i >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ || schedGroup == ProcessList.SCHED_GROUP_BACKGROUND || procState > PROCESS_STATE_TOP); i--) { ContentProviderConnection conn = cpr.connections.get(i); ProcessRecord client = conn.client; if (client == app) { // Being our own client is not interesting. continue; } if (computeClients) { computeOomAdjLocked(client, cachedAdj, topApp, doingAll, now, cycleReEval, true); } else { client.setCurRawAdj(client.setAdj); client.setCurRawProcState(client.setProcState); } if (shouldSkipDueToCycle(app, client, procState, adj, cycleReEval)) { continue; } int clientAdj = client.getCurRawAdj(); int clientProcState = client.getCurRawProcState(); if (clientProcState >= PROCESS_STATE_CACHED_ACTIVITY) { // If the other app is cached for any reason, for purposes here // we are going to consider it empty. clientProcState = PROCESS_STATE_CACHED_EMPTY; } String adjType = null; if (adj > clientAdj) { if (app.hasShownUi && !app.getCachedIsHomeProcess() && clientAdj > ProcessList.PERCEPTIBLE_APP_ADJ) { adjType = "cch-ui-provider"; } else { adj = clientAdj > ProcessList.FOREGROUND_APP_ADJ ? clientAdj : ProcessList.FOREGROUND_APP_ADJ; app.setCurRawAdj(adj); adjType = "provider"; } app.setCached(app.isCached() & client.isCached()); } if (clientProcState <= PROCESS_STATE_FOREGROUND_SERVICE) { if (adjType == null) { adjType = "provider"; } if (clientProcState == PROCESS_STATE_TOP) { clientProcState = PROCESS_STATE_BOUND_TOP; } else { clientProcState = PROCESS_STATE_BOUND_FOREGROUND_SERVICE; } } conn.trackProcState(clientProcState, mAdjSeq, now); if (procState > clientProcState) { procState = clientProcState; app.setCurRawProcState(procState); } if (client.getCurrentSchedulingGroup() > schedGroup) { schedGroup = ProcessList.SCHED_GROUP_DEFAULT; } if (adjType != null) { app.adjType = adjType; app.adjTypeCode = ActivityManager.RunningAppProcessInfo .REASON_PROVIDER_IN_USE; app.adjSource = client; app.adjSourceProcState = clientProcState; app.adjTarget = cpr.name; if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise to " + adjType + ": " + app + ", due to " + client + " adj=" + adj + " procState=" + ProcessList.makeProcStateString(procState)); } } } // If the provider has external (non-framework) process // dependencies, ensure that its adjustment is at least // FOREGROUND_APP_ADJ. if (cpr.hasExternalProcessHandles()) { if (adj > ProcessList.FOREGROUND_APP_ADJ) { adj = ProcessList.FOREGROUND_APP_ADJ; app.setCurRawAdj(adj); schedGroup = ProcessList.SCHED_GROUP_DEFAULT; app.setCached(false); app.adjType = "ext-provider"; app.adjTarget = cpr.name; if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise adj to external provider: " + app); } } if (procState > PROCESS_STATE_IMPORTANT_FOREGROUND) { procState = PROCESS_STATE_IMPORTANT_FOREGROUND; app.setCurRawProcState(procState); if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise procstate to external provider: " + app); } } } } if (app.lastProviderTime > 0 && (app.lastProviderTime + mConstants.CONTENT_PROVIDER_RETAIN_TIME) > now) { if (adj > ProcessList.PREVIOUS_APP_ADJ) { adj = ProcessList.PREVIOUS_APP_ADJ; schedGroup = ProcessList.SCHED_GROUP_BACKGROUND; app.setCached(false); app.adjType = "recent-provider"; if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise adj to recent provider: " + app); } } if (procState > PROCESS_STATE_LAST_ACTIVITY) { procState = PROCESS_STATE_LAST_ACTIVITY; app.adjType = "recent-provider"; if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise procstate to recent provider: " + app); } } } if (procState >= PROCESS_STATE_CACHED_EMPTY) { if (app.hasClientActivities()) { // This is a cached process, but with client activities. Mark it so. procState = PROCESS_STATE_CACHED_ACTIVITY_CLIENT; app.adjType = "cch-client-act"; } else if (app.treatLikeActivity) { // This is a cached process, but somebody wants us to treat it like it has // an activity, okay! procState = PROCESS_STATE_CACHED_ACTIVITY; app.adjType = "cch-as-act"; } } if (adj == ProcessList.SERVICE_ADJ) { if (doingAll && !cycleReEval) { app.serviceb = mNewNumAServiceProcs > (mNumServiceProcs/3); mNewNumServiceProcs++; //Slog.i(TAG, "ADJ " + app + " serviceb=" + app.serviceb); if (!app.serviceb) { // This service isn't far enough down on the LRU list to // normally be a B service, but if we are low on RAM and it // is large we want to force it down since we would prefer to // keep launcher over it. if (mService.mLastMemoryLevel > ProcessStats.ADJ_MEM_FACTOR_NORMAL && app.lastPss >= mProcessList.getCachedRestoreThresholdKb()) { app.serviceHighRam = true; app.serviceb = true; //Slog.i(TAG, "ADJ " + app + " high ram!"); } else { mNewNumAServiceProcs++; //Slog.i(TAG, "ADJ " + app + " not high ram!"); } } else { app.serviceHighRam = false; } } if (app.serviceb) { adj = ProcessList.SERVICE_B_ADJ; } } app.setCurRawAdj(adj); //Slog.i(TAG, "OOM ADJ " + app + ": pid=" + app.pid + // " adj=" + adj + " curAdj=" + app.curAdj + " maxAdj=" + app.maxAdj); if (adj > app.maxAdj) { adj = app.maxAdj; if (app.maxAdj <= ProcessList.PERCEPTIBLE_LOW_APP_ADJ) { schedGroup = ProcessList.SCHED_GROUP_DEFAULT; } } // Put bound foreground services in a special sched group for additional // restrictions on screen off if (procState >= PROCESS_STATE_BOUND_FOREGROUND_SERVICE && mService.mWakefulness != PowerManagerInternal.WAKEFULNESS_AWAKE) { if (schedGroup > ProcessList.SCHED_GROUP_RESTRICTED) { schedGroup = ProcessList.SCHED_GROUP_RESTRICTED; } } // apply capability from FGS. if (app.hasForegroundServices()) { capability |= capabilityFromFGS; } capability |= getDefaultCapability(app, procState); // Do final modification to adj. Everything we do between here and applying // the final setAdj must be done in this function, because we will also use // it when computing the final cached adj later. Note that we don't need to // worry about this for max adj above, since max adj will always be used to // keep it out of the cached vaues. app.curAdj = app.modifyRawOomAdj(adj); if (app.processName.equals("cn.weipass.service") || app.processName.equals("com.wiseasy.nfcservice") || app.processName.startsWith("cn.wiseasy.leopardclaw") || app.processName.startsWith("com.wangpos.updatespdemo") || app.processName.equals("wangpos.sdk4.base") || app.processName.equals("com.nlscan.scantool") || app.processName.equals("wangpos.sdk4.emv") || app.processName.equals("wangpos.sdk4.keymanager") || app.processName.equals("com.android.externalstorage") || app.processName.equals("com.android.settings.intelligence") || app.processName.equals("android.process.media") || app.processName.equals("com.android.aging") || app.processName.equals("com.wpos.sdkdemo") || app.processName.equals("com.android.settings") || app.processName.equals("com.android.managedprovisioning") || app.processName.equals("com.google.android.apps.work.clouddpc") || app.processName.equals("com.qualcomm.qti.qms.service.connectionsecurity") || app.processName.equals("com.google.android.configupdater") || app.processName.equals("com.google.process.gapps") || app.processName.equals("com.android.vending") || app.processName.equals("com.google.process.gservices") || app.processName.equals("com.google.android.gms.ui")) { app.curAdj = 0; } app.curCapability = capability; app.setCurrentSchedulingGroup(schedGroup); app.setCurProcState(procState); app.setCurRawProcState(procState); app.setHasForegroundActivities(foregroundActivities); app.completedAdjSeq = mAdjSeq; if ("com.idtech.watchdog".equals(app.processName)) { adj = 200; // Target OOM adj value schedGroup = ProcessList.SCHED_GROUP_DEFAULT; procState = ActivityManager.PROCESS_STATE_SERVICE; // Example state, choose appropriately // Skip further adj computations for this process app.curRawAdj = adj; app.curAdj = adj; app.setSchedGroup = schedGroup; //app.setCurProcState(procState); return true; // Or break out of the adjustment logic if possible } // if curAdj or curProcState improved, then this process was promoted return app.curAdj < prevAppAdj || app.getCurProcState() < prevProcState || app.curCapability != prevCapability ; } 普通应用com.idtech.watchdog 如何再framework和lmkd 调整 被容易杀掉
最新发布
08-23
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值