1 /*
<lambda>null2  * Copyright (C) 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.permissioncontroller.hibernation
18 
19 import android.Manifest
20 import android.Manifest.permission.UPDATE_PACKAGES_WITHOUT_USER_ACTION
21 import android.accessibilityservice.AccessibilityService
22 import android.annotation.SuppressLint
23 import android.app.ActivityManager
24 import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_CANT_SAVE_STATE
25 import android.app.AppOpsManager
26 import android.app.Notification
27 import android.app.NotificationChannel
28 import android.app.NotificationManager
29 import android.app.PendingIntent
30 import android.app.PendingIntent.FLAG_IMMUTABLE
31 import android.app.PendingIntent.FLAG_ONE_SHOT
32 import android.app.PendingIntent.FLAG_UPDATE_CURRENT
33 import android.app.admin.DeviceAdminReceiver
34 import android.app.admin.DevicePolicyManager
35 import android.app.job.JobInfo
36 import android.app.job.JobParameters
37 import android.app.job.JobScheduler
38 import android.app.job.JobService
39 import android.app.role.RoleManager
40 import android.app.usage.UsageStats
41 import android.app.usage.UsageStatsManager.INTERVAL_DAILY
42 import android.app.usage.UsageStatsManager.INTERVAL_MONTHLY
43 import android.content.BroadcastReceiver
44 import android.content.ComponentName
45 import android.content.Context
46 import android.content.Intent
47 import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
48 import android.content.Intent.FLAG_RECEIVER_FOREGROUND
49 import android.content.SharedPreferences
50 import android.content.pm.PackageManager
51 import android.content.pm.PackageManager.PERMISSION_GRANTED
52 import android.os.Build
53 import android.os.Bundle
54 import android.os.Process
55 import android.os.SystemClock
56 import android.os.UserHandle
57 import android.os.UserManager
58 import android.printservice.PrintService
59 import android.provider.DeviceConfig
60 import android.provider.DeviceConfig.NAMESPACE_APP_HIBERNATION
61 import android.provider.Settings
62 import android.safetycenter.SafetyCenterManager
63 import android.safetycenter.SafetyEvent
64 import android.safetycenter.SafetySourceData
65 import android.safetycenter.SafetySourceIssue
66 import android.safetycenter.SafetySourceIssue.Action
67 import android.service.autofill.AutofillService
68 import android.service.dreams.DreamService
69 import android.service.notification.NotificationListenerService
70 import android.service.voice.VoiceInteractionService
71 import android.service.wallpaper.WallpaperService
72 import android.telephony.TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS
73 import android.telephony.TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS
74 import android.util.Log
75 import android.view.inputmethod.InputMethod
76 import androidx.annotation.ChecksSdkIntAtLeast
77 import androidx.annotation.MainThread
78 import androidx.annotation.RequiresApi
79 import androidx.lifecycle.MutableLiveData
80 import androidx.preference.PreferenceManager
81 import com.android.modules.utils.build.SdkLevel
82 import com.android.permissioncontroller.Constants
83 import com.android.permissioncontroller.DeviceUtils
84 import com.android.permissioncontroller.DumpableLog
85 import com.android.permissioncontroller.PermissionControllerApplication
86 import com.android.permissioncontroller.R
87 import com.android.permissioncontroller.hibernation.v31.HibernationController
88 import com.android.permissioncontroller.hibernation.v31.InstallerPackagesLiveData
89 import com.android.permissioncontroller.permission.data.AllPackageInfosLiveData
90 import com.android.permissioncontroller.permission.data.AppOpLiveData
91 import com.android.permissioncontroller.permission.data.BroadcastReceiverLiveData
92 import com.android.permissioncontroller.permission.data.CarrierPrivilegedStatusLiveData
93 import com.android.permissioncontroller.permission.data.DataRepositoryForPackage
94 import com.android.permissioncontroller.permission.data.HasIntentAction
95 import com.android.permissioncontroller.permission.data.LauncherPackagesLiveData
96 import com.android.permissioncontroller.permission.data.ServiceLiveData
97 import com.android.permissioncontroller.permission.data.SmartUpdateMediatorLiveData
98 import com.android.permissioncontroller.permission.data.UsageStatsLiveData
99 import com.android.permissioncontroller.permission.data.get
100 import com.android.permissioncontroller.permission.data.getUnusedPackages
101 import com.android.permissioncontroller.permission.model.livedatatypes.LightPackageInfo
102 import com.android.permissioncontroller.permission.service.revokeAppPermissions
103 import com.android.permissioncontroller.permission.utils.IPC
104 import com.android.permissioncontroller.permission.utils.KotlinUtils
105 import com.android.permissioncontroller.permission.utils.StringUtils
106 import com.android.permissioncontroller.permission.utils.Utils
107 import com.android.permissioncontroller.permission.utils.forEachInParallel
108 import java.util.Date
109 import java.util.Random
110 import java.util.concurrent.TimeUnit
111 import kotlinx.coroutines.Dispatchers.Main
112 import kotlinx.coroutines.GlobalScope
113 import kotlinx.coroutines.Job
114 import kotlinx.coroutines.launch
115 
116 private const val LOG_TAG = "HibernationPolicy"
117 const val DEBUG_OVERRIDE_THRESHOLDS = false
118 const val DEBUG_HIBERNATION_POLICY = false
119 
120 private var SKIP_NEXT_RUN = false
121 
122 private val DEFAULT_UNUSED_THRESHOLD_MS = TimeUnit.DAYS.toMillis(90)
123 
124 fun getUnusedThresholdMs() =
125     when {
126         DEBUG_OVERRIDE_THRESHOLDS -> TimeUnit.SECONDS.toMillis(1)
127         else ->
128             DeviceConfig.getLong(
129                 DeviceConfig.NAMESPACE_PERMISSIONS,
130                 Utils.PROPERTY_HIBERNATION_UNUSED_THRESHOLD_MILLIS,
131                 DEFAULT_UNUSED_THRESHOLD_MS
132             )
133     }
134 
135 private val DEFAULT_CHECK_FREQUENCY_MS = TimeUnit.DAYS.toMillis(15)
136 
getCheckFrequencyMsnull137 private fun getCheckFrequencyMs() =
138     DeviceConfig.getLong(
139         DeviceConfig.NAMESPACE_PERMISSIONS,
140         Utils.PROPERTY_HIBERNATION_CHECK_FREQUENCY_MILLIS,
141         DEFAULT_CHECK_FREQUENCY_MS
142     )
143 
144 // Intentionally kept value of the key same as before because we want to continue reading value of
145 // this shared preference stored by previous versions of PermissionController
146 const val PREF_KEY_START_TIME_OF_UNUSED_APP_TRACKING = "first_boot_time"
147 const val PREF_KEY_BOOT_TIME_SNAPSHOT = "ah_boot_time_snapshot"
148 const val PREF_KEY_ELAPSED_REALTIME_SNAPSHOT = "ah_elapsed_realtime_snapshot"
149 
150 private const val PREFS_FILE_NAME = "unused_apps_prefs"
151 private const val PREF_KEY_UNUSED_APPS_REVIEW = "unused_apps_need_review"
152 const val SNAPSHOT_UNINITIALIZED = -1L
153 private const val ACTION_SET_UP_HIBERNATION =
154     "com.android.permissioncontroller.action.SET_UP_HIBERNATION"
155 val ONE_DAY_MS = TimeUnit.DAYS.toMillis(1)
156 
157 fun isHibernationEnabled(): Boolean {
158     return SdkLevel.isAtLeastS() &&
159         DeviceConfig.getBoolean(
160             NAMESPACE_APP_HIBERNATION,
161             Utils.PROPERTY_APP_HIBERNATION_ENABLED,
162             true /* defaultValue */
163         )
164 }
165 
166 /**
167  * Whether hibernation defaults on and affects apps that target pre-S. Has no effect if
168  * [isHibernationEnabled] is false.
169  */
hibernationTargetsPreSAppsnull170 fun hibernationTargetsPreSApps(): Boolean {
171     return DeviceConfig.getBoolean(
172         NAMESPACE_APP_HIBERNATION,
173         Utils.PROPERTY_HIBERNATION_TARGETS_PRE_S_APPS,
174         false /* defaultValue */
175     )
176 }
177 
178 @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codename = "UpsideDownCake")
isSystemExemptFromHibernationEnablednull179 fun isSystemExemptFromHibernationEnabled(): Boolean {
180     return SdkLevel.isAtLeastU() &&
181         DeviceConfig.getBoolean(
182             NAMESPACE_APP_HIBERNATION,
183             Utils.PROPERTY_SYSTEM_EXEMPT_HIBERNATION_ENABLED,
184             true /* defaultValue */
185         )
186 }
187 
188 /** Remove the unused apps notification. */
cancelUnusedAppsNotificationnull189 fun cancelUnusedAppsNotification(context: Context) {
190     context
191         .getSystemService(NotificationManager::class.java)!!
192         .cancel(HibernationJobService::class.java.simpleName, Constants.UNUSED_APPS_NOTIFICATION_ID)
193 }
194 
195 /**
196  * Checks if we need to show the safety center card and sends the appropriate source data. If the
197  * user has not reviewed the latest auto-revoked apps, we show the card. Otherwise, we ensure
198  * nothing is shown.
199  */
200 @RequiresApi(Build.VERSION_CODES.TIRAMISU)
201 @Suppress("MissingPermission")
rescanAndPushDataToSafetyCenternull202 fun rescanAndPushDataToSafetyCenter(
203     context: Context,
204     sessionId: Long,
205     safetyEvent: SafetyEvent,
206 ) {
207     val safetyCenterManager: SafetyCenterManager =
208         context.getSystemService(SafetyCenterManager::class.java)!!
209     if (getUnusedAppsReviewNeeded(context)) {
210         val seeUnusedAppsAction =
211             Action.Builder(
212                     Constants.UNUSED_APPS_SAFETY_CENTER_SEE_UNUSED_APPS_ID,
213                     context.getString(R.string.unused_apps_safety_center_action_title),
214                     makeUnusedAppsIntent(context, sessionId)
215                 )
216                 .build()
217 
218         val issue =
219             SafetySourceIssue.Builder(
220                     Constants.UNUSED_APPS_SAFETY_CENTER_ISSUE_ID,
221                     context.getString(R.string.unused_apps_safety_center_card_title),
222                     context.getString(R.string.unused_apps_safety_center_card_content),
223                     SafetySourceData.SEVERITY_LEVEL_INFORMATION,
224                     Constants.UNUSED_APPS_SAFETY_CENTER_ISSUE_ID
225                 )
226                 .addAction(seeUnusedAppsAction)
227                 .setOnDismissPendingIntent(makeDismissIntent(context, sessionId))
228                 .setIssueCategory(SafetySourceIssue.ISSUE_CATEGORY_DEVICE)
229                 .build()
230 
231         val safetySourceData = SafetySourceData.Builder().addIssue(issue).build()
232         safetyCenterManager.setSafetySourceData(
233             Constants.UNUSED_APPS_SAFETY_CENTER_SOURCE_ID,
234             safetySourceData,
235             safetyEvent
236         )
237     } else {
238         safetyCenterManager.setSafetySourceData(
239             Constants.UNUSED_APPS_SAFETY_CENTER_SOURCE_ID,
240             /* safetySourceData= */ null,
241             safetyEvent
242         )
243     }
244 }
245 
246 /**
247  * Set whether we show the safety center card to the user to review their auto-revoked permissions.
248  */
setUnusedAppsReviewNeedednull249 fun setUnusedAppsReviewNeeded(context: Context, needsReview: Boolean) {
250     val sharedPreferences = context.sharedPreferences
251     if (
252         sharedPreferences.contains(PREF_KEY_UNUSED_APPS_REVIEW) &&
253             sharedPreferences.getBoolean(PREF_KEY_UNUSED_APPS_REVIEW, false) == needsReview
254     ) {
255         return
256     }
257     sharedPreferences.edit().putBoolean(PREF_KEY_UNUSED_APPS_REVIEW, needsReview).apply()
258 }
259 
getUnusedAppsReviewNeedednull260 private fun getUnusedAppsReviewNeeded(context: Context): Boolean {
261     return context.sharedPreferences.getBoolean(PREF_KEY_UNUSED_APPS_REVIEW, false)
262 }
263 
264 /**
265  * Receiver of the following broadcasts:
266  * <ul>
267  * <li> {@link Intent.ACTION_BOOT_COMPLETED}
268  * <li> {@link #ACTION_SET_UP_HIBERNATION}
269  * <li> {@link Intent.ACTION_TIME_CHANGED}
270  * <li> {@link Intent.ACTION_TIMEZONE_CHANGED}
271  * </ul>
272  */
273 class HibernationBroadcastReceiver : BroadcastReceiver() {
274 
onReceivenull275     override fun onReceive(context: Context, intent: Intent) {
276         val action = intent.action
277         if (action == Intent.ACTION_BOOT_COMPLETED || action == ACTION_SET_UP_HIBERNATION) {
278             if (DEBUG_HIBERNATION_POLICY) {
279                 DumpableLog.i(
280                     LOG_TAG,
281                     "scheduleHibernationJob " +
282                         "with frequency ${getCheckFrequencyMs()}ms " +
283                         "and threshold ${getUnusedThresholdMs()}ms"
284                 )
285             }
286 
287             initStartTimeOfUnusedAppTracking(context.sharedPreferences)
288 
289             // If this user is a profile, then its hibernation/auto-revoke will be handled by the
290             // primary user
291             if (isProfile(context)) {
292                 if (DEBUG_HIBERNATION_POLICY) {
293                     DumpableLog.i(
294                         LOG_TAG,
295                         "user ${Process.myUserHandle().identifier} is a profile." +
296                             " Not running hibernation job."
297                     )
298                 }
299                 return
300             } else if (DEBUG_HIBERNATION_POLICY) {
301                 DumpableLog.i(
302                     LOG_TAG,
303                     "user ${Process.myUserHandle().identifier} is a profile" +
304                         "owner. Running hibernation job."
305                 )
306             }
307 
308             if (isNewJobScheduleRequired(context)) {
309                 // periodic jobs normally run immediately, which is unnecessarily premature
310                 SKIP_NEXT_RUN = true
311                 @Suppress("MissingPermission")
312                 val jobInfo =
313                     JobInfo.Builder(
314                             Constants.HIBERNATION_JOB_ID,
315                             ComponentName(context, HibernationJobService::class.java)
316                         )
317                         .setPeriodic(getCheckFrequencyMs())
318                         // persist this job across boots
319                         .setPersisted(true)
320                         .build()
321                 val status = context.getSystemService(JobScheduler::class.java)!!.schedule(jobInfo)
322                 if (status != JobScheduler.RESULT_SUCCESS) {
323                     DumpableLog.e(
324                         LOG_TAG,
325                         "Could not schedule " +
326                             "${HibernationJobService::class.java.simpleName}: $status"
327                     )
328                 }
329             }
330         }
331         if (action == Intent.ACTION_TIME_CHANGED || action == Intent.ACTION_TIMEZONE_CHANGED) {
332             adjustStartTimeOfUnusedAppTracking(context.sharedPreferences)
333         }
334     }
335 
336     // UserManager#isProfile was already a systemAPI, linter started complaining after it
337     // was exposed as a public API thinking it was a newly exposed API.
338     @SuppressLint("NewApi")
isProfilenull339     private fun isProfile(context: Context): Boolean {
340         val userManager = context.getSystemService(UserManager::class.java)!!
341         return userManager.isProfile
342     }
343 
344     /**
345      * Returns whether a new job needs to be scheduled. A persisted job is used to keep the schedule
346      * across boots, but that job needs to be scheduled a first time and whenever the check
347      * frequency changes.
348      */
isNewJobScheduleRequirednull349     private fun isNewJobScheduleRequired(context: Context): Boolean {
350         // check if the job is already scheduled or needs a change
351         var scheduleNewJob = false
352         val existingJob: JobInfo? =
353             context
354                 .getSystemService(JobScheduler::class.java)!!
355                 .getPendingJob(Constants.HIBERNATION_JOB_ID)
356         if (existingJob == null) {
357             if (DEBUG_HIBERNATION_POLICY) {
358                 DumpableLog.i(LOG_TAG, "No existing job, scheduling a new one")
359             }
360             scheduleNewJob = true
361         } else if (existingJob.intervalMillis != getCheckFrequencyMs()) {
362             if (DEBUG_HIBERNATION_POLICY) {
363                 DumpableLog.i(LOG_TAG, "Interval frequency has changed, updating job")
364             }
365             scheduleNewJob = true
366         } else {
367             if (DEBUG_HIBERNATION_POLICY) {
368                 DumpableLog.i(LOG_TAG, "Job already scheduled.")
369             }
370         }
371         return scheduleNewJob
372     }
373 }
374 
375 /**
376  * Gets apps that are unused and should hibernate as a map of the user and their hibernateable apps.
377  */
378 @MainThread
379 @Suppress("MissingPermission")
getAppsToHibernatenull380 private suspend fun getAppsToHibernate(
381     context: Context,
382 ): Map<UserHandle, List<LightPackageInfo>> {
383     val now = System.currentTimeMillis()
384     val startTimeOfUnusedAppTracking = getStartTimeOfUnusedAppTracking(context.sharedPreferences)
385 
386     val allPackagesByUser =
387         AllPackageInfosLiveData.getInitializedValue(forceUpdate = true) ?: emptyMap()
388     val allPackagesByUserByUid =
389         allPackagesByUser.mapValues { (_, pkgs) -> pkgs.groupBy { pkg -> pkg.uid } }
390     val unusedApps = allPackagesByUser.toMutableMap()
391 
392     val userStats =
393         UsageStatsLiveData[
394                 getUnusedThresholdMs(),
395                 if (DEBUG_OVERRIDE_THRESHOLDS) INTERVAL_DAILY else INTERVAL_MONTHLY]
396             .getInitializedValue()
397             ?: emptyMap()
398     if (DEBUG_HIBERNATION_POLICY) {
399         for ((user, stats) in userStats) {
400             DumpableLog.i(
401                 LOG_TAG,
402                 "Usage stats for user ${user.identifier}: " +
403                     stats
404                         .map { stat -> stat.packageName to Date(stat.lastTimePackageUsed()) }
405                         .toMap()
406             )
407         }
408     }
409     for (user in unusedApps.keys.toList()) {
410         if (user !in userStats.keys) {
411             if (DEBUG_HIBERNATION_POLICY) {
412                 DumpableLog.i(LOG_TAG, "Ignoring user ${user.identifier}")
413             }
414             unusedApps.remove(user)
415         }
416     }
417 
418     for ((user, stats) in userStats) {
419         var unusedUserApps = unusedApps[user] ?: continue
420 
421         unusedUserApps =
422             unusedUserApps.filter { packageInfo ->
423                 val pkgName = packageInfo.packageName
424 
425                 val uidPackages =
426                     allPackagesByUserByUid[user]!![packageInfo.uid]?.map { info ->
427                         info.packageName
428                     }
429                         ?: emptyList()
430                 if (pkgName !in uidPackages) {
431                     Log.wtf(
432                         LOG_TAG,
433                         "Package $pkgName not among packages for " +
434                             "its uid ${packageInfo.uid}: $uidPackages"
435                     )
436                 }
437                 var lastTimePkgUsed: Long = stats.lastTimePackageUsed(uidPackages)
438 
439                 // Limit by install time
440                 lastTimePkgUsed = Math.max(lastTimePkgUsed, packageInfo.firstInstallTime)
441 
442                 // Limit by first boot time
443                 lastTimePkgUsed = Math.max(lastTimePkgUsed, startTimeOfUnusedAppTracking)
444 
445                 // Handle cross-profile apps
446                 if (context.isPackageCrossProfile(pkgName)) {
447                     for ((otherUser, otherStats) in userStats) {
448                         if (otherUser == user) {
449                             continue
450                         }
451                         lastTimePkgUsed =
452                             maxOf(lastTimePkgUsed, otherStats.lastTimePackageUsed(pkgName))
453                     }
454                 }
455 
456                 // Threshold check - whether app is unused
457                 now - lastTimePkgUsed > getUnusedThresholdMs()
458             }
459 
460         unusedApps[user] = unusedUserApps
461         if (DEBUG_HIBERNATION_POLICY) {
462             DumpableLog.i(
463                 LOG_TAG,
464                 "Unused apps for user ${user.identifier}: " +
465                     "${unusedUserApps.map { it.packageName }}"
466             )
467         }
468     }
469 
470     val appsToHibernate = mutableMapOf<UserHandle, List<LightPackageInfo>>()
471     val userManager = context.getSystemService(UserManager::class.java)
472     for ((user, userApps) in unusedApps) {
473         if (userManager == null || !userManager.isUserUnlocked(user)) {
474             DumpableLog.w(LOG_TAG, "Skipping $user - locked direct boot state")
475             continue
476         }
477         var userAppsToHibernate = mutableListOf<LightPackageInfo>()
478         userApps.forEachInParallel(Main) { pkg: LightPackageInfo ->
479             if (isPackageHibernationExemptBySystem(pkg, user)) {
480                 return@forEachInParallel
481             }
482 
483             if (isPackageHibernationExemptByUser(context, pkg)) {
484                 return@forEachInParallel
485             }
486 
487             val packageName = pkg.packageName
488             val packageImportance =
489                 context
490                     .getSystemService(ActivityManager::class.java)!!
491                     .getPackageImportance(packageName)
492             if (packageImportance <= IMPORTANCE_CANT_SAVE_STATE) {
493                 // Process is running in a state where it should not be killed
494                 DumpableLog.i(
495                     LOG_TAG,
496                     "Skipping hibernation - $packageName running with importance " +
497                         "$packageImportance"
498                 )
499                 return@forEachInParallel
500             }
501 
502             if (DEBUG_HIBERNATION_POLICY) {
503                 DumpableLog.i(
504                     LOG_TAG,
505                     "unused app $packageName - last used on " +
506                         userStats[user]?.lastTimePackageUsed(packageName)?.let(::Date)
507                 )
508             }
509 
510             synchronized(userAppsToHibernate) { userAppsToHibernate.add(pkg) }
511         }
512         appsToHibernate.put(user, userAppsToHibernate)
513     }
514     return appsToHibernate
515 }
516 
517 /**
518  * Gets the last time we consider the package used based off its usage stats. On pre-S devices this
519  * looks at last time visible which tracks explicit usage. In S, we add component usage which tracks
520  * various forms of implicit usage (e.g. service bindings).
521  */
UsageStatsnull522 fun UsageStats.lastTimePackageUsed(): Long {
523     var lastTimePkgUsed = this.lastTimeVisible
524     if (SdkLevel.isAtLeastS()) {
525         lastTimePkgUsed = maxOf(lastTimePkgUsed, this.lastTimeAnyComponentUsed)
526     }
527     return lastTimePkgUsed
528 }
529 
Listnull530 private fun List<UsageStats>.lastTimePackageUsed(pkgNames: List<String>): Long {
531     var result = 0L
532     for (stat in this) {
533         if (stat.packageName in pkgNames) {
534             result = Math.max(result, stat.lastTimePackageUsed())
535         }
536     }
537     return result
538 }
539 
Listnull540 private fun List<UsageStats>.lastTimePackageUsed(pkgName: String): Long {
541     return lastTimePackageUsed(listOf(pkgName))
542 }
543 
544 /** Checks if the given package is exempt from hibernation in a way that's not user-overridable */
545 @Suppress("MissingPermission")
isPackageHibernationExemptBySystemnull546 suspend fun isPackageHibernationExemptBySystem(
547     pkg: LightPackageInfo,
548     user: UserHandle,
549 ): Boolean {
550     val launcherPkgs = LauncherPackagesLiveData.getInitializedValue() ?: emptyList()
551     if (!launcherPkgs.contains(pkg.packageName)) {
552         if (DEBUG_HIBERNATION_POLICY) {
553             DumpableLog.i(LOG_TAG, "Exempted ${pkg.packageName} - Package is not on launcher")
554         }
555         return true
556     }
557     val exemptServicePkgs = ExemptServicesLiveData[user].getInitializedValue() ?: emptyMap()
558     if (!exemptServicePkgs[pkg.packageName].isNullOrEmpty()) {
559         return true
560     }
561     if (Utils.isUserDisabledOrWorkProfile(user)) {
562         if (DEBUG_HIBERNATION_POLICY) {
563             DumpableLog.i(
564                 LOG_TAG,
565                 "Exempted ${pkg.packageName} - $user is disabled or a work profile"
566             )
567         }
568         return true
569     }
570 
571     if (pkg.uid == Process.SYSTEM_UID) {
572         if (DEBUG_HIBERNATION_POLICY) {
573             DumpableLog.i(LOG_TAG, "Exempted ${pkg.packageName} - Package shares system uid")
574         }
575         return true
576     }
577 
578     val context = PermissionControllerApplication.get()
579     if (context.getSystemService(DevicePolicyManager::class.java)!!.isDeviceManaged) {
580         // TODO(b/237065504): Use proper system API to check if the device is financed in U.
581         val isFinancedDevice =
582             Settings.Global.getInt(context.contentResolver, "device_owner_type", 0) == 1
583         if (!isFinancedDevice) {
584             if (DEBUG_HIBERNATION_POLICY) {
585                 DumpableLog.i(LOG_TAG, "Exempted ${pkg.packageName} - device is managed")
586             }
587             return true
588         }
589     }
590 
591     val carrierPrivilegedStatus =
592         CarrierPrivilegedStatusLiveData[pkg.packageName].getInitializedValue()
593     if (
594         carrierPrivilegedStatus != CARRIER_PRIVILEGE_STATUS_HAS_ACCESS &&
595             carrierPrivilegedStatus != CARRIER_PRIVILEGE_STATUS_NO_ACCESS
596     ) {
597         DumpableLog.w(
598             LOG_TAG,
599             "Error carrier privileged status for ${pkg.packageName}: " + carrierPrivilegedStatus
600         )
601     }
602     if (carrierPrivilegedStatus == CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
603         if (DEBUG_HIBERNATION_POLICY) {
604             DumpableLog.i(LOG_TAG, "Exempted ${pkg.packageName} - carrier privileged")
605         }
606         return true
607     }
608 
609     if (
610         PermissionControllerApplication.get()
611             .packageManager
612             .checkPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE, pkg.packageName) ==
613             PERMISSION_GRANTED
614     ) {
615         if (DEBUG_HIBERNATION_POLICY) {
616             DumpableLog.i(
617                 LOG_TAG,
618                 "Exempted ${pkg.packageName} " + "- holder of READ_PRIVILEGED_PHONE_STATE"
619             )
620         }
621         return true
622     }
623 
624     val emergencyRoleHolders =
625         context
626             .getSystemService(android.app.role.RoleManager::class.java)!!
627             .getRoleHolders(RoleManager.ROLE_EMERGENCY)
628     if (emergencyRoleHolders.contains(pkg.packageName)) {
629         if (DEBUG_HIBERNATION_POLICY) {
630             DumpableLog.i(LOG_TAG, "Exempted ${pkg.packageName} - emergency app")
631         }
632         return true
633     }
634 
635     if (SdkLevel.isAtLeastS()) {
636         val hasInstallOrUpdatePermissions =
637             context.checkPermission(Manifest.permission.INSTALL_PACKAGES, -1 /* pid */, pkg.uid) ==
638                 PERMISSION_GRANTED ||
639                 context.checkPermission(
640                     Manifest.permission.INSTALL_PACKAGE_UPDATES,
641                     -1 /* pid */,
642                     pkg.uid
643                 ) == PERMISSION_GRANTED
644         val hasUpdatePackagesWithoutUserActionPermission =
645             context.checkPermission(UPDATE_PACKAGES_WITHOUT_USER_ACTION, -1 /* pid */, pkg.uid) ==
646                 PERMISSION_GRANTED
647         val isInstallerOfRecord =
648             (InstallerPackagesLiveData[user].getInitializedValue() ?: emptyList()).contains(
649                 pkg.packageName
650             ) && hasUpdatePackagesWithoutUserActionPermission
651         // Grant if app w/ privileged install/update permissions or app is an installer app that
652         // updates packages without user action.
653         if (hasInstallOrUpdatePermissions || isInstallerOfRecord) {
654             if (DEBUG_HIBERNATION_POLICY) {
655                 DumpableLog.i(LOG_TAG, "Exempted ${pkg.packageName} - installer app")
656             }
657             return true
658         }
659 
660         val roleHolders =
661             context
662                 .getSystemService(android.app.role.RoleManager::class.java)!!
663                 .getRoleHolders(RoleManager.ROLE_SYSTEM_WELLBEING)
664         if (roleHolders.contains(pkg.packageName)) {
665             if (DEBUG_HIBERNATION_POLICY) {
666                 DumpableLog.i(LOG_TAG, "Exempted ${pkg.packageName} - wellbeing app")
667             }
668             return true
669         }
670     }
671 
672     if (SdkLevel.isAtLeastT()) {
673         val roleHolders =
674             context
675                 .getSystemService(android.app.role.RoleManager::class.java)!!
676                 .getRoleHolders(RoleManager.ROLE_DEVICE_POLICY_MANAGEMENT)
677         if (roleHolders.contains(pkg.packageName)) {
678             if (DEBUG_HIBERNATION_POLICY) {
679                 DumpableLog.i(LOG_TAG, "Exempted ${pkg.packageName} - device policy manager app")
680             }
681             return true
682         }
683     }
684 
685     if (
686         isSystemExemptFromHibernationEnabled() &&
687             AppOpLiveData[
688                     pkg.packageName, AppOpsManager.OPSTR_SYSTEM_EXEMPT_FROM_HIBERNATION, pkg.uid]
689                 .getInitializedValue() == AppOpsManager.MODE_ALLOWED
690     ) {
691         if (DEBUG_HIBERNATION_POLICY) {
692             DumpableLog.i(
693                 LOG_TAG,
694                 "Exempted ${pkg.packageName} - has OP_SYSTEM_EXEMPT_FROM_HIBERNATION"
695             )
696         }
697         return true
698     }
699 
700     return false
701 }
702 
703 /**
704  * Checks if the given package is exempt from hibernation/auto revoke in a way that's
705  * user-overridable
706  */
isPackageHibernationExemptByUsernull707 suspend fun isPackageHibernationExemptByUser(
708     context: Context,
709     pkg: LightPackageInfo,
710 ): Boolean {
711     val packageName = pkg.packageName
712     val packageUid = pkg.uid
713 
714     val allowlistAppOpMode =
715         AppOpLiveData[
716                 packageName, AppOpsManager.OPSTR_AUTO_REVOKE_PERMISSIONS_IF_UNUSED, packageUid]
717             .getInitializedValue()
718     if (allowlistAppOpMode == AppOpsManager.MODE_DEFAULT) {
719         // Initial state - allowlist not explicitly overridden by either user or installer
720         if (DEBUG_OVERRIDE_THRESHOLDS) {
721             // Suppress exemptions to allow debugging
722             return false
723         }
724 
725         if (hibernationTargetsPreSApps()) {
726             // Default on if overridden
727             return false
728         }
729 
730         // Q- packages exempt by default, except R- on Auto since Auto-Revoke was skipped in R
731         val maxTargetSdkVersionForExemptApps =
732             if (context.packageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
733                 android.os.Build.VERSION_CODES.R
734             } else {
735                 android.os.Build.VERSION_CODES.Q
736             }
737 
738         return pkg.targetSdkVersion <= maxTargetSdkVersionForExemptApps
739     }
740     // Check whether user/installer exempt
741     return allowlistAppOpMode != AppOpsManager.MODE_ALLOWED
742 }
743 
Contextnull744 private fun Context.isPackageCrossProfile(pkg: String): Boolean {
745     return packageManager.checkPermission(Manifest.permission.INTERACT_ACROSS_PROFILES, pkg) ==
746         PERMISSION_GRANTED ||
747         packageManager.checkPermission(Manifest.permission.INTERACT_ACROSS_USERS, pkg) ==
748             PERMISSION_GRANTED ||
749         packageManager.checkPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL, pkg) ==
750             PERMISSION_GRANTED
751 }
752 
753 val Context.sharedPreferences: SharedPreferences
754     get() {
755         return PreferenceManager.getDefaultSharedPreferences(this)
756     }
757 
758 internal class SystemTime {
759     var actualSystemTime: Long = SNAPSHOT_UNINITIALIZED
760     var actualRealtime: Long = SNAPSHOT_UNINITIALIZED
761     var diffSystemTime: Long = SNAPSHOT_UNINITIALIZED
762 }
763 
getSystemTimenull764 private fun getSystemTime(sharedPreferences: SharedPreferences): SystemTime {
765     val systemTime = SystemTime()
766     val systemTimeSnapshot =
767         sharedPreferences.getLong(PREF_KEY_BOOT_TIME_SNAPSHOT, SNAPSHOT_UNINITIALIZED)
768     if (systemTimeSnapshot == SNAPSHOT_UNINITIALIZED) {
769         DumpableLog.e(LOG_TAG, "PREF_KEY_BOOT_TIME_SNAPSHOT is not initialized")
770         return systemTime
771     }
772 
773     val realtimeSnapshot =
774         sharedPreferences.getLong(PREF_KEY_ELAPSED_REALTIME_SNAPSHOT, SNAPSHOT_UNINITIALIZED)
775     if (realtimeSnapshot == SNAPSHOT_UNINITIALIZED) {
776         DumpableLog.e(LOG_TAG, "PREF_KEY_ELAPSED_REALTIME_SNAPSHOT is not initialized")
777         return systemTime
778     }
779     systemTime.actualSystemTime = System.currentTimeMillis()
780     systemTime.actualRealtime = SystemClock.elapsedRealtime()
781     val expectedSystemTime = systemTime.actualRealtime - realtimeSnapshot + systemTimeSnapshot
782     systemTime.diffSystemTime = systemTime.actualSystemTime - expectedSystemTime
783     return systemTime
784 }
785 
getStartTimeOfUnusedAppTrackingnull786 fun getStartTimeOfUnusedAppTracking(sharedPreferences: SharedPreferences): Long {
787     val startTimeOfUnusedAppTracking =
788         sharedPreferences.getLong(
789             PREF_KEY_START_TIME_OF_UNUSED_APP_TRACKING,
790             SNAPSHOT_UNINITIALIZED
791         )
792 
793     // If the preference is not initialized then use the current system time.
794     if (startTimeOfUnusedAppTracking == SNAPSHOT_UNINITIALIZED) {
795         val actualSystemTime = System.currentTimeMillis()
796         sharedPreferences
797             .edit()
798             .putLong(PREF_KEY_START_TIME_OF_UNUSED_APP_TRACKING, actualSystemTime)
799             .apply()
800         return actualSystemTime
801     }
802 
803     val diffSystemTime = getSystemTime(sharedPreferences).diffSystemTime
804     // If the value stored is older than a day adjust start time.
805     if (diffSystemTime > ONE_DAY_MS) {
806         adjustStartTimeOfUnusedAppTracking(sharedPreferences)
807     }
808     return sharedPreferences.getLong(
809         PREF_KEY_START_TIME_OF_UNUSED_APP_TRACKING,
810         SNAPSHOT_UNINITIALIZED
811     )
812 }
813 
initStartTimeOfUnusedAppTrackingnull814 private fun initStartTimeOfUnusedAppTracking(sharedPreferences: SharedPreferences) {
815     val systemTimeSnapshot = System.currentTimeMillis()
816     if (
817         sharedPreferences.getLong(
818             PREF_KEY_START_TIME_OF_UNUSED_APP_TRACKING,
819             SNAPSHOT_UNINITIALIZED
820         ) == SNAPSHOT_UNINITIALIZED
821     ) {
822         sharedPreferences
823             .edit()
824             .putLong(PREF_KEY_START_TIME_OF_UNUSED_APP_TRACKING, systemTimeSnapshot)
825             .apply()
826     }
827     val realtimeSnapshot = SystemClock.elapsedRealtime()
828     sharedPreferences
829         .edit()
830         .putLong(PREF_KEY_BOOT_TIME_SNAPSHOT, systemTimeSnapshot)
831         .putLong(PREF_KEY_ELAPSED_REALTIME_SNAPSHOT, realtimeSnapshot)
832         .apply()
833 }
834 
adjustStartTimeOfUnusedAppTrackingnull835 private fun adjustStartTimeOfUnusedAppTracking(sharedPreferences: SharedPreferences) {
836     val systemTime = getSystemTime(sharedPreferences)
837     val startTimeOfUnusedAppTracking =
838         sharedPreferences.getLong(
839             PREF_KEY_START_TIME_OF_UNUSED_APP_TRACKING,
840             SNAPSHOT_UNINITIALIZED
841         )
842     if (startTimeOfUnusedAppTracking == SNAPSHOT_UNINITIALIZED) {
843         DumpableLog.e(LOG_TAG, "PREF_KEY_START_TIME_OF_UNUSED_APP_TRACKING is not initialized")
844         return
845     }
846     val adjustedStartTimeOfUnusedAppTracking =
847         startTimeOfUnusedAppTracking + systemTime.diffSystemTime
848     sharedPreferences
849         .edit()
850         .putLong(PREF_KEY_START_TIME_OF_UNUSED_APP_TRACKING, adjustedStartTimeOfUnusedAppTracking)
851         .putLong(PREF_KEY_BOOT_TIME_SNAPSHOT, systemTime.actualSystemTime)
852         .putLong(PREF_KEY_ELAPSED_REALTIME_SNAPSHOT, systemTime.actualRealtime)
853         .apply()
854 }
855 
856 /** Make intent to go to unused apps page. */
makeUnusedAppsIntentnull857 private fun makeUnusedAppsIntent(context: Context, sessionId: Long): PendingIntent {
858     val clickIntent =
859         Intent(Intent.ACTION_MANAGE_UNUSED_APPS).apply {
860             putExtra(Constants.EXTRA_SESSION_ID, sessionId)
861             flags = FLAG_ACTIVITY_NEW_TASK
862         }
863     val pendingIntent =
864         PendingIntent.getActivity(context, 0, clickIntent, FLAG_UPDATE_CURRENT or FLAG_IMMUTABLE)
865     return pendingIntent
866 }
867 
868 /** Make intent for when safety center card is dismissed. */
makeDismissIntentnull869 private fun makeDismissIntent(context: Context, sessionId: Long): PendingIntent {
870     val dismissIntent =
871         Intent(context, DismissHandler::class.java).apply {
872             putExtra(Constants.EXTRA_SESSION_ID, sessionId)
873             flags = FLAG_RECEIVER_FOREGROUND
874         }
875     return PendingIntent.getBroadcast(
876         context,
877         /* requestCode= */ 0,
878         dismissIntent,
879         FLAG_ONE_SHOT or FLAG_UPDATE_CURRENT or FLAG_IMMUTABLE
880     )
881 }
882 
883 /** Broadcast receiver class for when safety center card is dismissed. */
884 class DismissHandler : BroadcastReceiver() {
onReceivenull885     override fun onReceive(context: Context?, intent: Intent?) {
886         setUnusedAppsReviewNeeded(context!!, false)
887     }
888 }
889 
890 /**
891  * A job to check for apps unused in the last [getUnusedThresholdMs]ms every [getCheckFrequencyMs]ms
892  * and hibernate the app / revoke their runtime permissions.
893  */
894 @Suppress("MissingPermission")
895 class HibernationJobService : JobService() {
896     var job: Job? = null
897     var jobStartTime: Long = -1L
898 
onStartJobnull899     override fun onStartJob(params: JobParameters?): Boolean {
900         if (DEBUG_HIBERNATION_POLICY) {
901             DumpableLog.i(LOG_TAG, "onStartJob")
902         }
903 
904         if (SKIP_NEXT_RUN) {
905             SKIP_NEXT_RUN = false
906             if (DEBUG_HIBERNATION_POLICY) {
907                 DumpableLog.i(LOG_TAG, "Skipping auto revoke first run when scheduled by system")
908             }
909             jobFinished(params, false)
910             return true
911         }
912 
913         jobStartTime = System.currentTimeMillis()
914         job =
915             GlobalScope.launch(Main) {
916                 try {
917                     var sessionId = Constants.INVALID_SESSION_ID
918                     while (sessionId == Constants.INVALID_SESSION_ID) {
919                         sessionId = Random().nextLong()
920                     }
921 
922                     val appsToHibernate = getAppsToHibernate(this@HibernationJobService)
923                     var hibernatedApps: Set<Pair<String, UserHandle>> = emptySet()
924                     if (isHibernationEnabled()) {
925                         val hibernationController =
926                             HibernationController(
927                                 this@HibernationJobService,
928                                 getUnusedThresholdMs(),
929                                 hibernationTargetsPreSApps()
930                             )
931                         hibernatedApps = hibernationController.hibernateApps(appsToHibernate)
932                     }
933                     val revokedApps =
934                         revokeAppPermissions(appsToHibernate, this@HibernationJobService, sessionId)
935                     val unusedApps: Set<Pair<String, UserHandle>> = hibernatedApps + revokedApps
936                     if (unusedApps.isNotEmpty()) {
937                         showUnusedAppsNotification(
938                             unusedApps.size,
939                             sessionId,
940                             Process.myUserHandle()
941                         )
942                         if (
943                             SdkLevel.isAtLeastT() &&
944                                 revokedApps.isNotEmpty() &&
945                                 getSystemService(SafetyCenterManager::class.java)!!
946                                     .isSafetyCenterEnabled
947                         ) {
948                             setUnusedAppsReviewNeeded(this@HibernationJobService, true)
949                             rescanAndPushDataToSafetyCenter(
950                                 this@HibernationJobService,
951                                 sessionId,
952                                 SafetyEvent.Builder(
953                                         SafetyEvent.SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED
954                                     )
955                                     .build()
956                             )
957                         }
958                     }
959                 } catch (e: Exception) {
960                     DumpableLog.e(LOG_TAG, "Failed to auto-revoke permissions", e)
961                 }
962                 jobFinished(params, false)
963             }
964         return true
965     }
966 
showUnusedAppsNotificationnull967     private fun showUnusedAppsNotification(numUnused: Int, sessionId: Long, user: UserHandle) {
968         val notificationManager = getSystemService(NotificationManager::class.java)!!
969 
970         val permissionReminderChannel =
971             NotificationChannel(
972                 Constants.PERMISSION_REMINDER_CHANNEL_ID,
973                 getString(R.string.permission_reminders),
974                 NotificationManager.IMPORTANCE_LOW
975             )
976         notificationManager.createNotificationChannel(permissionReminderChannel)
977 
978         var notifTitle: String
979         var notifContent: String
980         if (isHibernationEnabled()) {
981             notifTitle =
982                 StringUtils.getIcuPluralsString(
983                     this,
984                     R.string.unused_apps_notification_title,
985                     numUnused
986                 )
987             notifContent = getString(R.string.unused_apps_notification_content)
988         } else {
989             notifTitle = getString(R.string.auto_revoke_permission_notification_title)
990             notifContent = getString(R.string.auto_revoke_permission_notification_content)
991         }
992 
993         // Notification won't appear on TV, because notifications are considered distruptive on TV
994         val b =
995             Notification.Builder(this, Constants.PERMISSION_REMINDER_CHANNEL_ID)
996                 .setContentTitle(notifTitle)
997                 .setContentText(notifContent)
998                 .setStyle(Notification.BigTextStyle().bigText(notifContent))
999                 .setColor(getColor(android.R.color.system_notification_accent_color))
1000                 .setAutoCancel(true)
1001                 .setContentIntent(makeUnusedAppsIntent(this, sessionId))
1002         val extras = Bundle()
1003         if (DeviceUtils.isAuto(this)) {
1004             val settingsIcon =
1005                 KotlinUtils.getSettingsIcon(application, user, applicationContext.packageManager)
1006             extras.putBoolean(Constants.NOTIFICATION_EXTRA_USE_LAUNCHER_ICON, false)
1007             b.setLargeIcon(settingsIcon)
1008         }
1009         if (
1010             SdkLevel.isAtLeastT() &&
1011                 getSystemService(SafetyCenterManager::class.java)!!.isSafetyCenterEnabled
1012         ) {
1013             val notificationResources = KotlinUtils.getSafetyCenterNotificationResources(this)
1014 
1015             extras.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME, notificationResources.appLabel)
1016             b.setSmallIcon(notificationResources.smallIcon)
1017                 .setColor(notificationResources.color)
1018                 .addExtras(extras)
1019         } else {
1020             // Use standard Settings branding
1021             Utils.getSettingsLabelForNotifications(applicationContext.packageManager)?.let {
1022                 settingsLabel ->
1023                 extras.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME, settingsLabel.toString())
1024                 b.setSmallIcon(R.drawable.ic_settings_24dp).addExtras(extras)
1025             }
1026         }
1027 
1028         notificationManager.notify(
1029             HibernationJobService::class.java.simpleName,
1030             Constants.UNUSED_APPS_NOTIFICATION_ID,
1031             b.build()
1032         )
1033         GlobalScope.launch(IPC) {
1034             // Preload the unused packages
1035             getUnusedPackages().getInitializedValue(staleOk = true)
1036         }
1037     }
1038 
onStopJobnull1039     override fun onStopJob(params: JobParameters?): Boolean {
1040         DumpableLog.w(LOG_TAG, "onStopJob after ${System.currentTimeMillis() - jobStartTime}ms")
1041         job?.cancel()
1042         return true
1043     }
1044 }
1045 
1046 /**
1047  * Packages using exempt services for the current user (package-name -> list<service-interfaces>
1048  * implemented by the package)
1049  */
1050 class ExemptServicesLiveData(private val user: UserHandle) :
1051     SmartUpdateMediatorLiveData<Map<String, List<String>>>() {
1052     private val serviceLiveDatas: List<SmartUpdateMediatorLiveData<Set<String>>> =
1053         listOf(
1054             ServiceLiveData[
1055                 InputMethod.SERVICE_INTERFACE, Manifest.permission.BIND_INPUT_METHOD, user],
1056             ServiceLiveData[
1057                 NotificationListenerService.SERVICE_INTERFACE,
1058                 Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE,
1059                 user],
1060             ServiceLiveData[
1061                 AccessibilityService.SERVICE_INTERFACE,
1062                 Manifest.permission.BIND_ACCESSIBILITY_SERVICE,
1063                 user],
1064             ServiceLiveData[
1065                 WallpaperService.SERVICE_INTERFACE, Manifest.permission.BIND_WALLPAPER, user],
1066             ServiceLiveData[
1067                 VoiceInteractionService.SERVICE_INTERFACE,
1068                 Manifest.permission.BIND_VOICE_INTERACTION,
1069                 user],
1070             ServiceLiveData[
1071                 PrintService.SERVICE_INTERFACE, Manifest.permission.BIND_PRINT_SERVICE, user],
1072             ServiceLiveData[
1073                 DreamService.SERVICE_INTERFACE, Manifest.permission.BIND_DREAM_SERVICE, user],
1074             ServiceLiveData[
1075                 AutofillService.SERVICE_INTERFACE, Manifest.permission.BIND_AUTOFILL_SERVICE, user],
1076             ServiceLiveData[
1077                 DevicePolicyManager.ACTION_DEVICE_ADMIN_SERVICE,
1078                 Manifest.permission.BIND_DEVICE_ADMIN,
1079                 user],
1080             BroadcastReceiverLiveData[
1081                 DeviceAdminReceiver.ACTION_DEVICE_ADMIN_ENABLED,
1082                 Manifest.permission.BIND_DEVICE_ADMIN,
1083                 user]
1084         )
1085 
1086     init {
<lambda>null1087         serviceLiveDatas.forEach { addSource(it) { update() } }
1088     }
1089 
onUpdatenull1090     override fun onUpdate() {
1091         if (serviceLiveDatas.all { it.isInitialized }) {
1092             val pksToServices = mutableMapOf<String, MutableList<String>>()
1093 
1094             serviceLiveDatas.forEach { serviceLD ->
1095                 serviceLD.value!!.forEach { packageName ->
1096                     pksToServices
1097                         .getOrPut(packageName, { mutableListOf() })
1098                         .add((serviceLD as? HasIntentAction)?.intentAction ?: "???")
1099                 }
1100             }
1101 
1102             value = pksToServices
1103         }
1104     }
1105 
1106     /**
1107      * Repository for ExemptServiceLiveData
1108      *
1109      * <p> Key value is user
1110      */
1111     companion object : DataRepositoryForPackage<UserHandle, ExemptServicesLiveData>() {
newValuenull1112         override fun newValue(key: UserHandle): ExemptServicesLiveData {
1113             return ExemptServicesLiveData(key)
1114         }
1115     }
1116 }
1117 
1118 /** Live data for whether the hibernation feature is enabled or not. */
1119 object HibernationEnabledLiveData : MutableLiveData<Boolean>() {
1120     init {
1121         postValue(
1122             SdkLevel.isAtLeastS() &&
1123                 DeviceConfig.getBoolean(
1124                     NAMESPACE_APP_HIBERNATION,
1125                     Utils.PROPERTY_APP_HIBERNATION_ENABLED,
1126                     true /* defaultValue */
1127                 )
1128         )
1129         DeviceConfig.addOnPropertiesChangedListener(
1130             NAMESPACE_APP_HIBERNATION,
1131             PermissionControllerApplication.get().mainExecutor,
propertiesnull1132             { properties ->
1133                 for (key in properties.keyset) {
1134                     if (key == Utils.PROPERTY_APP_HIBERNATION_ENABLED) {
1135                         value =
1136                             SdkLevel.isAtLeastS() &&
1137                                 properties.getBoolean(key, true /* defaultValue */)
1138                         break
1139                     }
1140                 }
1141             }
1142         )
1143     }
1144 }
1145