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)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市挽封,隨后出現(xiàn)的幾起案子旭从,更是在濱河造成了極大的恐慌,老刑警劉巖场仲,帶你破解...
    沈念sama閱讀 221,576評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件和悦,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡渠缕,警方通過(guò)查閱死者的電腦和手機(jī)鸽素,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,515評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)亦鳞,“玉大人馍忽,你說(shuō)我怎么就攤上這事⊙嗖睿” “怎么了遭笋?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,017評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)徒探。 經(jīng)常有香客問(wèn)我瓦呼,道長(zhǎng),這世上最難降的妖魔是什么测暗? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,626評(píng)論 1 296
  • 正文 為了忘掉前任央串,我火速辦了婚禮,結(jié)果婚禮上碗啄,老公的妹妹穿的比我還像新娘质和。我一直安慰自己,他們只是感情好稚字,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,625評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布饲宿。 她就那樣靜靜地躺著厦酬,像睡著了一般。 火紅的嫁衣襯著肌膚如雪瘫想。 梳的紋絲不亂的頭發(fā)上弃锐,一...
    開(kāi)封第一講書(shū)人閱讀 52,255評(píng)論 1 308
  • 那天,我揣著相機(jī)與錄音殿托,去河邊找鬼霹菊。 笑死,一個(gè)胖子當(dāng)著我的面吹牛支竹,可吹牛的內(nèi)容都是我干的旋廷。 我是一名探鬼主播,決...
    沈念sama閱讀 40,825評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼礼搁,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼饶碘!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起馒吴,我...
    開(kāi)封第一講書(shū)人閱讀 39,729評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤扎运,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后饮戳,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體豪治,經(jīng)...
    沈念sama閱讀 46,271評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,363評(píng)論 3 340
  • 正文 我和宋清朗相戀三年扯罐,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了负拟。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,498評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡歹河,死狀恐怖掩浙,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情秸歧,我是刑警寧澤厨姚,帶...
    沈念sama閱讀 36,183評(píng)論 5 350
  • 正文 年R本政府宣布,位于F島的核電站键菱,受9級(jí)特大地震影響谬墙,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜纱耻,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,867評(píng)論 3 333
  • 文/蒙蒙 一芭梯、第九天 我趴在偏房一處隱蔽的房頂上張望险耀。 院中可真熱鬧弄喘,春花似錦、人聲如沸甩牺。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,338評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至急但,卻和暖如春澎媒,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背波桩。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,458評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工戒努, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人镐躲。 一個(gè)月前我還...
    沈念sama閱讀 48,906評(píng)論 3 376
  • 正文 我出身青樓储玫,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親萤皂。 傳聞我的和親對(duì)象是個(gè)殘疾皇子撒穷,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,507評(píng)論 2 359