Android(安卓)O(8.0)后台service限制
Android的每次平台更新,都一直在努力收紧应用权限。8.0也不例外,这次是限制应用后台启动service的权限。相关的问题现象,可参考http://blog.csdn.net/chenshengfa/article/details/71407704。
不过本文除了吐槽下新的后台限制对8.0平台适配带来的影响外,主要是说说本人在开发中发现的一些可规避的方法。先声明一下,由于应用权限问题,本文并不适合普通应用开发,仅做研究参考。
虽然Android官方在限制了后台service启动的同时,也给出了替代方案。但毕竟有些功能,是其他方案无法替代或即便替代了也无法达到以往的效果的。比如监听网络变化、监听亮灭屏等这些需求。真是一去不复返的坑,对于一些习惯了耍流氓的我们,真真是不方便了啊。以下,是方案正文。
1. 对于所有应用可以使用的解决方法。
1). 平台区分,8.0以下,爱咋咋地,以前怎样还怎样。
2). 8.0以上,统一一个常驻service,转为前台。方法为:将startService改成startForegroundService,并在对应的service创建的时候,使用startForeground注册自己的notification。
3). 对,这个方法是带notification的,也就是说,如果想用常驻service,就得让用户知道。
2. 对于系统应用,可以使用的方法(需要有system权限)。
可能有人说,我都系统应用了,哪儿要这么麻烦,我已经是特殊员工啦,Android禁止我干嘛。对哦,你要是有7大姑8大姨的也想享受权限怎么搞?我所负责的应用,就有这么一款,处在灰色地带的,一部分有system权限,另一部分小弟却在局子外边混。
首先,我们看一下源码中的启动部分
ActiveServices.java -> startServiceLocked(),里边有这么一段
if(!r.startRequested && !fgRequired) { // Before going further -- if this app isnot allowed to start services in the // background, then at this point we aren'tgoing to let it period. final int allowed =mAm.getAppStartModeLocked(r.appInfo.uid, r.packageName, r.appInfo.targetSdkVersion,callingPid, false, false); if (allowed !=ActivityManager.APP_START_MODE_NORMAL) { Slog.w(TAG, "Background start notallowed: service " + service + " to " +r.name.flattenToShortString() + " from pid=" +callingPid + " uid=" + callingUid + " pkg=" +callingPackage); if (allowed ==ActivityManager.APP_START_MODE_DELAYED) { // In this case we are silentlydisabling the app, to disrupt as // little as possible existing apps. return null; } // This app knows it is in the newmodel where this operation is not // allowed, so tell it what hashappened. UidRecord uidRec = mAm.mActiveUids.get(r.appInfo.uid); return new ComponentName("?","app is in background uid " + uidRec); }}
不难发现,如果service不是fgRequired前台唤醒,那就得走这段了,获取service所持有的应用MODE是否为APP_START_MODE_NORMAL。也就是说,一旦应用为APP_START_MODE_NORMAL,就可以开开心心的启动后台service了。进入ActivityManagerService.java ->getAppStartModeLocked()查看定义,核心基本上就是这个了
final intstartMode = (alwaysRestrict) ? appRestrictedInBackgroundLocked(uid,packageName, packageTargetSdk) :appServicesRestrictedInBackgroundLocked(uid, packageName, packageTargetSdk);
直接看appServicesRestrictedInBackgroundLocked()
intappServicesRestrictedInBackgroundLocked(int uid, String packageName, intpackageTargetSdk) { // Persistent app? if (mPackageManagerInt.isPackagePersistent(packageName)){ if (DEBUG_BACKGROUND_CHECK) { Slog.i(TAG, "App " + uid+ "/" + packageName + " is persistent; notrestricted in background"); } return ActivityManager.APP_START_MODE_NORMAL; } // Non-persistent but backgroundwhitelisted? if (uidOnBackgroundWhitelist(uid)) { if (DEBUG_BACKGROUND_CHECK) { Slog.i(TAG, "App " + uid+ "/" + packageName + " on backgroundwhitelist; not restricted in background"); } returnActivityManager.APP_START_MODE_NORMAL; } // Is this app on the battery whitelist? if (isOnDeviceIdleWhitelistLocked(uid)) { if (DEBUG_BACKGROUND_CHECK) { Slog.i(TAG, "App " + uid+ "/" + packageName + " on idle whitelist;not restricted in background"); } returnActivityManager.APP_START_MODE_NORMAL; } // None of the service-policy criteriaapply, so we apply the common criteria return appRestrictedInBackgroundLocked(uid,packageName, packageTargetSdk);}
然后就是各种判断了,作为有system权限的应用,就可以给其他应用开后门啦。
其中的isOnDeviceIdleWhitelistLocked,用户可用通过进入设置->应用详情->管理电池应用,此处可以通过开关进行改变这个值,或者设置->电池->右上方的菜单,电池优化中,修改应用列表即可。
如上,收尾。
更多相关文章
- 如何在后台运行Linux命令?
- Root你的设备
- Android用SQLite存储数据详解
- Android(安卓)截图功能源码的分析
- Activity详解一 配置、启动和关闭activity
- Android:这是一份全面 & 详细的Webview使用攻略
- Android的事件类型及事件处理机制
- Android(安卓)View事件分发机制
- Android编程研究(二)——Message和message Handler