Android O(8.0)后台service限制

来源:互联网 发布:网络新词汇大全 编辑:程序博客网 时间:2024/06/17 09:17

        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,用户可用通过进入设置->应用详情->管理电池应用,此处可以通过开关进行改变这个值,或者设置->电池->右上方的菜单,电池优化中,修改应用列表即可。

 

如上,收尾。

原创粉丝点击