android系统服务分析,源码分析Android系统后台应用启动服务crash

源码分析Android系统后台应用启动服务crash

有的时候表面上看起来似乎一切运行正常,但是通过查看日志发现是有问题的。

下面这段日志,截取自某设备开机过程的日志。通过查看,发现有GMS的crash。

--------- beginning of crash

03-09 03:46:43.183 3804 3804 E AndroidRuntime: FATAL EXCEPTION: main

03-09 03:46:43.183 3804 3804 E AndroidRuntime: Process: com.google.android.gms.persistent, PID: 3804

03-09 03:46:43.183 3804 3804 E AndroidRuntime: java.lang.RuntimeException: Unable to start receiver com.google.android.gms.chimera.GmsIntentOperationService$PersistentTrustedReceiver: java.lang.IllegalStateException: Not allowed to start service Intent { act=android.intent.action.LOCKED_BOOT_COMPLETED flg=0x9000010 cmp=com.google.android.gms/.chimera.GmsIntentOperationService (has extras) }: app is in background uid UidRecord{bf88dc8 u0a10 RCVR idle change:uncached procs:2 seq(0,0,0)}

03-09 03:46:43.183 3804 3804 E AndroidRuntime: at android.app.ActivityThread.handleReceiver(ActivityThread.java:3388)

03-09 03:46:43.183 3804 3804 E AndroidRuntime: at android.app.ActivityThread.access$1200(ActivityThread.java:199)

03-09 03:46:43.183 3804 3804 E AndroidRuntime: at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1661)

03-09 03:46:43.183 3804 3804 E AndroidRuntime: at android.os.Handler.dispatchMessage(Handler.java:106)

03-09 03:46:43.183 3804 3804 E AndroidRuntime: at android.os.Looper.loop(Looper.java:193)

03-09 03:46:43.183 3804 3804 E AndroidRuntime: at android.app.ActivityThread.main(ActivityThread.java:6669)

03-09 03:46:43.183 3804 3804 E AndroidRuntime: at java.lang.reflect.Method.invoke(Native Method)

03-09 03:46:43.183 3804 3804 E AndroidRuntime: at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)

03-09 03:46:43.183 3804 3804 E AndroidRuntime: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:866)

03-09 03:46:43.183 3804 3804 E AndroidRuntime: Caused by: java.lang.IllegalStateException: Not allowed to start service Intent { act=android.intent.action.LOCKED_BOOT_COMPLETED flg=0x9000010 cmp=com.google.android.gms/.chimera.GmsIntentOperationService (has extras) }: app is in background uid UidRecord{bf88dc8 u0a10 RCVR idle change:uncached procs:2 seq(0,0,0)}

03-09 03:46:43.183 3804 3804 E AndroidRuntime: at android.app.ContextImpl.startServiceCommon(ContextImpl.java:1577)

03-09 03:46:43.183 3804 3804 E AndroidRuntime: at android.app.ContextImpl.startService(ContextImpl.java:1532)

03-09 03:46:43.183 3804 3804 E AndroidRuntime: at android.content.ContextWrapper.startService(ContextWrapper.java:664)

03-09 03:46:43.183 3804 3804 E AndroidRuntime: at iap.startService(:com.google.android.gms@19831064@19.8.31 (080306-284611645):3)

03-09 03:46:43.183 3804 3804 E AndroidRuntime: at android.content.ContextWrapper.startService(ContextWrapper.java:664)

03-09 03:46:43.183 3804 3804 E AndroidRuntime: at android.content.ContextWrapper.startService(ContextWrapper.java:664)

03-09 03:46:43.183 3804 3804 E AndroidRuntime: at android.content.ContextWrapper.startService(ContextWrapper.java:664)

03-09 03:46:43.183 3804 3804 E AndroidRuntime: at bkn.a(:com.google.android.gms@19831064@19.8.31 (080306-284611645):4)

03-09 03:46:43.183 3804 3804 E AndroidRuntime: at bkn.onReceive(:com.google.android.gms@19831064@19.8.31 (080306-284611645):7)

03-09 03:46:43.183 3804 3804 E AndroidRuntime: at gvr.onReceive(:com.google.android.gms@19831064@19.8.31 (080306-284611645):3)

03-09 03:46:43.183 3804 3804 E AndroidRuntime: at android.app.ActivityThread.handleReceiver(ActivityThread.java:3379)

03-09 03:46:43.183 3804 3804 E AndroidRuntime: ... 8 more

复制代码

下面,我们按照问题的解决顺序分析一下这题:

这是什么问题?

从源码的角度弄清楚产生问题的原因

找到解决方案

这是什么问题?

首先分析日志

--------- beginning of crash

复制代码

系统发生了crash

AndroidRuntime: Process: com.google.android.gms.persistent, PID: 3900

复制代码

发生crash的是应用程序进程com.google.android.gms.persistent

AndroidRuntime: java.lang.RuntimeException: Unable to start receiver com.google.android.gms.chimera.GmsIntentOperationService$PersistentTrustedReceiver: java.lang.IllegalStateException: Not allowed to start service Intent { act=android.intent.action.LOCKED_BOOT_COMPLETED flg=0x9000010 cmp=com.google.android.gms/.chimera.GmsIntentOperationService (has extras) }: app is in background uid UidRecord{bf88dc8 u0a10 RCVR idle change:uncached procs:2 seq(0,0,0)}

复制代码

启动GmsIntentOperationService服务的时候,系统抛出了Android运行时异常。原因是后台uid的app不允许启动服务。其实就是不允许后台应用启动服务。

AndroidRuntime: at android.app.ContextImpl.startServiceCommon(ContextImpl.java:1577)

复制代码

直接看Caused by下面一行,这个运行时异常是ContexImpl类的startServiceCommon()方法抛出的,位于ContextImpl.java文件的1577行。

从源码角度弄清楚产生问题的原因

frameworks/base/core/java/android/app/ContextImpl.java

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("?")) {

throw new IllegalStateException(

line1577 "Not allowed to start service " + service + ": " + cn.getClassName());

}

}

return cn;

} catch (RemoteException e) {

throw e.rethrowFromSystemServer();

}

}

复制代码

AMS返回的ComponentName是经过特殊处理的,本题中返回的包名是“?”号。Android系统的Framework过于庞大,在熟悉或不熟悉的情况下,都可以通过搜索关键字app is in background uid,直接找到问题相关的具体代码段。

Android/frameworks/base$ grep -r "app is in background uid"

services/core/java/com/android/server/am/ActiveServices.java: return new ComponentName("?", "app is in background uid " + uidRec);

复制代码

ActiveServices.startServiceLocked()方法过于庞大,只贴关键部分代码

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 (forcedStandby || (!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, forcedStandby);

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 + " 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.mActiveUids.get(r.appInfo.uid);

return new ComponentName("?", "app is in background uid " + uidRec);

}

}

... ...

ComponentName cmp = startServiceInnerLocked(smap, service, r, callerFg, addToStarting);

return cmp;

}

复制代码

AMS.getAppStartModeLocked()方法返回的不是ActivityManager.APP_START_MODE_NORMAL,这里就返回一个packagename是"?"包名是原因的ComponentName,阻断service的进一步启动。

getAppStartModeLocked()方法是取得应用程序的启动模式。应用程序有四中启动模式,分别是:正常启动、延迟启动、延迟启动并抛出异常、取消启动。

/** @hide Mode for {@link IActivityManager#isAppStartModeDisabled}: normal free-to-run operation. */

public static final int APP_START_MODE_NORMAL = 0;

/** @hide Mode for {@link IActivityManager#isAppStartModeDisabled}: delay running until later. */

public static final int APP_START_MODE_DELAYED = 1;

/** @hide Mode for {@link IActivityManager#isAppStartModeDisabled}: delay running until later, with

* rigid errors (throwing exception). */

public static final int APP_START_MODE_DELAYED_RIGID = 2;

/** @hide Mode for {@link IActivityManager#isAppStartModeDisabled}: disable/cancel pending

* launches; this is the mode for ephemeral apps. */

public static final int APP_START_MODE_DISABLED = 3;

复制代码

frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

int getAppStartModeLocked(int uid, String packageName, int packageTargetSdk,

int callingPid, boolean alwaysRestrict, boolean disabledOnly, boolean forcedStandby) {

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 || forcedStandby || 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, false)

+ " onwhitelist(ei)=" + isOnDeviceIdleWhitelistLocked(uid, true));

}

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;

}

复制代码

系统有埋日志,先放开DEBUG_BACKGROUND_CHECK,重新编译service.jar,push到设备里面,再来看一下打印信息,可以提升分析问题的效率。

03-09 03:46:42.926 3109 3784 D ActivityManager: checkAllowBackground: uid=10010 pkg=com.google.android.gms rec=UidRecord{9514de0 u0a10 RCVR idle change:uncached procs:2 seq(0,0,0)} always=false idle=true

03-09 03:46:42.926 3109 3784 I ActivityManager: App 10010/com.google.android.gms targets O+, restricted

03-09 03:46:42.926 3109 3784 D ActivityManager: checkAllowBackground: uid=10010 pkg=com.google.android.gms startMode=2 onwhitelist=false onwhitelist(ei)=false

03-09 03:46:42.926 3109 3784 W ActivityManager: Background start not allowed: service Intent { act=android.intent.action.LOCKED_BOOT_COMPLETED flg=0x9000010 cmp=com.google.android.gms/.chimera.GmsIntentOperationService (has extras) } to com.google.android.gms/.chimera.GmsIntentOperationService from pid=3900 uid=10010 pkg=com.google.android.gms startFg?=false

复制代码

从日志看start mode是2,即APP_START_MODE_DELAYED_RIGID。alwaysRestrict前面传入的是false。下面进入方法appServicesRestrictedInBackgroundLocked()

int appServicesRestrictedInBackgroundLocked(int uid, String packageName, int packageTargetSdk) {

// Persistent app?

if (mPackageManagerInt.isPackagePersistent(packageName)) {

if (DEBUG_BACKGROUND_CHECK) {

Slog.i(TAG, "App " + uid + "/" + packageName

+ " is persistent; not restricted in background");

}

return ActivityManager.APP_START_MODE_NORMAL;

}

// Non-persistent but background whitelisted?

if (uidOnBackgroundWhitelist(uid)) {

if (DEBUG_BACKGROUND_CHECK) {

Slog.i(TAG, "App " + uid + "/" + packageName

+ " on background whitelist; not restricted in background");

}

return ActivityManager.APP_START_MODE_NORMAL;

}

// Is this app on the battery whitelist?

if (isOnDeviceIdleWhitelistLocked(uid, /*allowExceptIdleToo=*/ false)) {

if (DEBUG_BACKGROUND_CHECK) {

Slog.i(TAG, "App " + uid + "/" + packageName

+ " on idle whitelist; not restricted in background");

}

return ActivityManager.APP_START_MODE_NORMAL;

}

// None of the service-policy criteria apply, so we apply the common criteria

return appRestrictedInBackgroundLocked(uid, packageName, packageTargetSdk);

}

复制代码

三个if都不成立,接着进入方法appRestrictedInBackgroundLocked()

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:

// If force-background-check is enabled, restrict all apps that aren't whitelisted.

if (mForceBackgroundCheck &&

!UserHandle.isCore(uid) &&

!isOnDeviceIdleWhitelistLocked(uid, /*allowExceptIdleToo=*/ true)) {

if (DEBUG_BACKGROUND_CHECK) {

Slog.i(TAG, "Force background check: " +

uid + "/" + packageName + " restricted");

}

return ActivityManager.APP_START_MODE_DELAYED;

}

return ActivityManager.APP_START_MODE_NORMAL;

case AppOpsManager.MODE_IGNORED:

return ActivityManager.APP_START_MODE_DELAYED;

default:

return ActivityManager.APP_START_MODE_DELAYED_RIGID;

}

}

复制代码

设备的系统版本是P,所以在第一个if直接返回,返回的启动模式刚好是APP_START_MODE_DELAYED_RIGID。

后台应用限制,Android O以上的版本不允许后台应用启动服务。

在这套检查逻辑里面是有三个后门的,通过这些后门,可以突破上面的限制,使后台应用可以启动服务。后门在方法appServicesRestrictedInBackgroundLocked()里面。

后门1 Persistent app。应用程序把自己设置为Persistent app,可以突破后台应用无法启动服务的限制。

android:allowBackup="true"

android:icon="@mipmap/ic_launcher"

android:label="@string/app_name"

android:supportsRtl="true"

android:persistent="true"

android:theme="@style/AppTheme"

复制代码后门2 应用在后台应用白名单里面

/**

* Non-persistent appId whitelist for background restrictions

*/

int[] mBackgroundAppIdWhitelist = new int[] {

BLUETOOTH_UID

};

复制代码后门3 应用在电池白名单里面

boolean isOnDeviceIdleWhitelistLocked(int uid, boolean allowExceptIdleToo) {

final int appId = UserHandle.getAppId(uid);

final int[] whitelist = allowExceptIdleToo

? mDeviceIdleExceptIdleWhitelist

: mDeviceIdleWhitelist;

return Arrays.binarySearch(whitelist, appId) >= 0

|| Arrays.binarySearch(mDeviceIdleTempWhitelist, appId) >= 0

|| mPendingTempWhitelist.indexOfKey(uid) >= 0;

}

复制代码

总结一下:这题产生的原因是后台应用gms想要启动服务,Android P系统不允许,被拦截并抛出异常。

找到解决方案

原因清楚了,系统的整个处理逻辑也了解,解决思路就很清晰了。可以从三个后门入手找解决方案。后门一是应用端处理,GMS是第三方应用无源代码,无法处理。后门二和后门三是系统端处理,看着后门2比较清晰,我们选择此门,确定代码层面的解决方法。

需要拿到GMS的uid字符串

反编译GMS.apk,从AndroidManifest.xml拿到sharedUserId字符串

android:sharedUserId="com.google.uid.shared"

复制代码在系统里面为GMS添加uid定义

Android/frameworks/base/core/java/android/os/Process.java

public static final int GOOGLE_UID = 1069;

复制代码

Android/frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java

private static final int GOOGLE_UID = Process.GOOGLE_UID;

mSettings.addSharedUserLPw("com.google.uid.shared", GOOGLE_UID,

ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);

复制代码添加GMS的uid到AMS里面的后台应用白名单

Android/frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

int[] mBackgroundAppIdWhitelist = new int[] {

BLUETOOTH_UID,

GOOGLE_UID

};

复制代码

b739ec46bb5c46d9c0aa4ce35ba1ea56.png

关于找一找教程网

本站文章仅代表作者观点,不代表本站立场,所有文章非营利性免费分享。

本站提供了软件编程、网站开发技术、服务器运维、人工智能等等IT技术文章,希望广大程序员努力学习,让我们用科技改变世界。

[源码分析Android系统后台应用启动服务crash]http://www.zyiz.net/tech/detail-116597.html