美文网首页AndroidWay
BackgroundProcess-startService的问

BackgroundProcess-startService的问

作者: 抠脚大汗 | 来源:发表于2018-08-22 15:17 被阅读267次

环境

在androidO上编译的项目,项目会监听锁屏/开屏的广播,调起独立进程的server,运行至此crash报错


image.png

报错关键字

Not allowed to start service Intentapp is in background uid UidRecord

分析

在androidO之前,从未发生过类似报错,初步怀疑是兼容性问题;从报错信息来看,能猜到因为app在一个后台的uid中导致不能开启一个服务;从栈信息来看很明显是startServiceAPI这个接口报的错;

那思路从追查日志报错的位置出发,ContextImpl方法startService

   @Override
    public ComponentName startService(Intent service) {
        warnIfCallingFromSystemProcess();
        return startServiceCommon(service, false, mUser);
    }

warnIfCallingFromSystemProcess();查看

    /**
     * Logs a warning if the system process directly called a method such as
     * {@link #startService(Intent)} instead of {@link #startServiceAsUser(Intent, UserHandle)}.
     * The "AsUser" variants allow us to properly enforce the user's restrictions.
     */
    private void warnIfCallingFromSystemProcess() {
        if (Process.myUid() == Process.SYSTEM_UID) {
            Slog.w(TAG, "Calling a method in the system process without a qualified user: "
                    + Debug.getCallers(5));
        }
    }

这个对应到crash信息中一个warning,主要是由于监听开锁屏广播,被调起的BroadcastReciver属于满足了Process.myUid() == Process.SYSTEM_UID,提示了一个waring.

startServiceCommon()查看

    private ComponentName startServiceCommon(Intent service, boolean requireForeground,
            UserHandle user) {
        try {
            validateServiceIntent(service);
            service.prepareToLeaveProcess(this);
            ComponentName cn = ActivityManager.getService().startService(
                mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(
                            getContentResolver()), requireForeground,
                            getOpPackageName(), user.getIdentifier());
            if (cn != null) {
                if (cn.getPackageName().equals("!")) {
                    throw new SecurityException(
                            "Not allowed to start service " + service
                            + " without permission " + cn.getClassName());
                } else if (cn.getPackageName().equals("!!")) {
                    throw new SecurityException(
                            "Unable to start service " + service
                            + ": " + cn.getClassName());
                } else if (cn.getPackageName().equals("?")) {
                    //在这里看到了crash日志,错误类型也匹配,进入else的前提是cn.getPackageName().equals("?")
                    throw new IllegalStateException(
                            "Not allowed to start service " + service + ": " + cn.getClassName());
                }
            }
            return cn;
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

这个方法在调用的时候,就比androidO之前的版本多了一个boolean requireForeground参数,这是问题点。
return cn;cn是通过Binder方式跨进程获取的ActivityManagerNative.getDefault().startService,直接到ActivityManagerService中查看startService方法

    @Override
    public ComponentName startService(IApplicationThread caller, Intent service,
            String resolvedType, boolean requireForeground, String callingPackage, int userId)
            throws TransactionTooLargeException {
        …………
        synchronized(this) {
            final int callingPid = Binder.getCallingPid();
            final int callingUid = Binder.getCallingUid();
            final long origId = Binder.clearCallingIdentity();
            ComponentName res;
            try {
                res = mServices.startServiceLocked(caller, service,
                        resolvedType, callingPid, callingUid,
                        requireForeground, callingPackage, userId);
            } finally {
                Binder.restoreCallingIdentity(origId);
            }
            return res;
        }
    }

mServices是ActiveServices的实例,查看startServiceLocked方法

    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) {
            // Before going further -- if this app is not allowed to start services in the
            // background, then at this point we aren't going 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 not allowed: 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 silently disabling the app, to disrupt as
                    // little as possible existing apps.
                    return null;
                }
                // This app knows it is in the new model where this operation is not
                // allowed, so tell it what has happened.
                UidRecord uidRec = mAm.mActiveUids.get(r.appInfo.uid);
                //在这里又看到另外一段日志,并且给要返回的compoentName赋值了一个?作为packageName了
                return new ComponentName("?", "app is in background uid " + uidRec);
            }
        }
        …………
    }

看一下进入条件是!r.startRequested && !fgRequired
r是ServiceRecord的实例,startRequested意义是否有明确的组件调用为flase。我们传入的fgRequired为false。

解决

问题的解决思路是把fgRequired改为true,就不会走到异常,返回一个package为"?"的ComponentName的对象,返回了正常的Component组件也不会出现crash的报错了。
AndroidO提供了一个新的接口调起Service

    @Override
    public ComponentName startForegroundService(Intent service) {
        warnIfCallingFromSystemProcess();
        return startServiceCommon(service, true, mUser);
    }

这里调用startServiceCommon直接传true.在后续一系列的Binder调用中,直到分析问题的ActiveServices类startServiceLocked方法,requireForeground是AndroidO新加入的参数。
如下兼容就可以了如果要在低版本编译,需要反射调用startForegroundService

    if (Build.VERSION.SDK_INT >= 26) {//Android8.0
         context.startForegroundService(service);
    } else {
         context.startService(service);
    }

相关文章

  • BackgroundProcess-startService的问

    环境 在androidO上编译的项目,项目会监听锁屏/开屏的广播,调起独立进程的server,运行至此crash报...

  • 该问的问,不该问的别问

    该问的问,不该问的别问 毕竟你不了解我 如果每个人都了解我的话 那我得有多普通 别问我缺什么 好像是缺爱情的真实部...

  • 问?问!问!!

    问,是思维的逻辑起点。 只有问,才有思维。 只有问,才易思考。 只有不断地追问,才能使思维更加深刻从而让你挖掘事物...

  • 问,问,问

    问,问,问木易冬青作 鸟儿藏于云琼之中你不知道 我不知道——它要去往何方是否知晓了——北方秋黄、秋收冬藏 几处闲田...

  • 【贰拾柒帆】问!问!问!探索真正的自己。

    人的一生时刻应该做的就是探究自己。 问:生活中的我们自已为然的事情,但我们缺少探索 如果在你的家乡投资一座核电厂,...

  • 问1问

    在2007年荣获“北京奥运会推荐果品”奖的是什么水果?

  • 【问无所问】

    今日再给朋友做量催前朋友希望能从它的元神那里得知自己的真实身份得知自己此世的使命,但当进入催眠态后我却提不出什么有...

  • 问,不须问

    天上的明月不曾变过 地上的江河滔滔流去 我一次又一次的问 这不会有结果的结果 天上宫阙是海市里的蜃楼 又是虚无缥缈...

  • 问天,问地,问自己?

    来源‖指尖的温柔 问天?问地?问自己? 人为什么活的那么累? 缘分为什么分分合合? 情为什么如此难以把握? 人生的...

  • 问天,问地,问山海

    我问天,你为什么高不可攀? 天说,我理解你, 我把你的思念挂在了云端。 这样,它便会随着雨滴的飘落, 让干枯的河床...

网友评论

    本文标题:BackgroundProcess-startService的问

    本文链接:https://www.haomeiwen.com/subject/ojaeiftx.html