android T 前臺(tái)Service

獲取android 13

用戶控制: 用戶在長(zhǎng)時(shí)間運(yùn)行的應(yīng)用程序上獲得更多透明度和控制權(quán):

  • 前臺(tái)服務(wù)仍然需要包含通知,并且應(yīng)用程序必須請(qǐng)求權(quán)限才能顯示通知。
  • FGS 通知現(xiàn)在可以被用戶關(guān)閉而不影響 FGS
  • 用戶可以在任務(wù)管理器中查看長(zhǎng)時(shí)間運(yùn)行的應(yīng)用列表
    • 任務(wù)管理器還允許用戶停止應(yīng)用程序

通知權(quán)限

image.png

介紹

權(quán)限定義

  1. android T 之前的通知:默認(rèn)允許通知推送,需要用戶跳轉(zhuǎn)至多級(jí)設(shè)置頁(yè)面去設(shè)置通知權(quán)限。
  2. android T新增前臺(tái)服務(wù)的通知權(quán)限(運(yùn)行時(shí)權(quán)限):

使用

權(quán)限使用

  1. android 13及以上: 應(yīng)用自行控制權(quán)限對(duì)話框顯示時(shí)間
  2. android 12L 及以下: 通常應(yīng)用啟動(dòng)時(shí)彈框

豁免:與媒體會(huì)話有關(guān)的通知不受此行為變更的影響。

代碼

在ServiceRecord的構(gòu)造方法中會(huì)回調(diào)updateFgsHasNotificationPermission方法,去異步(避免死鎖)從NMS中去獲取此app是否有權(quán)限發(fā)送通知;ServiceRecord僅記錄,具體權(quán)限校驗(yàn)在NMS中。

// ServiceRecord.java
// Whether FGS package has permissions to show notifications.
boolean mFgsHasNotificationPermission;


private void updateFgsHasNotificationPermission() {
    // Do asynchronous communication with notification manager to avoid deadlocks.
    final String localPackageName = packageName;
    final int appUid = appInfo.uid;

    ams.mHandler.post(new Runnable() {
        public void run() {
            NotificationManagerInternal nm = LocalServices.getService(
                    NotificationManagerInternal.class);
            if (nm == null) {
                return;
            }
            // Record whether the package has permission to notify the user
            mFgsHasNotificationPermission = nm.areNotificationsEnabledForPackage(
                    localPackageName, appUid);
        }
    });
}

前臺(tái)服務(wù)任務(wù)管理器

介紹

前臺(tái)任務(wù)管理器
無(wú)論app的目標(biāo)sdk版本是多少,android 13上都允許用戶從抽屜式通知欄中停止前臺(tái)服務(wù)(整個(gè)應(yīng)用)。停止原因在ApplicationExitInfo表現(xiàn)為REASON_USER_REQUESTED。

image.png
image.png

豁免

以下應(yīng)用可以運(yùn)行前臺(tái)服務(wù),而完全不會(huì)顯示在任務(wù)管理器中:

  • 系統(tǒng)級(jí)應(yīng)用
  • 安全應(yīng)用,即具有 ROLE_EMERGENCY 角色的應(yīng)用
  • 處于演示模式的設(shè)備上的應(yīng)用

當(dāng)以下類型的應(yīng)用運(yùn)行前臺(tái)服務(wù)時(shí),它們會(huì)顯示在 FGS 任務(wù)管理器中,但應(yīng)用名稱旁邊沒(méi)有可以供用戶按的停止按鈕:

  • 設(shè)備所有者應(yīng)用
  • 資料所有者應(yīng)用
  • 常駐應(yīng)用
  • 具有 ROLE_DIALER 角色的應(yīng)用
    • 處于doze白名單應(yīng)用

代碼

// FgsManagerController.kt
fun updateUiControl() {
    uiControl = when (activityManager.getBackgroundRestrictionExemptionReason(uid)) {
        // 不顯示在任務(wù)管理器中
        PowerExemptionManager.REASON_SYSTEM_UID,
        PowerExemptionManager.REASON_DEVICE_DEMO_MODE -> UIControl.HIDE_ENTRY
        // 顯示在任務(wù)管理器,但是沒(méi)有停止按鈕
        PowerExemptionManager.REASON_ALLOWLISTED_PACKAGE,
        PowerExemptionManager.REASON_DEVICE_OWNER,
        PowerExemptionManager.REASON_DISALLOW_APPS_CONTROL,
        PowerExemptionManager.REASON_DPO_PROTECTED_APP,
        PowerExemptionManager.REASON_PROFILE_OWNER,
        PowerExemptionManager.REASON_PROC_STATE_PERSISTENT,
        PowerExemptionManager.REASON_PROC_STATE_PERSISTENT_UI,
        PowerExemptionManager.REASON_ROLE_DIALER,
        PowerExemptionManager.REASON_SYSTEM_MODULE -> UIControl.HIDE_BUTTON
        // 正常情況
        else -> UIControl.NORMAL
    }
    uiControlInitialized = true
}
// AppRestrictionController.java
/**
 * @return The reason code of whether or not the given UID should be exempted from background
 * restrictions here.
 *
 * <p>
 * Note: Call it with caution as it'll try to acquire locks in other services.
 * </p>
 */
@ReasonCode
int getBackgroundRestrictionExemptionReason(int uid) {
    // uid < 10000
    if (UserHandle.isCore(uid)) {
        return REASON_SYSTEM_UID;
    }
    // Whitelist system apps
    if (isOnSystemDeviceIdleAllowlist(uid)) {
        return REASON_SYSTEM_ALLOW_LISTED;
    }
    // Whitelist user apps , doze白名單,可聯(lián)系功耗同學(xué)添加
    if (isOnDeviceIdleAllowlist(uid)) {
        return REASON_ALLOWLISTED_PACKAGE;
    }
    // 暫無(wú)研究
    final ActivityManagerInternal am = mInjector.getActivityManagerInternal();
    if (am.isAssociatedCompanionApp(UserHandle.getUserId(uid), uid)) {
        return REASON_COMPANION_DEVICE_MANAGER;
    }
    // 處于演示模式的設(shè)備上的應(yīng)用
    if (UserManager.isDeviceInDemoMode(mContext) {
        return REASON_DEVICE_DEMO_MODE;
    }
    final int userId = UserHandle.getUserId(uid);
    if (mInjector.getUserManagerInternal()
            .hasUserRestriction(UserManager.DISALLOW_APPS_CONTROL, userId)) {
        return REASON_DISALLOW_APPS_CONTROL;
    }
    // 設(shè)備所有者
    if (am.isDeviceOwner(uid)) {
        return REASON_DEVICE_OWNER;
    }
    // 資料所有者
    if (am.isProfileOwner(uid)) {
        return REASON_PROFILE_OWNER;
    }
    // persistent常駐應(yīng)用
    final int uidProcState = am.getUidProcessState(uid);
    if (uidProcState <= PROCESS_STATE_PERSISTENT) {
        return REASON_PROC_STATE_PERSISTENT;
    } else if (uidProcState <= PROCESS_STATE_PERSISTENT_UI) {
        return REASON_PROC_STATE_PERSISTENT_UI;
    }
    final String[] packages = mInjector.getPackageManager().getPackagesForUid(uid);
    if (packages != null) {
        final AppOpsManager appOpsManager = mInjector.getAppOpsManager();
        final PackageManagerInternal pm = mInjector.getPackageManagerInternal();
        final AppStandbyInternal appStandbyInternal = mInjector.getAppStandbyInternal();
        for (String pkg : packages) {
        // VPN
            if (appOpsManager.checkOpNoThrow(AppOpsManager.OP_ACTIVATE_VPN,
                    uid, pkg) == AppOpsManager.MODE_ALLOWED) {
                return REASON_OP_ACTIVATE_VPN;
            } else if (appOpsManager.checkOpNoThrow(AppOpsManager.OP_ACTIVATE_PLATFORM_VPN,
                    uid, pkg) == AppOpsManager.MODE_ALLOWED) {
                return REASON_OP_ACTIVATE_PLATFORM_VPN;
            } else if (isSystemModule(pkg)) {
                return REASON_SYSTEM_MODULE;
            } else if (isCarrierApp(pkg)) {
                return REASON_CARRIER_PRIVILEGED_APP;
            } else if (isExemptedFromSysConfig(pkg)) {
                return REASON_SYSTEM_ALLOW_LISTED;
            } else if (mConstantsObserver.mBgRestrictionExemptedPackages.contains(pkg)) {
                return REASON_SYSTEM_ALLOW_LISTED;
            } else if (pm.isPackageStateProtected(pkg, userId)) {
                return REASON_DPO_PROTECTED_APP;
            } else if (appStandbyInternal.isActiveDeviceAdmin(pkg, userId)) {
                return REASON_ACTIVE_DEVICE_ADMIN;
            }
        }
    }
    // dialer角色
    if (isRoleHeldByUid(RoleManager.ROLE_DIALER, uid)) {
        return REASON_ROLE_DIALER;
    }
    if (isRoleHeldByUid(RoleManager.ROLE_EMERGENCY, uid)) {
        return REASON_ROLE_EMERGENCY;
    }
    return REASON_DENIED;
}

測(cè)試

adb shell dumpsys deviceidle // 查看是否在doze白名單,可以不顯示停止按鈕
adb shell cmd activity stop-app PACKAGE_NAME

監(jiān)控長(zhǎng)期運(yùn)行的前臺(tái)服務(wù)

介紹

長(zhǎng)時(shí)間運(yùn)行服務(wù)通知
如果系統(tǒng)檢測(cè)到您的應(yīng)用長(zhǎng)時(shí)間運(yùn)行某項(xiàng)前臺(tái)服務(wù)(在 24 小時(shí)的時(shí)間段內(nèi)至少運(yùn)行 20 小時(shí)),便會(huì)發(fā)送通知邀請(qǐng)用戶與前臺(tái)服務(wù) (FGS) 任務(wù)管理器互動(dòng)。該通知包含以下內(nèi)容:

APP is running in the background for a long time. Tap to review.

如果前臺(tái)服務(wù)的類型為 FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK 或 FOREGROUND_SERVICE_TYPE_LOCATION,系統(tǒng)將不會(huì)顯示此通知。

代碼

  1. 谷歌提交記錄
commit 64ac1a923065d38fa33789b315c716f4c93312fc
Author: Jing Ji <jji@google.com>
Date:   Sat Dec 11 03:14:45 2021 -0800

    Monitor long-running foreground services
    
    If a certain package has foreground services running for a long time,
    say the accumulated durations over last X hours are more than Y hours,
    system will post a notification to remind the user.
    
    Some type of apps are subjected to be exempted, i.e. if it's already
    in the device idle allowlist. More exemption could be added in
    the follow-up CLs.
    
    Bug: 200326767
    Bug: 203105544
    Test: atest FrameworksMockingServicesTests:BackgroundRestrictionTest
    Change-Id: I3a8f34c33e7a533240abc7cf4fa569a0956eec73
  1. 主要邏輯在新增的類:
//  監(jiān)控濫用(長(zhǎng)時(shí)間運(yùn)行)FGS 的跟蹤器
frameworks/base/services/core/java/com/android/server/am/AppFGSTracker.java 

顯示長(zhǎng)時(shí)間運(yùn)行通知

static final long DEFAULT_BG_FGS_LONG_RUNNING_THRESHOLD = 20 * ONE_HOUR;

對(duì)應(yīng)的流程如下:


image.png

移除長(zhǎng)時(shí)間運(yùn)行通知

若FGS已經(jīng)停止運(yùn)行則取消該通知


image.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,117評(píng)論 6 537
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,860評(píng)論 3 423
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 177,128評(píng)論 0 381
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 63,291評(píng)論 1 315
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,025評(píng)論 6 410
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書人閱讀 55,421評(píng)論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,477評(píng)論 3 444
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 42,642評(píng)論 0 289
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,177評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,970評(píng)論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,157評(píng)論 1 371
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,717評(píng)論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,410評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 34,821評(píng)論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 36,053評(píng)論 1 289
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,896評(píng)論 3 395
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,157評(píng)論 2 375