Android Fk:【JavaCrash】Android 26以后限制使用startService启动后台服务

Android Fk:【JavaCrash】Android 26以后限制使用startService启动后台服务

一. 问题概述

1.出错调用栈

E AndroidRuntime: java.lang.IllegalStateException: Not allowed to start service Intent { act=com.unionpay.uppay.action.HCE pkg=com.sankuai.meituan }: app is in background uid null

07-23 19:06:29.734 15328 15377 E AndroidRuntime: FATAL EXCEPTION: Thread-9
07-23 19:06:29.734 15328 15377 E AndroidRuntime: Process: com.unionpay.uppay, PID: 15328
07-23 19:06:29.734 15328 15377 E AndroidRuntime: java.lang.IllegalStateException: Not allowed to start service Intent { act=com.unionpay.uppay.action.HCE pkg=com.sankuai.meituan }: app is in background uid null
07-23 19:06:29.734 15328 15377 E AndroidRuntime: at android.app.ContextImpl.startServiceCommon(ContextImpl.java:1515)
07-23 19:06:29.734 15328 15377 E AndroidRuntime: at android.app.ContextImpl.startService(ContextImpl.java:1471)
07-23 19:06:29.734 15328 15377 E AndroidRuntime: at android.content.ContextWrapper.startService(ContextWrapper.java:654)
07-23 19:06:29.734 15328 15377 E AndroidRuntime: at com.unionpay.mobile.android.hce.f.a(Unknown Source:33)
07-23 19:06:29.734 15328 15377 E AndroidRuntime: at com.unionpay.mobile.android.hce.h.run(Unknown Source:6)

2.主要原因:

Android O 8.0(API 26之后) 之后不再允许后台service直接通过startService方式去启动, 具体行为变更
如果针对 Android 8.0 的应用尝试在不允许其创建后台服务的情况下使用 startService() 函数,则该函数将引发一个 IllegalStateException。新的 Context.startForegroundService() 函数将启动一个前台服务。
现在,即使应用在后台运行, 系统也允许其调用 Context.startForegroundService()。不过,应用必须在创建服务后的五秒内调用该服务的 startForeground() 函数。

3.解决办法:

1. 修改启动方式

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    context.startForegroundService(intent);
} else {
    context.startService(intent);
}
//并且在service里再调用startForeground方法,不然就会出现ANR
context.startForeground(SERVICE_ID, builder.getNotification());

该种方式启动用户会有感知啊,有个通知额,==

2. 调用者作好保护,防止被炸

try {
      Intent cameraIntent = new Intent("XXX.XXX");
      cameraIntent.setPackage("com.XXX.XXX");
      mContext.startServiceAsUser(cameraIntent, UserHandle.CURRENT);
 } catch (Exception e) {
      Slog.e(TAG, "IllegalAccessException", e);
}

3. 放弃使用起后台服务唤醒进程

采用jobJobScheduler替换需要起后台服务的唤醒操作方式。

二.原因分析

1. 先搜所log打印的地方

搜索“Not allowed to start service”

    //frameworks/base/core/java/android/app/ContextImpl.java
    private ComponentName startServiceCommon(Intent service, boolean requireForeground,
            UserHandle user) {
        try {

            ComponentName cn = ActivityManager.getService().startService(
                mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(
                            getContentResolver()), requireForeground,
                            getOpPackageName(), user.getIdentifier());
            if (cn != null) {
                ...
                } else if (cn.getPackageName().equals("?")) {
                //看出是在这里抛的异常
                    throw new IllegalStateException(
                            "Not allowed to start service " + service + ": " + cn.getClassName());
                }
            }
            return cn;
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

搜索“app is in background”

    //frameworks/base/services/core/java/com/android/server/am/ActiveServices.java
    ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,
            int callingPid, int callingUid, boolean fgRequired, String callingPackage, final int userId)
            throws TransactionTooLargeException {
        // If this isn't a direct-to-foreground start, check our ability to kick off an
        // arbitrary service
        if (!r.startRequested && !fgRequired) {
             ...
             return new ComponentName("?", "app is in background uid " + uidRec);
             ...
        }
    }

下面就是找这两者之间的联系了,概括在图中:


这里写图片描述

三.总结

  1. 从抛异常的地方可以看出,最终将是调用者会crash(如果不保护处理的话)
    即 A应用进程以startservice的形式调用B应用进程的service,如果满足以下条件:
    a.O及O以上的手机平台上
    b.B应用进程的AndroidManifest里声明了targetSdk大于与等于26
    c.B应用进程不是persistent应用
    d.B应用进程当前进入后台且处于idle状态
    e.B应用不在电源管理的白名单中
    f.B应用进程不再运行后台运行的白名单中
    此时A应用进程就会crash(如果不做相关保护的话)
  2. A应用进程可以是system_server
  3. O及O以上的app尽量不要通过起后台service进行操作,需要用到后台service的话可以通过JobScheduler进行处理,即如果你是个简单的三方应用,不要再使用
    调用后台service的形式唤醒应用了,调用者会很危险!!!
    4.具体流程图如下:
    (提供上面简图的draw.io的xml文件,可以使用draw.io导入修改
    提供如下时序图的uml文件,可使用带plantuml插件的inteliJ AS打开编辑
    https://pan.baidu.com/s/17MO9CXdcSBLI_kuywvCuZA)
    这里写图片描述
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 224,642评论 6 522
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 96,168评论 3 402
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 171,809评论 0 366
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 60,921评论 1 300
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 69,924评论 6 399
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 53,415评论 1 314
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 41,794评论 3 428
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 40,765评论 0 279
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 47,297评论 1 324
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 39,331评论 3 345
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 41,458评论 1 354
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 37,065评论 5 350
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 42,777评论 3 337
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 33,233评论 0 25
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 34,366评论 1 275
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 50,001评论 3 381
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 46,524评论 2 365