• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * Copyright (C) 2020 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 @file:JvmName("AutoRevokePermissions")
18 
19 package com.android.permissioncontroller.permission.service
20 
21 import android.Manifest
22 import android.accessibilityservice.AccessibilityService
23 import android.app.ActivityManager
24 import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_TOP_SLEEPING
25 import android.app.AppOpsManager
26 import android.app.AppOpsManager.MODE_ALLOWED
27 import android.app.AppOpsManager.MODE_DEFAULT
28 import android.app.Notification
29 import android.app.NotificationChannel
30 import android.app.NotificationManager
31 import android.app.NotificationManager.IMPORTANCE_LOW
32 import android.app.PendingIntent
33 import android.app.PendingIntent.FLAG_ONE_SHOT
34 import android.app.PendingIntent.FLAG_UPDATE_CURRENT
35 import android.app.admin.DeviceAdminReceiver
36 import android.app.admin.DevicePolicyManager
37 import android.app.job.JobInfo
38 import android.app.job.JobParameters
39 import android.app.job.JobScheduler
40 import android.app.job.JobService
41 import android.app.usage.UsageStats
42 import android.app.usage.UsageStatsManager.INTERVAL_DAILY
43 import android.app.usage.UsageStatsManager.INTERVAL_MONTHLY
44 import android.content.BroadcastReceiver
45 import android.content.ComponentName
46 import android.content.Context
47 import android.content.Intent
48 import android.content.SharedPreferences
49 import android.content.pm.PackageManager
50 import android.content.pm.PackageManager.FLAG_PERMISSION_AUTO_REVOKED
51 import android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET
52 import android.content.pm.PackageManager.PERMISSION_GRANTED
53 import android.net.NetworkScoreManager
54 import android.os.Bundle
55 import android.os.Process.myUserHandle
56 import android.os.UserHandle
57 import android.os.UserManager
58 import android.printservice.PrintService
59 import android.provider.DeviceConfig
60 import android.provider.Settings
61 import android.service.attention.AttentionService
62 import android.service.autofill.AutofillService
63 import android.service.autofill.augmented.AugmentedAutofillService
64 import android.service.dreams.DreamService
65 import android.service.notification.NotificationListenerService
66 import android.service.textclassifier.TextClassifierService
67 import android.service.voice.VoiceInteractionService
68 import android.service.wallpaper.WallpaperService
69 import android.telephony.TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS
70 import android.telephony.TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS
71 import android.util.Log
72 import android.view.inputmethod.InputMethod
73 import androidx.annotation.MainThread
74 import androidx.preference.PreferenceManager
75 import com.android.permissioncontroller.Constants
76 import com.android.permissioncontroller.Constants.ACTION_MANAGE_AUTO_REVOKE
77 import com.android.permissioncontroller.Constants.AUTO_REVOKE_NOTIFICATION_ID
78 import com.android.permissioncontroller.Constants.EXTRA_SESSION_ID
79 import com.android.permissioncontroller.Constants.INVALID_SESSION_ID
80 import com.android.permissioncontroller.Constants.PERMISSION_REMINDER_CHANNEL_ID
81 import com.android.permissioncontroller.DumpableLog
82 import com.android.permissioncontroller.PermissionControllerStatsLog
83 import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED
84 import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__AUTO_UNUSED_APP_PERMISSION_REVOKED
85 import com.android.permissioncontroller.R
86 import com.android.permissioncontroller.permission.data.AllPackageInfosLiveData
87 import com.android.permissioncontroller.permission.data.AppOpLiveData
88 import com.android.permissioncontroller.permission.data.BroadcastReceiverLiveData
89 import com.android.permissioncontroller.permission.data.CarrierPrivilegedStatusLiveData
90 import com.android.permissioncontroller.permission.data.DataRepositoryForPackage
91 import com.android.permissioncontroller.permission.data.HasIntentAction
92 import com.android.permissioncontroller.permission.data.LightAppPermGroupLiveData
93 import com.android.permissioncontroller.permission.data.PackagePermissionsLiveData
94 import com.android.permissioncontroller.permission.data.ServiceLiveData
95 import com.android.permissioncontroller.permission.data.SmartUpdateMediatorLiveData
96 import com.android.permissioncontroller.permission.data.UnusedAutoRevokedPackagesLiveData
97 import com.android.permissioncontroller.permission.data.UsageStatsLiveData
98 import com.android.permissioncontroller.permission.data.UsersLiveData
99 import com.android.permissioncontroller.permission.data.get
100 import com.android.permissioncontroller.permission.model.livedatatypes.LightAppPermGroup
101 import com.android.permissioncontroller.permission.model.livedatatypes.LightPackageInfo
102 import com.android.permissioncontroller.permission.service.AutoRevokePermissionsProto.AutoRevokePermissionsDumpProto
103 import com.android.permissioncontroller.permission.service.AutoRevokePermissionsProto.PackageProto
104 import com.android.permissioncontroller.permission.service.AutoRevokePermissionsProto.PerUserProto
105 import com.android.permissioncontroller.permission.service.AutoRevokePermissionsProto.PermissionGroupProto
106 import com.android.permissioncontroller.permission.service.AutoRevokePermissionsProto.TeamFoodSettingsProto
107 import com.android.permissioncontroller.permission.ui.ManagePermissionsActivity
108 import com.android.permissioncontroller.permission.utils.IPC
109 import com.android.permissioncontroller.permission.utils.KotlinUtils
110 import com.android.permissioncontroller.permission.utils.Utils
111 import com.android.permissioncontroller.permission.utils.Utils.PROPERTY_AUTO_REVOKE_CHECK_FREQUENCY_MILLIS
112 import com.android.permissioncontroller.permission.utils.Utils.PROPERTY_AUTO_REVOKE_UNUSED_THRESHOLD_MILLIS
113 import com.android.permissioncontroller.permission.utils.application
114 import com.android.permissioncontroller.permission.utils.forEachInParallel
115 import com.android.permissioncontroller.permission.utils.updatePermissionFlags
116 import kotlinx.coroutines.Dispatchers.Main
117 import kotlinx.coroutines.GlobalScope
118 import kotlinx.coroutines.Job
119 import kotlinx.coroutines.async
120 import kotlinx.coroutines.launch
121 import java.util.Date
122 import java.util.Random
123 import java.util.concurrent.TimeUnit.DAYS
124 import java.util.concurrent.TimeUnit.SECONDS
125 import java.util.concurrent.atomic.AtomicBoolean
126 
127 private const val LOG_TAG = "AutoRevokePermissions"
128 private const val DEBUG_OVERRIDE_THRESHOLDS = false
129 // TODO eugenesusla: temporarily enabled for extra logs during dogfooding
130 private const val DEBUG = true || DEBUG_OVERRIDE_THRESHOLDS
131 
132 private const val AUTO_REVOKE_ENABLED = true
133 
134 private var SKIP_NEXT_RUN = false
135 
136 private val EXEMPT_PERMISSIONS = listOf(
137         android.Manifest.permission.ACTIVITY_RECOGNITION)
138 
139 private val DEFAULT_UNUSED_THRESHOLD_MS =
140         if (AUTO_REVOKE_ENABLED) DAYS.toMillis(90) else Long.MAX_VALUE
141 fun getUnusedThresholdMs(context: Context) = when {
142     DEBUG_OVERRIDE_THRESHOLDS -> SECONDS.toMillis(1)
143     TeamfoodSettings.get(context) != null -> TeamfoodSettings.get(context)!!.unusedThresholdMs
144     else -> DeviceConfig.getLong(DeviceConfig.NAMESPACE_PERMISSIONS,
145             PROPERTY_AUTO_REVOKE_UNUSED_THRESHOLD_MILLIS,
146             DEFAULT_UNUSED_THRESHOLD_MS)
147 }
148 
149 private val DEFAULT_CHECK_FREQUENCY_MS = DAYS.toMillis(15)
getCheckFrequencyMsnull150 private fun getCheckFrequencyMs(context: Context) = when {
151     TeamfoodSettings.get(context) != null -> TeamfoodSettings.get(context)!!.checkFrequencyMs
152     else -> DeviceConfig.getLong(
153             DeviceConfig.NAMESPACE_PERMISSIONS,
154             PROPERTY_AUTO_REVOKE_CHECK_FREQUENCY_MILLIS,
155             DEFAULT_CHECK_FREQUENCY_MS)
156 }
157 
158 private val SERVER_LOG_ID =
159     PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__AUTO_UNUSED_APP_PERMISSION_REVOKED
160 
161 private val PREF_KEY_FIRST_BOOT_TIME = "first_boot_time"
162 
isAutoRevokeEnablednull163 fun isAutoRevokeEnabled(context: Context): Boolean {
164     return getCheckFrequencyMs(context) > 0 &&
165             getUnusedThresholdMs(context) > 0 &&
166             getUnusedThresholdMs(context) != Long.MAX_VALUE
167 }
168 
169 /**
170  * @return dump of auto revoke service as a proto
171  */
dumpAutoRevokePermissionsnull172 suspend fun dumpAutoRevokePermissions(context: Context): AutoRevokePermissionsDumpProto {
173     val teamFoodSettings = GlobalScope.async(IPC) {
174         TeamfoodSettings.get(context)?.dump()
175                 ?: TeamFoodSettingsProto.newBuilder().build()
176     }
177 
178     val dumpData = GlobalScope.async(IPC) {
179         AutoRevokeDumpLiveData(context).getInitializedValue()
180     }
181 
182     return AutoRevokePermissionsDumpProto.newBuilder()
183             .setTeamfoodSettings(teamFoodSettings.await())
184             .addAllUsers(dumpData.await().dumpUsers())
185             .build()
186 }
187 
188 /**
189  * Receiver of the onBoot event.
190  */
191 class AutoRevokeOnBootReceiver : BroadcastReceiver() {
192 
onReceivenull193     override fun onReceive(context: Context, intent: Intent?) {
194 
195         // Auto-revoke is not enabled on Automotive devices
196         if (context.packageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
197             DumpableLog.i(LOG_TAG, "Auto-revoke not scheduled on Automotive devices")
198             return
199         }
200 
201         // Init firstBootTime
202         val firstBootTime = context.firstBootTime
203 
204         if (DEBUG) {
205             DumpableLog.i(LOG_TAG, "scheduleAutoRevokePermissions " +
206                 "with frequency ${getCheckFrequencyMs(context)}ms " +
207                 "and threshold ${getUnusedThresholdMs(context)}ms")
208         }
209 
210         val userManager = context.getSystemService(UserManager::class.java)!!
211         // If this user is a profile, then its auto revoke will be handled by the primary user
212         if (userManager.isProfile) {
213             if (DEBUG) {
214                 DumpableLog.i(LOG_TAG, "user ${myUserHandle().identifier} is a profile. Not " +
215                     "running Auto Revoke.")
216             }
217             return
218         } else if (DEBUG) {
219             DumpableLog.i(LOG_TAG, "user ${myUserHandle().identifier} is a profile owner. " +
220                 "Running Auto Revoke.")
221         }
222 
223         SKIP_NEXT_RUN = true
224 
225         val jobInfo = JobInfo.Builder(
226             Constants.AUTO_REVOKE_JOB_ID,
227             ComponentName(context, AutoRevokeService::class.java))
228             .setPeriodic(getCheckFrequencyMs(context))
229             .build()
230         val status = context.getSystemService(JobScheduler::class.java)!!.schedule(jobInfo)
231         if (status != JobScheduler.RESULT_SUCCESS) {
232             DumpableLog.e(LOG_TAG,
233                 "Could not schedule ${AutoRevokeService::class.java.simpleName}: $status")
234         }
235     }
236 }
237 
238 @MainThread
revokePermissionsOnUnusedAppsnull239 private suspend fun revokePermissionsOnUnusedApps(
240     context: Context,
241     sessionId: Long = INVALID_SESSION_ID
242 ):
243     List<Pair<String, UserHandle>> {
244     if (!isAutoRevokeEnabled(context)) {
245         return emptyList()
246     }
247 
248     val now = System.currentTimeMillis()
249     val firstBootTime = context.firstBootTime
250 
251     // TODO ntmyren: remove once b/154796729 is fixed
252     Log.i(LOG_TAG, "getting UserPackageInfoLiveData for all users " +
253         "in AutoRevokePermissions")
254     val allPackagesByUser = AllPackageInfosLiveData.getInitializedValue()
255     val allPackagesByUserByUid = allPackagesByUser.mapValues { (_, pkgs) ->
256         pkgs.groupBy { pkg -> pkg.uid }
257     }
258     val unusedApps = allPackagesByUser.toMutableMap()
259 
260     val userStats = UsageStatsLiveData[getUnusedThresholdMs(context),
261         if (DEBUG_OVERRIDE_THRESHOLDS) INTERVAL_DAILY else INTERVAL_MONTHLY].getInitializedValue()
262     if (DEBUG) {
263         for ((user, stats) in userStats) {
264             DumpableLog.i(LOG_TAG, "Usage stats for user ${user.identifier}: " +
265                     stats.map { stat ->
266                         stat.packageName to Date(stat.lastTimeVisible)
267                     }.toMap())
268         }
269     }
270     for (user in unusedApps.keys.toList()) {
271         if (user !in userStats.keys) {
272             if (DEBUG) {
273                 DumpableLog.i(LOG_TAG, "Ignoring user ${user.identifier}")
274             }
275             unusedApps.remove(user)
276         }
277     }
278 
279     for ((user, stats) in userStats) {
280         var unusedUserApps = unusedApps[user] ?: continue
281 
282         unusedUserApps = unusedUserApps.filter { packageInfo ->
283             val pkgName = packageInfo.packageName
284 
285             val uidPackages = allPackagesByUserByUid[user]!![packageInfo.uid]
286                     ?.map { info -> info.packageName } ?: emptyList()
287             if (pkgName !in uidPackages) {
288                 Log.wtf(LOG_TAG, "Package $pkgName not among packages for " +
289                         "its uid ${packageInfo.uid}: $uidPackages")
290             }
291             var lastTimeVisible: Long = stats.lastTimeVisible(uidPackages)
292 
293             // Limit by install time
294             lastTimeVisible = Math.max(lastTimeVisible, packageInfo.firstInstallTime)
295 
296             // Limit by first boot time
297             lastTimeVisible = Math.max(lastTimeVisible, firstBootTime)
298 
299             // Handle cross-profile apps
300             if (context.isPackageCrossProfile(pkgName)) {
301                 for ((otherUser, otherStats) in userStats) {
302                     if (otherUser == user) {
303                         continue
304                     }
305                     lastTimeVisible = Math.max(lastTimeVisible, otherStats.lastTimeVisible(pkgName))
306                 }
307             }
308 
309             // Threshold check - whether app is unused
310             now - lastTimeVisible > getUnusedThresholdMs(context)
311         }
312 
313         unusedApps[user] = unusedUserApps
314         if (DEBUG) {
315             DumpableLog.i(LOG_TAG, "Unused apps for user ${user.identifier}: " +
316                 "${unusedUserApps.map { it.packageName }}")
317         }
318     }
319 
320     val revokedApps = mutableListOf<Pair<String, UserHandle>>()
321     val userManager = context.getSystemService(UserManager::class.java)
322     for ((user, userApps) in unusedApps) {
323         if (userManager == null || !userManager.isUserUnlocked(user)) {
324             DumpableLog.w(LOG_TAG, "Skipping $user - locked direct boot state")
325             continue
326         }
327         userApps.forEachInParallel(Main) { pkg: LightPackageInfo ->
328             if (pkg.grantedPermissions.isEmpty()) {
329                 return@forEachInParallel
330             }
331 
332             if (isPackageAutoRevokePermanentlyExempt(pkg, user)) {
333                 return@forEachInParallel
334             }
335 
336             val packageName = pkg.packageName
337             if (isPackageAutoRevokeExempt(context, pkg)) {
338                 return@forEachInParallel
339             }
340 
341             val anyPermsRevoked = AtomicBoolean(false)
342             val pkgPermGroups: Map<String, List<String>>? =
343                 PackagePermissionsLiveData[packageName, user]
344                     .getInitializedValue()
345 
346             pkgPermGroups?.entries?.forEachInParallel(Main) { (groupName, _) ->
347                 if (groupName == PackagePermissionsLiveData.NON_RUNTIME_NORMAL_PERMS) {
348                     return@forEachInParallel
349                 }
350 
351                 val group: LightAppPermGroup =
352                     LightAppPermGroupLiveData[packageName, groupName, user]
353                         .getInitializedValue()
354                         ?: return@forEachInParallel
355 
356                 val fixed = group.isBackgroundFixed || group.isForegroundFixed
357                 val granted = group.permissions.any { (_, perm) ->
358                     perm.isGrantedIncludingAppOp && perm.name !in EXEMPT_PERMISSIONS
359                 }
360                 if (!fixed &&
361                     granted &&
362                     !group.isGrantedByDefault &&
363                     !group.isGrantedByRole &&
364                     group.isUserSensitive) {
365 
366                     val revocablePermissions = group.permissions.keys.toList()
367 
368                     if (revocablePermissions.isEmpty()) {
369                         return@forEachInParallel
370                     }
371 
372                     if (DEBUG) {
373                         DumpableLog.i(LOG_TAG, "revokeUnused $packageName - $revocablePermissions" +
374                                 " - lastVisible on " +
375                                 userStats[user]?.lastTimeVisible(packageName)?.let(::Date))
376                     }
377 
378                     val uid = group.packageInfo.uid
379                     for (permName in revocablePermissions) {
380                         PermissionControllerStatsLog.write(
381                             PERMISSION_GRANT_REQUEST_RESULT_REPORTED,
382                             sessionId, uid, packageName, permName, false, SERVER_LOG_ID)
383                     }
384 
385                     val packageImportance = context
386                         .getSystemService(ActivityManager::class.java)!!
387                         .getPackageImportance(packageName)
388                     if (packageImportance > IMPORTANCE_TOP_SLEEPING) {
389                         if (DEBUG) {
390                             DumpableLog.i(LOG_TAG, "revoking $packageName - $revocablePermissions")
391                             DumpableLog.i(LOG_TAG, "State pre revocation: ${group.allPermissions}")
392                         }
393                         anyPermsRevoked.compareAndSet(false, true)
394 
395                         val bgRevokedState = KotlinUtils.revokeBackgroundRuntimePermissions(
396                                 context.application, group,
397                                 userFixed = false, oneTime = false,
398                                 filterPermissions = revocablePermissions)
399                         if (DEBUG) {
400                             DumpableLog.i(LOG_TAG,
401                                 "Bg state post revocation: ${bgRevokedState.allPermissions}")
402                         }
403                         val fgRevokedState = KotlinUtils.revokeForegroundRuntimePermissions(
404                             context.application, group,
405                             userFixed = false, oneTime = false,
406                             filterPermissions = revocablePermissions)
407                         if (DEBUG) {
408                             DumpableLog.i(LOG_TAG,
409                                 "Fg state post revocation: ${fgRevokedState.allPermissions}")
410                         }
411 
412                         for (permission in revocablePermissions) {
413                             context.packageManager.updatePermissionFlags(
414                                 permission, packageName, user,
415                                 FLAG_PERMISSION_AUTO_REVOKED to true,
416                                 FLAG_PERMISSION_USER_SET to false)
417                         }
418                     } else {
419                         DumpableLog.i(LOG_TAG,
420                             "Skipping auto-revoke - $packageName running with importance " +
421                                 "$packageImportance")
422                     }
423                 }
424             }
425 
426             if (anyPermsRevoked.get()) {
427                 synchronized(revokedApps) {
428                     revokedApps.add(pkg.packageName to user)
429                 }
430             }
431         }
432         if (DEBUG) {
433             synchronized(revokedApps) {
434                 DumpableLog.i(LOG_TAG,
435                         "Done auto-revoke for user ${user.identifier} - revoked $revokedApps")
436             }
437         }
438     }
439     return revokedApps
440 }
441 
Listnull442 private fun List<UsageStats>.lastTimeVisible(pkgNames: List<String>): Long {
443     var result = 0L
444     for (stat in this) {
445         if (stat.packageName in pkgNames) {
446             result = Math.max(result, stat.lastTimeVisible)
447         }
448     }
449     return result
450 }
451 
Listnull452 private fun List<UsageStats>.lastTimeVisible(pkgName: String): Long {
453     return lastTimeVisible(listOf(pkgName))
454 }
455 
456 /**
457  * Checks if the given package is exempt from auto revoke in a way that's not user-overridable
458  */
isPackageAutoRevokePermanentlyExemptnull459 suspend fun isPackageAutoRevokePermanentlyExempt(
460     pkg: LightPackageInfo,
461     user: UserHandle
462 ): Boolean {
463     if (!ExemptServicesLiveData[user]
464             .getInitializedValue()[pkg.packageName]
465             .isNullOrEmpty()) {
466         return true
467     }
468     if (Utils.isUserDisabledOrWorkProfile(user)) {
469         if (DEBUG) {
470             DumpableLog.i(LOG_TAG,
471                     "Exempted ${pkg.packageName} - $user is disabled or a work profile")
472         }
473         return true
474     }
475     val carrierPrivilegedStatus = CarrierPrivilegedStatusLiveData[pkg.packageName]
476             .getInitializedValue()
477     if (carrierPrivilegedStatus != CARRIER_PRIVILEGE_STATUS_HAS_ACCESS &&
478             carrierPrivilegedStatus != CARRIER_PRIVILEGE_STATUS_NO_ACCESS) {
479         DumpableLog.w(LOG_TAG, "Error carrier privileged status for ${pkg.packageName}: " +
480                 carrierPrivilegedStatus)
481     }
482     if (carrierPrivilegedStatus == CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
483         if (DEBUG) {
484             DumpableLog.i(LOG_TAG, "Exempted ${pkg.packageName} - carrier privileged")
485         }
486         return true
487     }
488     return false
489 }
490 
491 /**
492  * Checks if the given package is exempt from auto revoke in a way that's user-overridable
493  */
isPackageAutoRevokeExemptnull494 suspend fun isPackageAutoRevokeExempt(
495     context: Context,
496     pkg: LightPackageInfo
497 ): Boolean {
498     val packageName = pkg.packageName
499     val packageUid = pkg.uid
500 
501     val whitelistAppOpMode =
502         AppOpLiveData[packageName,
503             AppOpsManager.OPSTR_AUTO_REVOKE_PERMISSIONS_IF_UNUSED, packageUid]
504             .getInitializedValue()
505     if (whitelistAppOpMode == MODE_DEFAULT) {
506         // Initial state - whitelist not explicitly overridden by either user or installer
507 
508         if (DEBUG_OVERRIDE_THRESHOLDS) {
509             // Suppress exemptions to allow debugging
510             return false
511         }
512 
513         // Q- packages exempt by default, except for dogfooding
514         return pkg.targetSdkVersion <= android.os.Build.VERSION_CODES.Q &&
515                 TeamfoodSettings.get(context)?.enabledForPreRApps != true
516     }
517     // Check whether user/installer exempt
518     return whitelistAppOpMode != MODE_ALLOWED
519 }
520 
Contextnull521 private fun Context.isPackageCrossProfile(pkg: String): Boolean {
522     return packageManager.checkPermission(
523         Manifest.permission.INTERACT_ACROSS_PROFILES, pkg) == PERMISSION_GRANTED ||
524         packageManager.checkPermission(
525             Manifest.permission.INTERACT_ACROSS_USERS, pkg) == PERMISSION_GRANTED ||
526         packageManager.checkPermission(
527             Manifest.permission.INTERACT_ACROSS_USERS_FULL, pkg) == PERMISSION_GRANTED
528 }
529 
Contextnull530 private fun Context.forUser(user: UserHandle): Context {
531     return Utils.getUserContext(application, user)
532 }
533 
Contextnull534 private fun Context.forParentUser(): Context {
535     return Utils.getParentUserContext(this)
536 }
537 
getSystemServicenull538 private inline fun <reified T> Context.getSystemService() = getSystemService(T::class.java)!!
539 
540 val Context.sharedPreferences: SharedPreferences get() {
541     return PreferenceManager.getDefaultSharedPreferences(this)
542 }
543 
544 private val Context.firstBootTime: Long get() {
545     var time = sharedPreferences.getLong(PREF_KEY_FIRST_BOOT_TIME, -1L)
546     if (time > 0) {
547         return time
548     }
549     // This is the first boot
550     time = System.currentTimeMillis()
551     sharedPreferences.edit().putLong(PREF_KEY_FIRST_BOOT_TIME, time).apply()
552     return time
553 }
554 
555 /**
556  * A job to check for apps unused in the last [getUnusedThresholdMs]ms every
557  * [getCheckFrequencyMs]ms and [revokePermissionsOnUnusedApps] for them
558  */
559 class AutoRevokeService : JobService() {
560     var job: Job? = null
561     var jobStartTime: Long = -1L
562 
onStartJobnull563     override fun onStartJob(params: JobParameters?): Boolean {
564         if (DEBUG) {
565             DumpableLog.i(LOG_TAG, "onStartJob")
566         }
567 
568         if (SKIP_NEXT_RUN) {
569             SKIP_NEXT_RUN = false
570             if (DEBUG) {
571                 Log.i(LOG_TAG, "Skipping auto revoke first run when scheduled by system")
572             }
573             jobFinished(params, false)
574             return true
575         }
576 
577         jobStartTime = System.currentTimeMillis()
578         job = GlobalScope.launch(Main) {
579             try {
580                 var sessionId = INVALID_SESSION_ID
581                 while (sessionId == INVALID_SESSION_ID) {
582                     sessionId = Random().nextLong()
583                 }
584 
585                 val revokedApps = revokePermissionsOnUnusedApps(this@AutoRevokeService, sessionId)
586                 if (revokedApps.isNotEmpty()) {
587                     showAutoRevokeNotification(sessionId)
588                 }
589             } catch (e: Exception) {
590                 DumpableLog.e(LOG_TAG, "Failed to auto-revoke permissions", e)
591             }
592             jobFinished(params, false)
593         }
594         return true
595     }
596 
showAutoRevokeNotificationnull597     private suspend fun showAutoRevokeNotification(sessionId: Long) {
598         val notificationManager = getSystemService(NotificationManager::class.java)!!
599 
600         val permissionReminderChannel = NotificationChannel(
601             PERMISSION_REMINDER_CHANNEL_ID, getString(R.string.permission_reminders),
602             IMPORTANCE_LOW)
603         notificationManager.createNotificationChannel(permissionReminderChannel)
604 
605         val clickIntent = Intent(this, ManagePermissionsActivity::class.java).apply {
606             action = ACTION_MANAGE_AUTO_REVOKE
607             putExtra(EXTRA_SESSION_ID, sessionId)
608             flags = Intent.FLAG_ACTIVITY_NEW_TASK
609         }
610         val pendingIntent = PendingIntent.getActivity(this, 0, clickIntent,
611             FLAG_ONE_SHOT or FLAG_UPDATE_CURRENT)
612 
613         val b = Notification.Builder(this, PERMISSION_REMINDER_CHANNEL_ID)
614             .setContentTitle(getString(R.string.auto_revoke_permission_notification_title))
615             .setContentText(getString(
616                 R.string.auto_revoke_permission_notification_content))
617             .setStyle(Notification.BigTextStyle().bigText(getString(
618                 R.string.auto_revoke_permission_notification_content)))
619             .setSmallIcon(R.drawable.ic_settings_24dp)
620             .setColor(getColor(android.R.color.system_notification_accent_color))
621             .setAutoCancel(true)
622             .setContentIntent(pendingIntent)
623             .extend(Notification.TvExtender())
624         Utils.getSettingsLabelForNotifications(applicationContext.packageManager)?.let {
625             settingsLabel ->
626             val extras = Bundle()
627             extras.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME, settingsLabel.toString())
628             b.addExtras(extras)
629         }
630 
631         notificationManager.notify(AutoRevokeService::class.java.simpleName,
632             AUTO_REVOKE_NOTIFICATION_ID, b.build())
633         // Preload the auto revoked packages
634         UnusedAutoRevokedPackagesLiveData.getInitializedValue()
635     }
636 
637     companion object {
638         const val SHOW_AUTO_REVOKE = "showAutoRevoke"
639     }
640 
onStopJobnull641     override fun onStopJob(params: JobParameters?): Boolean {
642         DumpableLog.w(LOG_TAG, "onStopJob after ${System.currentTimeMillis() - jobStartTime}ms")
643         job?.cancel()
644         return true
645     }
646 }
647 
648 /**
649  * Packages using exempt services for the current user (package-name -> list<service-interfaces>
650  * implemented by the package)
651  */
652 class ExemptServicesLiveData(val user: UserHandle)
653     : SmartUpdateMediatorLiveData<Map<String, List<String>>>() {
654     private val serviceLiveDatas: List<SmartUpdateMediatorLiveData<Set<String>>> = listOf(
655             ServiceLiveData[InputMethod.SERVICE_INTERFACE,
656                     Manifest.permission.BIND_INPUT_METHOD,
657                     user],
658             ServiceLiveData[
659                     NotificationListenerService.SERVICE_INTERFACE,
660                     Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE,
661                     user],
662             ServiceLiveData[
663                     AccessibilityService.SERVICE_INTERFACE,
664                     Manifest.permission.BIND_ACCESSIBILITY_SERVICE,
665                     user],
666             ServiceLiveData[
667                     WallpaperService.SERVICE_INTERFACE,
668                     Manifest.permission.BIND_WALLPAPER,
669                     user],
670             ServiceLiveData[
671                     VoiceInteractionService.SERVICE_INTERFACE,
672                     Manifest.permission.BIND_VOICE_INTERACTION,
673                     user],
674             ServiceLiveData[
675                     AttentionService.SERVICE_INTERFACE,
676                     Manifest.permission.BIND_ATTENTION_SERVICE,
677                     user],
678             ServiceLiveData[
679                     TextClassifierService.SERVICE_INTERFACE,
680                     Manifest.permission.BIND_TEXTCLASSIFIER_SERVICE,
681                     user],
682             ServiceLiveData[
683                     PrintService.SERVICE_INTERFACE,
684                     Manifest.permission.BIND_PRINT_SERVICE,
685                     user],
686             ServiceLiveData[
687                     DreamService.SERVICE_INTERFACE,
688                     Manifest.permission.BIND_DREAM_SERVICE,
689                     user],
690             ServiceLiveData[
691                     NetworkScoreManager.ACTION_RECOMMEND_NETWORKS,
692                     Manifest.permission.BIND_NETWORK_RECOMMENDATION_SERVICE,
693                     user],
694             ServiceLiveData[
695                     AutofillService.SERVICE_INTERFACE,
696                     Manifest.permission.BIND_AUTOFILL_SERVICE,
697                     user],
698             ServiceLiveData[
699                     AugmentedAutofillService.SERVICE_INTERFACE,
700                     Manifest.permission.BIND_AUGMENTED_AUTOFILL_SERVICE,
701                     user],
702             ServiceLiveData[
703                     DevicePolicyManager.ACTION_DEVICE_ADMIN_SERVICE,
704                     Manifest.permission.BIND_DEVICE_ADMIN,
705                     user],
706             BroadcastReceiverLiveData[
707                     DeviceAdminReceiver.ACTION_DEVICE_ADMIN_ENABLED,
708                     Manifest.permission.BIND_DEVICE_ADMIN,
709                     user]
710     )
711 
712     init {
<lambda>null713         serviceLiveDatas.forEach { addSource(it) { updateIfActive() } }
714     }
715 
onUpdatenull716     override fun onUpdate() {
717         if (serviceLiveDatas.all { it.isInitialized }) {
718             val pksToServices = mutableMapOf<String, MutableList<String>>()
719 
720             serviceLiveDatas.forEach { serviceLD ->
721                 serviceLD.value!!.forEach { packageName ->
722                     pksToServices.getOrPut(packageName, { mutableListOf() })
723                             .add((serviceLD as? HasIntentAction)?.intentAction ?: "???")
724                 }
725             }
726 
727             value = pksToServices
728         }
729     }
730 
731     /**
732      * Repository for ExemptServiceLiveData
733      *
734      * <p> Key value is user
735      */
736     companion object : DataRepositoryForPackage<UserHandle, ExemptServicesLiveData>() {
newValuenull737         override fun newValue(key: UserHandle): ExemptServicesLiveData {
738             return ExemptServicesLiveData(key)
739         }
740     }
741 }
742 
743 private data class TeamfoodSettings(
744     val enabledForPreRApps: Boolean,
745     val unusedThresholdMs: Long,
746     val checkFrequencyMs: Long
747 ) {
748     companion object {
749         private var cached: TeamfoodSettings? = null
750 
getnull751         fun get(context: Context): TeamfoodSettings? {
752             if (cached != null) return cached
753 
754             return Settings.Global.getString(context.contentResolver,
755                 "auto_revoke_parameters" /* Settings.Global.AUTO_REVOKE_PARAMETERS */)?.let { str ->
756 
757                 if (DEBUG) {
758                     DumpableLog.i(LOG_TAG, "Parsing teamfood setting value: $str")
759                 }
760                 str.split(",")
761                     .mapNotNull {
762                         val keyValue = it.split("=")
763                         keyValue.getOrNull(0)?.let { key ->
764                             key to keyValue.getOrNull(1)
765                         }
766                     }
767                     .toMap()
768                     .let { pairs ->
769                         TeamfoodSettings(
770                             enabledForPreRApps = pairs["enabledForPreRApps"] == "true",
771                             unusedThresholdMs =
772                                 pairs["unusedThresholdMs"]?.toLongOrNull()
773                                         ?: DEFAULT_UNUSED_THRESHOLD_MS,
774                             checkFrequencyMs = pairs["checkFrequencyMs"]?.toLongOrNull()
775                                     ?: DEFAULT_CHECK_FREQUENCY_MS)
776                     }
777             }.also {
778                 cached = it
779                 if (DEBUG) {
780                     Log.i(LOG_TAG, "Parsed teamfood setting value: $it")
781                 }
782             }
783         }
784     }
785 
786     /**
787      * @return team food settings for dumping as as a proto
788      */
dumpnull789     suspend fun dump(): TeamFoodSettingsProto {
790         return TeamFoodSettingsProto.newBuilder()
791                 .setEnabledForPreRApps(enabledForPreRApps)
792                 .setUnusedThresholdMillis(unusedThresholdMs)
793                 .setCheckFrequencyMillis(checkFrequencyMs)
794                 .build()
795     }
796 }
797 
798 /** Data interesting to auto-revoke */
799 private class AutoRevokeDumpLiveData(context: Context) :
800         SmartUpdateMediatorLiveData<AutoRevokeDumpLiveData.AutoRevokeDumpData>() {
801     /** All data */
802     data class AutoRevokeDumpData(
803         val users: List<AutoRevokeDumpUserData>
804     ) {
dumpUsersnull805         fun dumpUsers(): List<PerUserProto> {
806             return users.map { it.dump() }
807         }
808     }
809 
810     /** Per user data */
811     data class AutoRevokeDumpUserData(
812         val user: UserHandle,
813         val pkgs: List<AutoRevokeDumpPackageData>
814     ) {
dumpnull815         fun dump(): PerUserProto {
816             val dump = PerUserProto.newBuilder()
817                     .setUserId(user.identifier)
818 
819             pkgs.forEach { dump.addPackages(it.dump()) }
820 
821             return dump.build()
822         }
823     }
824 
825     /** Per package data */
826     data class AutoRevokeDumpPackageData(
827         val uid: Int,
828         val packageName: String,
829         val firstInstallTime: Long,
830         val lastTimeVisible: Long?,
831         val implementedServices: List<String>,
832         val groups: List<AutoRevokeDumpGroupData>
833     ) {
dumpnull834         fun dump(): PackageProto {
835             val dump = PackageProto.newBuilder()
836                     .setUid(uid)
837                     .setPackageName(packageName)
838                     .setFirstInstallTime(firstInstallTime)
839 
840             lastTimeVisible?.let { dump.lastTimeVisible = lastTimeVisible }
841 
842             implementedServices.forEach { dump.addImplementedServices(it) }
843 
844             groups.forEach { dump.addGroups(it.dump()) }
845 
846             return dump.build()
847         }
848     }
849 
850     /** Per permission group data */
851     data class AutoRevokeDumpGroupData(
852         val groupName: String,
853         val isFixed: Boolean,
854         val isAnyGrantedIncludingAppOp: Boolean,
855         val isGrantedByDefault: Boolean,
856         val isGrantedByRole: Boolean,
857         val isUserSensitive: Boolean,
858         val isAutoRevoked: Boolean
859     ) {
dumpnull860         fun dump(): PermissionGroupProto {
861             return PermissionGroupProto.newBuilder()
862                     .setGroupName(groupName)
863                     .setIsFixed(isFixed)
864                     .setIsAnyGrantedIncludingAppop(isAnyGrantedIncludingAppOp)
865                     .setIsGrantedByDefault(isGrantedByDefault)
866                     .setIsGrantedByRole(isGrantedByRole)
867                     .setIsUserSensitive(isUserSensitive)
868                     .setIsAutoRevoked(isAutoRevoked)
869                     .build()
870         }
871     }
872 
873     /** All users */
874     private val users = UsersLiveData
875 
876     /** Exempt services for each user: user -> services */
877     private var services: MutableMap<UserHandle, ExemptServicesLiveData>? = null
878 
879     /** Usage stats: user -> list<usages> */
880     private val usages = UsageStatsLiveData[
881         getUnusedThresholdMs(context),
882         if (DEBUG_OVERRIDE_THRESHOLDS) INTERVAL_DAILY else INTERVAL_MONTHLY
883     ]
884 
885     /** All package infos: user -> pkg **/
886     private val packages = AllPackageInfosLiveData
887 
888     /** Group names of revoked permission groups: (user, pkg-name) -> set<group-name> **/
889     private val revokedPermGroupNames = UnusedAutoRevokedPackagesLiveData
890 
891     /**
892      * Group names for packages
893      * map<user, pkg-name> -> list<perm-group-name>. {@code null} before step 1
894      */
895     private var pkgPermGroupNames:
896             MutableMap<Pair<UserHandle, String>, PackagePermissionsLiveData>? = null
897 
898     /**
899      * Group state for packages
900      * map<(user, pkg-name) -> map<perm-group-name -> group>>, value {@code null} before step 2
901      */
902     private val pkgPermGroups =
903             mutableMapOf<Pair<UserHandle, String>,
904                     MutableMap<String, LightAppPermGroupLiveData>?>()
905 
906     /** If this live-data currently inside onUpdate */
907     private var isUpdating = false
908 
<lambda>null909     init {
910         addSource(revokedPermGroupNames) {
911             updateIfActive()
912         }
913 
914         addSource(users) {
915             services?.values?.forEach { removeSource(it) }
916             services = null
917 
918             updateIfActive()
919         }
920 
921         addSource(usages) {
922             updateIfActive()
923         }
924 
925         addSource(packages) {
926             pkgPermGroupNames?.values?.forEach { removeSource(it) }
927             pkgPermGroupNames = null
928             pkgPermGroups.values.forEach { it?.values?.forEach { removeSource(it) } }
929 
930             updateIfActive()
931         }
932     }
933 
onUpdatenull934     override fun onUpdate() {
935         // If a source is already ready, the call onUpdate when added. Suppress this
936         if (isUpdating) {
937             return
938         }
939         isUpdating = true
940 
941         // services/autoRevokeManifestExemptPackages step 1, users is loaded, nothing else
942         if (users.isInitialized && services == null) {
943             services = mutableMapOf()
944 
945             for (user in users.value!!) {
946                 val newServices = ExemptServicesLiveData[user]
947                 services!![user] = newServices
948 
949                 addSource(newServices) {
950                     updateIfActive()
951                 }
952             }
953         }
954 
955         // pkgPermGroupNames step 1, packages is loaded, nothing else
956         if (packages.isInitialized && pkgPermGroupNames == null) {
957             pkgPermGroupNames = mutableMapOf()
958 
959             for ((user, userPkgs) in packages.value!!) {
960                 for (pkg in userPkgs) {
961                     val newPermGroupNames = PackagePermissionsLiveData[pkg.packageName, user]
962                     pkgPermGroupNames!![user to pkg.packageName] = newPermGroupNames
963 
964                     addSource(newPermGroupNames) {
965                         pkgPermGroups[user to pkg.packageName]?.forEach { removeSource(it.value) }
966                         pkgPermGroups.remove(user to pkg.packageName)
967 
968                         updateIfActive()
969                     }
970                 }
971             }
972         }
973 
974         // pkgPermGroupNames step 2, packages and pkgPermGroupNames are loaded, but pkgPermGroups
975         // are not loaded yet
976         if (packages.isInitialized && pkgPermGroupNames != null) {
977             for ((user, userPkgs) in packages.value!!) {
978                 for (pkg in userPkgs) {
979                     if (pkgPermGroupNames!![user to pkg.packageName]?.isInitialized == true &&
980                             pkgPermGroups[user to pkg.packageName] == null) {
981                         pkgPermGroups[user to pkg.packageName] = mutableMapOf()
982 
983                         for (groupName in
984                                 pkgPermGroupNames!![user to pkg.packageName]!!.value!!.keys) {
985                             if (groupName == PackagePermissionsLiveData.NON_RUNTIME_NORMAL_PERMS) {
986                                 continue
987                             }
988 
989                             val newPkgPermGroup = LightAppPermGroupLiveData[pkg.packageName,
990                                     groupName, user]
991 
992                             pkgPermGroups[user to pkg.packageName]!![groupName] = newPkgPermGroup
993 
994                             addSource(newPkgPermGroup) { updateIfActive() }
995                         }
996                     }
997                 }
998             }
999         }
1000 
1001         // Final step, everything is loaded, generate data
1002         if (packages.isInitialized && usages.isInitialized && revokedPermGroupNames.isInitialized &&
1003                 pkgPermGroupNames?.values?.all { it.isInitialized } == true &&
1004                 pkgPermGroupNames?.size == pkgPermGroups.size &&
1005                 pkgPermGroups.values.all { it?.values?.all { it.isInitialized } == true } &&
1006                 services?.values?.all { it.isInitialized } == true) {
1007             val users = mutableListOf<AutoRevokeDumpUserData>()
1008 
1009             for ((user, userPkgs) in packages.value!!) {
1010                 val pkgs = mutableListOf<AutoRevokeDumpPackageData>()
1011 
1012                 for (pkg in userPkgs) {
1013                     val groups = mutableListOf<AutoRevokeDumpGroupData>()
1014 
1015                     for (groupName in pkgPermGroupNames!![user to pkg.packageName]!!.value!!.keys) {
1016                         if (groupName == PackagePermissionsLiveData.NON_RUNTIME_NORMAL_PERMS) {
1017                             continue
1018                         }
1019 
1020                         pkgPermGroups[user to pkg.packageName]?.let {
1021                             it[groupName]?.value?.apply {
1022                                 groups.add(AutoRevokeDumpGroupData(groupName,
1023                                         isBackgroundFixed || isForegroundFixed,
1024                                         permissions.any { (_, p) -> p.isGrantedIncludingAppOp },
1025                                         isGrantedByDefault,
1026                                         isGrantedByRole,
1027                                         isUserSensitive,
1028                                     revokedPermGroupNames.value?.let {
1029                                         it[pkg.packageName to user]
1030                                             ?.contains(groupName)
1031                                     } == true
1032                                 ))
1033                             }
1034                         }
1035                     }
1036 
1037                     pkgs.add(AutoRevokeDumpPackageData(pkg.uid, pkg.packageName,
1038                             pkg.firstInstallTime,
1039                             usages.value!![user]?.lastTimeVisible(pkg.packageName),
1040                             services!![user]?.value!![pkg.packageName] ?: emptyList(),
1041                             groups))
1042                 }
1043 
1044                 users.add(AutoRevokeDumpUserData(user, pkgs))
1045             }
1046 
1047             value = AutoRevokeDumpData(users)
1048         }
1049 
1050         isUpdating = false
1051     }
1052 }
1053