首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何修改startService()的源代码以识别是否从后台调用它?

如何修改startService()的源代码以识别是否从后台调用它?
EN

Stack Overflow用户
提问于 2021-02-22 11:04:49
回答 3查看 439关注 0票数 1

因为关于Android9,如果startService()是从背景调用的,它就会抛出一个。在我的开发人员控制台中,我多次看到这个异常:

代码语言:javascript
复制
java.lang.IllegalStateException:
  at android.app.ContextImpl.startServiceCommon (ContextImpl.java:1666)
  at android.app.ContextImpl.startService (ContextImpl.java:1611)

在这种情况下,谷歌建议调用startForegroundService(),并在5秒内调用startForeground()见“后台执行限制”

无论如何,从startService()前台调用是完全可以的。现在,我想知道Android是如何识别/决定应用程序在前台而不是错误地抛出一个IllegalStateException。

我开始挖掘Android9 9/10的源代码,并将其与8/7进行比较,以了解如何修改startService()以识别是否从前台/后台调用它。但我确信,在我之前,许多开发人员已经这样做了,如果他们能给出答案,我会很高兴的。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2021-03-05 15:29:29

跟着这个链接到Android的源代码,我们找到了getAppStartModeLocked()

代码语言:javascript
复制
int getAppStartModeLocked(int uid, String packageName, int packageTargetSdk,
        int callingPid, boolean alwaysRestrict, boolean disabledOnly) {
    UidRecord uidRec = mActiveUids.get(uid);
    if (DEBUG_BACKGROUND_CHECK) Slog.d(TAG, "checkAllowBackground: uid=" + uid + " pkg="
            + packageName + " rec=" + uidRec + " always=" + alwaysRestrict + " idle="
            + (uidRec != null ? uidRec.idle : false));
    if (uidRec == null || alwaysRestrict || uidRec.idle) {
        boolean ephemeral;
        if (uidRec == null) {
            ephemeral = getPackageManagerInternalLocked().isPackageEphemeral(
                    UserHandle.getUserId(uid), packageName);
        } else {
            ephemeral = uidRec.ephemeral;
        }
        if (ephemeral) {
            // We are hard-core about ephemeral apps not running in the background.
            return ActivityManager.APP_START_MODE_DISABLED;
        } else {
            if (disabledOnly) {
                // The caller is only interested in whether app starts are completely
                // disabled for the given package (that is, it is an instant app).  So
                // we don't need to go further, which is all just seeing if we should
                // apply a "delayed" mode for a regular app.
                return ActivityManager.APP_START_MODE_NORMAL;
            }
            final int startMode = (alwaysRestrict)
                    ? appRestrictedInBackgroundLocked(uid, packageName, packageTargetSdk)
                    : appServicesRestrictedInBackgroundLocked(uid, packageName,
                            packageTargetSdk);
            if (DEBUG_BACKGROUND_CHECK) Slog.d(TAG, "checkAllowBackground: uid=" + uid
                    + " pkg=" + packageName + " startMode=" + startMode
                    + " onwhitelist=" + isOnDeviceIdleWhitelistLocked(uid));
            if (startMode == ActivityManager.APP_START_MODE_DELAYED) {
                // This is an old app that has been forced into a "compatible as possible"
                // mode of background check.  To increase compatibility, we will allow other
                // foreground apps to cause its services to start.
                if (callingPid >= 0) {
                    ProcessRecord proc;
                    synchronized (mPidsSelfLocked) {
                        proc = mPidsSelfLocked.get(callingPid);
                    }
                    if (proc != null &&
                            !ActivityManager.isProcStateBackground(proc.curProcState)) {
                        // Whoever is instigating this is in the foreground, so we will allow it
                        // to go through.
                        return ActivityManager.APP_START_MODE_NORMAL;
                    }
                }
            }
            return startMode;
        }
    }
    return ActivityManager.APP_START_MODE_NORMAL;
}

方法appRestrictedInBackgroundLocked() (也是从appServicesRestrictedInBackgroundLocked()调用作为后备)决定startMode

代码语言:javascript
复制
// Unified app-op and target sdk check
int appRestrictedInBackgroundLocked(int uid, String packageName, int packageTargetSdk) {
    // Apps that target O+ are always subject to background check
    if (packageTargetSdk >= Build.VERSION_CODES.O) {
        if (DEBUG_BACKGROUND_CHECK) {
            Slog.i(TAG, "App " + uid + "/" + packageName + " targets O+, restricted");
        }
        return ActivityManager.APP_START_MODE_DELAYED_RIGID;
    }
    // ...and legacy apps get an AppOp check
    int appop = mAppOpsService.noteOperation(AppOpsManager.OP_RUN_IN_BACKGROUND,
            uid, packageName);
    if (DEBUG_BACKGROUND_CHECK) {
        Slog.i(TAG, "Legacy app " + uid + "/" + packageName + " bg appop " + appop);
    }
    switch (appop) {
        case AppOpsManager.MODE_ALLOWED:
            return ActivityManager.APP_START_MODE_NORMAL;
        case AppOpsManager.MODE_IGNORED:
            return ActivityManager.APP_START_MODE_DELAYED;
        default:
            return ActivityManager.APP_START_MODE_DELAYED_RIGID;
    }
}

但是关于前景或背景的最终决定是在ActivityManager.isProcStateBackground(uidRec.setProcState)中完成的。

代码语言:javascript
复制
/** @hide Should this process state be considered a background state? */
public static final boolean isProcStateBackground(int procState) {
    return procState >= PROCESS_STATE_TRANSIENT_BACKGROUND;
}

因此,这里的第一种方法的这一部分获取前景或背景的当前状态:

代码语言:javascript
复制
                ProcessRecord proc;
                synchronized (mPidsSelfLocked) {
                    proc = mPidsSelfLocked.get(callingPid);
                }
票数 2
EN

Stack Overflow用户

发布于 2021-02-24 13:36:44

在AOSP10 (10.0.0_r25)中:

服务器端:

在frameworks\base\services\core\java\com\android\server\am\ActiveServices.java:的startServiceLocked中

代码语言:javascript
复制
        // 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, forcedStandby);
        if (allowed != ActivityManager.APP_START_MODE_NORMAL) {
            Slog.w(TAG, "Background start not allowed: service "
                    + service + " to " + r.shortInstanceName
                    + " from pid=" + callingPid + " uid=" + callingUid
                    + " pkg=" + callingPackage + " startFg?=" + fgRequired);
            ......
            // This app knows it is in the new model where this operation is not
            // allowed, so tell it what has happened.
            UidRecord uidRec = mAm.mProcessList.getUidRecordLocked(r.appInfo.uid);
            return new ComponentName("?", "app is in background uid " + uidRec);
        }

然后在客户端:

在ContextImpl.java中作为您的日志:

代码语言:javascript
复制
            else if (cn.getPackageName().equals("?")) {
                throw new IllegalStateException(
                        "Not allowed to start service " + service + ": " + cn.getClassName());
            }
票数 4
EN

Stack Overflow用户

发布于 2021-03-04 05:24:29

当应用程序在下面的情况下,它将被允许从背景开始。

  1. 这个应用程序是持久的。只有预置的系统应用程序才能做到这一点。
  2. 这个应用程序不是空闲。当应用程序进程在背景中运行一段时间时,它将被设置为空闲。
  3. 该应用程序是空闲的,但在一个白名单。backgroundWhitelistUid。只有带有系统uid的应用程序才能将应用程序添加到这个列表中。
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/66314488

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档