我的编程空间,编程开发者的网络收藏夹
学习永远不晚

android 10+从后台启动 Activity 的限制

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

android 10+从后台启动 Activity 的限制

在这里插入图片描述

限制后台启动activity

如果未满足相关条件,则后台不允许启动activity,并会打印如下相关的log:

// anything that has fallen through would currently be abortedSlog.w(TAG, "Background activity start [callingPackage: " + callingPackage       + "; callingUid: " + callingUid       + "; appSwitchState: " + appSwitchState       + "; isCallingUidForeground: " + isCallingUidForeground       + "; callingUidHasAnyVisibleWindow: " + callingUidHasAnyVisibleWindow       + "; callingUidProcState: " + DebugUtils.valueToString(ActivityManager.class,      "PROCESS_STATE_", callingUidProcState)       + "; isCallingUidPersistentSystemProcess: " + isCallingUidPersistentSystemProcess       + "; realCallingUid: " + realCallingUid       + "; isRealCallingUidForeground: " + isRealCallingUidForeground       + "; realCallingUidHasAnyVisibleWindow: " + realCallingUidHasAnyVisibleWindow       + "; realCallingUidProcState: " + DebugUtils.valueToString(ActivityManager.class,          "PROCESS_STATE_", realCallingUidProcState)       + "; isRealCallingUidPersistentSystemProcess: "       + isRealCallingUidPersistentSystemProcess       + "; originatingPendingIntent: " + originatingPendingIntent       + "; allowBackgroundActivityStart: " + allowBackgroundActivityStart       + "; intent: " + intent       + "; callerApp: " + callerApp       + "; inVisibleTask: " + (callerApp != null && callerApp.hasActivityInVisibleTask())       + "]");

限制的例外情况

在 Android 10 或更高版本上运行的应用只有在满足以下一项或多项条件时,才能启动 Activity:

ROOT_UID/SYSTEM_UID/NFC_UID等重要uid
final boolean useCallingUidState =        originatingPendingIntent == null || checkedOptions == null                || !checkedOptions.getIgnorePendingIntentCreatorForegroundState();.......if (useCallingUidState) {    if (callingUid == Process.ROOT_UID || callingAppId == Process.SYSTEM_UID            || callingAppId == Process.NFC_UID) {        if (DEBUG_ACTIVITY_STARTS) {            Slog.d(TAG,                    "Activity start allowed for important callingUid (" + callingUid + ")");        }        return false;    }    ......
home 进程
        if (useCallingUidState) {          ......            if (isHomeApp(callingUid, callingPackage)) {                if (DEBUG_ACTIVITY_STARTS) {                    Slog.d(TAG,"Activity start allowed for home app callingUid (" + callingUid + ")");                }                return false;            }
Ime窗口所在uid下的进程
        if (useCallingUidState) {         ......            // IME should always be allowed to start activity, like IME settings.            final WindowState imeWindow = mRootWindowContainer.getCurrentInputMethodWindow();            if (imeWindow != null && callingAppId == imeWindow.mOwnerUid) {                if (DEBUG_ACTIVITY_STARTS) {                    Slog.d(TAG, "Activity start allowed for active ime (" + callingUid + ")");                }                return false;            }
应用具有可见窗口,例如前台 Activity
应用是persistent系统进程,并正在执行UI操作
// 是persistent系统进程,并正在执行UI操作final boolean isCallingUidPersistentSystemProcess =        callingUidProcState <= ActivityManager.PROCESS_STATE_PERSISTENT_UI;// 如果允许应用程序切换,则允许具有可见应用程序窗口的普通应用程序启动活动,// 或者将允许具有非应用程序可见窗口的动态壁纸等应用程序。final boolean appSwitchAllowedOrFg =        appSwitchState == APP_SWITCH_ALLOW || appSwitchState == APP_SWITCH_FG_ONLY;final boolean allowCallingUidStartActivity =        ((appSwitchAllowedOrFg || mService.mActiveUids.hasNonAppVisibleWindow(callingUid))        && callingUidHasAnyVisibleWindow)        || isCallingUidPersistentSystemProcess;if (useCallingUidState && allowCallingUidStartActivity) {    if (DEBUG_ACTIVITY_STARTS) {        Slog.d(TAG, "Activity start allowed: callingUidHasAnyVisibleWindow = " + callingUid                + ", isCallingUidPersistentSystemProcess = "                + isCallingUidPersistentSystemProcess);    }    return false;}
应用声明了START_ACTIVITIES_FROM_BACKGROUND 权限

特权system/priv-app/目录下的app才可以申请豁免

<!-- @SystemApi @hide Allows an application to start activities from background --><permission android:name="android.permission.START_ACTIVITIES_FROM_BACKGROUND"android:protectionLevel="signature|privileged|vendorPrivileged|oem|verifier|role" />
        if (useCallingUidState) {            // don't abort if the callingUid has START_ACTIVITIES_FROM_BACKGROUND permission            if (mService.checkPermission(                    START_ACTIVITIES_FROM_BACKGROUND, callingPid, callingUid)                    == PERMISSION_GRANTED) {                if (DEBUG_ACTIVITY_STARTS) {                    Slog.d(TAG,"Background activity start allowed: START_ACTIVITIES_FROM_BACKGROUND "        + "permission granted for uid "        + callingUid);                }                return false;            }
应用是Recent组件所在uid下进程(这里一般为home进程)
        if (useCallingUidState) {        .......            // don't abort if the caller has the same uid as the recents component            if (mSupervisor.mRecentTasks.isCallerRecents(callingUid)) {                if (DEBUG_ACTIVITY_STARTS) {                    Slog.d(TAG, "Background activity start allowed: callingUid (" + callingUid+ ") is recents");                }                return false;            }
应用是设备所有者

应用是在设备所有者模式下运行的设备政策控制器。示例用例包括完全托管的企业设备,以及数字标识牌和自助服务终端等专用设备

        if (useCallingUidState) {          ......            // don't abort if the callingUid is the device owner            if (mService.isDeviceOwner(callingUid)) {                if (DEBUG_ACTIVITY_STARTS) {                    Slog.d(TAG, "Background activity start allowed: callingUid (" + callingUid+ ") is device owner");                }                return false;            }
应用通过 CompanionDeviceManager API 与配套硬件设备相关联。

此 API 支持应用启动 API,以响应用户在配对设备上执行的操作。

        if (useCallingUidState) {           ......            // don't abort if the callingUid has companion device            final int callingUserId = UserHandle.getUserId(callingUid);            if (mService.isAssociatedCompanionApp(callingUserId,                    callingUid)) {                if (DEBUG_ACTIVITY_STARTS) {                    Slog.d(TAG, "Background activity start allowed: callingUid (" + callingUid+ ") is companion app");                }                return false;            }
用户已向应用授予 SYSTEM_ALERT_WINDOW 权限

**注意:**在 Android 10(Go 版本)上运行的应用无法获得SYSTEM_ALERT_WINDOW权限
注意:如果应用程序以 API 级别 23 或更高级别为目标,则应用程序用户必须通过权限管理屏幕明确向应用程序授予此权限。

<permission android:name="android.permission.SYSTEM_ALERT_WINDOW"        android:label="@string/permlab_systemAlertWindow"        android:description="@string/permdesc_systemAlertWindow"        android:protectionLevel="signature|setup|appop|installer|pre23|development" />
        if (useCallingUidState) {           ......            // 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;            }        }

关于areBackgroundActivityStartsAllowed系列

如果此时我们没有 callerApp,则没有向 startActivity() 提供调用者。 基于 PendingIntent 的启动就是这种情况,因为创建者的进程可能未启动且处于活动状态。 如果是这种情况,我们会在调用者允许的情况下为 send() 调用者检索 WindowProcessController,以便我们可以根据其状态做出决定。

// 遗留行为允许使用调用者前台状态绕过 BAL 限制。final boolean balAllowedByPiSender =        PendingIntentRecord.isPendingIntentBalAllowedByCaller(checkedOptions);int callerAppUid = callingUid;if (callerApp == null && balAllowedByPiSender) {    callerApp = mService.getProcessController(realCallingPid, realCallingUid);    callerAppUid = realCallingUid;}if (callerApp != null && useCallingUidState) {    // first check the original calling process    if (callerApp.areBackgroundActivityStartsAllowed(appSwitchState)) {        if (DEBUG_ACTIVITY_STARTS) {            Slog.d(TAG, "Background activity start allowed: callerApp process (pid = "                    + callerApp.getPid() + ", uid = " + callerAppUid + ") is allowed");        }        return false;    }    // only if that one wasn't allowed, 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(appSwitchState)) {                if (DEBUG_ACTIVITY_STARTS) {                    Slog.d(TAG,"Background activity start allowed: process " + proc.getPid()        + " from uid " + callerAppUid + " is allowed");                }                return false;            }        }    }}
应用的某个 Activity 刚在不久前启动/结束

如果不允许应用程序切换,我们将忽略所有启动activity宽限期异常,因此应用程序无法在按下主页按钮后在 onPause() 中自行启动。

在停止应用程序切换的时间之后启动或finish activity的10s内可后台启动Activity

    boolean areBackgroundActivityStartsAllowed(int pid, int uid, String packageName,            int appSwitchState, boolean isCheckingForFgsStart,            boolean hasActivityInVisibleTask, boolean hasBackgroundActivityStartPrivileges,            long lastStopAppSwitchesTime, long lastActivityLaunchTime,            long lastActivityFinishTime) {        if (appSwitchState == APP_SWITCH_ALLOW) {            // 如果调用者中的任何activity最近启动或finish,则允许,            final long now = SystemClock.uptimeMillis();            // 启动或finish有10s宽限            if (now - lastActivityLaunchTime < ACTIVITY_BG_START_GRACE_PERIOD_MS                    || now - lastActivityFinishTime < ACTIVITY_BG_START_GRACE_PERIOD_MS) {                // 得在停止应用程序切换的时间之后启动或finish才行                if (lastActivityLaunchTime > lastStopAppSwitchesTime                        || lastActivityFinishTime > lastStopAppSwitchesTime) {                    if (DEBUG_ACTIVITY_STARTS) {                        Slog.d(TAG, "[Process(" + pid    + ")] Activity start allowed: within "    + ACTIVITY_BG_START_GRACE_PERIOD_MS + "ms grace period");                    }                    return true;                }                if (DEBUG_ACTIVITY_STARTS) {                    Slog.d(TAG, "[Process(" + pid + ")] Activity start within "+ ACTIVITY_BG_START_GRACE_PERIOD_MS+ "ms grace period but also within stop app switch window");                }            }        }
具有后台启动activity特权的Active Instrumentation所在进程
        // Allow if the proc is instrumenting with background activity starts privs.        if (hasBackgroundActivityStartPrivileges) {            if (DEBUG_ACTIVITY_STARTS) {                Slog.d(TAG, "[Process(" + pid                        + ")] Activity start allowed: process instrumenting with background "                        + "activity starts privileges");            }            return true;        }
应用在前台任务的返回栈中拥有 Activity

android U或许会取消

    boolean hasActivityInVisibleTask() {        return (mActivityStateFlags & ACTIVITY_STATE_FLAG_HAS_ACTIVITY_IN_VISIBLE_TASK) != 0;    }
        // Allow if the caller has an activity in any foreground task.        if (hasActivityInVisibleTask                && (appSwitchState == APP_SWITCH_ALLOW || appSwitchState == APP_SWITCH_FG_ONLY)) {            if (DEBUG_ACTIVITY_STARTS) {                Slog.d(TAG, "[Process(" + pid                        + ")] Activity start allowed: process has activity in foreground task");            }            return true;        }
应用中的某个服务被另一个可见应用绑定

请注意,绑定到服务的应用必须保持可见,以便后台应用成功启动 Activity。

        // Allow if the caller is bound by a UID that's currently foreground.        if (isBoundByForegroundUid()) {            if (DEBUG_ACTIVITY_STARTS) {                Slog.d(TAG, "[Process(" + pid                        + ")] Activity start allowed: process bound by foreground uid");            }            return true;        }    private boolean isBoundByForegroundUid() {        synchronized (this) {            if (mBoundClientUids != null) {                for (int i = mBoundClientUids.size() - 1; i >= 0; i--) {                    if (mUidHasActiveVisibleWindowPredicate.test(mBoundClientUids.get(i))) {                        return true;                    }                }            }        }        return false;    }
        @HotPath(caller = HotPath.START_SERVICE)    boolean hasActiveVisibleWindow(int uid) {        if (mVisibleActivityProcessTracker.hasVisibleActivity(uid)) {            return true;        }        return mActiveUids.hasNonAppVisibleWindow(uid);    }
应用收到系统的 PendingIntent 通知

对于服务和广播接收器的挂起 Intent,应用可在该挂起 Intent 发送几秒钟后启动 Activity

应用收到另一个可见应用发送的 PendingIntent
应用的 Service有带BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS的connection

需要声明START_ACTIVITIES_FROM_BACKGROUND权限

Android 10 (API 级别 29) 及更高版本对后台应用可启动 Activity 的时间施加限制。这些限制有助于最大限度地减少对用户造成的中断,并且可以让用户更好地控制其屏幕上显示的内容。
**注意:**为启动 Activity,系统仍会将运行前台服务的应用视为“后台”应用。

详情见BackgroundLaunchProcessController 介绍

        // Allow if the flag was explicitly set.        if (isBackgroundStartAllowedByToken(uid, packageName, isCheckingForFgsStart)) {            if (DEBUG_ACTIVITY_STARTS) {                Slog.d(TAG, "[Process(" + pid                        + ")] Activity start allowed: process allowed by token");            }            return true;        }
应用在具有相同uid的现有Task中启动活动
        // Do not allow background activity start in new task or in a task that uid is not present.        // Also do not allow pinned window to start single instance activity in background,        // as it will recreate the window and makes it to foreground.        boolean blockBalInTask = (newTask                || !targetTask.isUidPresent(mCallingUid)                || (LAUNCH_SINGLE_INSTANCE == mLaunchMode && targetTask.inPinnedWindowingMode()));        if (mRestrictedBgActivity && blockBalInTask && handleBackgroundActivityAbort(r)) {            Slog.e(TAG, "Abort background activity starts from " + mCallingUid);            return START_ABORTED;        }

如何申请豁免

权限相关

申请START_ACTIVITIES_FROM_BACKGROUND 权限

特权system/priv-app/目录下的app才可以申请豁免

<uses-permission android:name="android.permission.START_ACTIVITIES_FROM_BACKGROUND" />
<permissions>    <privapp-permissions package="com.android.xxx">        <permission name="android.permission.START_ACTIVITIES_FROM_BACKGROUND" />     privapp-permissions>permissions>
申请SYSTEM_ALERT_WINDOW权限

**注意:**在 Android 10(Go 版本)上运行的应用无法获得SYSTEM_ALERT_WINDOW权限
注意:如果应用程序以 API 级别 23 或更高级别为目标,则应用程序用户必须通过权限管理屏幕明确向应用程序授予此权限。

 <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />

Service有特殊client绑定

应用中的某个服务被另一个可见应用绑定
应用中的某个服务被另一个应用以BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS flag绑定

另一个应用需要声明START_ACTIVITIES_FROM_BACKGROUND权限

来源地址:https://blog.csdn.net/xiaoyantan/article/details/128401740

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

android 10+从后台启动 Activity 的限制

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Android 后台启动startService()相关问题的解决

需求 有一个用户需要这样一个功能,要求是APP能在充电的时候自动进入APP的一个界面 我寻思着,这玩意用普通权限做不了呀,不过APP有root权限倒也无妨,于是便决定采用Service去做后台服务 问题 某天,在Bugly看到如下的错误报告
2023-08-17

编程热搜

  • Android:VolumeShaper
    VolumeShaper(支持版本改一下,minsdkversion:26,android8.0(api26)进一步学习对声音的编辑,可以让音频的声音有变化的播放 VolumeShaper.Configuration的三个参数 durati
    Android:VolumeShaper
  • Android崩溃异常捕获方法
    开发中最让人头疼的是应用突然爆炸,然后跳回到桌面。而且我们常常不知道这种状况会何时出现,在应用调试阶段还好,还可以通过调试工具的日志查看错误出现在哪里。但平时使用的时候给你闹崩溃,那你就欲哭无泪了。 那么今天主要讲一下如何去捕捉系统出现的U
    Android崩溃异常捕获方法
  • android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
    系统的设置–>电池–>使用情况中,统计的能耗的使用情况也是以power_profile.xml的value作为基础参数的1、我的手机中power_profile.xml的内容: HTC t328w代码如下:
    android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
  • Android SQLite数据库基本操作方法
    程序的最主要的功能在于对数据进行操作,通过对数据进行操作来实现某个功能。而数据库就是很重要的一个方面的,Android中内置了小巧轻便,功能却很强的一个数据库–SQLite数据库。那么就来看一下在Android程序中怎么去操作SQLite数
    Android SQLite数据库基本操作方法
  • ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
    工作的时候为了方便直接打开编辑文件,一些常用的软件或者文件我们会放在桌面,但是在ubuntu20.04下直接直接拖拽文件到桌面根本没有效果,在进入桌面后发现软件列表中的软件只能收藏到面板,无法复制到桌面使用,不知道为什么会这样,似乎并不是很
    ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
  • android获取当前手机号示例程序
    代码如下: public String getLocalNumber() { TelephonyManager tManager =
    android获取当前手机号示例程序
  • Android音视频开发(三)TextureView
    简介 TextureView与SurfaceView类似,可用于显示视频或OpenGL场景。 与SurfaceView的区别 SurfaceView不能使用变换和缩放等操作,不能叠加(Overlay)两个SurfaceView。 Textu
    Android音视频开发(三)TextureView
  • android获取屏幕高度和宽度的实现方法
    本文实例讲述了android获取屏幕高度和宽度的实现方法。分享给大家供大家参考。具体分析如下: 我们需要获取Android手机或Pad的屏幕的物理尺寸,以便于界面的设计或是其他功能的实现。下面就介绍讲一讲如何获取屏幕的物理尺寸 下面的代码即
    android获取屏幕高度和宽度的实现方法
  • Android自定义popupwindow实例代码
    先来看看效果图:一、布局
  • Android第一次实验
    一、实验原理 1.1实验目标 编程实现用户名与密码的存储与调用。 1.2实验要求 设计用户登录界面、登录成功界面、用户注册界面,用户注册时,将其用户名、密码保存到SharedPreference中,登录时输入用户名、密码,读取SharedP
    Android第一次实验

目录