Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Start in Background Permission

panwj edited this page Nov 8, 2019 · 10 revisions

关于start in background 权限问题

关于小米手机后台弹activity

在小米手机的应用权限管理中有一个"后台弹出界面权限",该项权限会限制当APP处在后台时弹出Activity的动作,该权限在某些机型上是默认关闭的, 如果不给权限的情况下在后台启动activity,会无反应,且在日志中会有 ‘ExtraActivityManagerService: MIUILOG- Permission Denied Activity’提示

关于小米设备后台启动activity比较好的链接:https://juejin.im/post/5d328ce3e51d454fbf540aad

关于android 10后台启动activity限制比较好的分析链接: https://juejin.im/post/5d788982f265da03ca11983c

小米机型解决方案

我们可以判断是否开起了start in background权限,如果没有开启,跳转到相应的界面进行授权,这时需要注意一个问题,小米的有些机型start in background权限授予界面是单独的一个页面,有的则是在应用详情页面,所以,对于跳转授权的页面我们需要适配一下, 具体如下:

判断是否授予start in background的方法如下:

public static boolean canBackgroundStart(Context context) {
 try {
 AppOpsManager ops = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
 int op = 10021; // >= 23
 // ops.checkOpNoThrow(op, uid, packageName)
 Method method = ops.getClass().getMethod("checkOpNoThrow", new Class[]
 {int.class, int.class, String.class}
 );
 Integer result = (Integer) method.invoke(ops, op, android.os.Process.myUid(), context.getPackageName());
 return result == AppOpsManager.MODE_ALLOWED;
 } catch (Exception e) {
 e.printStackTrace();
 }
 return true;
 }

跳转到相应授权界面的方法如下:

private void openOpsSettings() {
 try {
 if (DeviceUtils.isMIUI() && !PermissionHelper.canBackgroundStart(getApplicationContext())) {
 try {
 Intent intent = new Intent();
 intent.setAction("miui.intent.action.APP_PERM_EDITOR");
 intent.addCategory(Intent.CATEGORY_DEFAULT);
 intent.putExtra("extra_pkgname", getApplicationContext().getPackageName());
 this.startActivityForResult(intent, REQUEST_DRAW_CODE);
 } catch (Exception e) {
 e.printStackTrace();
 if (!PermissionHelper.isHoldAlertWindowPermission(getApplicationContext())) {
 Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
 Uri.parse("package:" + getApplicationContext().getPackageName()));
 this.startActivityForResult(intent, REQUEST_DRAW_CODE);
 return;
 } else {
 SharedPreferencesUtil.put(getApplicationContext(), Constants.PREF_UPDATE_GUIDE_FLOAT_KEY, false);
 }
 }
 return;
 }
 if (!PermissionHelper.isHoldAlertWindowPermission(getApplicationContext())) {
 Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
 Uri.parse("package:" + getApplicationContext().getPackageName()));
 this.startActivityForResult(intent, REQUEST_DRAW_CODE);
 return;
 } else {
 SharedPreferencesUtil.put(getApplicationContext(), Constants.PREF_UPDATE_GUIDE_FLOAT_KEY, false);
 }
 if (!PermissionHelper.canBackgroundStart(getApplicationContext())) {
 Intent intent = new Intent();
 intent.setAction("miui.intent.action.APP_PERM_EDITOR");
 intent.addCategory(Intent.CATEGORY_DEFAULT);
 intent.putExtra("extra_pkgname", getApplicationContext().getPackageName());
 this.startActivityForResult(intent, REQUEST_DRAW_CODE);
 }
 } catch (Exception e) {
 try {
 Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
 Uri uri = Uri.fromParts("package", getApplicationContext().getPackageName(), null);
 intent.setData(uri);
 this.startActivityForResult(intent, REQUEST_DRAW_CODE);
 } catch (Exception e1) {
 e.printStackTrace();
 }
 }
 }

android 10关于start in background权限的现象与解决办法

经过测试,android 10的设置中没有找到start in background权限的设置开关,但是在android 10上后台是不能启动activity的,以上提供的判断start in background权限是否开启,目前只适用与小米,那么android 10设备怎么做才能在后台启动activity呢?

我们首先需要明白以下两个点:

点一: Go 设备上的 SYSTEM_ALERT_WINDOW 在 Android Q(Go 版本)设备上运行的应用无法获得 SYSTEM_ALERT_WINDOW 权限。这是因为绘制叠加层窗口会使用过多的内存,这对低内存 Android 设备的性能十分有害。

如果 Go 设备上的应用发送具有 ACTION_MANAGE_OVERLAY_PERMISSION 操作的 intent,则系统会自动拒绝此请求,并将用户转到设置屏幕,上面会显示不允许授予此权限,原因是它会减慢设备的运行速度。如果 Go 设备上的应用调用 Settings.canDrawOverlays(),则此方法始终返回 false。同样,这些限制不适用于在设备升级到 Android Q 之前便已收到 SYSTEM_ALERT_WINDOW 权限的应用。

带来的问题: android Q(go版本),将无法显示悬浮菜单

点二: 后台启动activity

小米: 在授权页面有一个start in background的授权条目,只有授予了权限才能在后台启动activity pixel: android 10, 在设置或者其他页面没找到相关权限的地方,按照官网提示,setting--->开发者选项中也没有找到限制后台启动的选项,经过测试发现,如果没有授予显示在应用上层(display pop-up window)的权限, 在后台无法启动页面,但授予权限后在后台可以启动activity,且在日志中有如下提示:Background activity start for screen.recorder allowed because SYSTEM_ALERT_WINDOW permission is granted.

ps1: 结合1, android Q(go版本),新安装应用或者升级上来的但不具有display pop-up window权限的应用,无法跳转到ACTION_MANAGE_OVERLAY_PERMISSION授权页面,因此无法开启该权限,所以在android Q(go版本)无法在后台弹出activity, 且无法使用悬浮窗

ps2:android Q在后台弹出activity会有该log: Background activity start for screen.recorder allowed because SYSTEM_ALERT_WINDOW permission is granted.

关于android 10后台启动activity限制分析

默认情况下,是拒绝后台启动的。也就是说,用户启动的话,禁止在后台直接启动的。 具体的判断代码是在 shouldAbortBackgroundActivityStart 方法中,因为这个方法算是判断的核心方法了,因此贴了完整代码。

boolean shouldAbortBackgroundActivityStart(int callingUid, int callingPid,
 final String callingPackage, int realCallingUid, int realCallingPid,
 WindowProcessController callerApp, PendingIntentRecord originatingPendingIntent,
 boolean allowBackgroundActivityStart, Intent intent) {
 // don't abort for the most important UIDs
 final int callingAppId = UserHandle.getAppId(callingUid);
 if (callingUid == Process.ROOT_UID || callingAppId == Process.SYSTEM_UID
 || callingAppId == Process.NFC_UID) {
 return false;
 }
 // don't abort if the callingUid has a visible window or is a persistent system process
 final int callingUidProcState = mService.getUidState(callingUid);
 final boolean callingUidHasAnyVisibleWindow =
 mService.mWindowManager.mRoot.isAnyNonToastWindowVisibleForUid(callingUid);
 final boolean isCallingUidForeground = callingUidHasAnyVisibleWindow
 || callingUidProcState == ActivityManager.PROCESS_STATE_TOP
 || callingUidProcState == ActivityManager.PROCESS_STATE_BOUND_TOP;
 final boolean isCallingUidPersistentSystemProcess =
 callingUidProcState <= ActivityManager.PROCESS_STATE_PERSISTENT_UI;
 if (callingUidHasAnyVisibleWindow || isCallingUidPersistentSystemProcess) {
 return false;
 }
 // take realCallingUid into consideration
 final int realCallingUidProcState = (callingUid == realCallingUid)
 ? callingUidProcState
 : mService.getUidState(realCallingUid);
 final boolean realCallingUidHasAnyVisibleWindow = (callingUid == realCallingUid)
 ? callingUidHasAnyVisibleWindow
 : mService.mWindowManager.mRoot.isAnyNonToastWindowVisibleForUid(realCallingUid);
 final boolean isRealCallingUidForeground = (callingUid == realCallingUid)
 ? isCallingUidForeground
 : realCallingUidHasAnyVisibleWindow
 || realCallingUidProcState == ActivityManager.PROCESS_STATE_TOP;
 final int realCallingAppId = UserHandle.getAppId(realCallingUid);
 final boolean isRealCallingUidPersistentSystemProcess = (callingUid == realCallingUid)
 ? isCallingUidPersistentSystemProcess
 : (realCallingAppId == Process.SYSTEM_UID)
 || realCallingUidProcState <= ActivityManager.PROCESS_STATE_PERSISTENT_UI;
 if (realCallingUid != callingUid) {
 // don't abort if the realCallingUid has a visible window
 if (realCallingUidHasAnyVisibleWindow) {
 return false;
 }
 // if the realCallingUid is a persistent system process, abort if the IntentSender
 // wasn't whitelisted to start an activity
 if (isRealCallingUidPersistentSystemProcess && allowBackgroundActivityStart) {
 return false;
 }
 // don't abort if the realCallingUid is an associated companion app
 if (mService.isAssociatedCompanionApp(UserHandle.getUserId(realCallingUid),
 realCallingUid)) {
 return false;
 }
 }
 // don't abort if the callingUid has START_ACTIVITIES_FROM_BACKGROUND permission
 if (mService.checkPermission(START_ACTIVITIES_FROM_BACKGROUND, callingPid, callingUid)
 == PERMISSION_GRANTED) {
 return false;
 }
 // don't abort if the caller has the same uid as the recents component
 if (mSupervisor.mRecentTasks.isCallerRecents(callingUid)) {
 return false;
 }
 // don't abort if the callingUid is the device owner
 if (mService.isDeviceOwner(callingUid)) {
 return false;
 }
 // don't abort if the callingUid has companion device
 final int callingUserId = UserHandle.getUserId(callingUid);
 if (mService.isAssociatedCompanionApp(callingUserId, callingUid)) {
 return false;
 }
 // If we don't have callerApp at this point, no caller was provided to startActivity().
 // That's the case for PendingIntent-based starts, since the creator's process might not be
 // up and alive. If that's the case, we retrieve the WindowProcessController for the send()
 // caller, so that we can make the decision based on its foreground/whitelisted state.
 int callerAppUid = callingUid;
 if (callerApp == null) {
 callerApp = mService.getProcessController(realCallingPid, realCallingUid);
 callerAppUid = realCallingUid;
 }
 // don't abort if the callerApp or other processes of that uid are whitelisted in any way
 if (callerApp != null) {
 // first check the original calling process
 if (callerApp.areBackgroundActivityStartsAllowed()) {
 return false;
 }
 // only if that one wasn't whitelisted, check the other ones
 final ArraySet<WindowProcessController> uidProcesses =
 mService.mProcessMap.getProcesses(callerAppUid);
 if (uidProcesses != null) {
 for (int i = uidProcesses.size() - 1; i >= 0; i--) {
 final WindowProcessController proc = uidProcesses.valueAt(i);
 if (proc != callerApp && proc.areBackgroundActivityStartsAllowed()) {
 return false;
 }
 }
 }
 }
 // don't abort if the callingUid has SYSTEM_ALERT_WINDOW permission
 if (mService.hasSystemAlertWindowPermission(callingUid, callingPid, callingPackage)) {
 Slog.w(TAG, "Background activity start for " + callingPackage
 + " allowed because SYSTEM_ALERT_WINDOW permission is granted.");
 return false;
 }
 // anything that has fallen through would currently be aborted
 Slog.w(TAG, "Background activity start [callingPackage: " + callingPackage
 + "; callingUid: " + callingUid
 + "; isCallingUidForeground: " + isCallingUidForeground
 + "; isCallingUidPersistentSystemProcess: " + isCallingUidPersistentSystemProcess
 + "; realCallingUid: " + realCallingUid
 + "; isRealCallingUidForeground: " + isRealCallingUidForeground
 + "; isRealCallingUidPersistentSystemProcess: "
 + isRealCallingUidPersistentSystemProcess
 + "; originatingPendingIntent: " + originatingPendingIntent
 + "; isBgStartWhitelisted: " + allowBackgroundActivityStart
 + "; intent: " + intent
 + "; callerApp: " + callerApp
 + "]");
 // log aborted activity start to TRON
 if (mService.isActivityStartsLoggingEnabled()) {
 mSupervisor.getActivityMetricsLogger().logAbortedBgActivityStart(intent, callerApp,
 callingUid, callingPackage, callingUidProcState, callingUidHasAnyVisibleWindow,
 realCallingUid, realCallingUidProcState, realCallingUidHasAnyVisibleWindow,
 (originatingPendingIntent != null));
 }
 return true;
 }

一个是前面显式设置的白名单属性allowBackgroundActivityStart, 另一个是应用的SYSTEM_ALERT_WINDOW权限,还有一个就是后台启动Activity的权限。

Clone this wiki locally

AltStyle によって変換されたページ (->オリジナル) /