diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2023-11-02 22:45:31 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2023-11-02 22:45:31 +0000 |
commit | 0200d6fbca83dca25736cdef8fa67f4c55552ae0 (patch) | |
tree | d4c2bbf2e2847a3569ebe3a3119ac5d6586eec16 /PermissionController/src/com/android | |
parent | ec92dfe7d68b2aec544825ebe61c45df98cae6b7 (diff) | |
parent | 17440e9d4d86da90a850ad04dbc41cb59d30cdec (diff) | |
download | Permission-0200d6fbca83dca25736cdef8fa67f4c55552ae0.tar.gz |
Snap for 11041982 from 17440e9d4d86da90a850ad04dbc41cb59d30cdec to mainline-uwb-releaseaml_uwb_341310300aml_uwb_341310030android14-mainline-uwb-release
Change-Id: If31740c980b44eae1671d1a80e4f088e8f9ef7e4
Diffstat (limited to 'PermissionController/src/com/android')
184 files changed, 6948 insertions, 5398 deletions
diff --git a/PermissionController/src/com/android/permissioncontroller/DumpableLog.kt b/PermissionController/src/com/android/permissioncontroller/DumpableLog.kt index bbce5bf5c..56682d018 100644 --- a/PermissionController/src/com/android/permissioncontroller/DumpableLog.kt +++ b/PermissionController/src/com/android/permissioncontroller/DumpableLog.kt @@ -20,9 +20,7 @@ import android.util.Log import com.android.permissioncontroller.Constants.LOGS_TO_DUMP_FILE import java.io.File -/** - * Like {@link Log} but stores the logs in a file which can later be dumped via {@link #dump} - */ +/** Like {@link Log} but stores the logs in a file which can later be dumped via {@link #dump} */ object DumpableLog { private const val MAX_FILE_SIZE = 64 * 1024 @@ -33,41 +31,31 @@ object DumpableLog { file.createNewFile() } - /** - * Equivalent to {@link Log.v} - */ + /** Equivalent to {@link Log.v} */ fun v(tag: String, message: String, exception: Throwable? = null) { Log.v(tag, message, exception) addLogToDump("v", tag, message, exception) } - /** - * Equivalent to {@link Log.d} - */ + /** Equivalent to {@link Log.d} */ fun d(tag: String, message: String, exception: Throwable? = null) { Log.d(tag, message, exception) addLogToDump("d", tag, message, exception) } - /** - * Equivalent to {@link Log.i} - */ + /** Equivalent to {@link Log.i} */ fun i(tag: String, message: String, exception: Throwable? = null) { Log.i(tag, message, exception) addLogToDump("i", tag, message, exception) } - /** - * Equivalent to {@link Log.w} - */ + /** Equivalent to {@link Log.w} */ fun w(tag: String, message: String, exception: Throwable? = null) { Log.w(tag, message, exception) addLogToDump("w", tag, message, exception) } - /** - * Equivalent to {@link Log.e} - */ + /** Equivalent to {@link Log.e} */ fun e(tag: String, message: String, exception: Throwable? = null) { Log.e(tag, message, exception) addLogToDump("e", tag, message, exception) @@ -83,17 +71,17 @@ object DumpableLog { dump.subList(dump.size / 2, dump.size).forEach { file.appendText(it + "\n") } } - file.appendText("${System.currentTimeMillis()} $tag:$level $message " + - "${exception?.let { it.message + Log.getStackTraceString(it) } ?: ""}\n") + file.appendText( + "${System.currentTimeMillis()} $tag:$level $message " + + "${exception?.let { it.message + Log.getStackTraceString(it) } ?: ""}\n" + ) } } - /** - * @return the previously logged entries - */ + /** @return the previously logged entries */ suspend fun get(): List<String> { synchronized(lock) { return file.readLines() } } -}
\ No newline at end of file +} diff --git a/PermissionController/src/com/android/permissioncontroller/auto/DrivingDecisionReminderService.kt b/PermissionController/src/com/android/permissioncontroller/auto/DrivingDecisionReminderService.kt index b62f8d721..b2cf75fed 100644 --- a/PermissionController/src/com/android/permissioncontroller/auto/DrivingDecisionReminderService.kt +++ b/PermissionController/src/com/android/permissioncontroller/auto/DrivingDecisionReminderService.kt @@ -55,9 +55,7 @@ import java.util.Random */ class DrivingDecisionReminderService : Service() { - /** - * Information needed to show a reminder about a permission decisions. - */ + /** Information needed to show a reminder about a permission decisions. */ data class PermissionReminder( val packageName: String, val permissionGroup: String, @@ -109,22 +107,26 @@ class DrivingDecisionReminderService : Service() { packageName: String, permGroupName: String ) { - Car.createCar( - context, - /* handler= */ null, - Car.CAR_WAIT_TIMEOUT_DO_NOT_WAIT) { car: Car, ready: Boolean -> + Car.createCar(context, /* handler= */ null, Car.CAR_WAIT_TIMEOUT_DO_NOT_WAIT) { + car: Car, + ready: Boolean -> // just give up if we can't connect to the car if (ready) { - val restrictionsManager = car.getCarManager( - Car.CAR_UX_RESTRICTION_SERVICE) as CarUxRestrictionsManager - if (restrictionsManager.currentCarUxRestrictions - .isRequiresDistractionOptimization) { + val restrictionsManager = + car.getCarManager(Car.CAR_UX_RESTRICTION_SERVICE) + as CarUxRestrictionsManager + if ( + restrictionsManager.currentCarUxRestrictions + .isRequiresDistractionOptimization + ) { context.startService( createIntent( context, packageName, permGroupName, - Process.myUserHandle())) + Process.myUserHandle() + ) + ) } } car.disconnect() @@ -156,28 +158,32 @@ class DrivingDecisionReminderService : Service() { } private fun scheduleNotificationForUnrestrictedState() { - Car.createCar(this, null, - Car.CAR_WAIT_TIMEOUT_DO_NOT_WAIT - ) { createdCar: Car?, ready: Boolean -> + Car.createCar(this, null, Car.CAR_WAIT_TIMEOUT_DO_NOT_WAIT) { + createdCar: Car?, + ready: Boolean -> car = createdCar if (ready) { onCarReady() } else { - DumpableLog.w(LOG_TAG, - "Car service disconnected, no notification will be scheduled") + DumpableLog.w( + LOG_TAG, + "Car service disconnected, no notification will be scheduled" + ) stopSelf() } } } private fun onCarReady() { - carUxRestrictionsManager = car?.getCarManager( - Car.CAR_UX_RESTRICTION_SERVICE) as CarUxRestrictionsManager + carUxRestrictionsManager = + car?.getCarManager(Car.CAR_UX_RESTRICTION_SERVICE) as CarUxRestrictionsManager DumpableLog.d(LOG_TAG, "Registering UX restriction listener") carUxRestrictionsManager?.registerListener { restrictions -> if (!restrictions.isRequiresDistractionOptimization) { - DumpableLog.d(LOG_TAG, - "UX restrictions no longer required - showing reminder notification") + DumpableLog.d( + LOG_TAG, + "UX restrictions no longer required - showing reminder notification" + ) showRecentGrantDecisionsPostDriveNotification() stopSelf() } @@ -185,10 +191,12 @@ class DrivingDecisionReminderService : Service() { } private fun parseStartIntent(intent: Intent?): PermissionReminder? { - if (intent == null || - !intent.hasExtra(EXTRA_PACKAGE_NAME) || - !intent.hasExtra(EXTRA_PERMISSION_GROUP) || - !intent.hasExtra(EXTRA_USER)) { + if ( + intent == null || + !intent.hasExtra(EXTRA_PACKAGE_NAME) || + !intent.hasExtra(EXTRA_PERMISSION_GROUP) || + !intent.hasExtra(EXTRA_USER) + ) { DumpableLog.e(LOG_TAG, "Missing extras from intent $intent") return null } @@ -202,21 +210,25 @@ class DrivingDecisionReminderService : Service() { fun showRecentGrantDecisionsPostDriveNotification() { val notificationManager = getSystemService(NotificationManager::class.java)!! - val permissionReminderChannel = NotificationChannel( - Constants.PERMISSION_REMINDER_CHANNEL_ID, getString(R.string.permission_reminders), - NotificationManager.IMPORTANCE_HIGH) + val permissionReminderChannel = + NotificationChannel( + Constants.PERMISSION_REMINDER_CHANNEL_ID, + getString(R.string.permission_reminders), + NotificationManager.IMPORTANCE_HIGH + ) notificationManager.createNotificationChannel(permissionReminderChannel) - notificationManager.notify(DrivingDecisionReminderService::class.java.simpleName, + notificationManager.notify( + DrivingDecisionReminderService::class.java.simpleName, Constants.PERMISSION_DECISION_REMINDER_NOTIFICATION_ID, - createNotification(createNotificationTitle(), createNotificationContent())) + createNotification(createNotificationTitle(), createNotificationContent()) + ) logNotificationPresented() } private fun createNotificationTitle(): String { - return applicationContext - .getString(R.string.post_drive_permission_decision_reminder_title) + return applicationContext.getString(R.string.post_drive_permission_decision_reminder_title) } @VisibleForTesting @@ -224,76 +236,106 @@ class DrivingDecisionReminderService : Service() { val packageLabels: MutableList<String> = mutableListOf() val permissionGroupNames: MutableList<String> = mutableListOf() for (permissionReminder in permissionReminders) { - val packageLabel = getLabelForPackage(permissionReminder.packageName, - permissionReminder.user) - val permissionGroupLabel = getPermGroupLabel(applicationContext, - permissionReminder.permissionGroup).toString() + val packageLabel = + getLabelForPackage(permissionReminder.packageName, permissionReminder.user) + val permissionGroupLabel = + getPermGroupLabel(applicationContext, permissionReminder.permissionGroup).toString() packageLabels.add(packageLabel) permissionGroupNames.add(permissionGroupLabel) } val packageLabelsDistinct = packageLabels.distinct() val permissionGroupNamesDistinct = permissionGroupNames.distinct() return if (packageLabelsDistinct.size > 1) { - StringUtils.getIcuPluralsString(applicationContext, + StringUtils.getIcuPluralsString( + applicationContext, R.string.post_drive_permission_decision_reminder_summary_multi_apps, - (packageLabels.size - 1), packageLabelsDistinct[0]) + (packageLabels.size - 1), + packageLabelsDistinct[0] + ) } else if (permissionGroupNamesDistinct.size == 2) { getString( R.string.post_drive_permission_decision_reminder_summary_1_app_2_permissions, - packageLabelsDistinct[0], permissionGroupNamesDistinct[0], - permissionGroupNamesDistinct[1]) + packageLabelsDistinct[0], + permissionGroupNamesDistinct[0], + permissionGroupNamesDistinct[1] + ) } else if (permissionGroupNamesDistinct.size > 2) { getString( R.string.post_drive_permission_decision_reminder_summary_1_app_multi_permission, - permissionGroupNamesDistinct.size, packageLabelsDistinct[0]) + permissionGroupNamesDistinct.size, + packageLabelsDistinct[0] + ) } else { getString( R.string.post_drive_permission_decision_reminder_summary_1_app_1_permission, - packageLabelsDistinct[0], permissionGroupNamesDistinct[0]) + packageLabelsDistinct[0], + permissionGroupNamesDistinct[0] + ) } } @VisibleForTesting fun getLabelForPackage(packageName: String, user: UserHandle): String { - return BidiFormatter.getInstance().unicodeWrap( - getPackageLabel(application, packageName, user)) + return BidiFormatter.getInstance() + .unicodeWrap(getPackageLabel(application, packageName, user)) } private fun createNotification(title: String, body: String): Notification { - val clickIntent = Intent(PermissionManager.ACTION_REVIEW_PERMISSION_DECISIONS).apply { - putExtra(Constants.EXTRA_SESSION_ID, sessionId) - putExtra(AutoReviewPermissionDecisionsFragment.EXTRA_SOURCE, - AutoReviewPermissionDecisionsFragment.EXTRA_SOURCE_NOTIFICATION) - flags = Intent.FLAG_ACTIVITY_NEW_TASK - } - val pendingIntent = PendingIntent.getActivity(this, 0, clickIntent, - PendingIntent.FLAG_ONE_SHOT or PendingIntent.FLAG_UPDATE_CURRENT or - PendingIntent.FLAG_IMMUTABLE) + val clickIntent = + Intent(PermissionManager.ACTION_REVIEW_PERMISSION_DECISIONS).apply { + putExtra(Constants.EXTRA_SESSION_ID, sessionId) + putExtra( + AutoReviewPermissionDecisionsFragment.EXTRA_SOURCE, + AutoReviewPermissionDecisionsFragment.EXTRA_SOURCE_NOTIFICATION + ) + flags = Intent.FLAG_ACTIVITY_NEW_TASK + } + val pendingIntent = + PendingIntent.getActivity( + this, + 0, + clickIntent, + PendingIntent.FLAG_ONE_SHOT or + PendingIntent.FLAG_UPDATE_CURRENT or + PendingIntent.FLAG_IMMUTABLE + ) - val settingsDrawable = KotlinUtils.getBadgedPackageIcon( - application, - getSettingsPackageName(applicationContext.packageManager), - permissionReminders.first().user) - val settingsIcon = if (settingsDrawable != null) { - KotlinUtils.convertToBitmap(settingsDrawable) - } else { - null - } + val settingsDrawable = + KotlinUtils.getBadgedPackageIcon( + application, + getSettingsPackageName(applicationContext.packageManager), + permissionReminders.first().user + ) + val settingsIcon = + if (settingsDrawable != null) { + KotlinUtils.convertToBitmap(settingsDrawable) + } else { + null + } - val b = Notification.Builder(this, Constants.PERMISSION_REMINDER_CHANNEL_ID) - .setContentTitle(title) - .setContentText(body) - .setSmallIcon(R.drawable.ic_settings_24dp) - .setLargeIcon(settingsIcon) - .setColor(getColor(android.R.color.system_notification_accent_color)) - .setAutoCancel(true) - .setContentIntent(pendingIntent) - .addExtras(Bundle().apply { - putBoolean("com.android.car.notification.EXTRA_USE_LAUNCHER_ICON", false) - }) - // Auto doesn't show icons for actions - .addAction(Notification.Action.Builder(/* icon= */ null, - getString(R.string.go_to_settings), pendingIntent).build()) + val b = + Notification.Builder(this, Constants.PERMISSION_REMINDER_CHANNEL_ID) + .setContentTitle(title) + .setContentText(body) + .setSmallIcon(R.drawable.ic_settings_24dp) + .setLargeIcon(settingsIcon) + .setColor(getColor(android.R.color.system_notification_accent_color)) + .setAutoCancel(true) + .setContentIntent(pendingIntent) + .addExtras( + Bundle().apply { + putBoolean("com.android.car.notification.EXTRA_USE_LAUNCHER_ICON", false) + } + ) + // Auto doesn't show icons for actions + .addAction( + Notification.Action.Builder( + /* icon= */ null, + getString(R.string.go_to_settings), + pendingIntent + ) + .build() + ) Utils.getSettingsLabelForNotifications(applicationContext.packageManager)?.let { label -> val extras = Bundle() extras.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME, label.toString()) @@ -311,6 +353,8 @@ class DrivingDecisionReminderService : Service() { private fun logNotificationPresented() { PermissionControllerStatsLog.write( PermissionControllerStatsLog.PERMISSION_REMINDER_NOTIFICATION_INTERACTED, - sessionId, PERMISSION_REMINDER_NOTIFICATION_INTERACTED__RESULT__NOTIFICATION_PRESENTED) + sessionId, + PERMISSION_REMINDER_NOTIFICATION_INTERACTED__RESULT__NOTIFICATION_PRESENTED + ) } -}
\ No newline at end of file +} diff --git a/PermissionController/src/com/android/permissioncontroller/hibernation/HibernationPolicy.kt b/PermissionController/src/com/android/permissioncontroller/hibernation/HibernationPolicy.kt index 6e901fa26..6aa5d0f12 100644 --- a/PermissionController/src/com/android/permissioncontroller/hibernation/HibernationPolicy.kt +++ b/PermissionController/src/com/android/permissioncontroller/hibernation/HibernationPolicy.kt @@ -114,26 +114,31 @@ import kotlinx.coroutines.launch private const val LOG_TAG = "HibernationPolicy" const val DEBUG_OVERRIDE_THRESHOLDS = false -// TODO eugenesusla: temporarily enabled for extra logs during dogfooding -const val DEBUG_HIBERNATION_POLICY = true || DEBUG_OVERRIDE_THRESHOLDS +const val DEBUG_HIBERNATION_POLICY = false private var SKIP_NEXT_RUN = false private val DEFAULT_UNUSED_THRESHOLD_MS = TimeUnit.DAYS.toMillis(90) -fun getUnusedThresholdMs() = when { - DEBUG_OVERRIDE_THRESHOLDS -> TimeUnit.SECONDS.toMillis(1) - else -> DeviceConfig.getLong(DeviceConfig.NAMESPACE_PERMISSIONS, - Utils.PROPERTY_HIBERNATION_UNUSED_THRESHOLD_MILLIS, - DEFAULT_UNUSED_THRESHOLD_MS) -} +fun getUnusedThresholdMs() = + when { + DEBUG_OVERRIDE_THRESHOLDS -> TimeUnit.SECONDS.toMillis(1) + else -> + DeviceConfig.getLong( + DeviceConfig.NAMESPACE_PERMISSIONS, + Utils.PROPERTY_HIBERNATION_UNUSED_THRESHOLD_MILLIS, + DEFAULT_UNUSED_THRESHOLD_MS + ) + } private val DEFAULT_CHECK_FREQUENCY_MS = TimeUnit.DAYS.toMillis(15) -private fun getCheckFrequencyMs() = DeviceConfig.getLong( - DeviceConfig.NAMESPACE_PERMISSIONS, +private fun getCheckFrequencyMs() = + DeviceConfig.getLong( + DeviceConfig.NAMESPACE_PERMISSIONS, Utils.PROPERTY_HIBERNATION_CHECK_FREQUENCY_MILLIS, - DEFAULT_CHECK_FREQUENCY_MS) + DEFAULT_CHECK_FREQUENCY_MS + ) // Intentionally kept value of the key same as before because we want to continue reading value of // this shared preference stored by previous versions of PermissionController @@ -150,8 +155,11 @@ val ONE_DAY_MS = TimeUnit.DAYS.toMillis(1) fun isHibernationEnabled(): Boolean { return SdkLevel.isAtLeastS() && - DeviceConfig.getBoolean(NAMESPACE_APP_HIBERNATION, Utils.PROPERTY_APP_HIBERNATION_ENABLED, - true /* defaultValue */) + DeviceConfig.getBoolean( + NAMESPACE_APP_HIBERNATION, + Utils.PROPERTY_APP_HIBERNATION_ENABLED, + true /* defaultValue */ + ) } /** @@ -159,30 +167,33 @@ fun isHibernationEnabled(): Boolean { * [isHibernationEnabled] is false. */ fun hibernationTargetsPreSApps(): Boolean { - return DeviceConfig.getBoolean(NAMESPACE_APP_HIBERNATION, + return DeviceConfig.getBoolean( + NAMESPACE_APP_HIBERNATION, Utils.PROPERTY_HIBERNATION_TARGETS_PRE_S_APPS, - false /* defaultValue */) + false /* defaultValue */ + ) } @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codename = "UpsideDownCake") fun isSystemExemptFromHibernationEnabled(): Boolean { - return SdkLevel.isAtLeastU() && DeviceConfig.getBoolean(NAMESPACE_APP_HIBERNATION, + return SdkLevel.isAtLeastU() && + DeviceConfig.getBoolean( + NAMESPACE_APP_HIBERNATION, Utils.PROPERTY_SYSTEM_EXEMPT_HIBERNATION_ENABLED, - true /* defaultValue */) + true /* defaultValue */ + ) } -/** - * Remove the unused apps notification. - */ +/** Remove the unused apps notification. */ fun cancelUnusedAppsNotification(context: Context) { - context.getSystemService(NotificationManager::class.java)!!.cancel( - HibernationJobService::class.java.simpleName, - Constants.UNUSED_APPS_NOTIFICATION_ID) + context + .getSystemService(NotificationManager::class.java)!! + .cancel(HibernationJobService::class.java.simpleName, Constants.UNUSED_APPS_NOTIFICATION_ID) } /** - * Checks if we need to show the safety center card and sends the appropriate source data. If - * the user has not reviewed the latest auto-revoked apps, we show the card. Otherwise, we ensure + * Checks if we need to show the safety center card and sends the appropriate source data. If the + * user has not reviewed the latest auto-revoked apps, we show the card. Otherwise, we ensure * nothing is shown. */ @RequiresApi(Build.VERSION_CODES.TIRAMISU) @@ -194,36 +205,40 @@ fun rescanAndPushDataToSafetyCenter( val safetyCenterManager: SafetyCenterManager = context.getSystemService(SafetyCenterManager::class.java)!! if (getUnusedAppsReviewNeeded(context)) { - val seeUnusedAppsAction = Action.Builder( - Constants.UNUSED_APPS_SAFETY_CENTER_SEE_UNUSED_APPS_ID, - context.getString(R.string.unused_apps_safety_center_action_title), - makeUnusedAppsIntent(context, sessionId)) - .build() - - val issue = SafetySourceIssue.Builder( - Constants.UNUSED_APPS_SAFETY_CENTER_ISSUE_ID, - context.getString(R.string.unused_apps_safety_center_card_title), - context.getString(R.string.unused_apps_safety_center_card_content), - SafetySourceData.SEVERITY_LEVEL_INFORMATION, - Constants.UNUSED_APPS_SAFETY_CENTER_ISSUE_ID) - .addAction(seeUnusedAppsAction) - .setOnDismissPendingIntent(makeDismissIntent(context, sessionId)) - .setIssueCategory(SafetySourceIssue.ISSUE_CATEGORY_DEVICE) - .build() - - val safetySourceData = SafetySourceData.Builder() - .addIssue(issue) - .build() + val seeUnusedAppsAction = + Action.Builder( + Constants.UNUSED_APPS_SAFETY_CENTER_SEE_UNUSED_APPS_ID, + context.getString(R.string.unused_apps_safety_center_action_title), + makeUnusedAppsIntent(context, sessionId) + ) + .build() + + val issue = + SafetySourceIssue.Builder( + Constants.UNUSED_APPS_SAFETY_CENTER_ISSUE_ID, + context.getString(R.string.unused_apps_safety_center_card_title), + context.getString(R.string.unused_apps_safety_center_card_content), + SafetySourceData.SEVERITY_LEVEL_INFORMATION, + Constants.UNUSED_APPS_SAFETY_CENTER_ISSUE_ID + ) + .addAction(seeUnusedAppsAction) + .setOnDismissPendingIntent(makeDismissIntent(context, sessionId)) + .setIssueCategory(SafetySourceIssue.ISSUE_CATEGORY_DEVICE) + .build() + + val safetySourceData = SafetySourceData.Builder().addIssue(issue).build() safetyCenterManager.setSafetySourceData( Constants.UNUSED_APPS_SAFETY_CENTER_SOURCE_ID, safetySourceData, - safetyEvent) + safetyEvent + ) } else { safetyCenterManager.setSafetySourceData( Constants.UNUSED_APPS_SAFETY_CENTER_SOURCE_ID, /* safetySourceData= */ null, - safetyEvent) + safetyEvent + ) } } @@ -232,8 +247,10 @@ fun rescanAndPushDataToSafetyCenter( */ fun setUnusedAppsReviewNeeded(context: Context, needsReview: Boolean) { val sharedPreferences = context.sharedPreferences - if (sharedPreferences.contains(PREF_KEY_UNUSED_APPS_REVIEW) && - sharedPreferences.getBoolean(PREF_KEY_UNUSED_APPS_REVIEW, false) == needsReview) { + if ( + sharedPreferences.contains(PREF_KEY_UNUSED_APPS_REVIEW) && + sharedPreferences.getBoolean(PREF_KEY_UNUSED_APPS_REVIEW, false) == needsReview + ) { return } sharedPreferences.edit().putBoolean(PREF_KEY_UNUSED_APPS_REVIEW, needsReview).apply() @@ -246,10 +263,10 @@ private fun getUnusedAppsReviewNeeded(context: Context): Boolean { /** * Receiver of the following broadcasts: * <ul> - * <li> {@link Intent.ACTION_BOOT_COMPLETED} - * <li> {@link #ACTION_SET_UP_HIBERNATION} - * <li> {@link Intent.ACTION_TIME_CHANGED} - * <li> {@link Intent.ACTION_TIMEZONE_CHANGED} + * <li> {@link Intent.ACTION_BOOT_COMPLETED} + * <li> {@link #ACTION_SET_UP_HIBERNATION} + * <li> {@link Intent.ACTION_TIME_CHANGED} + * <li> {@link Intent.ACTION_TIMEZONE_CHANGED} * </ul> */ class HibernationBroadcastReceiver : BroadcastReceiver() { @@ -258,9 +275,12 @@ class HibernationBroadcastReceiver : BroadcastReceiver() { val action = intent.action if (action == Intent.ACTION_BOOT_COMPLETED || action == ACTION_SET_UP_HIBERNATION) { if (DEBUG_HIBERNATION_POLICY) { - DumpableLog.i(LOG_TAG, "scheduleHibernationJob " + - "with frequency ${getCheckFrequencyMs()}ms " + - "and threshold ${getUnusedThresholdMs()}ms") + DumpableLog.i( + LOG_TAG, + "scheduleHibernationJob " + + "with frequency ${getCheckFrequencyMs()}ms " + + "and threshold ${getUnusedThresholdMs()}ms" + ) } initStartTimeOfUnusedAppTracking(context.sharedPreferences) @@ -269,32 +289,40 @@ class HibernationBroadcastReceiver : BroadcastReceiver() { // primary user if (isProfile(context)) { if (DEBUG_HIBERNATION_POLICY) { - DumpableLog.i(LOG_TAG, - "user ${Process.myUserHandle().identifier} is a profile." + - " Not running hibernation job.") + DumpableLog.i( + LOG_TAG, + "user ${Process.myUserHandle().identifier} is a profile." + + " Not running hibernation job." + ) } return } else if (DEBUG_HIBERNATION_POLICY) { - DumpableLog.i(LOG_TAG, - "user ${Process.myUserHandle().identifier} is a profile" + - "owner. Running hibernation job.") + DumpableLog.i( + LOG_TAG, + "user ${Process.myUserHandle().identifier} is a profile" + + "owner. Running hibernation job." + ) } if (isNewJobScheduleRequired(context)) { // periodic jobs normally run immediately, which is unnecessarily premature SKIP_NEXT_RUN = true - val jobInfo = JobInfo.Builder( - Constants.HIBERNATION_JOB_ID, - ComponentName(context, HibernationJobService::class.java)) - .setPeriodic(getCheckFrequencyMs()) - // persist this job across boots - .setPersisted(true) - .build() - val status = - context.getSystemService(JobScheduler::class.java)!!.schedule(jobInfo) + val jobInfo = + JobInfo.Builder( + Constants.HIBERNATION_JOB_ID, + ComponentName(context, HibernationJobService::class.java) + ) + .setPeriodic(getCheckFrequencyMs()) + // persist this job across boots + .setPersisted(true) + .build() + val status = context.getSystemService(JobScheduler::class.java)!!.schedule(jobInfo) if (status != JobScheduler.RESULT_SUCCESS) { - DumpableLog.e(LOG_TAG, "Could not schedule " + - "${HibernationJobService::class.java.simpleName}: $status") + DumpableLog.e( + LOG_TAG, + "Could not schedule " + + "${HibernationJobService::class.java.simpleName}: $status" + ) } } } @@ -319,8 +347,10 @@ class HibernationBroadcastReceiver : BroadcastReceiver() { private fun isNewJobScheduleRequired(context: Context): Boolean { // check if the job is already scheduled or needs a change var scheduleNewJob = false - val existingJob: JobInfo? = context.getSystemService(JobScheduler::class.java)!! - .getPendingJob(Constants.HIBERNATION_JOB_ID) + val existingJob: JobInfo? = + context + .getSystemService(JobScheduler::class.java)!! + .getPendingJob(Constants.HIBERNATION_JOB_ID) if (existingJob == null) { if (DEBUG_HIBERNATION_POLICY) { DumpableLog.i(LOG_TAG, "No existing job, scheduling a new one") @@ -351,19 +381,24 @@ private suspend fun getAppsToHibernate( val startTimeOfUnusedAppTracking = getStartTimeOfUnusedAppTracking(context.sharedPreferences) val allPackagesByUser = AllPackageInfosLiveData.getInitializedValue(forceUpdate = true) - val allPackagesByUserByUid = allPackagesByUser.mapValues { (_, pkgs) -> - pkgs.groupBy { pkg -> pkg.uid } - } + val allPackagesByUserByUid = + allPackagesByUser.mapValues { (_, pkgs) -> pkgs.groupBy { pkg -> pkg.uid } } val unusedApps = allPackagesByUser.toMutableMap() - val userStats = UsageStatsLiveData[getUnusedThresholdMs(), - if (DEBUG_OVERRIDE_THRESHOLDS) INTERVAL_DAILY else INTERVAL_MONTHLY].getInitializedValue() + val userStats = + UsageStatsLiveData[ + getUnusedThresholdMs(), + if (DEBUG_OVERRIDE_THRESHOLDS) INTERVAL_DAILY else INTERVAL_MONTHLY] + .getInitializedValue() if (DEBUG_HIBERNATION_POLICY) { for ((user, stats) in userStats) { - DumpableLog.i(LOG_TAG, "Usage stats for user ${user.identifier}: " + - stats.map { stat -> - stat.packageName to Date(stat.lastTimePackageUsed()) - }.toMap()) + DumpableLog.i( + LOG_TAG, + "Usage stats for user ${user.identifier}: " + + stats + .map { stat -> stat.packageName to Date(stat.lastTimePackageUsed()) } + .toMap() + ) } } for (user in unusedApps.keys.toList()) { @@ -378,42 +413,52 @@ private suspend fun getAppsToHibernate( for ((user, stats) in userStats) { var unusedUserApps = unusedApps[user] ?: continue - unusedUserApps = unusedUserApps.filter { packageInfo -> - val pkgName = packageInfo.packageName - - val uidPackages = allPackagesByUserByUid[user]!![packageInfo.uid] - ?.map { info -> info.packageName } ?: emptyList() - if (pkgName !in uidPackages) { - Log.wtf(LOG_TAG, "Package $pkgName not among packages for " + - "its uid ${packageInfo.uid}: $uidPackages") - } - var lastTimePkgUsed: Long = stats.lastTimePackageUsed(uidPackages) - - // Limit by install time - lastTimePkgUsed = Math.max(lastTimePkgUsed, packageInfo.firstInstallTime) - - // Limit by first boot time - lastTimePkgUsed = Math.max(lastTimePkgUsed, startTimeOfUnusedAppTracking) + unusedUserApps = + unusedUserApps.filter { packageInfo -> + val pkgName = packageInfo.packageName - // Handle cross-profile apps - if (context.isPackageCrossProfile(pkgName)) { - for ((otherUser, otherStats) in userStats) { - if (otherUser == user) { - continue + val uidPackages = + allPackagesByUserByUid[user]!![packageInfo.uid]?.map { info -> + info.packageName + } + ?: emptyList() + if (pkgName !in uidPackages) { + Log.wtf( + LOG_TAG, + "Package $pkgName not among packages for " + + "its uid ${packageInfo.uid}: $uidPackages" + ) + } + var lastTimePkgUsed: Long = stats.lastTimePackageUsed(uidPackages) + + // Limit by install time + lastTimePkgUsed = Math.max(lastTimePkgUsed, packageInfo.firstInstallTime) + + // Limit by first boot time + lastTimePkgUsed = Math.max(lastTimePkgUsed, startTimeOfUnusedAppTracking) + + // Handle cross-profile apps + if (context.isPackageCrossProfile(pkgName)) { + for ((otherUser, otherStats) in userStats) { + if (otherUser == user) { + continue + } + lastTimePkgUsed = + maxOf(lastTimePkgUsed, otherStats.lastTimePackageUsed(pkgName)) } - lastTimePkgUsed = - maxOf(lastTimePkgUsed, otherStats.lastTimePackageUsed(pkgName)) } - } - // Threshold check - whether app is unused - now - lastTimePkgUsed > getUnusedThresholdMs() - } + // Threshold check - whether app is unused + now - lastTimePkgUsed > getUnusedThresholdMs() + } unusedApps[user] = unusedUserApps if (DEBUG_HIBERNATION_POLICY) { - DumpableLog.i(LOG_TAG, "Unused apps for user ${user.identifier}: " + - "${unusedUserApps.map { it.packageName }}") + DumpableLog.i( + LOG_TAG, + "Unused apps for user ${user.identifier}: " + + "${unusedUserApps.map { it.packageName }}" + ) } } @@ -435,25 +480,29 @@ private suspend fun getAppsToHibernate( } val packageName = pkg.packageName - val packageImportance = context - .getSystemService(ActivityManager::class.java)!! - .getPackageImportance(packageName) + val packageImportance = + context + .getSystemService(ActivityManager::class.java)!! + .getPackageImportance(packageName) if (packageImportance <= IMPORTANCE_CANT_SAVE_STATE) { // Process is running in a state where it should not be killed - DumpableLog.i(LOG_TAG, + DumpableLog.i( + LOG_TAG, "Skipping hibernation - $packageName running with importance " + - "$packageImportance") + "$packageImportance" + ) return@forEachInParallel } if (DEBUG_HIBERNATION_POLICY) { - DumpableLog.i(LOG_TAG, "unused app $packageName - last used on " + - userStats[user]?.lastTimePackageUsed(packageName)?.let(::Date)) + DumpableLog.i( + LOG_TAG, + "unused app $packageName - last used on " + + userStats[user]?.lastTimePackageUsed(packageName)?.let(::Date) + ) } - synchronized(userAppsToHibernate) { - userAppsToHibernate.add(pkg) - } + synchronized(userAppsToHibernate) { userAppsToHibernate.add(pkg) } } appsToHibernate.put(user, userAppsToHibernate) } @@ -461,9 +510,9 @@ private suspend fun getAppsToHibernate( } /** - * Gets the last time we consider the package used based off its usage stats. On pre-S devices - * this looks at last time visible which tracks explicit usage. In S, we add component usage - * which tracks various forms of implicit usage (e.g. service bindings). + * Gets the last time we consider the package used based off its usage stats. On pre-S devices this + * looks at last time visible which tracks explicit usage. In S, we add component usage which tracks + * various forms of implicit usage (e.g. service bindings). */ fun UsageStats.lastTimePackageUsed(): Long { var lastTimePkgUsed = this.lastTimeVisible @@ -487,9 +536,7 @@ private fun List<UsageStats>.lastTimePackageUsed(pkgName: String): Long { return lastTimePackageUsed(listOf(pkgName)) } -/** - * Checks if the given package is exempt from hibernation in a way that's not user-overridable - */ +/** Checks if the given package is exempt from hibernation in a way that's not user-overridable */ suspend fun isPackageHibernationExemptBySystem( pkg: LightPackageInfo, user: UserHandle, @@ -500,23 +547,22 @@ suspend fun isPackageHibernationExemptBySystem( } return true } - if (!ExemptServicesLiveData[user] - .getInitializedValue()[pkg.packageName] - .isNullOrEmpty()) { + if (!ExemptServicesLiveData[user].getInitializedValue()[pkg.packageName].isNullOrEmpty()) { return true } if (Utils.isUserDisabledOrWorkProfile(user)) { if (DEBUG_HIBERNATION_POLICY) { - DumpableLog.i(LOG_TAG, - "Exempted ${pkg.packageName} - $user is disabled or a work profile") + DumpableLog.i( + LOG_TAG, + "Exempted ${pkg.packageName} - $user is disabled or a work profile" + ) } return true } - if (pkg.uid == Process.SYSTEM_UID){ + if (pkg.uid == Process.SYSTEM_UID) { if (DEBUG_HIBERNATION_POLICY) { - DumpableLog.i(LOG_TAG, - "Exempted ${pkg.packageName} - Package shares system uid") + DumpableLog.i(LOG_TAG, "Exempted ${pkg.packageName} - Package shares system uid") } return true } @@ -524,8 +570,8 @@ suspend fun isPackageHibernationExemptBySystem( val context = PermissionControllerApplication.get() if (context.getSystemService(DevicePolicyManager::class.java)!!.isDeviceManaged) { // TODO(b/237065504): Use proper system API to check if the device is financed in U. - val isFinancedDevice = Settings.Global.getInt( - context.contentResolver, "device_owner_type", 0) == 1 + val isFinancedDevice = + Settings.Global.getInt(context.contentResolver, "device_owner_type", 0) == 1 if (!isFinancedDevice) { if (DEBUG_HIBERNATION_POLICY) { DumpableLog.i(LOG_TAG, "Exempted ${pkg.packageName} - device is managed") @@ -534,12 +580,16 @@ suspend fun isPackageHibernationExemptBySystem( } } - val carrierPrivilegedStatus = CarrierPrivilegedStatusLiveData[pkg.packageName] - .getInitializedValue() - if (carrierPrivilegedStatus != CARRIER_PRIVILEGE_STATUS_HAS_ACCESS && - carrierPrivilegedStatus != CARRIER_PRIVILEGE_STATUS_NO_ACCESS) { - DumpableLog.w(LOG_TAG, "Error carrier privileged status for ${pkg.packageName}: " + - carrierPrivilegedStatus) + val carrierPrivilegedStatus = + CarrierPrivilegedStatusLiveData[pkg.packageName].getInitializedValue() + if ( + carrierPrivilegedStatus != CARRIER_PRIVILEGE_STATUS_HAS_ACCESS && + carrierPrivilegedStatus != CARRIER_PRIVILEGE_STATUS_NO_ACCESS + ) { + DumpableLog.w( + LOG_TAG, + "Error carrier privileged status for ${pkg.packageName}: " + carrierPrivilegedStatus + ) } if (carrierPrivilegedStatus == CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) { if (DEBUG_HIBERNATION_POLICY) { @@ -548,19 +598,24 @@ suspend fun isPackageHibernationExemptBySystem( return true } - if (PermissionControllerApplication.get() + if ( + PermissionControllerApplication.get() .packageManager - .checkPermission( - Manifest.permission.READ_PRIVILEGED_PHONE_STATE, - pkg.packageName) == PERMISSION_GRANTED) { + .checkPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE, pkg.packageName) == + PERMISSION_GRANTED + ) { if (DEBUG_HIBERNATION_POLICY) { - DumpableLog.i(LOG_TAG, "Exempted ${pkg.packageName} " + - "- holder of READ_PRIVILEGED_PHONE_STATE") + DumpableLog.i( + LOG_TAG, + "Exempted ${pkg.packageName} " + "- holder of READ_PRIVILEGED_PHONE_STATE" + ) } return true } - val emergencyRoleHolders = context.getSystemService(android.app.role.RoleManager::class.java)!! + val emergencyRoleHolders = + context + .getSystemService(android.app.role.RoleManager::class.java)!! .getRoleHolders(RoleManager.ROLE_EMERGENCY) if (emergencyRoleHolders.contains(pkg.packageName)) { if (DEBUG_HIBERNATION_POLICY) { @@ -571,19 +626,19 @@ suspend fun isPackageHibernationExemptBySystem( if (SdkLevel.isAtLeastS()) { val hasInstallOrUpdatePermissions = + context.checkPermission(Manifest.permission.INSTALL_PACKAGES, -1 /* pid */, pkg.uid) == + PERMISSION_GRANTED || context.checkPermission( - Manifest.permission.INSTALL_PACKAGES, -1 /* pid */, pkg.uid) == - PERMISSION_GRANTED || - context.checkPermission( - Manifest.permission.INSTALL_PACKAGE_UPDATES, -1 /* pid */, pkg.uid) == - PERMISSION_GRANTED + Manifest.permission.INSTALL_PACKAGE_UPDATES, + -1 /* pid */, + pkg.uid + ) == PERMISSION_GRANTED val hasUpdatePackagesWithoutUserActionPermission = - context.checkPermission( - UPDATE_PACKAGES_WITHOUT_USER_ACTION, -1 /* pid */, pkg.uid) == - PERMISSION_GRANTED + context.checkPermission(UPDATE_PACKAGES_WITHOUT_USER_ACTION, -1 /* pid */, pkg.uid) == + PERMISSION_GRANTED val isInstallerOfRecord = - InstallerPackagesLiveData[user].getInitializedValue().contains(pkg.packageName) && - hasUpdatePackagesWithoutUserActionPermission + InstallerPackagesLiveData[user].getInitializedValue().contains(pkg.packageName) && + hasUpdatePackagesWithoutUserActionPermission // Grant if app w/ privileged install/update permissions or app is an installer app that // updates packages without user action. if (hasInstallOrUpdatePermissions || isInstallerOfRecord) { @@ -593,7 +648,9 @@ suspend fun isPackageHibernationExemptBySystem( return true } - val roleHolders = context.getSystemService(android.app.role.RoleManager::class.java)!! + val roleHolders = + context + .getSystemService(android.app.role.RoleManager::class.java)!! .getRoleHolders(RoleManager.ROLE_SYSTEM_WELLBEING) if (roleHolders.contains(pkg.packageName)) { if (DEBUG_HIBERNATION_POLICY) { @@ -604,8 +661,10 @@ suspend fun isPackageHibernationExemptBySystem( } if (SdkLevel.isAtLeastT()) { - val roleHolders = context.getSystemService(android.app.role.RoleManager::class.java)!! - .getRoleHolders(RoleManager.ROLE_DEVICE_POLICY_MANAGEMENT) + val roleHolders = + context + .getSystemService(android.app.role.RoleManager::class.java)!! + .getRoleHolders(RoleManager.ROLE_DEVICE_POLICY_MANAGEMENT) if (roleHolders.contains(pkg.packageName)) { if (DEBUG_HIBERNATION_POLICY) { DumpableLog.i(LOG_TAG, "Exempted ${pkg.packageName} - device policy manager app") @@ -614,9 +673,12 @@ suspend fun isPackageHibernationExemptBySystem( } } - if (isSystemExemptFromHibernationEnabled() && AppOpLiveData[pkg.packageName, - AppOpsManager.OPSTR_SYSTEM_EXEMPT_FROM_HIBERNATION, - pkg.uid].getInitializedValue() == AppOpsManager.MODE_ALLOWED) { + if ( + isSystemExemptFromHibernationEnabled() && + AppOpLiveData[ + pkg.packageName, AppOpsManager.OPSTR_SYSTEM_EXEMPT_FROM_HIBERNATION, pkg.uid] + .getInitializedValue() == AppOpsManager.MODE_ALLOWED + ) { if (DEBUG_HIBERNATION_POLICY) { DumpableLog.i( LOG_TAG, @@ -641,8 +703,8 @@ suspend fun isPackageHibernationExemptByUser( val packageUid = pkg.uid val allowlistAppOpMode = - AppOpLiveData[packageName, - AppOpsManager.OPSTR_AUTO_REVOKE_PERMISSIONS_IF_UNUSED, packageUid] + AppOpLiveData[ + packageName, AppOpsManager.OPSTR_AUTO_REVOKE_PERMISSIONS_IF_UNUSED, packageUid] .getInitializedValue() if (allowlistAppOpMode == AppOpsManager.MODE_DEFAULT) { // Initial state - allowlist not explicitly overridden by either user or installer @@ -658,11 +720,11 @@ suspend fun isPackageHibernationExemptByUser( // Q- packages exempt by default, except R- on Auto since Auto-Revoke was skipped in R val maxTargetSdkVersionForExemptApps = - if (context.packageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) { - android.os.Build.VERSION_CODES.R - } else { - android.os.Build.VERSION_CODES.Q - } + if (context.packageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) { + android.os.Build.VERSION_CODES.R + } else { + android.os.Build.VERSION_CODES.Q + } return pkg.targetSdkVersion <= maxTargetSdkVersionForExemptApps } @@ -671,18 +733,18 @@ suspend fun isPackageHibernationExemptByUser( } private fun Context.isPackageCrossProfile(pkg: String): Boolean { - return packageManager.checkPermission( - Manifest.permission.INTERACT_ACROSS_PROFILES, pkg) == PERMISSION_GRANTED || - packageManager.checkPermission( - Manifest.permission.INTERACT_ACROSS_USERS, pkg) == PERMISSION_GRANTED || - packageManager.checkPermission( - Manifest.permission.INTERACT_ACROSS_USERS_FULL, pkg) == PERMISSION_GRANTED + return packageManager.checkPermission(Manifest.permission.INTERACT_ACROSS_PROFILES, pkg) == + PERMISSION_GRANTED || + packageManager.checkPermission(Manifest.permission.INTERACT_ACROSS_USERS, pkg) == + PERMISSION_GRANTED || + packageManager.checkPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL, pkg) == + PERMISSION_GRANTED } val Context.sharedPreferences: SharedPreferences get() { - return PreferenceManager.getDefaultSharedPreferences(this) -} + return PreferenceManager.getDefaultSharedPreferences(this) + } internal class SystemTime { var actualSystemTime: Long = SNAPSHOT_UNINITIALIZED @@ -692,15 +754,15 @@ internal class SystemTime { private fun getSystemTime(sharedPreferences: SharedPreferences): SystemTime { val systemTime = SystemTime() - val systemTimeSnapshot = sharedPreferences.getLong(PREF_KEY_BOOT_TIME_SNAPSHOT, - SNAPSHOT_UNINITIALIZED) + val systemTimeSnapshot = + sharedPreferences.getLong(PREF_KEY_BOOT_TIME_SNAPSHOT, SNAPSHOT_UNINITIALIZED) if (systemTimeSnapshot == SNAPSHOT_UNINITIALIZED) { DumpableLog.e(LOG_TAG, "PREF_KEY_BOOT_TIME_SNAPSHOT is not initialized") return systemTime } - val realtimeSnapshot = sharedPreferences.getLong(PREF_KEY_ELAPSED_REALTIME_SNAPSHOT, - SNAPSHOT_UNINITIALIZED) + val realtimeSnapshot = + sharedPreferences.getLong(PREF_KEY_ELAPSED_REALTIME_SNAPSHOT, SNAPSHOT_UNINITIALIZED) if (realtimeSnapshot == SNAPSHOT_UNINITIALIZED) { DumpableLog.e(LOG_TAG, "PREF_KEY_ELAPSED_REALTIME_SNAPSHOT is not initialized") return systemTime @@ -713,14 +775,19 @@ private fun getSystemTime(sharedPreferences: SharedPreferences): SystemTime { } fun getStartTimeOfUnusedAppTracking(sharedPreferences: SharedPreferences): Long { - val startTimeOfUnusedAppTracking = sharedPreferences.getLong( - PREF_KEY_START_TIME_OF_UNUSED_APP_TRACKING, SNAPSHOT_UNINITIALIZED) + val startTimeOfUnusedAppTracking = + sharedPreferences.getLong( + PREF_KEY_START_TIME_OF_UNUSED_APP_TRACKING, + SNAPSHOT_UNINITIALIZED + ) // If the preference is not initialized then use the current system time. if (startTimeOfUnusedAppTracking == SNAPSHOT_UNINITIALIZED) { val actualSystemTime = System.currentTimeMillis() - sharedPreferences.edit() - .putLong(PREF_KEY_START_TIME_OF_UNUSED_APP_TRACKING, actualSystemTime).apply() + sharedPreferences + .edit() + .putLong(PREF_KEY_START_TIME_OF_UNUSED_APP_TRACKING, actualSystemTime) + .apply() return actualSystemTime } @@ -729,20 +796,28 @@ fun getStartTimeOfUnusedAppTracking(sharedPreferences: SharedPreferences): Long if (diffSystemTime > ONE_DAY_MS) { adjustStartTimeOfUnusedAppTracking(sharedPreferences) } - return sharedPreferences.getLong(PREF_KEY_START_TIME_OF_UNUSED_APP_TRACKING, - SNAPSHOT_UNINITIALIZED) + return sharedPreferences.getLong( + PREF_KEY_START_TIME_OF_UNUSED_APP_TRACKING, + SNAPSHOT_UNINITIALIZED + ) } private fun initStartTimeOfUnusedAppTracking(sharedPreferences: SharedPreferences) { val systemTimeSnapshot = System.currentTimeMillis() - if (sharedPreferences - .getLong(PREF_KEY_START_TIME_OF_UNUSED_APP_TRACKING, SNAPSHOT_UNINITIALIZED) - == SNAPSHOT_UNINITIALIZED) { - sharedPreferences.edit() - .putLong(PREF_KEY_START_TIME_OF_UNUSED_APP_TRACKING, systemTimeSnapshot).apply() + if ( + sharedPreferences.getLong( + PREF_KEY_START_TIME_OF_UNUSED_APP_TRACKING, + SNAPSHOT_UNINITIALIZED + ) == SNAPSHOT_UNINITIALIZED + ) { + sharedPreferences + .edit() + .putLong(PREF_KEY_START_TIME_OF_UNUSED_APP_TRACKING, systemTimeSnapshot) + .apply() } val realtimeSnapshot = SystemClock.elapsedRealtime() - sharedPreferences.edit() + sharedPreferences + .edit() .putLong(PREF_KEY_BOOT_TIME_SNAPSHOT, systemTimeSnapshot) .putLong(PREF_KEY_ELAPSED_REALTIME_SNAPSHOT, realtimeSnapshot) .apply() @@ -751,49 +826,52 @@ private fun initStartTimeOfUnusedAppTracking(sharedPreferences: SharedPreference private fun adjustStartTimeOfUnusedAppTracking(sharedPreferences: SharedPreferences) { val systemTime = getSystemTime(sharedPreferences) val startTimeOfUnusedAppTracking = - sharedPreferences.getLong(PREF_KEY_START_TIME_OF_UNUSED_APP_TRACKING, - SNAPSHOT_UNINITIALIZED) + sharedPreferences.getLong( + PREF_KEY_START_TIME_OF_UNUSED_APP_TRACKING, + SNAPSHOT_UNINITIALIZED + ) if (startTimeOfUnusedAppTracking == SNAPSHOT_UNINITIALIZED) { DumpableLog.e(LOG_TAG, "PREF_KEY_START_TIME_OF_UNUSED_APP_TRACKING is not initialized") return } val adjustedStartTimeOfUnusedAppTracking = startTimeOfUnusedAppTracking + systemTime.diffSystemTime - sharedPreferences.edit() + sharedPreferences + .edit() .putLong(PREF_KEY_START_TIME_OF_UNUSED_APP_TRACKING, adjustedStartTimeOfUnusedAppTracking) .putLong(PREF_KEY_BOOT_TIME_SNAPSHOT, systemTime.actualSystemTime) .putLong(PREF_KEY_ELAPSED_REALTIME_SNAPSHOT, systemTime.actualRealtime) .apply() } -/** - * Make intent to go to unused apps page. - */ +/** Make intent to go to unused apps page. */ private fun makeUnusedAppsIntent(context: Context, sessionId: Long): PendingIntent { - val clickIntent = Intent(Intent.ACTION_MANAGE_UNUSED_APPS).apply { - putExtra(Constants.EXTRA_SESSION_ID, sessionId) - flags = FLAG_ACTIVITY_NEW_TASK - } - val pendingIntent = PendingIntent.getActivity(context, 0, clickIntent, - FLAG_UPDATE_CURRENT or FLAG_IMMUTABLE) + val clickIntent = + Intent(Intent.ACTION_MANAGE_UNUSED_APPS).apply { + putExtra(Constants.EXTRA_SESSION_ID, sessionId) + flags = FLAG_ACTIVITY_NEW_TASK + } + val pendingIntent = + PendingIntent.getActivity(context, 0, clickIntent, FLAG_UPDATE_CURRENT or FLAG_IMMUTABLE) return pendingIntent } -/** - * Make intent for when safety center card is dismissed. - */ +/** Make intent for when safety center card is dismissed. */ private fun makeDismissIntent(context: Context, sessionId: Long): PendingIntent { - val dismissIntent = Intent(context, DismissHandler::class.java).apply { - putExtra(Constants.EXTRA_SESSION_ID, sessionId) - flags = FLAG_RECEIVER_FOREGROUND - } - return PendingIntent.getBroadcast(context, /* requestCode= */ 0, dismissIntent, - FLAG_ONE_SHOT or FLAG_UPDATE_CURRENT or FLAG_IMMUTABLE) + val dismissIntent = + Intent(context, DismissHandler::class.java).apply { + putExtra(Constants.EXTRA_SESSION_ID, sessionId) + flags = FLAG_RECEIVER_FOREGROUND + } + return PendingIntent.getBroadcast( + context, + /* requestCode= */ 0, + dismissIntent, + FLAG_ONE_SHOT or FLAG_UPDATE_CURRENT or FLAG_IMMUTABLE + ) } -/** - * Broadcast receiver class for when safety center card is dismissed. - */ +/** Broadcast receiver class for when safety center card is dismissed. */ class DismissHandler : BroadcastReceiver() { override fun onReceive(context: Context?, intent: Intent?) { setUnusedAppsReviewNeeded(context!!, false) @@ -801,8 +879,8 @@ class DismissHandler : BroadcastReceiver() { } /** - * A job to check for apps unused in the last [getUnusedThresholdMs]ms every - * [getCheckFrequencyMs]ms and hibernate the app / revoke their runtime permissions. + * A job to check for apps unused in the last [getUnusedThresholdMs]ms every [getCheckFrequencyMs]ms + * and hibernate the app / revoke their runtime permissions. */ class HibernationJobService : JobService() { var job: Job? = null @@ -823,58 +901,75 @@ class HibernationJobService : JobService() { } jobStartTime = System.currentTimeMillis() - job = GlobalScope.launch(Main) { - try { - var sessionId = Constants.INVALID_SESSION_ID - while (sessionId == Constants.INVALID_SESSION_ID) { - sessionId = Random().nextLong() - } + job = + GlobalScope.launch(Main) { + try { + var sessionId = Constants.INVALID_SESSION_ID + while (sessionId == Constants.INVALID_SESSION_ID) { + sessionId = Random().nextLong() + } - val appsToHibernate = getAppsToHibernate(this@HibernationJobService) - var hibernatedApps: Set<Pair<String, UserHandle>> = emptySet() - if (isHibernationEnabled()) { - val hibernationController = - HibernationController(this@HibernationJobService, getUnusedThresholdMs(), - hibernationTargetsPreSApps()) - hibernatedApps = hibernationController.hibernateApps(appsToHibernate) - } - val revokedApps = revokeAppPermissions( - appsToHibernate, this@HibernationJobService, sessionId) - val unusedApps: Set<Pair<String, UserHandle>> = hibernatedApps + revokedApps - if (unusedApps.isNotEmpty()) { - showUnusedAppsNotification(unusedApps.size, sessionId) - if (SdkLevel.isAtLeastT() && - revokedApps.isNotEmpty() && - getSystemService(SafetyCenterManager::class.java)!!.isSafetyCenterEnabled) { - setUnusedAppsReviewNeeded(this@HibernationJobService, true) - rescanAndPushDataToSafetyCenter( - this@HibernationJobService, - sessionId, - SafetyEvent.Builder(SafetyEvent.SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED) - .build()) + val appsToHibernate = getAppsToHibernate(this@HibernationJobService) + var hibernatedApps: Set<Pair<String, UserHandle>> = emptySet() + if (isHibernationEnabled()) { + val hibernationController = + HibernationController( + this@HibernationJobService, + getUnusedThresholdMs(), + hibernationTargetsPreSApps() + ) + hibernatedApps = hibernationController.hibernateApps(appsToHibernate) } + val revokedApps = + revokeAppPermissions(appsToHibernate, this@HibernationJobService, sessionId) + val unusedApps: Set<Pair<String, UserHandle>> = hibernatedApps + revokedApps + if (unusedApps.isNotEmpty()) { + showUnusedAppsNotification(unusedApps.size, sessionId) + if ( + SdkLevel.isAtLeastT() && + revokedApps.isNotEmpty() && + getSystemService(SafetyCenterManager::class.java)!! + .isSafetyCenterEnabled + ) { + setUnusedAppsReviewNeeded(this@HibernationJobService, true) + rescanAndPushDataToSafetyCenter( + this@HibernationJobService, + sessionId, + SafetyEvent.Builder( + SafetyEvent.SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED + ) + .build() + ) + } + } + } catch (e: Exception) { + DumpableLog.e(LOG_TAG, "Failed to auto-revoke permissions", e) } - } catch (e: Exception) { - DumpableLog.e(LOG_TAG, "Failed to auto-revoke permissions", e) + jobFinished(params, false) } - jobFinished(params, false) - } return true } private fun showUnusedAppsNotification(numUnused: Int, sessionId: Long) { val notificationManager = getSystemService(NotificationManager::class.java)!! - val permissionReminderChannel = NotificationChannel( - Constants.PERMISSION_REMINDER_CHANNEL_ID, getString(R.string.permission_reminders), - NotificationManager.IMPORTANCE_LOW) + val permissionReminderChannel = + NotificationChannel( + Constants.PERMISSION_REMINDER_CHANNEL_ID, + getString(R.string.permission_reminders), + NotificationManager.IMPORTANCE_LOW + ) notificationManager.createNotificationChannel(permissionReminderChannel) var notifTitle: String var notifContent: String if (isHibernationEnabled()) { - notifTitle = StringUtils.getIcuPluralsString(this, - R.string.unused_apps_notification_title, numUnused) + notifTitle = + StringUtils.getIcuPluralsString( + this, + R.string.unused_apps_notification_title, + numUnused + ) notifContent = getString(R.string.unused_apps_notification_content) } else { notifTitle = getString(R.string.auto_revoke_permission_notification_title) @@ -882,16 +977,19 @@ class HibernationJobService : JobService() { } // Notification won't appear on TV, because notifications are considered distruptive on TV - val b = Notification.Builder(this, Constants.PERMISSION_REMINDER_CHANNEL_ID) - .setContentTitle(notifTitle) - .setContentText(notifContent) - .setStyle(Notification.BigTextStyle().bigText(notifContent)) - .setColor(getColor(android.R.color.system_notification_accent_color)) - .setAutoCancel(true) - .setContentIntent(makeUnusedAppsIntent(this, sessionId)) + val b = + Notification.Builder(this, Constants.PERMISSION_REMINDER_CHANNEL_ID) + .setContentTitle(notifTitle) + .setContentText(notifContent) + .setStyle(Notification.BigTextStyle().bigText(notifContent)) + .setColor(getColor(android.R.color.system_notification_accent_color)) + .setAutoCancel(true) + .setContentIntent(makeUnusedAppsIntent(this, sessionId)) val extras = Bundle() - if (SdkLevel.isAtLeastT() && - getSystemService(SafetyCenterManager::class.java)!!.isSafetyCenterEnabled) { + if ( + SdkLevel.isAtLeastT() && + getSystemService(SafetyCenterManager::class.java)!!.isSafetyCenterEnabled + ) { val notificationResources = KotlinUtils.getSafetyCenterNotificationResources(this) extras.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME, notificationResources.appLabel) @@ -903,13 +1001,15 @@ class HibernationJobService : JobService() { Utils.getSettingsLabelForNotifications(applicationContext.packageManager)?.let { settingsLabel -> extras.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME, settingsLabel.toString()) - b.setSmallIcon(R.drawable.ic_settings_24dp) - .addExtras(extras) + b.setSmallIcon(R.drawable.ic_settings_24dp).addExtras(extras) } } - notificationManager.notify(HibernationJobService::class.java.simpleName, - Constants.UNUSED_APPS_NOTIFICATION_ID, b.build()) + notificationManager.notify( + HibernationJobService::class.java.simpleName, + Constants.UNUSED_APPS_NOTIFICATION_ID, + b.build() + ) GlobalScope.launch(IPC) { // Preload the unused packages getUnusedPackages().getInitializedValue(staleOk = true) @@ -929,47 +1029,39 @@ class HibernationJobService : JobService() { */ class ExemptServicesLiveData(private val user: UserHandle) : SmartUpdateMediatorLiveData<Map<String, List<String>>>() { - private val serviceLiveDatas: List<SmartUpdateMediatorLiveData<Set<String>>> = listOf( - ServiceLiveData[InputMethod.SERVICE_INTERFACE, - Manifest.permission.BIND_INPUT_METHOD, - user], + private val serviceLiveDatas: List<SmartUpdateMediatorLiveData<Set<String>>> = + listOf( ServiceLiveData[ - NotificationListenerService.SERVICE_INTERFACE, - Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE, - user], + InputMethod.SERVICE_INTERFACE, Manifest.permission.BIND_INPUT_METHOD, user], ServiceLiveData[ - AccessibilityService.SERVICE_INTERFACE, - Manifest.permission.BIND_ACCESSIBILITY_SERVICE, - user], + NotificationListenerService.SERVICE_INTERFACE, + Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE, + user], ServiceLiveData[ - WallpaperService.SERVICE_INTERFACE, - Manifest.permission.BIND_WALLPAPER, - user], + AccessibilityService.SERVICE_INTERFACE, + Manifest.permission.BIND_ACCESSIBILITY_SERVICE, + user], ServiceLiveData[ - VoiceInteractionService.SERVICE_INTERFACE, - Manifest.permission.BIND_VOICE_INTERACTION, - user], + WallpaperService.SERVICE_INTERFACE, Manifest.permission.BIND_WALLPAPER, user], ServiceLiveData[ - PrintService.SERVICE_INTERFACE, - Manifest.permission.BIND_PRINT_SERVICE, - user], + VoiceInteractionService.SERVICE_INTERFACE, + Manifest.permission.BIND_VOICE_INTERACTION, + user], ServiceLiveData[ - DreamService.SERVICE_INTERFACE, - Manifest.permission.BIND_DREAM_SERVICE, - user], + PrintService.SERVICE_INTERFACE, Manifest.permission.BIND_PRINT_SERVICE, user], ServiceLiveData[ - AutofillService.SERVICE_INTERFACE, - Manifest.permission.BIND_AUTOFILL_SERVICE, - user], + DreamService.SERVICE_INTERFACE, Manifest.permission.BIND_DREAM_SERVICE, user], ServiceLiveData[ - DevicePolicyManager.ACTION_DEVICE_ADMIN_SERVICE, - Manifest.permission.BIND_DEVICE_ADMIN, - user], + AutofillService.SERVICE_INTERFACE, Manifest.permission.BIND_AUTOFILL_SERVICE, user], + ServiceLiveData[ + DevicePolicyManager.ACTION_DEVICE_ADMIN_SERVICE, + Manifest.permission.BIND_DEVICE_ADMIN, + user], BroadcastReceiverLiveData[ - DeviceAdminReceiver.ACTION_DEVICE_ADMIN_ENABLED, - Manifest.permission.BIND_DEVICE_ADMIN, - user] - ) + DeviceAdminReceiver.ACTION_DEVICE_ADMIN_ENABLED, + Manifest.permission.BIND_DEVICE_ADMIN, + user] + ) init { serviceLiveDatas.forEach { addSource(it) { update() } } @@ -981,8 +1073,9 @@ class ExemptServicesLiveData(private val user: UserHandle) : serviceLiveDatas.forEach { serviceLD -> serviceLD.value!!.forEach { packageName -> - pksToServices.getOrPut(packageName, { mutableListOf() }) - .add((serviceLD as? HasIntentAction)?.intentAction ?: "???") + pksToServices + .getOrPut(packageName, { mutableListOf() }) + .add((serviceLD as? HasIntentAction)?.intentAction ?: "???") } } @@ -1002,23 +1095,26 @@ class ExemptServicesLiveData(private val user: UserHandle) : } } -/** - * Live data for whether the hibernation feature is enabled or not. - */ -object HibernationEnabledLiveData : - MutableLiveData<Boolean>() { +/** Live data for whether the hibernation feature is enabled or not. */ +object HibernationEnabledLiveData : MutableLiveData<Boolean>() { init { - postValue(SdkLevel.isAtLeastS() && - DeviceConfig.getBoolean(NAMESPACE_APP_HIBERNATION, - Utils.PROPERTY_APP_HIBERNATION_ENABLED, true /* defaultValue */)) + postValue( + SdkLevel.isAtLeastS() && + DeviceConfig.getBoolean( + NAMESPACE_APP_HIBERNATION, + Utils.PROPERTY_APP_HIBERNATION_ENABLED, + true /* defaultValue */ + ) + ) DeviceConfig.addOnPropertiesChangedListener( NAMESPACE_APP_HIBERNATION, PermissionControllerApplication.get().mainExecutor, { properties -> for (key in properties.keyset) { if (key == Utils.PROPERTY_APP_HIBERNATION_ENABLED) { - value = SdkLevel.isAtLeastS() && - properties.getBoolean(key, true /* defaultValue */) + value = + SdkLevel.isAtLeastS() && + properties.getBoolean(key, true /* defaultValue */) break } } diff --git a/PermissionController/src/com/android/permissioncontroller/hibernation/v31/HibernationController.kt b/PermissionController/src/com/android/permissioncontroller/hibernation/v31/HibernationController.kt index 5aa07c232..e81c7a207 100644 --- a/PermissionController/src/com/android/permissioncontroller/hibernation/v31/HibernationController.kt +++ b/PermissionController/src/com/android/permissioncontroller/hibernation/v31/HibernationController.kt @@ -28,9 +28,7 @@ import com.android.permissioncontroller.DumpableLog import com.android.permissioncontroller.permission.data.HibernatedPackagesLiveData import com.android.permissioncontroller.permission.model.livedatatypes.LightPackageInfo -/** - * Hibernation controller that handles modifying hibernation state. - */ +/** Hibernation controller that handles modifying hibernation state. */ @RequiresApi(Build.VERSION_CODES.S) class HibernationController( private val context: Context, @@ -62,8 +60,7 @@ class HibernationController( if (hibernationManager.isHibernatingForUser(pkg.packageName)) { continue } - if (!targetsPreS && - pkg.targetSdkVersion < Build.VERSION_CODES.S) { + if (!targetsPreS && pkg.targetSdkVersion < Build.VERSION_CODES.S) { // Only apps targeting S or above can be truly hibernated. continue } @@ -81,8 +78,10 @@ class HibernationController( context.getSystemService(APP_HIBERNATION_SERVICE) as AppHibernationManager val globallyHibernatedApps = mutableSetOf<String>() for ((pkgName, _) in hibernatedApps) { - if (globallyHibernatedApps.contains(pkgName) || - hibernationManager.isHibernatingGlobally(pkgName)) { + if ( + globallyHibernatedApps.contains(pkgName) || + hibernationManager.isHibernatingGlobally(pkgName) + ) { continue } @@ -99,9 +98,11 @@ class HibernationController( HibernatedPackagesLiveData.update() } if (DEBUG_HIBERNATION) { - DumpableLog.i(LOG_TAG, + DumpableLog.i( + LOG_TAG, "Done hibernating apps $hibernatedApps \n " + - "Globally hibernating apps $globallyHibernatedApps") + "Globally hibernating apps $globallyHibernatedApps" + ) } return hibernatedApps diff --git a/PermissionController/src/com/android/permissioncontroller/hibernation/v31/InstallerPackagesLiveData.kt b/PermissionController/src/com/android/permissioncontroller/hibernation/v31/InstallerPackagesLiveData.kt index f1a19294a..ebe57ec36 100644 --- a/PermissionController/src/com/android/permissioncontroller/hibernation/v31/InstallerPackagesLiveData.kt +++ b/PermissionController/src/com/android/permissioncontroller/hibernation/v31/InstallerPackagesLiveData.kt @@ -27,17 +27,13 @@ import com.android.permissioncontroller.permission.data.DataRepositoryForPackage import com.android.permissioncontroller.permission.data.SmartAsyncMediatorLiveData import kotlinx.coroutines.Job -/** - * Packages that are the installer of record for some package on the device. - */ +/** Packages that are the installer of record for some package on the device. */ @RequiresApi(Build.VERSION_CODES.S) -class InstallerPackagesLiveData(private val user: UserHandle) - : SmartAsyncMediatorLiveData<Set<String>>() { +class InstallerPackagesLiveData(private val user: UserHandle) : + SmartAsyncMediatorLiveData<Set<String>>() { init { - addSource(AllPackageInfosLiveData) { - update() - } + addSource(AllPackageInfosLiveData) { update() } } override suspend fun loadDataAndPostValue(job: Job) { diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/AllPackageInfosLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/AllPackageInfosLiveData.kt index c2655ecff..b3a28eefe 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/data/AllPackageInfosLiveData.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/data/AllPackageInfosLiveData.kt @@ -20,9 +20,7 @@ import android.os.UserHandle import com.android.permissioncontroller.permission.data.AllPackageInfosLiveData.addSource import com.android.permissioncontroller.permission.model.livedatatypes.LightPackageInfo -/** - * A LiveData which tracks the PackageInfos of all of the packages in the system, for all users. - */ +/** A LiveData which tracks the PackageInfos of all of the packages in the system, for all users. */ object AllPackageInfosLiveData : SmartUpdateMediatorLiveData<Map<UserHandle, List<LightPackageInfo>>>() { @@ -30,9 +28,7 @@ object AllPackageInfosLiveData : private val userPackageInfos = mutableMapOf<UserHandle, List<LightPackageInfo>>() init { - addSource(UsersLiveData) { - update() - } + addSource(UsersLiveData) { update() } } override fun onUpdate() { diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/AppOpLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/AppOpLiveData.kt index 5a0abeaa1..1e44f16bd 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/data/AppOpLiveData.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/data/AppOpLiveData.kt @@ -27,12 +27,12 @@ import com.android.permissioncontroller.PermissionControllerApplication * @param packageName The name of the package * @param op The name of the appop * @param uid The uid of the package - * * @see AppOpsManager */ // TODO eugenesusla: observe appops // TODO eugenesusla: use for external storage -class AppOpLiveData private constructor( +class AppOpLiveData +private constructor( private val app: Application, private val packageName: String, private val op: String, @@ -52,13 +52,18 @@ class AppOpLiveData private constructor( /** * Repository for AppOpLiveData. - * <p> Key value is a triple of string package name, string appop, and - * package uid, value is its corresponding LiveData. + * + * <p> Key value is a triple of string package name, string appop, and package uid, value is its + * corresponding LiveData. */ companion object : DataRepository<Triple<String, String, Int>, AppOpLiveData>() { override fun newValue(key: Triple<String, String, Int>): AppOpLiveData { - return AppOpLiveData(PermissionControllerApplication.get(), - key.first, key.second, key.third) + return AppOpLiveData( + PermissionControllerApplication.get(), + key.first, + key.second, + key.third + ) } } -}
\ No newline at end of file +} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/AppPermGroupUiInfoLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/AppPermGroupUiInfoLiveData.kt index 7c69d6078..b17098a13 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/data/AppPermGroupUiInfoLiveData.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/data/AppPermGroupUiInfoLiveData.kt @@ -32,17 +32,17 @@ import com.android.permissioncontroller.permission.model.livedatatypes.LightPack import com.android.permissioncontroller.permission.model.livedatatypes.LightPermGroupInfo import com.android.permissioncontroller.permission.model.livedatatypes.LightPermInfo import com.android.permissioncontroller.permission.model.livedatatypes.PermState -import com.android.permissioncontroller.permission.utils.PermissionMapping.isPlatformPermissionGroup import com.android.permissioncontroller.permission.utils.LocationUtils +import com.android.permissioncontroller.permission.utils.PermissionMapping.isPlatformPermissionGroup import com.android.permissioncontroller.permission.utils.Utils import kotlinx.coroutines.Job /** * A LiveData representing UI properties of an App Permission Group: * <ul> - * <li>shouldShow</li> - * <li>isSystem</li> - * <li>isGranted</li> + * <li>shouldShow</li> + * <li>isSystem</li> + * <li>isGranted</li> * </ul> * * @param app The current application @@ -50,7 +50,8 @@ import kotlinx.coroutines.Job * @param permGroupName The name of the permission group whose permissions are observed * @param user The user of the package */ -class AppPermGroupUiInfoLiveData private constructor( +class AppPermGroupUiInfoLiveData +private constructor( private val app: Application, private val packageName: String, private val permGroupName: String, @@ -65,21 +66,19 @@ class AppPermGroupUiInfoLiveData private constructor( private val isHealth = Utils.isHealthPermissionGroup(permGroupName) init { - isSpecialLocation = LocationUtils.isLocationGroupAndProvider(app, - permGroupName, packageName) || - LocationUtils.isLocationGroupAndControllerExtraPackage(app, permGroupName, packageName) + isSpecialLocation = + LocationUtils.isLocationGroupAndProvider(app, permGroupName, packageName) || + LocationUtils.isLocationGroupAndControllerExtraPackage( + app, + permGroupName, + packageName + ) - addSource(packageInfoLiveData) { - update() - } + addSource(packageInfoLiveData) { update() } - addSource(permGroupLiveData) { - update() - } + addSource(permGroupLiveData) { update() } - addSource(permissionStateLiveData) { - update() - } + addSource(permissionStateLiveData) { update() } } override suspend fun loadDataAndPostValue(job: Job) { @@ -91,27 +90,36 @@ class AppPermGroupUiInfoLiveData private constructor( val permissionState = permissionStateLiveData.value if (packageInfo == null || permissionGroup == null || permissionState == null) { - if (packageInfoLiveData.isInitialized && permGroupLiveData.isInitialized && - permissionStateLiveData.isInitialized) { + if ( + packageInfoLiveData.isInitialized && + permGroupLiveData.isInitialized && + permissionStateLiveData.isInitialized + ) { invalidateSingle(Triple(packageName, permGroupName, user)) postValue(null) } return } - postValue(getAppPermGroupUiInfo(packageInfo, permissionGroup.groupInfo, - permissionGroup.permissionInfos, permissionState)) + postValue( + getAppPermGroupUiInfo( + packageInfo, + permissionGroup.groupInfo, + permissionGroup.permissionInfos, + permissionState + ) + ) } /** - * Determines if the UI should show a given package, if that package is a system app, and - * if it has granted permissions in this LiveData's permission group. + * Determines if the UI should show a given package, if that package is a system app, and if it + * has granted permissions in this LiveData's permission group. * * @param packageInfo The PackageInfo of the package we wish to examine * @param groupInfo The groupInfo of the permission group we wish to examine * @param allPermInfos All of the PermissionInfos in the permission group - * @param permissionState The flags and grant state for all permissions in the permission - * group that this package requests + * @param permissionState The flags and grant state for all permissions in the permission group + * that this package requests */ private fun getAppPermGroupUiInfo( packageInfo: LightPackageInfo, @@ -126,10 +134,11 @@ class AppPermGroupUiInfoLiveData private constructor( val requestedPermissionInfos = allPermInfos.filter { permissionState.containsKey(it.key) }.values - val shouldShow = packageInfo.enabled && - isGrantableAndNotLegacyPlatform(packageInfo, groupInfo, requestedPermissionInfos) && - (!isStorage || Utils.shouldShowStorage(packageInfo)) && - (!isHealth || Utils.shouldShowHealthPermission(packageInfo, groupInfo.name)) + val shouldShow = + packageInfo.enabled && + isGrantableAndNotLegacyPlatform(packageInfo, groupInfo, requestedPermissionInfos) && + (!isStorage || Utils.shouldShowStorage(packageInfo)) && + (!isHealth || Utils.shouldShowHealthPermission(packageInfo, groupInfo.name)) val isSystemApp = !isUserSensitive(permissionState) @@ -147,19 +156,17 @@ class AppPermGroupUiInfoLiveData private constructor( * * @param packageInfo The PackageInfo of the package we are examining * @param groupInfo The Permission Group Info of the permission group we are examining - * @param permissionInfos The LightPermInfos corresponding to the permissions in the - * permission group that this package requests - * + * @param permissionInfos The LightPermInfos corresponding to the permissions in the permission + * group that this package requests * @return True if the app permission group is grantable, and is not a legacy system permission, - * false otherwise. + * false otherwise. */ private fun isGrantableAndNotLegacyPlatform( packageInfo: LightPackageInfo, groupInfo: LightPermGroupInfo, permissionInfos: Collection<LightPermInfo> ): Boolean { - if (groupInfo.packageName == Utils.OS_PKG && - !isPlatformPermissionGroup(groupInfo.name)) { + if (groupInfo.packageName == Utils.OS_PKG && !isPlatformPermissionGroup(groupInfo.name)) { return false } @@ -167,8 +174,9 @@ class AppPermGroupUiInfoLiveData private constructor( var hasPreRuntime = false for (permissionInfo in permissionInfos) { - if (permissionInfo.protectionFlags and - PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY == 0) { + if ( + permissionInfo.protectionFlags and PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY == 0 + ) { hasPreRuntime = true } @@ -177,8 +185,9 @@ class AppPermGroupUiInfoLiveData private constructor( } } - val isGrantingAllowed = (!packageInfo.isInstantApp || hasInstantPerm) && - (packageInfo.targetSdkVersion >= Build.VERSION_CODES.M || hasPreRuntime) + val isGrantingAllowed = + (!packageInfo.isInstantApp || hasInstantPerm) && + (packageInfo.targetSdkVersion >= Build.VERSION_CODES.M || hasPreRuntime) if (!isGrantingAllowed) { return false } @@ -191,10 +200,9 @@ class AppPermGroupUiInfoLiveData private constructor( * then it is considered a system app, and hidden in the UI by default. * * @param permissionState The permission flags and grant state corresponding to the permissions - * in this group requested by a given app - * + * in this group requested by a given app * @return Whether or not this package requests a user sensitive permission in the given - * permission group + * permission group */ private fun isUserSensitive(permissionState: Map<String, PermState>): Boolean { if (!isPlatformPermissionGroup(permGroupName)) { @@ -204,10 +212,12 @@ class AppPermGroupUiInfoLiveData private constructor( for (permissionName in permissionState.keys) { val flags = permissionState[permissionName]?.permFlags ?: return true val granted = permissionState[permissionName]?.granted ?: return true - if ((granted && + if ( + (granted && flags and PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED != 0) || - (!granted && - flags and PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED != 0)) { + (!granted && + flags and PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED != 0) + ) { return true } } @@ -218,32 +228,30 @@ class AppPermGroupUiInfoLiveData private constructor( * Determines if the app permission group is user set * * @param permissionState The permission flags and grant state corresponding to the permissions - * in this group requested by a given app - * + * in this group requested by a given app * @return Whether or not any of the permissions in this group have been set or fixed by the - * user + * user */ private fun isUserSet(permissionState: Map<String, PermState>): Boolean { - val flagMask = PackageManager.FLAG_PERMISSION_USER_SET or - PackageManager.FLAG_PERMISSION_USER_FIXED + val flagMask = + PackageManager.FLAG_PERMISSION_USER_SET or PackageManager.FLAG_PERMISSION_USER_FIXED return permissionState.any { (it.value.permFlags and flagMask) != 0 } } /** - * Determines if this app permission group is granted, granted in foreground only, or denied. - * It is granted if it either requests no background permissions, and has at least one requested - * permission that is granted, or has granted at least one requested background permission. - * It is granted in foreground only if it has at least one non-background permission granted, - * and has denied all requested background permissions. It is denied if all requested - * permissions are denied. + * Determines if this app permission group is granted, granted in foreground only, or denied. It + * is granted if it either requests no background permissions, and has at least one requested + * permission that is granted, or has granted at least one requested background permission. It + * is granted in foreground only if it has at least one non-background permission granted, and + * has denied all requested background permissions. It is denied if all requested permissions + * are denied. * * @param permissionState The permission flags and grant state corresponding to the permissions - * in this group requested by a given app - * @param allPermInfos All of the permissionInfos in the permission group of this app - * permission group - * + * in this group requested by a given app + * @param allPermInfos All of the permissionInfos in the permission group of this app permission + * group * @return The int code corresponding to the app permission group state, either allowed, allowed - * in foreground only, or denied. + * in foreground only, or denied. */ private fun getGrantedIncludingBackground( permissionState: Map<String, PermState>, @@ -262,36 +270,46 @@ class AppPermGroupUiInfoLiveData private constructor( val permInfo = allPermInfos[permName] ?: continue permInfo.backgroundPermission?.let { backgroundPerm -> hasPermWithBackground = true - if (permissionState[backgroundPerm]?.granted == true && + if ( + permissionState[backgroundPerm]?.granted == true && (permissionState[backgroundPerm]!!.permFlags and - PackageManager.FLAG_PERMISSION_ONE_TIME == 0) && - specialLocationState != false) { + PackageManager.FLAG_PERMISSION_ONE_TIME == 0) && + specialLocationState != false + ) { return PermGrantState.PERMS_ALLOWED_ALWAYS } } - isUserFixed = isUserFixed || + isUserFixed = + isUserFixed || permState.permFlags and PackageManager.FLAG_PERMISSION_USER_FIXED != 0 } // isOneTime indicates whether all granted permissions in permission states are one-time // permissions - val isOneTime = permissionState.any { - it.value.permFlags and PackageManager.FLAG_PERMISSION_ONE_TIME != 0 } && + val isOneTime = + permissionState.any { + it.value.permFlags and PackageManager.FLAG_PERMISSION_ONE_TIME != 0 + } && !permissionState.any { it.value.permFlags and PackageManager.FLAG_PERMISSION_ONE_TIME == 0 && - it.value.granted } + it.value.granted + } val supportsRuntime = pkg.targetSdkVersion >= Build.VERSION_CODES.M - val anyAllowed = specialLocationState ?: permissionState.any { (_, state) -> - state.granted || (supportsRuntime && - (state.permFlags and PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0) - } + val anyAllowed = + specialLocationState + ?: permissionState.any { (_, state) -> + state.granted || + (supportsRuntime && + (state.permFlags and PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != + 0) + } val onlySelectedPhotosGranted = permissionState.containsKey(READ_MEDIA_VISUAL_USER_SELECTED) && - permissionState.all { (permName, state) -> - (permName == READ_MEDIA_VISUAL_USER_SELECTED && state.granted) || - (permName != READ_MEDIA_VISUAL_USER_SELECTED && !state.granted) - } + permissionState.all { (permName, state) -> + (permName == READ_MEDIA_VISUAL_USER_SELECTED && state.granted) || + (permName != READ_MEDIA_VISUAL_USER_SELECTED && !state.granted) + } if (anyAllowed && (hasPermWithBackground || shouldShowAsForegroundGroup())) { return if (isOneTime) { PermGrantState.PERMS_ASK @@ -325,32 +343,38 @@ class AppPermGroupUiInfoLiveData private constructor( } // The permission of the extra location controller package is determined by the // status of the controller package itself. - if (LocationUtils.isLocationGroupAndControllerExtraPackage(userContext, - permGroupName, packageName)) { + if ( + LocationUtils.isLocationGroupAndControllerExtraPackage( + userContext, + permGroupName, + packageName + ) + ) { return LocationUtils.isExtraLocationControllerPackageEnabled(userContext) } return null } private fun isFullFilesAccessGranted(pkg: LightPackageInfo): Boolean { - val packageState = if (!FullStoragePermissionAppsLiveData.isStale) { - val fullStoragePackages = FullStoragePermissionAppsLiveData.value ?: return false - fullStoragePackages.find { - it.packageName == packageName && it.user == user - } ?: return false - } else { - val appOpsManager = Utils.getUserContext(app, UserHandle.getUserHandleForUid(pkg.uid)) - .getSystemService(AppOpsManager::class.java)!! - FullStoragePermissionAppsLiveData.getFullStorageStateForPackage( - appOpsManager, pkg) ?: return false - } + val packageState = + if (!FullStoragePermissionAppsLiveData.isStale) { + val fullStoragePackages = FullStoragePermissionAppsLiveData.value ?: return false + fullStoragePackages.find { it.packageName == packageName && it.user == user } + ?: return false + } else { + val appOpsManager = + Utils.getUserContext(app, UserHandle.getUserHandleForUid(pkg.uid)) + .getSystemService(AppOpsManager::class.java)!! + FullStoragePermissionAppsLiveData.getFullStorageStateForPackage(appOpsManager, pkg) + ?: return false + } return !packageState.isLegacy && packageState.isGranted } // TODO moltmann-team: Actually change mic/camera to be a foreground only permission private fun shouldShowAsForegroundGroup(): Boolean { return permGroupName.equals(Manifest.permission_group.CAMERA) || - permGroupName.equals(Manifest.permission_group.MICROPHONE) + permGroupName.equals(Manifest.permission_group.MICROPHONE) } override fun onLocationStateChange(enabled: Boolean) { @@ -375,15 +399,19 @@ class AppPermGroupUiInfoLiveData private constructor( /** * Repository for AppPermGroupUiInfoLiveDatas. - * <p> Key value is a triple of string package name, string permission group name, and UserHandle, - * value is its corresponding LiveData. + * + * <p> Key value is a triple of string package name, string permission group name, and + * UserHandle, value is its corresponding LiveData. */ - companion object : DataRepositoryForPackage<Triple<String, String, UserHandle>, - AppPermGroupUiInfoLiveData>() { - override fun newValue(key: Triple<String, String, UserHandle>): - AppPermGroupUiInfoLiveData { - return AppPermGroupUiInfoLiveData(PermissionControllerApplication.get(), - key.first, key.second, key.third) + companion object : + DataRepositoryForPackage<Triple<String, String, UserHandle>, AppPermGroupUiInfoLiveData>() { + override fun newValue(key: Triple<String, String, UserHandle>): AppPermGroupUiInfoLiveData { + return AppPermGroupUiInfoLiveData( + PermissionControllerApplication.get(), + key.first, + key.second, + key.third + ) } } } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/AttributionLabelLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/AttributionLabelLiveData.kt index b55c736ad..fac901a04 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/data/AttributionLabelLiveData.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/data/AttributionLabelLiveData.kt @@ -22,12 +22,12 @@ import android.content.res.Resources.ID_NULL import android.os.UserHandle import android.util.Log import com.android.permissioncontroller.PermissionControllerApplication +import java.io.FileNotFoundException import kotlinx.coroutines.Job import org.xmlpull.v1.XmlPullParser import org.xmlpull.v1.XmlPullParser.END_DOCUMENT import org.xmlpull.v1.XmlPullParser.END_TAG import org.xmlpull.v1.XmlPullParser.START_TAG -import java.io.FileNotFoundException private const val MANIFEST_FILE_NAME = "AndroidManifest.xml" private const val MANIFEST_TAG = "manifest" @@ -43,7 +43,8 @@ private const val LABEL_ATTR = "label" * <p>Obviously the resource is found in the package, hence needs to be loaded via a Resources * object created for this package. */ -class AttributionLabelLiveData private constructor( +class AttributionLabelLiveData +private constructor( private val app: Application, private val attributionTag: String?, private val packageName: String, @@ -61,14 +62,15 @@ class AttributionLabelLiveData private constructor( return } - val pkgContext = try { - app.createPackageContextAsUser(packageName, 0, user) - } catch (e: NameNotFoundException) { - Log.e(LOG_TAG, "Cannot find $packageName for $user") + val pkgContext = + try { + app.createPackageContextAsUser(packageName, 0, user) + } catch (e: NameNotFoundException) { + Log.e(LOG_TAG, "Cannot find $packageName for $user") - postValue(null) - return - } + postValue(null) + return + } // TODO (moltmann): Read this from PackageInfo once available var cookie = 0 @@ -76,11 +78,12 @@ class AttributionLabelLiveData private constructor( // Some resources have multiple "AndroidManifest.xml" loaded and hence we need // to find the right one cookie++ - val parser = try { - pkgContext.assets.openXmlResourceParser(cookie, MANIFEST_FILE_NAME) - } catch (e: FileNotFoundException) { - break - } + val parser = + try { + pkgContext.assets.openXmlResourceParser(cookie, MANIFEST_FILE_NAME) + } catch (e: FileNotFoundException) { + break + } try { do { @@ -109,8 +112,9 @@ class AttributionLabelLiveData private constructor( } if (parser.getAttributeValue(ANDROID_NS, TAG_ATTR) == attributionTag) { - postValue(parser.getAttributeResourceValue(ANDROID_NS, LABEL_ATTR, - ID_NULL)) + postValue( + parser.getAttributeResourceValue(ANDROID_NS, LABEL_ATTR, ID_NULL) + ) return } else { parser.skipTag() @@ -125,9 +129,7 @@ class AttributionLabelLiveData private constructor( postValue(null) } - /** - * Skip tag parser is currently pointing to (including all tags nested in it) - */ + /** Skip tag parser is currently pointing to (including all tags nested in it) */ private fun XmlPullParser.skipTag() { var depth = 1 while (depth != 0) { @@ -158,18 +160,25 @@ class AttributionLabelLiveData private constructor( /** * Repository for AttributionLiveData. + * * <p> Key value is a pair of string attribution tag, string package name, user handle, value is * its corresponding LiveData. */ - companion object : DataRepository<Triple<String?, String, UserHandle>, - AttributionLabelLiveData>() { + companion object : + DataRepository<Triple<String?, String, UserHandle>, AttributionLabelLiveData>() { override fun newValue(key: Triple<String?, String, UserHandle>): AttributionLabelLiveData { - return AttributionLabelLiveData(PermissionControllerApplication.get(), - key.first, key.second, key.third) + return AttributionLabelLiveData( + PermissionControllerApplication.get(), + key.first, + key.second, + key.third + ) } - operator fun get(attributionTag: String?, packageName: String, user: UserHandle): - AttributionLabelLiveData = - get(Triple(attributionTag, packageName, user)) + operator fun get( + attributionTag: String?, + packageName: String, + user: UserHandle + ): AttributionLabelLiveData = get(Triple(attributionTag, packageName, user)) } } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/AutoRevokedPackagesLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/AutoRevokedPackagesLiveData.kt index 70f857afb..2e6fab44d 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/data/AutoRevokedPackagesLiveData.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/data/AutoRevokedPackagesLiveData.kt @@ -21,8 +21,8 @@ import android.content.pm.PackageManager.FLAG_PERMISSION_AUTO_REVOKED import android.os.Build import android.os.UserHandle import android.util.Log -import com.android.permissioncontroller.permission.utils.PermissionMapping import com.android.permissioncontroller.permission.utils.KotlinUtils +import com.android.permissioncontroller.permission.utils.PermissionMapping import kotlinx.coroutines.Dispatchers.Main import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.Job @@ -34,15 +34,13 @@ import kotlinx.coroutines.launch * * ```(packageName, user) -> [groupName]``` */ -object AutoRevokedPackagesLiveData - : SmartAsyncMediatorLiveData<Map<Pair<String, UserHandle>, Set<String>>>() { +object AutoRevokedPackagesLiveData : + SmartAsyncMediatorLiveData<Map<Pair<String, UserHandle>, Set<String>>>() { private val LOG_TAG = AutoRevokedPackagesLiveData::class.java.simpleName init { - addSource(AllPackageInfosLiveData) { - update() - } + addSource(AllPackageInfosLiveData) { update() } } private val permStateLiveDatas = @@ -66,7 +64,8 @@ object AutoRevokedPackagesLiveData for ((idx, requestedPerm) in pkg.requestedPermissions.withIndex()) { val group = PermissionMapping.getGroupOfPlatformPermission(requestedPerm) ?: continue - val granted = (pkg.requestedPermissionsFlags[idx] and + val granted = + (pkg.requestedPermissionsFlags[idx] and PackageInfo.REQUESTED_PERMISSION_GRANTED) != 0 if (pkg.targetSdkVersion < Build.VERSION_CODES.M || !granted) { pkgGroups.add(Triple(pkg.packageName, group, user)) @@ -85,7 +84,6 @@ object AutoRevokedPackagesLiveData private fun observePermStateLiveDatas(packageGroups: Set<Triple<String, String, UserHandle>>) { GlobalScope.launch(Main.immediate) { - val (toAdd, toRemove) = KotlinUtils.getMapAndListDifferences(packageGroups, permStateLiveDatas) @@ -118,7 +116,8 @@ object AutoRevokedPackagesLiveData } else if (permState != null) { for ((_, state) in permState) { if (state.permFlags and FLAG_PERMISSION_AUTO_REVOKED != 0) { - packageAutoRevokedPermsList.getOrPut(packageUser) { mutableSetOf() } + packageAutoRevokedPermsList + .getOrPut(packageUser) { mutableSetOf() } .add(packagePermGroup.second) added = true break @@ -142,8 +141,7 @@ object AutoRevokedPackagesLiveData } private fun postCopyOfMap() { - val autoRevokedCopy = - mutableMapOf<Pair<String, UserHandle>, Set<String>>() + val autoRevokedCopy = mutableMapOf<Pair<String, UserHandle>, Set<String>>() for ((userPackage, permGroups) in packageAutoRevokedPermsList) { autoRevokedCopy[userPackage] = permGroups.toSet() } @@ -155,9 +153,7 @@ object AutoRevokedPackagesLiveData private val autoRevokedPackagesSetLiveData = object : SmartUpdateMediatorLiveData<Set<Pair<String, UserHandle>>>() { init { - addSource(AutoRevokedPackagesLiveData) { - update() - } + addSource(AutoRevokedPackagesLiveData) { update() } } override fun onUpdate() { @@ -168,4 +164,4 @@ private val autoRevokedPackagesSetLiveData = } } -val unusedAutoRevokePackagesLiveData = UnusedPackagesLiveData(autoRevokedPackagesSetLiveData)
\ No newline at end of file +val unusedAutoRevokePackagesLiveData = UnusedPackagesLiveData(autoRevokedPackagesSetLiveData) diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/BroadcastReceiverLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/BroadcastReceiverLiveData.kt index 0a296d977..e14a02115 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/data/BroadcastReceiverLiveData.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/data/BroadcastReceiverLiveData.kt @@ -41,9 +41,10 @@ class BroadcastReceiverLiveData( override val intentAction: String, private val permission: String, private val user: UserHandle -) : SmartAsyncMediatorLiveData<Set<String>>(), - PackageBroadcastReceiver.PackageBroadcastListener, - HasIntentAction { +) : + SmartAsyncMediatorLiveData<Set<String>>(), + PackageBroadcastReceiver.PackageBroadcastListener, + HasIntentAction { private val name = intentAction.substringAfterLast(".") @@ -51,9 +52,7 @@ class BroadcastReceiverLiveData( init { if (intentAction == DeviceAdminReceiver.ACTION_DEVICE_ADMIN_ENABLED) { - addSource(enabledDeviceAdminsLiveDataLiveData) { - updateAsync() - } + addSource(enabledDeviceAdminsLiveDataLiveData) { updateAsync() } } } @@ -65,15 +64,20 @@ class BroadcastReceiverLiveData( if (job.isCancelled) { return } - if (intentAction == DeviceAdminReceiver.ACTION_DEVICE_ADMIN_ENABLED && - !enabledDeviceAdminsLiveDataLiveData.isInitialized) { + if ( + intentAction == DeviceAdminReceiver.ACTION_DEVICE_ADMIN_ENABLED && + !enabledDeviceAdminsLiveDataLiveData.isInitialized + ) { return } - val packageNames = getUserContext(app, user).packageManager + val packageNames = + getUserContext(app, user) + .packageManager .queryBroadcastReceivers( - Intent(intentAction), - PackageManager.GET_RECEIVERS or PackageManager.GET_META_DATA) + Intent(intentAction), + PackageManager.GET_RECEIVERS or PackageManager.GET_META_DATA + ) .mapNotNull { resolveInfo -> if (resolveInfo?.activityInfo?.permission != permission) { return@mapNotNull null @@ -81,17 +85,22 @@ class BroadcastReceiverLiveData( val packageName = resolveInfo.activityInfo?.packageName if (!isReceiverEnabled(packageName)) { if (DEBUG_HIBERNATION_POLICY) { - DumpableLog.i(LOG_TAG, - "Not exempting $packageName - not an active $name " + - "for u${user.identifier}") + DumpableLog.i( + LOG_TAG, + "Not exempting $packageName - not an active $name " + + "for u${user.identifier}" + ) } return@mapNotNull null } packageName - }.toSet() + } + .toSet() if (DEBUG_HIBERNATION_POLICY) { - DumpableLog.i(LOG_TAG, - "Detected ${intentAction.substringAfterLast(".")}s: $packageNames") + DumpableLog.i( + LOG_TAG, + "Detected ${intentAction.substringAfterLast(".")}s: $packageNames" + ) } postValue(packageNames) @@ -127,13 +136,17 @@ class BroadcastReceiverLiveData( * <p> Key value is a (string intent action, required permission, user) triple, value is its * corresponding LiveData. */ - companion object : DataRepositoryForPackage<Triple<String, String, UserHandle>, - BroadcastReceiverLiveData>() { + companion object : + DataRepositoryForPackage<Triple<String, String, UserHandle>, BroadcastReceiverLiveData>() { private const val LOG_TAG = "BroadcastReceiverLiveData" override fun newValue(key: Triple<String, String, UserHandle>): BroadcastReceiverLiveData { - return BroadcastReceiverLiveData(PermissionControllerApplication.get(), - key.first, key.second, key.third) + return BroadcastReceiverLiveData( + PermissionControllerApplication.get(), + key.first, + key.second, + key.third + ) } } -}
\ No newline at end of file +} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/CarrierPrivilegedStatusLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/CarrierPrivilegedStatusLiveData.kt index fae7f223b..d84db741c 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/data/CarrierPrivilegedStatusLiveData.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/data/CarrierPrivilegedStatusLiveData.kt @@ -26,10 +26,9 @@ import com.android.permissioncontroller.PermissionControllerApplication * @param app The current application * @param packageName The name of the package */ -class CarrierPrivilegedStatusLiveData private constructor( - private val app: Application, - private val packageName: String -) : SmartUpdateMediatorLiveData<Int>() { +class CarrierPrivilegedStatusLiveData +private constructor(private val app: Application, private val packageName: String) : + SmartUpdateMediatorLiveData<Int>() { private val telephonyManager = app.getSystemService(TelephonyManager::class.java)!! @@ -44,13 +43,13 @@ class CarrierPrivilegedStatusLiveData private constructor( /** * Repository for [CarrierPrivilegedStatusLiveData]. + * * <p> Key value is a package name, value is its corresponding LiveData of * [android.telephony.Annotation.CarrierPrivilegeStatus] */ - companion object - : DataRepository<String, CarrierPrivilegedStatusLiveData>() { + companion object : DataRepository<String, CarrierPrivilegedStatusLiveData>() { override fun newValue(key: String): CarrierPrivilegedStatusLiveData { return CarrierPrivilegedStatusLiveData(PermissionControllerApplication.get(), key) } } -}
\ No newline at end of file +} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/CustomPermGroupNamesLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/CustomPermGroupNamesLiveData.kt index cb44c0a27..757472464 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/data/CustomPermGroupNamesLiveData.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/data/CustomPermGroupNamesLiveData.kt @@ -24,7 +24,6 @@ import com.android.permissioncontroller.permission.utils.PermissionMapping /** * A class which tracks the names of all custom permission groups in the system, including * non-grouped runtime permissions, the UNDEFINED group, and any group not defined by the system. - * */ object CustomPermGroupNamesLiveData : SmartUpdateMediatorLiveData<List<String>>() { @@ -32,9 +31,7 @@ object CustomPermGroupNamesLiveData : SmartUpdateMediatorLiveData<List<String>>( private val packagesLiveData = AllPackageInfosLiveData init { - addSource(packagesLiveData) { - update() - } + addSource(packagesLiveData) { update() } } override fun onUpdate() { @@ -49,15 +46,19 @@ object CustomPermGroupNamesLiveData : SmartUpdateMediatorLiveData<List<String>>( packageInfo.permissions.let { for (permission in it) { // We care only about installed runtime permissions. - if (permission.protection != PermissionInfo.PROTECTION_DANGEROUS || - permission.flags and PermissionInfo.FLAG_INSTALLED == 0) { + if ( + permission.protection != PermissionInfo.PROTECTION_DANGEROUS || + permission.flags and PermissionInfo.FLAG_INSTALLED == 0 + ) { continue } // If this permission is already in a group, no more work to do - if (groupNames.contains(permission.group) || - platformGroupNames.contains(permission.group) || - groupNames.contains(permission.name)) { + if ( + groupNames.contains(permission.group) || + platformGroupNames.contains(permission.group) || + groupNames.contains(permission.name) + ) { continue } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/DataRepository.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/DataRepository.kt index 5cb91f5c5..c6c4ec2d6 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/data/DataRepository.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/data/DataRepository.kt @@ -39,18 +39,16 @@ abstract class DataRepository<K, V : DataRepository.InactiveTimekeeper> : Compon private val TIME_THRESHOLD_ALL_NANOS: Long = 0 protected val lock = Any() - @GuardedBy("lock") - protected val data = mutableMapOf<K, V>() + @GuardedBy("lock") protected val data = mutableMapOf<K, V>() - /** - * Whether or not this data repository has been registered as a component callback yet - */ + /** Whether or not this data repository has been registered as a component callback yet */ private var registered = false - /** - * Whether or not this device is a low-RAM device. - */ - private var isLowMemoryDevice = PermissionControllerApplication.get().getSystemService( - ActivityManager::class.java)?.isLowRamDevice ?: false + /** Whether or not this device is a low-RAM device. */ + private var isLowMemoryDevice = + PermissionControllerApplication.get() + .getSystemService(ActivityManager::class.java) + ?.isLowRamDevice + ?: false init { PermissionControllerApplication.get().registerComponentCallbacks(this) @@ -60,7 +58,6 @@ abstract class DataRepository<K, V : DataRepository.InactiveTimekeeper> : Compon * Get a value from this repository, creating it if needed * * @param key The key associated with the desired Value - * * @return The cached or newly created Value for the given Key */ operator fun get(key: K): V { @@ -73,11 +70,9 @@ abstract class DataRepository<K, V : DataRepository.InactiveTimekeeper> : Compon * Generate a new value type from the given data * * @param key Information about this value object, used to instantiate it - * * @return The generated Value */ - @MainThread - protected abstract fun newValue(key: K): V + @MainThread protected abstract fun newValue(key: K): V /** * Remove LiveData objects with no observer based on the severity of the memory pressure. If @@ -91,15 +86,18 @@ abstract class DataRepository<K, V : DataRepository.InactiveTimekeeper> : Compon return } - trimInactiveData(threshold = when (level) { - ComponentCallbacks2.TRIM_MEMORY_BACKGROUND -> TIME_THRESHOLD_LAX_NANOS - ComponentCallbacks2.TRIM_MEMORY_MODERATE -> TIME_THRESHOLD_TIGHT_NANOS - ComponentCallbacks2.TRIM_MEMORY_COMPLETE -> TIME_THRESHOLD_ALL_NANOS - ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE -> TIME_THRESHOLD_LAX_NANOS - ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW -> TIME_THRESHOLD_TIGHT_NANOS - ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL -> TIME_THRESHOLD_ALL_NANOS - else -> return - }) + trimInactiveData( + threshold = + when (level) { + ComponentCallbacks2.TRIM_MEMORY_BACKGROUND -> TIME_THRESHOLD_LAX_NANOS + ComponentCallbacks2.TRIM_MEMORY_MODERATE -> TIME_THRESHOLD_TIGHT_NANOS + ComponentCallbacks2.TRIM_MEMORY_COMPLETE -> TIME_THRESHOLD_ALL_NANOS + ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE -> TIME_THRESHOLD_LAX_NANOS + ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW -> TIME_THRESHOLD_TIGHT_NANOS + ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL -> TIME_THRESHOLD_ALL_NANOS + else -> return + } + ) } override fun onLowMemory() { @@ -111,9 +109,7 @@ abstract class DataRepository<K, V : DataRepository.InactiveTimekeeper> : Compon } fun invalidateSingle(key: K) { - synchronized(lock) { - data.remove(key) - } + synchronized(lock) { data.remove(key) } } private fun trimInactiveData(threshold: Long) { @@ -127,8 +123,8 @@ abstract class DataRepository<K, V : DataRepository.InactiveTimekeeper> : Compon } /** - * Interface which describes an object which can track how long it has been inactive, and if - * it has any observers. + * Interface which describes an object which can track how long it has been inactive, and if it + * has any observers. */ interface InactiveTimekeeper { @@ -156,8 +152,8 @@ abstract class DataRepository<K, V : DataRepository.InactiveTimekeeper> : Compon * invalidating all values tied to a package. Expects key to be a pair or triple, with the package * name as the first value of the key. */ -abstract class DataRepositoryForPackage<K, V : DataRepository.InactiveTimekeeper> - : DataRepository<K, V>() { +abstract class DataRepositoryForPackage<K, V : DataRepository.InactiveTimekeeper> : + DataRepository<K, V>() { /** * Invalidates every value with the packageName in the key. @@ -175,9 +171,7 @@ abstract class DataRepositoryForPackage<K, V : DataRepository.InactiveTimekeeper } } -/** - * A convenience to retrieve data from a repository with a composite key - */ +/** A convenience to retrieve data from a repository with a composite key */ operator fun <K1, K2, V : DataRepository.InactiveTimekeeper> DataRepository<Pair<K1, K2>, V>.get( k1: K1, k2: K2 @@ -185,14 +179,10 @@ operator fun <K1, K2, V : DataRepository.InactiveTimekeeper> DataRepository<Pair return get(k1 to k2) } -/** - * A convenience to retrieve data from a repository with a composite key - */ -operator fun <K1, K2, K3, V : DataRepository.InactiveTimekeeper> - DataRepository<Triple<K1, K2, K3>, V>.get( - k1: K1, - k2: K2, - k3: K3 - ): V { +/** A convenience to retrieve data from a repository with a composite key */ +operator fun <K1, K2, K3, V : DataRepository.InactiveTimekeeper> DataRepository< + Triple<K1, K2, K3>, V +> + .get(k1: K1, k2: K2, k3: K3): V { return get(Triple(k1, k2, k3)) } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/DisabledPrintServicesLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/DisabledPrintServicesLiveData.kt index 3abb20564..4d9c2574b 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/data/DisabledPrintServicesLiveData.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/data/DisabledPrintServicesLiveData.kt @@ -30,28 +30,26 @@ import kotlinx.coroutines.Job * @param app The current application * @param user The user the services should be determined for */ -class DisabledPrintServicesLiveData( - private val app: Application, - private val user: UserHandle -) : SmartAsyncMediatorLiveData<List<String>>() { +class DisabledPrintServicesLiveData(private val app: Application, private val user: UserHandle) : + SmartAsyncMediatorLiveData<List<String>>() { override suspend fun loadDataAndPostValue(job: Job) { if (job.isCancelled) { return } - val packageNames = Settings.Secure.getString( - Utils.getUserContext(app, user).contentResolver, SETTING) + val packageNames = + Settings.Secure.getString(Utils.getUserContext(app, user).contentResolver, SETTING) ?.split(":") ?.map { pkgOrComponent -> if ('/' in pkgOrComponent) { - ComponentName.unflattenFromString(pkgOrComponent) - ?.packageName - ?: pkgOrComponent + ComponentName.unflattenFromString(pkgOrComponent)?.packageName + ?: pkgOrComponent } else { pkgOrComponent } - } ?: emptyList() + } + ?: emptyList() postValue(packageNames) } @@ -61,8 +59,7 @@ class DisabledPrintServicesLiveData( * * <p> Key value is a user, value is its corresponding LiveData. */ - companion object : DataRepositoryForPackage<UserHandle, - DisabledPrintServicesLiveData>() { + companion object : DataRepositoryForPackage<UserHandle, DisabledPrintServicesLiveData>() { /* Settings.Secure.DISABLED_PRINT_SERVICES */ private const val SETTING = "disabled_print_services" @@ -70,4 +67,4 @@ class DisabledPrintServicesLiveData( return DisabledPrintServicesLiveData(PermissionControllerApplication.get(), key) } } -}
\ No newline at end of file +} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/EnabledAccessibilityServicesLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/EnabledAccessibilityServicesLiveData.kt index 2b1391a98..9717d949c 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/data/EnabledAccessibilityServicesLiveData.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/data/EnabledAccessibilityServicesLiveData.kt @@ -21,8 +21,8 @@ import android.app.Application import android.os.UserHandle import android.view.accessibility.AccessibilityManager import com.android.permissioncontroller.PermissionControllerApplication -import com.android.permissioncontroller.permission.utils.componentInfo import com.android.permissioncontroller.permission.utils.Utils +import com.android.permissioncontroller.permission.utils.componentInfo import kotlinx.coroutines.Job /** @@ -42,7 +42,8 @@ class EnabledAccessibilityServicesLiveData( return } - val packageNames = Utils.getUserContext(app, user) + val packageNames = + Utils.getUserContext(app, user) .getSystemService(AccessibilityManager::class.java)!! .getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_ALL_MASK) .map { info: AccessibilityServiceInfo -> @@ -62,10 +63,10 @@ class EnabledAccessibilityServicesLiveData( * * <p> Key value is a user, value is its corresponding LiveData. */ - companion object : DataRepositoryForPackage<UserHandle, - EnabledAccessibilityServicesLiveData>() { + companion object : + DataRepositoryForPackage<UserHandle, EnabledAccessibilityServicesLiveData>() { override fun newValue(key: UserHandle): EnabledAccessibilityServicesLiveData { return EnabledAccessibilityServicesLiveData(PermissionControllerApplication.get(), key) } } -}
\ No newline at end of file +} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/EnabledDeviceAdminsLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/EnabledDeviceAdminsLiveData.kt index 60dcf59b0..eebcac06f 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/data/EnabledDeviceAdminsLiveData.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/data/EnabledDeviceAdminsLiveData.kt @@ -29,17 +29,16 @@ import kotlinx.coroutines.Job * @param app The current application * @param user The user the services should be determined for */ -class EnabledDeviceAdminsLiveData( - private val app: Application, - private val user: UserHandle -) : SmartAsyncMediatorLiveData<List<String>>() { +class EnabledDeviceAdminsLiveData(private val app: Application, private val user: UserHandle) : + SmartAsyncMediatorLiveData<List<String>>() { override suspend fun loadDataAndPostValue(job: Job) { if (job.isCancelled) { return } - val packageNames = Utils.getUserContext(app, user) + val packageNames = + Utils.getUserContext(app, user) .getSystemService(DevicePolicyManager::class.java)!! .activeAdmins ?.map { component -> component.packageName } @@ -53,10 +52,9 @@ class EnabledDeviceAdminsLiveData( * * <p> Key value is a user, value is its corresponding LiveData. */ - companion object : DataRepositoryForPackage<UserHandle, - EnabledDeviceAdminsLiveData>() { + companion object : DataRepositoryForPackage<UserHandle, EnabledDeviceAdminsLiveData>() { override fun newValue(key: UserHandle): EnabledDeviceAdminsLiveData { return EnabledDeviceAdminsLiveData(PermissionControllerApplication.get(), key) } } -}
\ No newline at end of file +} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/EnabledDreamServicesLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/EnabledDreamServicesLiveData.kt index 200384aab..9b8554e80 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/data/EnabledDreamServicesLiveData.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/data/EnabledDreamServicesLiveData.kt @@ -30,28 +30,26 @@ import kotlinx.coroutines.Job * @param app The current application * @param user The user the services should be determined for */ -class EnabledDreamServicesLiveData( - private val app: Application, - private val user: UserHandle -) : SmartAsyncMediatorLiveData<List<String>>() { +class EnabledDreamServicesLiveData(private val app: Application, private val user: UserHandle) : + SmartAsyncMediatorLiveData<List<String>>() { override suspend fun loadDataAndPostValue(job: Job) { if (job.isCancelled) { return } - val packageNames = Settings.Secure.getString( - Utils.getUserContext(app, user).contentResolver, SETTING) + val packageNames = + Settings.Secure.getString(Utils.getUserContext(app, user).contentResolver, SETTING) ?.split(",") ?.map { pkgOrComponent -> if ('/' in pkgOrComponent) { - ComponentName.unflattenFromString(pkgOrComponent) - ?.packageName - ?: pkgOrComponent + ComponentName.unflattenFromString(pkgOrComponent)?.packageName + ?: pkgOrComponent } else { pkgOrComponent } - } ?: emptyList() + } + ?: emptyList() postValue(packageNames) } @@ -61,8 +59,7 @@ class EnabledDreamServicesLiveData( * * <p> Key value is a user, value is its corresponding LiveData. */ - companion object : DataRepositoryForPackage<UserHandle, - EnabledDreamServicesLiveData>() { + companion object : DataRepositoryForPackage<UserHandle, EnabledDreamServicesLiveData>() { /* Settings.Secure.ENABLED_NOTIFICATION_LISTENERS */ private const val SETTING = "enabled_notification_listeners" @@ -70,4 +67,4 @@ class EnabledDreamServicesLiveData( return EnabledDreamServicesLiveData(PermissionControllerApplication.get(), key) } } -}
\ No newline at end of file +} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/EnabledInputMethodsLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/EnabledInputMethodsLiveData.kt index d0d2783ab..e48c432d8 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/data/EnabledInputMethodsLiveData.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/data/EnabledInputMethodsLiveData.kt @@ -30,22 +30,19 @@ import kotlinx.coroutines.Job * @param app The current application * @param user The user the services should be determined for */ -class EnabledInputMethodsLiveData( - private val app: Application, - private val user: UserHandle -) : SmartAsyncMediatorLiveData<List<String>>() { +class EnabledInputMethodsLiveData(private val app: Application, private val user: UserHandle) : + SmartAsyncMediatorLiveData<List<String>>() { override suspend fun loadDataAndPostValue(job: Job) { if (job.isCancelled) { return } - val packageNames = Utils.getUserContext(app, user) + val packageNames = + Utils.getUserContext(app, user) .getSystemService(InputMethodManager::class.java)!! .enabledInputMethodList - .map { info: InputMethodInfo -> - info.component.packageName - } + .map { info: InputMethodInfo -> info.component.packageName } postValue(packageNames) } @@ -55,10 +52,9 @@ class EnabledInputMethodsLiveData( * * <p> Key value is a user, value is its corresponding LiveData. */ - companion object : DataRepositoryForPackage<UserHandle, - EnabledInputMethodsLiveData>() { + companion object : DataRepositoryForPackage<UserHandle, EnabledInputMethodsLiveData>() { override fun newValue(key: UserHandle): EnabledInputMethodsLiveData { return EnabledInputMethodsLiveData(PermissionControllerApplication.get(), key) } } -}
\ No newline at end of file +} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/EnabledNotificationListenersLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/EnabledNotificationListenersLiveData.kt index f5c5d4bf1..7dd1567e4 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/data/EnabledNotificationListenersLiveData.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/data/EnabledNotificationListenersLiveData.kt @@ -40,20 +40,22 @@ class EnabledNotificationListenersLiveData( return } - val packageNames = Settings.Secure.getString( - Utils.getUserContext(app, user).contentResolver, - /* Settings.Secure.ENABLED_NOTIFICATION_LISTENERS */ - "enabled_notification_listeners") + val packageNames = + Settings.Secure.getString( + Utils.getUserContext(app, user).contentResolver, + /* Settings.Secure.ENABLED_NOTIFICATION_LISTENERS */ + "enabled_notification_listeners" + ) ?.split(":") ?.map { pkgOrComponent -> if ('/' in pkgOrComponent) { - ComponentName.unflattenFromString(pkgOrComponent) - ?.packageName - ?: pkgOrComponent + ComponentName.unflattenFromString(pkgOrComponent)?.packageName + ?: pkgOrComponent } else { pkgOrComponent } - } ?: emptyList() + } + ?: emptyList() postValue(packageNames) } @@ -63,10 +65,10 @@ class EnabledNotificationListenersLiveData( * * <p> Key value is a user, value is its corresponding LiveData. */ - companion object : DataRepositoryForPackage<UserHandle, - EnabledNotificationListenersLiveData>() { + companion object : + DataRepositoryForPackage<UserHandle, EnabledNotificationListenersLiveData>() { override fun newValue(key: UserHandle): EnabledNotificationListenersLiveData { return EnabledNotificationListenersLiveData(PermissionControllerApplication.get(), key) } } -}
\ No newline at end of file +} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/ForegroundPermNamesLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/ForegroundPermNamesLiveData.kt index 1471ed15f..513f5fc4e 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/data/ForegroundPermNamesLiveData.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/data/ForegroundPermNamesLiveData.kt @@ -39,11 +39,12 @@ object ForegroundPermNamesLiveData : SmartAsyncMediatorLiveData<Map<String, List val systemGroups = PermissionMapping.getPlatformPermissionGroups() val permMap = mutableMapOf<String, MutableList<String>>() for (groupName in systemGroups) { - val permInfos = try { - Utils.getInstalledRuntimePermissionInfosForGroup(app.packageManager, groupName) - } catch (e: PackageManager.NameNotFoundException) { - continue - } + val permInfos = + try { + Utils.getInstalledRuntimePermissionInfosForGroup(app.packageManager, groupName) + } catch (e: PackageManager.NameNotFoundException) { + continue + } for (permInfo in permInfos) { val backgroundPerm: String? = permInfo.backgroundPermission if (backgroundPerm != null) { @@ -54,4 +55,4 @@ object ForegroundPermNamesLiveData : SmartAsyncMediatorLiveData<Map<String, List } postValue(permMap) } -}
\ No newline at end of file +} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/FullStoragePermissionAppsLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/FullStoragePermissionAppsLiveData.kt index 0b27dbff0..4a2d3b68a 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/data/FullStoragePermissionAppsLiveData.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/data/FullStoragePermissionAppsLiveData.kt @@ -34,14 +34,13 @@ import kotlinx.coroutines.Job /** * A liveData which tracks all packages in the system which have full file permissions, as * represented by the OPSTR_LEGACY_STORAGE app op, not just media-only storage permissions. - * */ object FullStoragePermissionAppsLiveData : SmartAsyncMediatorLiveData<List<FullStoragePermissionAppsLiveData.FullStoragePackageState>>() { private val app: Application = PermissionControllerApplication.get() - private val standardPermGroupsPackagesLiveData = PermGroupsPackagesLiveData.get( - customGroups = false) + private val standardPermGroupsPackagesLiveData = + PermGroupsPackagesLiveData.get(customGroups = false) data class FullStoragePackageState( val packageName: String, @@ -51,12 +50,8 @@ object FullStoragePermissionAppsLiveData : ) init { - addSource(standardPermGroupsPackagesLiveData) { - updateAsync() - } - addSource(AllPackageInfosLiveData) { - updateAsync() - } + addSource(standardPermGroupsPackagesLiveData) { updateAsync() } + addSource(AllPackageInfosLiveData) { updateAsync() } } override suspend fun loadDataAndPostValue(job: Job) { @@ -65,14 +60,16 @@ object FullStoragePermissionAppsLiveData : val fullStoragePackages = mutableListOf<FullStoragePackageState>() for ((user, packageInfoList) in AllPackageInfosLiveData.value ?: emptyMap()) { - val userPackages = packageInfoList.filter { - storagePackages.contains(it.packageName to user) || - it.requestedPermissions.contains(MANAGE_EXTERNAL_STORAGE) - } + val userPackages = + packageInfoList.filter { + storagePackages.contains(it.packageName to user) || + it.requestedPermissions.contains(MANAGE_EXTERNAL_STORAGE) + } for (packageInfo in userPackages) { - fullStoragePackages.add(getFullStorageStateForPackage(appOpsManager, - packageInfo, user) ?: continue) + fullStoragePackages.add( + getFullStorageStateForPackage(appOpsManager, packageInfo, user) ?: continue + ) } } @@ -85,9 +82,8 @@ object FullStoragePermissionAppsLiveData : * @param appOpsManager The App Ops manager to use, if applicable * @param packageInfo The package whose state is to be determined * @param userHandle A preexisting UserHandle object to use. Otherwise, one will be created - * * @return the FullStoragePackageState for the package, or null if the package does not request - * full storage permissions + * full storage permissions */ fun getFullStorageStateForPackage( appOpsManager: AppOpsManager, @@ -97,31 +93,51 @@ object FullStoragePermissionAppsLiveData : val sdk = packageInfo.targetSdkVersion val user = userHandle ?: UserHandle.getUserHandleForUid(packageInfo.uid) if (sdk < Build.VERSION_CODES.P) { - return FullStoragePackageState(packageInfo.packageName, user, - isLegacy = true, isGranted = true) - } else if (sdk <= Build.VERSION_CODES.Q && - appOpsManager.unsafeCheckOpNoThrow(OPSTR_LEGACY_STORAGE, packageInfo.uid, - packageInfo.packageName) == MODE_ALLOWED) { - return FullStoragePackageState(packageInfo.packageName, user, - isLegacy = true, isGranted = true) + return FullStoragePackageState( + packageInfo.packageName, + user, + isLegacy = true, + isGranted = true + ) + } else if ( + sdk <= Build.VERSION_CODES.Q && + appOpsManager.unsafeCheckOpNoThrow( + OPSTR_LEGACY_STORAGE, + packageInfo.uid, + packageInfo.packageName + ) == MODE_ALLOWED + ) { + return FullStoragePackageState( + packageInfo.packageName, + user, + isLegacy = true, + isGranted = true + ) } if (MANAGE_EXTERNAL_STORAGE in packageInfo.requestedPermissions) { - val mode = appOpsManager.unsafeCheckOpNoThrow(OPSTR_MANAGE_EXTERNAL_STORAGE, - packageInfo.uid, packageInfo.packageName) - val granted = mode == MODE_ALLOWED || mode == MODE_FOREGROUND || - (mode == MODE_DEFAULT && - MANAGE_EXTERNAL_STORAGE in packageInfo.grantedPermissions) - return FullStoragePackageState(packageInfo.packageName, user, - isLegacy = false, isGranted = granted) + val mode = + appOpsManager.unsafeCheckOpNoThrow( + OPSTR_MANAGE_EXTERNAL_STORAGE, + packageInfo.uid, + packageInfo.packageName + ) + val granted = + mode == MODE_ALLOWED || + mode == MODE_FOREGROUND || + (mode == MODE_DEFAULT && + MANAGE_EXTERNAL_STORAGE in packageInfo.grantedPermissions) + return FullStoragePackageState( + packageInfo.packageName, + user, + isLegacy = false, + isGranted = granted + ) } return null } - /** - * Recalculate the LiveData - * TODO ntmyren: Make livedata properly observe app ops - */ + /** Recalculate the LiveData TODO ntmyren: Make livedata properly observe app ops */ fun recalculate() { updateAsync() } -}
\ No newline at end of file +} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/HasIntentAction.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/HasIntentAction.kt index 91557e441..1c0325ec6 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/data/HasIntentAction.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/data/HasIntentAction.kt @@ -16,9 +16,7 @@ package com.android.permissioncontroller.permission.data -/** - * An interface for classes that have an [Intent] action - */ +/** An interface for classes that have an [Intent] action */ interface HasIntentAction { val intentAction: String -}
\ No newline at end of file +} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/HibernatedPackagesLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/HibernatedPackagesLiveData.kt index 887998a2e..9fdb8411a 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/data/HibernatedPackagesLiveData.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/data/HibernatedPackagesLiveData.kt @@ -25,17 +25,12 @@ import com.android.permissioncontroller.PermissionControllerApplication import com.android.permissioncontroller.permission.utils.Utils.getUserContext import kotlinx.coroutines.Job -/** - * Tracks which packages have been hibernated. - */ -object HibernatedPackagesLiveData - : SmartAsyncMediatorLiveData<Set<Pair<String, UserHandle>>>() { +/** Tracks which packages have been hibernated. */ +object HibernatedPackagesLiveData : SmartAsyncMediatorLiveData<Set<Pair<String, UserHandle>>>() { private val LOG_TAG = HibernatedPackagesLiveData::class.java.simpleName init { - addSource(AllPackageInfosLiveData) { - update() - } + addSource(AllPackageInfosLiveData) { update() } } override suspend fun loadDataAndPostValue(job: Job) { @@ -57,8 +52,10 @@ object HibernatedPackagesLiveData hibernatingPackages.add(pkg.packageName to user) } } catch (e: Exception) { - DumpableLog.e(LOG_TAG, - "Failed to get hibernation state of package: ${pkg.packageName}") + DumpableLog.e( + LOG_TAG, + "Failed to get hibernation state of package: ${pkg.packageName}" + ) } } } @@ -69,25 +66,23 @@ object HibernatedPackagesLiveData } } -private val hibernatedOrRevokedPackagesLiveData = object - : SmartUpdateMediatorLiveData<Set<Pair<String, UserHandle>>>() { +private val hibernatedOrRevokedPackagesLiveData = + object : SmartUpdateMediatorLiveData<Set<Pair<String, UserHandle>>>() { - init { - addSource(AutoRevokedPackagesLiveData) { - update() - } - addSource(HibernatedPackagesLiveData) { - update() + init { + addSource(AutoRevokedPackagesLiveData) { update() } + addSource(HibernatedPackagesLiveData) { update() } } - } - override fun onUpdate() { - if (!AutoRevokedPackagesLiveData.isInitialized || - !HibernatedPackagesLiveData.isInitialized) { - return + override fun onUpdate() { + if ( + !AutoRevokedPackagesLiveData.isInitialized || + !HibernatedPackagesLiveData.isInitialized + ) { + return + } + value = AutoRevokedPackagesLiveData.value!!.keys + HibernatedPackagesLiveData.value!! } - value = AutoRevokedPackagesLiveData.value!!.keys + HibernatedPackagesLiveData.value!! } -} val unusedHibernatedOrRevokedPackagesLiveData = - UnusedPackagesLiveData(hibernatedOrRevokedPackagesLiveData)
\ No newline at end of file + UnusedPackagesLiveData(hibernatedOrRevokedPackagesLiveData) diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/HibernationSettingStateLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/HibernationSettingStateLiveData.kt index 606562641..75d965d02 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/data/HibernationSettingStateLiveData.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/data/HibernationSettingStateLiveData.kt @@ -34,6 +34,7 @@ import com.android.permissioncontroller.hibernation.isPackageHibernationExemptBy import com.android.permissioncontroller.hibernation.isPackageHibernationExemptByUser import com.android.permissioncontroller.permission.data.PackagePermissionsLiveData.Companion.NON_RUNTIME_NORMAL_PERMS import com.android.permissioncontroller.permission.model.livedatatypes.HibernationSettingState +import com.android.permissioncontroller.permission.service.AUTO_REVOKE_EXEMPT_PERMISSIONS import kotlinx.coroutines.Job /** @@ -43,14 +44,14 @@ import kotlinx.coroutines.Job * @param packageName The package name whose state we want * @param user The user for whom we want the package */ -class HibernationSettingStateLiveData private constructor( +class HibernationSettingStateLiveData +private constructor( private val app: Application, private val packageName: String, private val user: UserHandle ) : SmartAsyncMediatorLiveData<HibernationSettingState>(), AppOpsManager.OnOpChangedListener { - private val packagePermsLiveData = - PackagePermissionsLiveData[packageName, user] + private val packagePermsLiveData = PackagePermissionsLiveData[packageName, user] private val packageLiveData = LightPackageInfoLiveData[packageName, user] private val permStateLiveDatas = mutableMapOf<String, PermStateLiveData>() private val exemptServicesLiveData = ExemptServicesLiveData[user] @@ -64,26 +65,19 @@ class HibernationSettingStateLiveData private constructor( private var gotPastIsSystemExempt: Boolean = false init { - addSource(packagePermsLiveData) { - update() - } - addSource(packageLiveData) { - update() - } - addSource(exemptServicesLiveData) { - update() - } - addSource(HibernationEnabledLiveData) { - update() - } - Handler(app.mainLooper).postDelayed({ - logState() - }, DELAY_MS) + addSource(packagePermsLiveData) { update() } + addSource(packageLiveData) { update() } + addSource(exemptServicesLiveData) { update() } + addSource(HibernationEnabledLiveData) { update() } + Handler(app.mainLooper).postDelayed({ logState() }, DELAY_MS) } override suspend fun loadDataAndPostValue(job: Job) { - if (!packageLiveData.isInitialized || !packagePermsLiveData.isInitialized || - !exemptServicesLiveData.isInitialized) { + if ( + !packageLiveData.isInitialized || + !packagePermsLiveData.isInitialized || + !exemptServicesLiveData.isInitialized + ) { return } @@ -103,21 +97,30 @@ class HibernationSettingStateLiveData private constructor( val exemptBySystem = isPackageHibernationExemptBySystem(packageInfo, user) val exemptByUser = isPackageHibernationExemptByUser(app, packageInfo) - val eligibility = when { - !exemptBySystem && !exemptByUser -> HIBERNATION_ELIGIBILITY_ELIGIBLE - exemptBySystem -> HIBERNATION_ELIGIBILITY_EXEMPT_BY_SYSTEM - else -> HIBERNATION_ELIGIBILITY_EXEMPT_BY_USER - } + val eligibility = + when { + !exemptBySystem && !exemptByUser -> HIBERNATION_ELIGIBILITY_ELIGIBLE + exemptBySystem -> HIBERNATION_ELIGIBILITY_EXEMPT_BY_SYSTEM + else -> HIBERNATION_ELIGIBILITY_EXEMPT_BY_USER + } gotPastIsUserExempt = true val revocableGroups = mutableListOf<String>() if (!isPackageHibernationExemptBySystem(packageInfo, user)) { gotPastIsSystemExempt = true permStateLiveDatas.forEach { (groupName, liveData) -> - val default = liveData.value?.any { (_, permState) -> - permState.permFlags and (FLAG_PERMISSION_GRANTED_BY_DEFAULT or - FLAG_PERMISSION_GRANTED_BY_ROLE) != 0 - } ?: false - if (!default) { + val default = + liveData.value?.any { (_, permState) -> + permState.permFlags and + (FLAG_PERMISSION_GRANTED_BY_DEFAULT or + FLAG_PERMISSION_GRANTED_BY_ROLE) != 0 + } + ?: false + val allExempt = + liveData.value?.all { (permName, _) -> + permName in AUTO_REVOKE_EXEMPT_PERMISSIONS + } + ?: false + if (!default && !allExempt) { revocableGroups.add(groupName) } } @@ -148,29 +151,45 @@ class HibernationSettingStateLiveData private constructor( if (!isStale) { return } - Log.i(LOG_TAG, "overall state: isStale:$isStale, isInitialized:$isInitialized, " + + Log.i( + LOG_TAG, + "overall state: isStale:$isStale, isInitialized:$isInitialized, " + "value:$value, got perm LiveDatas:$gotPermLiveDatas, " + - "got isUserExempt$gotPastIsUserExempt, got isSystemExempt$gotPastIsSystemExempt") - Log.i(LOG_TAG, "packagePermsLivedata isStale:${packagePermsLiveData.isStale}, " + - "isInitialized:${packagePermsLiveData.isInitialized}") - Log.i(LOG_TAG, "ExemptServicesLiveData isStale:${exemptServicesLiveData.isStale}, " + - "isInitialized:${exemptServicesLiveData.isInitialized}") + "got isUserExempt$gotPastIsUserExempt, got isSystemExempt$gotPastIsSystemExempt" + ) + Log.i( + LOG_TAG, + "packagePermsLivedata isStale:${packagePermsLiveData.isStale}, " + + "isInitialized:${packagePermsLiveData.isInitialized}" + ) + Log.i( + LOG_TAG, + "ExemptServicesLiveData isStale:${exemptServicesLiveData.isStale}, " + + "isInitialized:${exemptServicesLiveData.isInitialized}" + ) Log.i(LOG_TAG, "HibernationEnabledLivedata value:${HibernationEnabledLiveData.value}") for ((group, liveData) in permStateLiveDatas) { - Log.i(LOG_TAG, "permStateLivedata $group isStale:${liveData.isStale}, " + - "isInitialized:${liveData.isInitialized}") + Log.i( + LOG_TAG, + "permStateLivedata $group isStale:${liveData.isStale}, " + + "isInitialized:${liveData.isInitialized}" + ) } } /** * Repository for HibernationSettingStateLiveDatas. + * * <p> Key value is a pair of string package name and UserHandle, value is its corresponding * LiveData. */ - companion object : DataRepositoryForPackage<Pair<String, UserHandle>, - HibernationSettingStateLiveData>() { + companion object : + DataRepositoryForPackage<Pair<String, UserHandle>, HibernationSettingStateLiveData>() { override fun newValue(key: Pair<String, UserHandle>): HibernationSettingStateLiveData { - return HibernationSettingStateLiveData(PermissionControllerApplication.get(), - key.first, key.second) + return HibernationSettingStateLiveData( + PermissionControllerApplication.get(), + key.first, + key.second + ) } } } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/LauncherPackagesLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/LauncherPackagesLiveData.kt index b512c7e4a..94bf230d7 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/data/LauncherPackagesLiveData.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/data/LauncherPackagesLiveData.kt @@ -18,39 +18,43 @@ package com.android.permissioncontroller.permission.data import android.content.Intent +import android.content.pm.PackageManager.FEATURE_LEANBACK import android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE import android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE -import android.content.pm.PackageManager.FEATURE_LEANBACK import com.android.permissioncontroller.PermissionControllerApplication import kotlinx.coroutines.Job -/** - * A livedata which stores a list of package names of packages which have launcher icons. - */ -object LauncherPackagesLiveData : SmartAsyncMediatorLiveData<Set<String>>(), - PackageBroadcastReceiver.PackageBroadcastListener { +/** A livedata which stores a list of package names of packages which have launcher icons. */ +object LauncherPackagesLiveData : + SmartAsyncMediatorLiveData<Set<String>>(), PackageBroadcastReceiver.PackageBroadcastListener { - private val LAUNCHER_INTENT = Intent(Intent.ACTION_MAIN, null) - .addCategory(Intent.CATEGORY_LAUNCHER) + private val LAUNCHER_INTENT = + Intent(Intent.ACTION_MAIN, null).addCategory(Intent.CATEGORY_LAUNCHER) // On ATV some apps may have a leanback launcher icon but no regular launcher icon - private val LEANBACK_LAUNCHER_INTENT = Intent(Intent.ACTION_MAIN, null) - .addCategory(Intent.CATEGORY_LEANBACK_LAUNCHER) + private val LEANBACK_LAUNCHER_INTENT = + Intent(Intent.ACTION_MAIN, null).addCategory(Intent.CATEGORY_LEANBACK_LAUNCHER) override suspend fun loadDataAndPostValue(job: Job) { val launcherPkgs = mutableSetOf<String>() loadPkgsFromIntent(launcherPkgs, LAUNCHER_INTENT) - if (PermissionControllerApplication.get().packageManager - .hasSystemFeature(FEATURE_LEANBACK)) { + if ( + PermissionControllerApplication.get().packageManager.hasSystemFeature(FEATURE_LEANBACK) + ) { loadPkgsFromIntent(launcherPkgs, LEANBACK_LAUNCHER_INTENT) } postValue(launcherPkgs) } private fun loadPkgsFromIntent(launcherPkgs: MutableSet<String>, intent: Intent) { - for (info in PermissionControllerApplication.get().packageManager.queryIntentActivities( - intent, MATCH_DIRECT_BOOT_AWARE or MATCH_DIRECT_BOOT_UNAWARE)) { + for (info in + PermissionControllerApplication.get() + .packageManager + .queryIntentActivities( + intent, + MATCH_DIRECT_BOOT_AWARE or MATCH_DIRECT_BOOT_UNAWARE + )) { launcherPkgs.add(info.activityInfo.packageName) } } @@ -69,4 +73,4 @@ object LauncherPackagesLiveData : SmartAsyncMediatorLiveData<Set<String>>(), super.onInactive() PackageBroadcastReceiver.removeAllCallback(this) } -}
\ No newline at end of file +} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/LightAppPermGroupLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/LightAppPermGroupLiveData.kt index 3621319a6..db606f68d 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/data/LightAppPermGroupLiveData.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/data/LightAppPermGroupLiveData.kt @@ -39,7 +39,8 @@ import com.android.permissioncontroller.permission.utils.Utils.OS_PKG * @param permGroupName The name of the permission group * @param user The user of the package */ -class LightAppPermGroupLiveData private constructor( +class LightAppPermGroupLiveData +private constructor( private val app: Application, private val packageName: String, private val permGroupName: String, @@ -55,13 +56,15 @@ class LightAppPermGroupLiveData private constructor( private val fgPermNamesLiveData = ForegroundPermNamesLiveData init { - isSpecialLocation = LocationUtils.isLocationGroupAndProvider(app, - permGroupName, packageName) || - LocationUtils.isLocationGroupAndControllerExtraPackage(app, permGroupName, packageName) + isSpecialLocation = + LocationUtils.isLocationGroupAndProvider(app, permGroupName, packageName) || + LocationUtils.isLocationGroupAndControllerExtraPackage( + app, + permGroupName, + packageName + ) - addSource(fgPermNamesLiveData) { - update() - } + addSource(fgPermNamesLiveData) { update() } val key = Triple(packageName, permGroupName, user) @@ -100,8 +103,10 @@ class LightAppPermGroupLiveData private constructor( val allForegroundPerms = fgPermNamesLiveData.value ?: return // Do not allow toggling pre-M custom perm groups - if (packageInfo.targetSdkVersion < Build.VERSION_CODES.M && - permGroup.groupInfo.packageName != OS_PKG) { + if ( + packageInfo.targetSdkVersion < Build.VERSION_CODES.M && + permGroup.groupInfo.packageName != OS_PKG + ) { value = LightAppPermGroup(packageInfo, permGroup.groupInfo, emptyMap()) return } @@ -110,8 +115,8 @@ class LightAppPermGroupLiveData private constructor( for ((permName, permState) in permStates) { val permInfo = permGroup.permissionInfos[permName] ?: continue val foregroundPerms = allForegroundPerms[permName] - permissionMap[permName] = LightPermission(packageInfo, permInfo, permState, - foregroundPerms) + permissionMap[permName] = + LightPermission(packageInfo, permInfo, permState, foregroundPerms) } // Determine if this app permission group is a special location package or provider @@ -119,17 +124,24 @@ class LightAppPermGroupLiveData private constructor( val userContext = Utils.getUserContext(app, user) if (LocationUtils.isLocationGroupAndProvider(userContext, permGroupName, packageName)) { specialLocationGrant = LocationUtils.isLocationEnabled(userContext) - } else if (LocationUtils.isLocationGroupAndControllerExtraPackage(app, permGroupName, - packageName)) { + } else if ( + LocationUtils.isLocationGroupAndControllerExtraPackage(app, permGroupName, packageName) + ) { // The permission of the extra location controller package is determined by the status // of the controller package itself. - specialLocationGrant = LocationUtils.isExtraLocationControllerPackageEnabled( - userContext) + specialLocationGrant = + LocationUtils.isExtraLocationControllerPackageEnabled(userContext) } val hasInstallToRuntimeSplit = hasInstallToRuntimeSplit(packageInfo, permissionMap) - value = LightAppPermGroup(packageInfo, permGroup.groupInfo, permissionMap, - hasInstallToRuntimeSplit, specialLocationGrant) + value = + LightAppPermGroup( + packageInfo, + permGroup.groupInfo, + permissionMap, + hasInstallToRuntimeSplit, + specialLocationGrant + ) } /** @@ -147,12 +159,13 @@ class LightAppPermGroupLiveData private constructor( for (spi in permissionManager.splitPermissions) { val splitPerm = spi.splitPermission - val pi = try { - app.packageManager.getPermissionInfo(splitPerm, 0) - } catch (e: PackageManager.NameNotFoundException) { - Log.w(LOG_TAG, "No such permission: $splitPerm", e) - continue - } + val pi = + try { + app.packageManager.getPermissionInfo(splitPerm, 0) + } catch (e: PackageManager.NameNotFoundException) { + Log.w(LOG_TAG, "No such permission: $splitPerm", e) + continue + } // Skip if split permission is not "install" permission. if (pi.protection != PermissionInfo.PROTECTION_NORMAL) { @@ -199,15 +212,19 @@ class LightAppPermGroupLiveData private constructor( /** * Repository for AppPermGroupLiveDatas. + * * <p> Key value is a triple of string package name, string permission group name, and * UserHandle, value is its corresponding LiveData. */ - companion object : DataRepositoryForPackage<Triple<String, String, UserHandle>, - LightAppPermGroupLiveData>() { - override fun newValue(key: Triple<String, String, UserHandle>): - LightAppPermGroupLiveData { - return LightAppPermGroupLiveData(PermissionControllerApplication.get(), - key.first, key.second, key.third) + companion object : + DataRepositoryForPackage<Triple<String, String, UserHandle>, LightAppPermGroupLiveData>() { + override fun newValue(key: Triple<String, String, UserHandle>): LightAppPermGroupLiveData { + return LightAppPermGroupLiveData( + PermissionControllerApplication.get(), + key.first, + key.second, + key.third + ) } } -}
\ No newline at end of file +} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/LightPackageInfoLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/LightPackageInfoLiveData.kt index 27681cd90..e657a6869 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/data/LightPackageInfoLiveData.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/data/LightPackageInfoLiveData.kt @@ -37,11 +37,13 @@ import kotlinx.coroutines.Job * @param packageName The name of the package this LiveData will watch for mode changes for * @param user The user for whom the packageInfo will be defined */ -class LightPackageInfoLiveData private constructor( +class LightPackageInfoLiveData +private constructor( private val app: Application, private val packageName: String, private val user: UserHandle -) : SmartAsyncMediatorLiveData<LightPackageInfo?>(alwaysUpdateOnActive = false), +) : + SmartAsyncMediatorLiveData<LightPackageInfo?>(alwaysUpdateOnActive = false), PackageBroadcastReceiver.PackageBroadcastListener, PermissionListenerMultiplexer.PermissionChangeCallback { @@ -49,13 +51,9 @@ class LightPackageInfoLiveData private constructor( private val userPackagesLiveData = UserPackageInfosLiveData[user] private var uid: Int? = null - /** - * The currently registered UID on which this LiveData is listening for permission changes. - */ + /** The currently registered UID on which this LiveData is listening for permission changes. */ private var registeredUid: Int? = null - /** - * Whether or not this package livedata is watching the UserPackageInfosLiveData - */ + /** Whether or not this package livedata is watching the UserPackageInfosLiveData */ private var watchingUserPackagesLiveData: Boolean = false /** @@ -73,8 +71,11 @@ class LightPackageInfoLiveData private constructor( uid = packageInfo.uid if (hasActiveObservers()) { - PermissionListenerMultiplexer.addOrReplaceCallback(registeredUid, - packageInfo.uid, this) + PermissionListenerMultiplexer.addOrReplaceCallback( + registeredUid, + packageInfo.uid, + this + ) registeredUid = uid } } @@ -95,30 +96,37 @@ class LightPackageInfoLiveData private constructor( if (job.isCancelled) { return } - postValue(try { - var flags = PackageManager.GET_PERMISSIONS - if (SdkLevel.isAtLeastS()) { - flags = flags or PackageManager.GET_ATTRIBUTIONS - } + postValue( + try { + var flags = PackageManager.GET_PERMISSIONS + if (SdkLevel.isAtLeastS()) { + flags = flags or PackageManager.GET_ATTRIBUTIONS + } - LightPackageInfo(Utils.getUserContext(app, user).packageManager - .getPackageInfo(packageName, flags)) - } catch (e: Exception) { - if (e is PackageManager.NameNotFoundException) { - Log.w(LOG_TAG, "Package \"$packageName\" not found for user $user") - } else { - val profiles = app.getSystemService(UserManager::class.java)!!.userProfiles - Log.e(LOG_TAG, "Failed to create context for user $user. " + - "User exists : ${user in profiles }", e) + LightPackageInfo( + Utils.getUserContext(app, user) + .packageManager + .getPackageInfo(packageName, flags) + ) + } catch (e: Exception) { + if (e is PackageManager.NameNotFoundException) { + Log.w(LOG_TAG, "Package \"$packageName\" not found for user $user") + } else { + val profiles = app.getSystemService(UserManager::class.java)!!.userProfiles + Log.e( + LOG_TAG, + "Failed to create context for user $user. " + + "User exists : ${user in profiles }", + e + ) + } + invalidateSingle(packageName to user) + null } - invalidateSingle(packageName to user) - null - }) + ) } - /** - * Callback from the PermissionListener. Either deletes or generates package data. - */ + /** Callback from the PermissionListener. Either deletes or generates package data. */ override fun onPermissionChange() { updateAsync() } @@ -131,8 +139,11 @@ class LightPackageInfoLiveData private constructor( registeredUid = uid PermissionListenerMultiplexer.addCallback(it, this) } - if (userPackagesLiveData.hasActiveObservers() && !watchingUserPackagesLiveData && - !userPackagesLiveData.permChangeStale) { + if ( + userPackagesLiveData.hasActiveObservers() && + !watchingUserPackagesLiveData && + !userPackagesLiveData.permChangeStale + ) { watchingUserPackagesLiveData = true addSource(userPackagesLiveData, userPackageInfosObserver) } else { @@ -140,9 +151,8 @@ class LightPackageInfoLiveData private constructor( } } - private val userPackageInfosObserver = Observer<List<LightPackageInfo>> { - updateFromUserPackageInfosLiveData() - } + private val userPackageInfosObserver = + Observer<List<LightPackageInfo>> { updateFromUserPackageInfosLiveData() } @MainThread private fun updateFromUserPackageInfosLiveData() { @@ -183,14 +193,18 @@ class LightPackageInfoLiveData private constructor( /** * Repository for LightPackageInfoLiveDatas + * * <p> Key value is a string package name and UserHandle pair, value is its corresponding * LiveData. */ - companion object : DataRepositoryForPackage<Pair<String, UserHandle>, - LightPackageInfoLiveData>() { + companion object : + DataRepositoryForPackage<Pair<String, UserHandle>, LightPackageInfoLiveData>() { override fun newValue(key: Pair<String, UserHandle>): LightPackageInfoLiveData { - return LightPackageInfoLiveData(PermissionControllerApplication.get(), - key.first, key.second) + return LightPackageInfoLiveData( + PermissionControllerApplication.get(), + key.first, + key.second + ) } } } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/LightPermInfoLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/LightPermInfoLiveData.kt index 6f33cb199..091c45b92 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/data/LightPermInfoLiveData.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/data/LightPermInfoLiveData.kt @@ -20,8 +20,8 @@ import android.app.Application import android.content.pm.PackageManager import android.util.Log import com.android.permissioncontroller.PermissionControllerApplication -import com.android.permissioncontroller.permission.utils.PermissionMapping.isRuntimePlatformPermission import com.android.permissioncontroller.permission.model.livedatatypes.LightPermInfo +import com.android.permissioncontroller.permission.utils.PermissionMapping.isRuntimePlatformPermission import com.android.permissioncontroller.permission.utils.Utils.OS_PKG import kotlinx.coroutines.Job @@ -31,11 +31,9 @@ import kotlinx.coroutines.Job * @param app current Application * @param permissionName name of the permission this LiveData will watch for mode changes for */ -class LightPermInfoLiveData private constructor( - private val app: Application, - private val permissionName: String -) : SmartAsyncMediatorLiveData<LightPermInfo>(), - PackageBroadcastReceiver.PackageBroadcastListener { +class LightPermInfoLiveData +private constructor(private val app: Application, private val permissionName: String) : + SmartAsyncMediatorLiveData<LightPermInfo>(), PackageBroadcastReceiver.PackageBroadcastListener { private val LOG_TAG = LightPermInfoLiveData::class.java.simpleName @@ -67,13 +65,14 @@ class LightPermInfoLiveData private constructor( return } - val newValue = try { - LightPermInfo(app.packageManager.getPermissionInfo(permissionName, 0)) - } catch (e: PackageManager.NameNotFoundException) { - Log.w(LOG_TAG, "Permission \"$permissionName\" not found") - invalidateSingle(permissionName) - null - } + val newValue = + try { + LightPermInfo(app.packageManager.getPermissionInfo(permissionName, 0)) + } catch (e: PackageManager.NameNotFoundException) { + Log.w(LOG_TAG, "Permission \"$permissionName\" not found") + invalidateSingle(permissionName) + null + } if (isImmutable()) { stopListeningForChanges() @@ -82,9 +81,7 @@ class LightPermInfoLiveData private constructor( postValue(newValue) } - /** - * @return if the permission state can never change - */ + /** @return if the permission state can never change */ private fun isImmutable(): Boolean { // The os package never changes value?.let { @@ -97,9 +94,7 @@ class LightPermInfoLiveData private constructor( return isRuntimePlatformPermission(permissionName) } - /** - * Start listing for changes to this permission if needed - */ + /** Start listing for changes to this permission if needed */ private fun startListeningForChanges() { if (!isListeningForChanges && !isImmutable()) { isListeningForChanges = true @@ -107,9 +102,7 @@ class LightPermInfoLiveData private constructor( } } - /** - * Stop listing for changes to this permission - */ + /** Stop listing for changes to this permission */ private fun stopListeningForChanges() { if (isListeningForChanges) { PackageBroadcastReceiver.removeAllCallback(this) diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/LoadAndFreezeLifeData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/LoadAndFreezeLifeData.kt index 5e8789a38..fd572e019 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/data/LoadAndFreezeLifeData.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/data/LoadAndFreezeLifeData.kt @@ -23,8 +23,8 @@ import androidx.lifecycle.SavedStateHandle * value forever. * * This even extends over live-cycle events as the data is stored in the {@link SaveStateHandle}. - * This means that the data has to be writable to {@link SavedStateHandle} though, i.e. - * Serialzable, Parcelable, list, set, map, or a literal + * This means that the data has to be writable to {@link SavedStateHandle} though, i.e. Serialzable, + * Parcelable, list, set, map, or a literal */ class LoadAndFreezeLifeData<T>( private val state: SavedStateHandle, diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/MicMutedLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/MicMutedLiveData.kt index f53f60345..0bd045e80 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/data/MicMutedLiveData.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/data/MicMutedLiveData.kt @@ -24,33 +24,35 @@ import android.media.AudioManager import com.android.permissioncontroller.PermissionControllerApplication import kotlinx.coroutines.Job -/** - * Tracks whether the mic is muted or not - */ -val micMutedLiveData = object : SmartAsyncMediatorLiveData<Boolean>() { - private val app = PermissionControllerApplication.get() - - private val isMicMuteRecevicer = object : BroadcastReceiver() { - override fun onReceive(context: Context?, intent: Intent?) { - update() +/** Tracks whether the mic is muted or not */ +val micMutedLiveData = + object : SmartAsyncMediatorLiveData<Boolean>() { + private val app = PermissionControllerApplication.get() + + private val isMicMuteRecevicer = + object : BroadcastReceiver() { + override fun onReceive(context: Context?, intent: Intent?) { + update() + } + } + + override suspend fun loadDataAndPostValue(job: Job) { + postValue(app.getSystemService(AudioManager::class.java).isMicrophoneMute()) } - } - - override suspend fun loadDataAndPostValue(job: Job) { - postValue(app.getSystemService(AudioManager::class.java).isMicrophoneMute()) - } - override fun onActive() { - super.onActive() + override fun onActive() { + super.onActive() - app.registerReceiver(isMicMuteRecevicer, - IntentFilter(AudioManager.ACTION_MICROPHONE_MUTE_CHANGED)) - update() - } + app.registerReceiver( + isMicMuteRecevicer, + IntentFilter(AudioManager.ACTION_MICROPHONE_MUTE_CHANGED) + ) + update() + } - override fun onInactive() { - super.onInactive() + override fun onInactive() { + super.onInactive() - app.unregisterReceiver(isMicMuteRecevicer) + app.unregisterReceiver(isMicMuteRecevicer) + } } -} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/OpUsageLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/OpUsageLiveData.kt index 805d497c4..7ca0f2d96 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/data/OpUsageLiveData.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/data/OpUsageLiveData.kt @@ -45,26 +45,28 @@ class OpUsageLiveData( private val app: Application, private val opNames: List<String>, private val usageDurationMs: Long -) : SmartAsyncMediatorLiveData<@JvmSuppressWildcards Map<String, List<OpAccess>>>(), - AppOpsManager.OnOpActiveChangedListener { +) : + SmartAsyncMediatorLiveData<@JvmSuppressWildcards Map<String, List<OpAccess>>>(), + AppOpsManager.OnOpActiveChangedListener { private val appOpsManager = app.getSystemService(AppOpsManager::class.java)!! override suspend fun loadDataAndPostValue(job: Job) { val now = System.currentTimeMillis() val opMap = mutableMapOf<String, MutableList<OpAccess>>() - val packageOps = try { - appOpsManager.getPackagesForOps(opNames.toTypedArray()) - } catch (e: NullPointerException) { - // older builds might not support all the app-ops requested - emptyList<AppOpsManager.PackageOps>() - } + val packageOps = + try { + appOpsManager.getPackagesForOps(opNames.toTypedArray()) + } catch (e: NullPointerException) { + // older builds might not support all the app-ops requested + emptyList<AppOpsManager.PackageOps>() + } for (packageOp in packageOps) { for (opEntry in packageOp.ops) { for ((attributionTag, attributedOpEntry) in opEntry.attributedOpEntries) { val user = UserHandle.getUserHandleForUid(packageOp.uid) - val lastAccessTime: Long = attributedOpEntry.getLastAccessTime( - OP_FLAGS_ALL_TRUSTED) + val lastAccessTime: Long = + attributedOpEntry.getLastAccessTime(OP_FLAGS_ALL_TRUSTED) if (lastAccessTime == -1L) { // There was no access, so skip @@ -78,34 +80,55 @@ class OpUsageLiveData( lastAccessDuration = 0 } - if (attributedOpEntry.isRunning || - lastAccessTime + lastAccessDuration > (now - usageDurationMs)) { + if ( + attributedOpEntry.isRunning || + lastAccessTime + lastAccessDuration > (now - usageDurationMs) + ) { val accessList = opMap.getOrPut(opEntry.opStr) { mutableListOf() } - val accessTime = if (attributedOpEntry.isRunning) { - OpAccess.IS_RUNNING - } else { - lastAccessTime - } + val accessTime = + if (attributedOpEntry.isRunning) { + OpAccess.IS_RUNNING + } else { + lastAccessTime + } val proxy = attributedOpEntry.getLastProxyInfo(OP_FLAGS_ALL_TRUSTED) var proxyAccess: OpAccess? = null if (proxy != null && proxy.packageName != null) { - proxyAccess = OpAccess(proxy.packageName!!, proxy.attributionTag, - UserHandle.getUserHandleForUid(proxy.uid), accessTime) + proxyAccess = + OpAccess( + proxy.packageName!!, + proxy.attributionTag, + UserHandle.getUserHandleForUid(proxy.uid), + accessTime + ) } - accessList.add(OpAccess(packageOp.packageName, attributionTag, - user, accessTime, proxyAccess)) + accessList.add( + OpAccess( + packageOp.packageName, + attributionTag, + user, + accessTime, + proxyAccess + ) + ) // TODO ntmyren: remove logs once b/160724034 is fixed - Log.i("OpUsageLiveData", "adding ${opEntry.opStr} for " + + Log.i( + "OpUsageLiveData", + "adding ${opEntry.opStr} for " + "${packageOp.packageName}/$attributionTag, access time of " + "$lastAccessTime, isRunning: ${attributedOpEntry.isRunning} " + "current time $now, duration $lastAccessDuration, proxy: " + - "${proxy?.packageName}") + "${proxy?.packageName}" + ) } else { - Log.i("OpUsageLiveData", "NOT adding ${opEntry.opStr} for " + + Log.i( + "OpUsageLiveData", + "NOT adding ${opEntry.opStr} for " + "${packageOp.packageName}/$attributionTag, access time of " + "$lastAccessTime, isRunning: ${attributedOpEntry.isRunning} " + - "current time $now, duration $lastAccessDuration") + "current time $now, duration $lastAccessDuration" + ) } } } @@ -180,26 +203,31 @@ data class OpAccess( const val IS_RUNNING = -1L @JvmField - val CREATOR = object : Parcelable.Creator<OpAccess> { - override fun createFromParcel(parcel: Parcel): OpAccess { - val packageName = parcel.readString()!! - val attributionTag = parcel.readString() - val user: UserHandle = parcel.readParcelable(UserHandle::class.java.classLoader)!! - val lastAccessTime = parcel.readLong() - var proxyAccess: OpAccess? = null - val proxyPackageName = parcel.readString() - if (proxyPackageName != null) { - proxyAccess = OpAccess(proxyPackageName, - parcel.readString(), - parcel.readParcelable(UserHandle::class.java.classLoader)!!, - lastAccessTime) + val CREATOR = + object : Parcelable.Creator<OpAccess> { + override fun createFromParcel(parcel: Parcel): OpAccess { + val packageName = parcel.readString()!! + val attributionTag = parcel.readString() + val user: UserHandle = + parcel.readParcelable(UserHandle::class.java.classLoader)!! + val lastAccessTime = parcel.readLong() + var proxyAccess: OpAccess? = null + val proxyPackageName = parcel.readString() + if (proxyPackageName != null) { + proxyAccess = + OpAccess( + proxyPackageName, + parcel.readString(), + parcel.readParcelable(UserHandle::class.java.classLoader)!!, + lastAccessTime + ) + } + return OpAccess(packageName, attributionTag, user, lastAccessTime, proxyAccess) } - return OpAccess(packageName, attributionTag, user, lastAccessTime, proxyAccess) - } - override fun newArray(size: Int): Array<OpAccess?> { - return arrayOfNulls(size) + override fun newArray(size: Int): Array<OpAccess?> { + return arrayOfNulls(size) + } } - } } } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/PackageBroadcastReceiver.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/PackageBroadcastReceiver.kt index b2e0236fa..09a7bb1e4 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/data/PackageBroadcastReceiver.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/data/PackageBroadcastReceiver.kt @@ -29,31 +29,24 @@ import kotlinx.coroutines.Dispatchers.Main import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch -/** - * Listens for package additions, replacements, and removals, and notifies listeners. - */ +/** Listens for package additions, replacements, and removals, and notifies listeners. */ object PackageBroadcastReceiver : BroadcastReceiver() { private val app: Application = PermissionControllerApplication.get() - private val intentFilter = IntentFilter(Intent.ACTION_PACKAGE_ADDED).apply { - addAction(Intent.ACTION_PACKAGE_REMOVED) - addAction(Intent.ACTION_PACKAGE_REPLACED) - addAction(Intent.ACTION_PACKAGE_CHANGED) - addDataScheme("package") - } + private val intentFilter = + IntentFilter(Intent.ACTION_PACKAGE_ADDED).apply { + addAction(Intent.ACTION_PACKAGE_REMOVED) + addAction(Intent.ACTION_PACKAGE_REPLACED) + addAction(Intent.ACTION_PACKAGE_CHANGED) + addDataScheme("package") + } - /** - * Map<packageName, callbacks listenening to package> - */ + /** Map<packageName, callbacks listenening to package> */ private val changeCallbacks = mutableMapOf<String, MutableSet<PackageBroadcastListener>>() - /** - * A list of listener IDs, which listen to all package additions, changes, and removals. - */ + /** A list of listener IDs, which listen to all package additions, changes, and removals. */ private val allCallbacks = mutableSetOf<PackageBroadcastListener>() - /** - * Add a callback which will be notified when the specified packaged is changed or removed. - */ + /** Add a callback which will be notified when the specified packaged is changed or removed. */ fun addChangeCallback(packageName: String, listener: PackageBroadcastListener) { GlobalScope.launch(Main.immediate) { val wasEmpty = hasNoListeners() @@ -61,8 +54,12 @@ object PackageBroadcastReceiver : BroadcastReceiver() { changeCallbacks.getOrPut(packageName, { mutableSetOf() }).add(listener) if (wasEmpty) { - app.applicationContext.registerReceiverForAllUsers(this@PackageBroadcastReceiver, - intentFilter, null, null) + app.applicationContext.registerReceiverForAllUsers( + this@PackageBroadcastReceiver, + intentFilter, + null, + null + ) } } } @@ -80,8 +77,12 @@ object PackageBroadcastReceiver : BroadcastReceiver() { allCallbacks.add(listener) if (wasEmpty) { - app.applicationContext.registerReceiverForAllUsers(this@PackageBroadcastReceiver, - intentFilter, null, null) + app.applicationContext.registerReceiverForAllUsers( + this@PackageBroadcastReceiver, + intentFilter, + null, + null + ) } } } @@ -171,9 +172,7 @@ object PackageBroadcastReceiver : BroadcastReceiver() { } } - /** - * A listener interface for objects desiring to be notified of package broadcasts. - */ + /** A listener interface for objects desiring to be notified of package broadcasts. */ interface PackageBroadcastListener { /** * To be called when a specific package has been changed, or when any package has been diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/PackagePermissionsLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/PackagePermissionsLiveData.kt index 49be1fbd0..cc050fae2 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/data/PackagePermissionsLiveData.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/data/PackagePermissionsLiveData.kt @@ -35,11 +35,9 @@ import kotlinx.coroutines.Job * @param packageName The name of the package this LiveData will watch for mode changes for * @param user The user for whom the packageInfo will be defined */ -class PackagePermissionsLiveData private constructor( - private val app: Application, - packageName: String, - user: UserHandle -) : SmartAsyncMediatorLiveData<Map<String, List<String>>?>() { +class PackagePermissionsLiveData +private constructor(private val app: Application, packageName: String, user: UserHandle) : + SmartAsyncMediatorLiveData<Map<String, List<String>>?>() { private val packageInfoLiveData = LightPackageInfoLiveData[packageName, user] @@ -60,25 +58,32 @@ class PackagePermissionsLiveData private constructor( for (permName in packageInfo.requestedPermissions) { var groupName = PermissionMapping.getGroupOfPlatformPermission(permName) if (groupName == null) { - val permInfo = try { - app.packageManager.getPermissionInfo(permName, 0) - } catch (e: PackageManager.NameNotFoundException) { - continue - } + val permInfo = + try { + app.packageManager.getPermissionInfo(permName, 0) + } catch (e: PackageManager.NameNotFoundException) { + continue + } - if (permInfo.flags and PermissionInfo.FLAG_INSTALLED == 0 || - permInfo.flags and PermissionInfo.FLAG_REMOVED != 0) { + if ( + permInfo.flags and PermissionInfo.FLAG_INSTALLED == 0 || + permInfo.flags and PermissionInfo.FLAG_REMOVED != 0 + ) { continue } - if (packageInfo.isInstantApp && permInfo.protectionFlags and - PermissionInfo.PROTECTION_FLAG_INSTANT == 0) { + if ( + packageInfo.isInstantApp && + permInfo.protectionFlags and PermissionInfo.PROTECTION_FLAG_INSTANT == 0 + ) { continue } - if (packageInfo.targetSdkVersion < Build.VERSION_CODES.M && - (permInfo.protectionFlags and PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY) != - 0) { + if ( + packageInfo.targetSdkVersion < Build.VERSION_CODES.M && + (permInfo.protectionFlags and + PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY) != 0 + ) { continue } @@ -104,14 +109,17 @@ class PackagePermissionsLiveData private constructor( /** * Repository for PackagePermissionsLiveData objects + * * <p> Key value is a string package name and userHandle, value is its corresponding LiveData. */ - companion object : DataRepositoryForPackage<Pair<String, UserHandle>, - PackagePermissionsLiveData>() { - override fun newValue(key: Pair<String, UserHandle>): - PackagePermissionsLiveData { - return PackagePermissionsLiveData(PermissionControllerApplication.get(), key.first, - key.second) + companion object : + DataRepositoryForPackage<Pair<String, UserHandle>, PackagePermissionsLiveData>() { + override fun newValue(key: Pair<String, UserHandle>): PackagePermissionsLiveData { + return PackagePermissionsLiveData( + PermissionControllerApplication.get(), + key.first, + key.second + ) } const val NON_RUNTIME_NORMAL_PERMS = "nonRuntimeNormalPerms" diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/PermGroupLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/PermGroupLiveData.kt index 78f2f72c6..d44fea233 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/data/PermGroupLiveData.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/data/PermGroupLiveData.kt @@ -36,19 +36,15 @@ import com.android.permissioncontroller.permission.utils.Utils * @param app The current application * @param groupName The name of the permission group this LiveData represents */ -class PermGroupLiveData private constructor( - private val app: Application, - private val groupName: String -) : SmartUpdateMediatorLiveData<PermGroup>(), - PackageBroadcastReceiver.PackageBroadcastListener { +class PermGroupLiveData +private constructor(private val app: Application, private val groupName: String) : + SmartUpdateMediatorLiveData<PermGroup>(), PackageBroadcastReceiver.PackageBroadcastListener { private val LOG_TAG = this::class.java.simpleName private val context = app.applicationContext!! - /** - * Map<packageName, LiveData<PackageInfo>> - */ + /** Map<packageName, LiveData<PackageInfo>> */ private val packageLiveDatas = mutableMapOf<String, LightPackageInfoLiveData>() private lateinit var groupInfo: PackageItemInfo @@ -69,25 +65,30 @@ class PermGroupLiveData private constructor( override fun onUpdate() { val permissionInfos = mutableMapOf<String, LightPermInfo>() - groupInfo = Utils.getGroupInfo(groupName, context) ?: run { - Log.e(LOG_TAG, "Invalid permission group $groupName") - invalidateSingle(groupName) - value = null - return - } - - when (groupInfo) { - is PermissionGroupInfo -> { - val permInfos = try { - Utils.getInstalledRuntimePermissionInfosForGroup(context.packageManager, - groupName) - } catch (e: PackageManager.NameNotFoundException) { + groupInfo = + Utils.getGroupInfo(groupName, context) + ?: run { Log.e(LOG_TAG, "Invalid permission group $groupName") invalidateSingle(groupName) value = null return } + when (groupInfo) { + is PermissionGroupInfo -> { + val permInfos = + try { + Utils.getInstalledRuntimePermissionInfosForGroup( + context.packageManager, + groupName + ) + } catch (e: PackageManager.NameNotFoundException) { + Log.e(LOG_TAG, "Invalid permission group $groupName") + invalidateSingle(groupName) + value = null + return + } + for (permInfo in permInfos) { permissionInfos[permInfo.name] = LightPermInfo(permInfo) } @@ -105,8 +106,8 @@ class PermGroupLiveData private constructor( value = permGroup - val packageNames = permissionInfos.values.map { permInfo -> permInfo.packageName } - .toMutableSet() + val packageNames = + permissionInfos.values.map { permInfo -> permInfo.packageName }.toMutableSet() packageNames.add(groupInfo.packageName) // TODO ntmyren: What if the package isn't installed for the system user? @@ -123,8 +124,8 @@ class PermGroupLiveData private constructor( } /** - * Load data, and register a package change listener. We must watch for package changes, - * because there is currently no listener for permission changes. + * Load data, and register a package change listener. We must watch for package changes, because + * there is currently no listener for permission changes. */ override fun onActive() { update() @@ -136,6 +137,7 @@ class PermGroupLiveData private constructor( /** * Repository for PermGroupLiveDatas. + * * <p> Key value is a string permission group name, value is its corresponding LiveData. */ companion object : DataRepository<String, PermGroupLiveData>() { diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/PermGroupUsageLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/PermGroupUsageLiveData.kt index 08f9bbfb4..175f389fa 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/data/PermGroupUsageLiveData.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/data/PermGroupUsageLiveData.kt @@ -41,19 +41,21 @@ class PermGroupUsageLiveData( private val usageDurationMs: Long ) : SmartUpdateMediatorLiveData<Map<String, List<OpAccess>>>() { /** Perm group name -> OpUsageLiveData */ - private val permGroupUsages = permGroupsNames.map { permGroup -> - val appops = getPlatformPermissionNamesOfGroup(permGroup).mapNotNull { permName -> - permissionToOp(permName) - } + private val permGroupUsages = + permGroupsNames + .map { permGroup -> + val appops = + getPlatformPermissionNamesOfGroup(permGroup).mapNotNull { permName -> + permissionToOp(permName) + } - permGroup to OpUsageLiveData[appops, usageDurationMs] - }.toMap() + permGroup to OpUsageLiveData[appops, usageDurationMs] + } + .toMap() init { for (usage in permGroupUsages.values) { - addSource(usage) { - update() - } + addSource(usage) { update() } } } @@ -68,25 +70,33 @@ class PermGroupUsageLiveData( } // Only keep the last access for a permission group - value = permGroupUsages.map { (permGroupName, usageLiveData) -> - // (packageName, attributionTag) -> access - val lastAccess = mutableMapOf<Pair<String, String?>, OpAccess>() - for (access in usageLiveData.value!!.values.flatten()) { - val key = access.packageName to access.attributionTag - if (access.isRunning || - lastAccess[key]?.lastAccessTime ?: 0 < access.lastAccessTime) { - lastAccess[key] = access - } - } + value = + permGroupUsages + .map { (permGroupName, usageLiveData) -> + // (packageName, attributionTag) -> access + val lastAccess = mutableMapOf<Pair<String, String?>, OpAccess>() + for (access in usageLiveData.value!!.values.flatten()) { + val key = access.packageName to access.attributionTag + if ( + access.isRunning || + lastAccess[key]?.lastAccessTime ?: 0 < access.lastAccessTime + ) { + lastAccess[key] = access + } + } - permGroupName to lastAccess.values.toList() - }.toMap() + permGroupName to lastAccess.values.toList() + } + .toMap() } companion object : DataRepository<Pair<List<String>, Long>, PermGroupUsageLiveData>() { override fun newValue(key: Pair<List<String>, Long>): PermGroupUsageLiveData { - return PermGroupUsageLiveData(PermissionControllerApplication.get(), key.first, - key.second) + return PermGroupUsageLiveData( + PermissionControllerApplication.get(), + key.first, + key.second + ) } } } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/PermGroupsPackagesLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/PermGroupsPackagesLiveData.kt index c44c2b473..ae9963538 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/data/PermGroupsPackagesLiveData.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/data/PermGroupsPackagesLiveData.kt @@ -31,10 +31,9 @@ import com.android.permissioncontroller.permission.utils.Utils.OS_PKG * * @param app The current application */ -class PermGroupsPackagesLiveData private constructor( - private val app: Application, - groupNamesLiveData: LiveData<List<String>> -) : SmartUpdateMediatorLiveData<Map<String, Set<Pair<String, UserHandle>>>>() { +class PermGroupsPackagesLiveData +private constructor(private val app: Application, groupNamesLiveData: LiveData<List<String>>) : + SmartUpdateMediatorLiveData<Map<String, Set<Pair<String, UserHandle>>>>() { private val packagesLiveData = AllPackageInfosLiveData private val permGroupLiveDatas = mutableMapOf<String, PermGroupLiveData>() @@ -47,8 +46,10 @@ class PermGroupsPackagesLiveData private constructor( val getLiveData = { groupName: String -> PermGroupLiveData[groupName] } setSourcesToDifference(groupNames, permGroupLiveDatas, getLiveData) { - if (packagesLiveData.isInitialized && - permGroupLiveDatas.all { it.value.isInitialized }) { + if ( + packagesLiveData.isInitialized && + permGroupLiveDatas.all { it.value.isInitialized } + ) { update() } } @@ -62,9 +63,9 @@ class PermGroupsPackagesLiveData private constructor( } /** - * Using the current list of permission groups, go through all packages in the system, - * and figure out which permission groups they have permissions for. If applicable, remove - * any lone-permission permission that are not requested by any packages. + * Using the current list of permission groups, go through all packages in the system, and + * figure out which permission groups they have permissions for. If applicable, remove any + * lone-permission permission that are not requested by any packages. */ override fun onUpdate() { if (groupNames.isEmpty()) { @@ -108,8 +109,10 @@ class PermGroupsPackagesLiveData private constructor( * group, if also empty. */ for (permGroup in permGroups) { - if (permGroup.groupInfo.isSinglePermGroup || - permGroup.name == Manifest.permission_group.UNDEFINED) { + if ( + permGroup.groupInfo.isSinglePermGroup || + permGroup.name == Manifest.permission_group.UNDEFINED + ) { val groupPackages = groupApps[permGroup.name] ?: continue if (groupPackages.isEmpty()) { groupApps.remove(permGroup.name) @@ -121,17 +124,22 @@ class PermGroupsPackagesLiveData private constructor( } companion object { - private val customInstance = PermGroupsPackagesLiveData( - PermissionControllerApplication.get(), CustomPermGroupNamesLiveData) - private val standardInstance = PermGroupsPackagesLiveData( - PermissionControllerApplication.get(), StandardPermGroupNamesLiveData) + private val customInstance = + PermGroupsPackagesLiveData( + PermissionControllerApplication.get(), + CustomPermGroupNamesLiveData + ) + private val standardInstance = + PermGroupsPackagesLiveData( + PermissionControllerApplication.get(), + StandardPermGroupNamesLiveData + ) /** * Get either the PermGroupsPackageLiveData instance corresponding either to the custom * permission groups, or the standard permission group. * * @param customGroups Whether to get the custom groups instance, or the standard - * * @return The specified PermGroupsPackageLiveData */ @JvmStatic diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/PermGroupsPackagesUiInfoLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/PermGroupsPackagesUiInfoLiveData.kt index 5d91ebfda..8bb33d1a9 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/data/PermGroupsPackagesUiInfoLiveData.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/data/PermGroupsPackagesUiInfoLiveData.kt @@ -37,8 +37,10 @@ import com.android.permissioncontroller.permission.utils.Utils class PermGroupsPackagesUiInfoLiveData( private val app: Application, private val groupNamesLiveData: LiveData<List<String>> -) : SmartUpdateMediatorLiveData< - @kotlin.jvm.JvmSuppressWildcards Map<String, PermGroupPackagesUiInfo?>>() { +) : + SmartUpdateMediatorLiveData< + @kotlin.jvm.JvmSuppressWildcards Map<String, PermGroupPackagesUiInfo?> + >() { private val SYSTEM_SHELL = "android.app.role.SYSTEM_SHELL" private val STAGGER_LOAD_TIME_MS = 50L @@ -54,11 +56,9 @@ class PermGroupsPackagesUiInfoLiveData( private val handler: Handler = Handler(Looper.getMainLooper()) - /** - * Map<permission group name, PermGroupUiLiveDatas> - */ - private val permGroupPackagesLiveDatas = mutableMapOf<String, - SinglePermGroupPackagesUiInfoLiveData>() + /** Map<permission group name, PermGroupUiLiveDatas> */ + private val permGroupPackagesLiveDatas = + mutableMapOf<String, SinglePermGroupPackagesUiInfoLiveData>() private val allPackageData = mutableMapOf<String, PermGroupPackagesUiInfo?>() private lateinit var groupNames: List<String> @@ -78,7 +78,7 @@ class PermGroupsPackagesUiInfoLiveData( private fun isGranted(grantState: AppPermGroupUiInfo.PermGrantState): Boolean { return grantState != AppPermGroupUiInfo.PermGrantState.PERMS_DENIED && - grantState != AppPermGroupUiInfo.PermGrantState.PERMS_ASK + grantState != AppPermGroupUiInfo.PermGrantState.PERMS_ASK } private fun createPermGroupPackageUiInfo( @@ -118,10 +118,19 @@ class PermGroupsPackagesUiInfoLiveData( } } } - val onlyShellGranted = grantedNonSystem == 0 && grantedSystem == 1 && + val onlyShellGranted = + grantedNonSystem == 0 && + grantedSystem == 1 && isPackageShell(firstGrantedSystemPackageName) - return PermGroupPackagesUiInfo(groupName, nonSystem, grantedNonSystem, - userInteractedNonSystem, grantedSystem, userInteractedSystem, onlyShellGranted) + return PermGroupPackagesUiInfo( + groupName, + nonSystem, + grantedNonSystem, + userInteractedNonSystem, + grantedSystem, + userInteractedSystem, + onlyShellGranted + ) } private fun isPackageShell(packageName: String?): Boolean { @@ -130,27 +139,30 @@ class PermGroupsPackagesUiInfoLiveData( } // This method is only called at most once per permission group, so no need to cache value - val roleManager = Utils.getSystemServiceSafe(PermissionControllerApplication.get(), - RoleManager::class.java) + val roleManager = + Utils.getSystemServiceSafe( + PermissionControllerApplication.get(), + RoleManager::class.java + ) return roleManager.getRoleHolders(SYSTEM_SHELL).contains(packageName) } override fun onUpdate() { /** - * Only update when either- - * We have a list of groups, and none have loaded their data, or + * Only update when either- We have a list of groups, and none have loaded their data, or * All packages have loaded their data */ val haveAllLiveDatas = groupNames.all { permGroupPackagesLiveDatas.contains(it) } val allInitialized = permGroupPackagesLiveDatas.all { it.value.isInitialized } for (groupName in groupNames) { - allPackageData[groupName] = if (haveAllLiveDatas && allInitialized) { - permGroupPackagesLiveDatas[groupName]?.value?.let { uiInfo -> - createPermGroupPackageUiInfo(groupName, uiInfo) + allPackageData[groupName] = + if (haveAllLiveDatas && allInitialized) { + permGroupPackagesLiveDatas[groupName]?.value?.let { uiInfo -> + createPermGroupPackageUiInfo(groupName, uiInfo) + } + } else { + null } - } else { - null - } } value = allPackageData.toMap() } @@ -172,7 +184,7 @@ class PermGroupsPackagesUiInfoLiveData( private fun addLiveDataDelayed(groupName: String, delayTimeMs: Long) { val liveData = SinglePermGroupPackagesUiInfoLiveData[groupName] permGroupPackagesLiveDatas[groupName] = liveData - handler.postDelayed( { addSource(liveData) { update() } }, delayTimeMs) + handler.postDelayed({ addSource(liveData) { update() } }, delayTimeMs) } override fun onActive() { diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/PermStateLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/PermStateLiveData.kt index c385cf0e5..53d0da6f5 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/data/PermStateLiveData.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/data/PermStateLiveData.kt @@ -28,21 +28,22 @@ import kotlinx.coroutines.Job /** * A LiveData which tracks the permission state for one permission group for one package. It - * includes both the granted state of every permission in the group, and the flags stored - * in the PermissionController service. + * includes both the granted state of every permission in the group, and the flags stored in the + * PermissionController service. * * @param app The current application * @param packageName The name of the package this LiveData will watch for mode changes for - * @param permGroupName The name of the permission group whose app ops this LiveData - * will watch + * @param permGroupName The name of the permission group whose app ops this LiveData will watch * @param user The user of the package */ -class PermStateLiveData private constructor( +class PermStateLiveData +private constructor( private val app: Application, private val packageName: String, private val permGroupName: String, private val user: UserHandle -) : SmartAsyncMediatorLiveData<Map<String, PermState>>(), +) : + SmartAsyncMediatorLiveData<Map<String, PermState>>(), PermissionListenerMultiplexer.PermissionChangeCallback { private val context = Utils.getUserContext(app, user) @@ -58,9 +59,7 @@ class PermStateLiveData private constructor( updateAsync() } - addSource(groupLiveData) { - updateAsync() - } + addSource(groupLiveData) { updateAsync() } } /** @@ -84,10 +83,11 @@ class PermStateLiveData private constructor( permissionGroup.permissionInfos[permissionName]?.let { permInfo -> val packageFlags = packageInfo.requestedPermissionsFlags[index] - val permFlags = context.packageManager.getPermissionFlags(permInfo.name, - packageName, user) - val granted = packageFlags and PackageInfo.REQUESTED_PERMISSION_GRANTED != 0 && - permFlags and PackageManager.FLAG_PERMISSION_REVOKED_COMPAT == 0 + val permFlags = + context.packageManager.getPermissionFlags(permInfo.name, packageName, user) + val granted = + packageFlags and PackageInfo.REQUESTED_PERMISSION_GRANTED != 0 && + permFlags and PackageManager.FLAG_PERMISSION_REVOKED_COMPAT == 0 if (job.isCancelled) { return @@ -105,15 +105,12 @@ class PermStateLiveData private constructor( private fun checkForUidUpdate(packageInfo: LightPackageInfo?) { if (packageInfo == null) { - registeredUid?.let { - PermissionListenerMultiplexer.removeCallback(it, this) - } + registeredUid?.let { PermissionListenerMultiplexer.removeCallback(it, this) } return } uid = packageInfo.uid if (uid != registeredUid && hasActiveObservers()) { - PermissionListenerMultiplexer.addOrReplaceCallback( - registeredUid, packageInfo.uid, this) + PermissionListenerMultiplexer.addOrReplaceCallback(registeredUid, packageInfo.uid, this) registeredUid = uid } } @@ -136,14 +133,19 @@ class PermStateLiveData private constructor( /** * Repository for PermStateLiveDatas. - * <p> Key value is a triple of string package name, string permission group name, and UserHandle, - * value is its corresponding LiveData. + * + * <p> Key value is a triple of string package name, string permission group name, and + * UserHandle, value is its corresponding LiveData. */ - companion object : DataRepositoryForPackage<Triple<String, String, UserHandle>, - PermStateLiveData>() { + companion object : + DataRepositoryForPackage<Triple<String, String, UserHandle>, PermStateLiveData>() { override fun newValue(key: Triple<String, String, UserHandle>): PermStateLiveData { - return PermStateLiveData(PermissionControllerApplication.get(), - key.first, key.second, key.third) + return PermStateLiveData( + PermissionControllerApplication.get(), + key.first, + key.second, + key.third + ) } } } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/PermissionChange.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/PermissionChange.kt index 29789b0f7..4959a7fba 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/data/PermissionChange.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/data/PermissionChange.kt @@ -19,10 +19,8 @@ package com.android.permissioncontroller.permission.data /** * A record of the user changing permissions for the app but not including any information on what * actual decision was made. This information is not included for privacy reasons and allows us to - * persist the data for longer periods of time than we'd be able to otherwise - * (e.g. [PermissionDecision]). + * persist the data for longer periods of time than we'd be able to otherwise (e.g. + * [PermissionDecision]). */ -data class PermissionChange( - override val packageName: String, - override val eventTime: Long -) : PermissionEvent(packageName, eventTime) +data class PermissionChange(override val packageName: String, override val eventTime: Long) : + PermissionEvent(packageName, eventTime) diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/PermissionEvent.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/PermissionEvent.kt index ad759795b..2fa64cd43 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/data/PermissionEvent.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/data/PermissionEvent.kt @@ -20,10 +20,7 @@ package com.android.permissioncontroller.permission.data * A record of a permission event caused by the user. * * @param packageName package name of the app the event is for - * @param eventTime the time of the event, in epoch time. Should be rounded to day-level - * precision for user privacy. + * @param eventTime the time of the event, in epoch time. Should be rounded to day-level precision + * for user privacy. */ -abstract class PermissionEvent( - open val packageName: String, - open val eventTime: Long -) +abstract class PermissionEvent(open val packageName: String, open val eventTime: Long) diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/PermissionListenerMultiplexer.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/PermissionListenerMultiplexer.kt index d6d532341..fb0f3077a 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/data/PermissionListenerMultiplexer.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/data/PermissionListenerMultiplexer.kt @@ -20,24 +20,19 @@ import android.app.Application import android.content.pm.PackageManager import com.android.permissioncontroller.PermissionControllerApplication -/** - * Serves as a single shared Permission Change Listener for all AppPermissionGroupLiveDatas. - * - */ +/** Serves as a single shared Permission Change Listener for all AppPermissionGroupLiveDatas. */ object PermissionListenerMultiplexer : PackageManager.OnPermissionsChangedListener { private val app: Application = PermissionControllerApplication.get() /** - * Map<UID, list of PermissionChangeCallbacks that wish to be informed when - * permissions are updated for that UID> + * Map<UID, list of PermissionChangeCallbacks that wish to be informed when permissions are + * updated for that UID> */ private val callbacks = mutableMapOf<Int, MutableList<PermissionChangeCallback>>() private val pm = app.applicationContext.packageManager override fun onPermissionsChanged(uid: Int) { - callbacks[uid]?.toList()?.forEach { callback -> - callback.onPermissionChange() - } + callbacks[uid]?.toList()?.forEach { callback -> callback.onPermissionChange() } } fun addOrReplaceCallback(oldUid: Int?, newUid: Int, callback: PermissionChangeCallback) { @@ -78,4 +73,4 @@ object PermissionListenerMultiplexer : PackageManager.OnPermissionsChangedListen interface PermissionChangeCallback { fun onPermissionChange() } -}
\ No newline at end of file +} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/PreinstalledUserPackageInfosLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/PreinstalledUserPackageInfosLiveData.kt index b4205acff..243fe5b03 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/data/PreinstalledUserPackageInfosLiveData.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/data/PreinstalledUserPackageInfosLiveData.kt @@ -33,23 +33,23 @@ import kotlinx.coroutines.Job * @param app The current application * @param user The user whose packages are desired */ -class PreinstalledUserPackageInfosLiveData private constructor( - private val app: Application, - private val user: UserHandle -) : SmartAsyncMediatorLiveData<@kotlin.jvm.JvmSuppressWildcards List<LightPackageInfo>>( - isStaticVal = true, alwaysUpdateOnActive = false -) { +class PreinstalledUserPackageInfosLiveData +private constructor(private val app: Application, private val user: UserHandle) : + SmartAsyncMediatorLiveData<@kotlin.jvm.JvmSuppressWildcards List<LightPackageInfo>>( + isStaticVal = true, + alwaysUpdateOnActive = false + ) { - /** - * Get all of the preinstalled packages in the system for this user - */ + /** Get all of the preinstalled packages in the system for this user */ override suspend fun loadDataAndPostValue(job: Job) { if (job.isCancelled) { return } - val packageInfos = app.applicationContext.packageManager - .getInstalledPackagesAsUser(GET_PERMISSIONS or MATCH_UNINSTALLED_PACKAGES - or MATCH_FACTORY_ONLY, user.identifier) + val packageInfos = + app.applicationContext.packageManager.getInstalledPackagesAsUser( + GET_PERMISSIONS or MATCH_UNINSTALLED_PACKAGES or MATCH_FACTORY_ONLY, + user.identifier + ) postValue(packageInfos.map { packageInfo -> LightPackageInfo(packageInfo) }) } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/RoleHoldersLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/RoleHoldersLiveData.kt index 2cf17fb95..744b5bdbd 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/data/RoleHoldersLiveData.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/data/RoleHoldersLiveData.kt @@ -29,7 +29,8 @@ import kotlinx.coroutines.Job * @param app The current application * @param roleName The name of the role */ -class RoleHoldersLiveData private constructor( +class RoleHoldersLiveData +private constructor( private val app: Application, private val roleName: String, private val user: UserHandle @@ -57,6 +58,7 @@ class RoleHoldersLiveData private constructor( /** * Repository for RoleHoldersLiveData. + * * <p> Key value is the name of the role. */ companion object : DataRepository<Pair<String, UserHandle>, RoleHoldersLiveData>() { diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/RoleListenerMultiplexer.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/RoleListenerMultiplexer.kt index ac853439d..fefaa5fc4 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/data/RoleListenerMultiplexer.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/data/RoleListenerMultiplexer.kt @@ -23,16 +23,14 @@ import android.os.UserHandle import androidx.annotation.GuardedBy import com.android.permissioncontroller.PermissionControllerApplication -/** - * Serves as a single shared Role Change Listener. - */ +/** Serves as a single shared Role Change Listener. */ object RoleListenerMultiplexer : OnRoleHoldersChangedListener { private val app: Application = PermissionControllerApplication.get() @GuardedBy("lock") - private val callbacks = mutableMapOf<UserHandle, - MutableMap<String, MutableList<RoleHoldersChangeCallback>>>() + private val callbacks = + mutableMapOf<UserHandle, MutableMap<String, MutableList<RoleHoldersChangeCallback>>>() private val roleManager = app.getSystemService(RoleManager::class.java)!! @@ -40,12 +38,8 @@ object RoleListenerMultiplexer : OnRoleHoldersChangedListener { override fun onRoleHoldersChanged(roleName: String, user: UserHandle) { val callbacksCopy: List<RoleHoldersChangeCallback>? - synchronized(lock) { - callbacksCopy = callbacks[user]?.get(roleName)?.toList() - } - callbacksCopy?.forEach { listener -> - listener.onRoleHoldersChanged() - } + synchronized(lock) { callbacksCopy = callbacks[user]?.get(roleName)?.toList() } + callbacksCopy?.forEach { listener -> listener.onRoleHoldersChanged() } } fun addCallback(roleName: String, user: UserHandle, callback: RoleHoldersChangeCallback) { @@ -88,4 +82,4 @@ object RoleListenerMultiplexer : OnRoleHoldersChangedListener { interface RoleHoldersChangeCallback { fun onRoleHoldersChanged() } -}
\ No newline at end of file +} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/SelectedAutofillServiceLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/SelectedAutofillServiceLiveData.kt index 9aced3e2b..3ab59237c 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/data/SelectedAutofillServiceLiveData.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/data/SelectedAutofillServiceLiveData.kt @@ -29,20 +29,19 @@ import kotlinx.coroutines.Job * @param app The current application * @param user The user the services should be determined for */ -class SelectedAutofillServiceLiveData( - private val app: Application, - private val user: UserHandle -) : SmartAsyncMediatorLiveData<String?>() { +class SelectedAutofillServiceLiveData(private val app: Application, private val user: UserHandle) : + SmartAsyncMediatorLiveData<String?>() { override suspend fun loadDataAndPostValue(job: Job) { if (job.isCancelled) { return } - val packageName = Utils.getUserContext(app, user) - .getSystemService(AutofillManager::class.java) - ?.autofillServiceComponentName - ?.packageName + val packageName = + Utils.getUserContext(app, user) + .getSystemService(AutofillManager::class.java) + ?.autofillServiceComponentName + ?.packageName postValue(packageName) } @@ -52,10 +51,9 @@ class SelectedAutofillServiceLiveData( * * <p> Key value is a user, value is its corresponding LiveData. */ - companion object : DataRepositoryForPackage<UserHandle, - SelectedAutofillServiceLiveData>() { + companion object : DataRepositoryForPackage<UserHandle, SelectedAutofillServiceLiveData>() { override fun newValue(key: UserHandle): SelectedAutofillServiceLiveData { return SelectedAutofillServiceLiveData(PermissionControllerApplication.get(), key) } } -}
\ No newline at end of file +} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/SelectedVoiceInteractionServiceLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/SelectedVoiceInteractionServiceLiveData.kt index 72a6da139..9bb749323 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/data/SelectedVoiceInteractionServiceLiveData.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/data/SelectedVoiceInteractionServiceLiveData.kt @@ -40,12 +40,14 @@ class SelectedVoiceInteractionServiceLiveData( return } - val packageName = Settings.Secure.getString( - Utils.getUserContext(app, user).contentResolver, - // Settings.Secure.VOICE_INTERACTION_SERVICE - "voice_interaction_service") - ?.let(ComponentName::unflattenFromString) - ?.packageName + val packageName = + Settings.Secure.getString( + Utils.getUserContext(app, user).contentResolver, + // Settings.Secure.VOICE_INTERACTION_SERVICE + "voice_interaction_service" + ) + ?.let(ComponentName::unflattenFromString) + ?.packageName postValue(packageName) } @@ -55,11 +57,13 @@ class SelectedVoiceInteractionServiceLiveData( * * <p> Key value is a user, value is its corresponding LiveData. */ - companion object : DataRepositoryForPackage<UserHandle, - SelectedVoiceInteractionServiceLiveData>() { + companion object : + DataRepositoryForPackage<UserHandle, SelectedVoiceInteractionServiceLiveData>() { override fun newValue(key: UserHandle): SelectedVoiceInteractionServiceLiveData { return SelectedVoiceInteractionServiceLiveData( - PermissionControllerApplication.get(), key) + PermissionControllerApplication.get(), + key + ) } } -}
\ No newline at end of file +} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/SelectedWallpaperServiceLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/SelectedWallpaperServiceLiveData.kt index e4c1314c1..d004f79dc 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/data/SelectedWallpaperServiceLiveData.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/data/SelectedWallpaperServiceLiveData.kt @@ -29,24 +29,23 @@ import kotlinx.coroutines.Job * @param app The current application * @param user The user the services should be determined for */ -class SelectedWallpaperServiceLiveData( - private val app: Application, - private val user: UserHandle -) : SmartAsyncMediatorLiveData<String?>() { +class SelectedWallpaperServiceLiveData(private val app: Application, private val user: UserHandle) : + SmartAsyncMediatorLiveData<String?>() { override suspend fun loadDataAndPostValue(job: Job) { if (job.isCancelled) { return } - val packageName = try { - Utils.getUserContext(app, user) + val packageName = + try { + Utils.getUserContext(app, user) .getSystemService(WallpaperManager::class.java) ?.wallpaperInfo ?.packageName - } catch (e: NullPointerException) { - null - } + } catch (e: NullPointerException) { + null + } postValue(packageName) } @@ -56,10 +55,9 @@ class SelectedWallpaperServiceLiveData( * * <p> Key value is a user, value is its corresponding LiveData. */ - companion object : DataRepositoryForPackage<UserHandle, - SelectedWallpaperServiceLiveData>() { + companion object : DataRepositoryForPackage<UserHandle, SelectedWallpaperServiceLiveData>() { override fun newValue(key: UserHandle): SelectedWallpaperServiceLiveData { return SelectedWallpaperServiceLiveData(PermissionControllerApplication.get(), key) } } -}
\ No newline at end of file +} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/ServiceLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/ServiceLiveData.kt index 6d59fd585..2deae79cc 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/data/ServiceLiveData.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/data/ServiceLiveData.kt @@ -49,9 +49,10 @@ class ServiceLiveData( override val intentAction: String, private val permission: String, private val user: UserHandle -) : SmartAsyncMediatorLiveData<Set<String>>(), - PackageBroadcastReceiver.PackageBroadcastListener, - HasIntentAction { +) : + SmartAsyncMediatorLiveData<Set<String>>(), + PackageBroadcastReceiver.PackageBroadcastListener, + HasIntentAction { private val name = intentAction.substringAfterLast(".") @@ -60,7 +61,7 @@ class ServiceLiveData( private val enabledNotificationListenersLiveData = EnabledNotificationListenersLiveData[user] private val selectedWallpaperServiceLiveData = SelectedWallpaperServiceLiveData[user] private val selectedVoiceInteractionServiceLiveData = - SelectedVoiceInteractionServiceLiveData[user] + SelectedVoiceInteractionServiceLiveData[user] private val selectedAutofillServiceLiveData = SelectedAutofillServiceLiveData[user] private val enabledDreamServicesLiveData = EnabledDreamServicesLiveData[user] private val disabledPrintServicesLiveData = DisabledPrintServicesLiveData[user] @@ -68,49 +69,31 @@ class ServiceLiveData( init { if (intentAction == AccessibilityService.SERVICE_INTERFACE) { - addSource(enabledAccessibilityServicesLiveData) { - updateAsync() - } + addSource(enabledAccessibilityServicesLiveData) { updateAsync() } } if (intentAction == InputMethod.SERVICE_INTERFACE) { - addSource(enabledInputMethodsLiveData) { - updateAsync() - } + addSource(enabledInputMethodsLiveData) { updateAsync() } } if (intentAction == NotificationListenerService.SERVICE_INTERFACE) { - addSource(enabledNotificationListenersLiveData) { - updateAsync() - } + addSource(enabledNotificationListenersLiveData) { updateAsync() } } if (intentAction == WallpaperService.SERVICE_INTERFACE) { - addSource(selectedWallpaperServiceLiveData) { - updateAsync() - } + addSource(selectedWallpaperServiceLiveData) { updateAsync() } } if (intentAction == VoiceInteractionService.SERVICE_INTERFACE) { - addSource(selectedVoiceInteractionServiceLiveData) { - updateAsync() - } + addSource(selectedVoiceInteractionServiceLiveData) { updateAsync() } } if (intentAction == AutofillService.SERVICE_INTERFACE) { - addSource(selectedAutofillServiceLiveData) { - updateAsync() - } + addSource(selectedAutofillServiceLiveData) { updateAsync() } } if (intentAction == DreamService.SERVICE_INTERFACE) { - addSource(enabledDreamServicesLiveData) { - updateAsync() - } + addSource(enabledDreamServicesLiveData) { updateAsync() } } if (intentAction == PrintService.SERVICE_INTERFACE) { - addSource(disabledPrintServicesLiveData) { - updateAsync() - } + addSource(disabledPrintServicesLiveData) { updateAsync() } } if (intentAction == DevicePolicyManager.ACTION_DEVICE_ADMIN_SERVICE) { - addSource(enabledDeviceAdminsLiveDataLiveData) { - updateAsync() - } + addSource(enabledDeviceAdminsLiveDataLiveData) { updateAsync() } } } @@ -122,48 +105,69 @@ class ServiceLiveData( if (job.isCancelled) { return } - if (intentAction == AccessibilityService.SERVICE_INTERFACE && - !enabledAccessibilityServicesLiveData.isInitialized) { + if ( + intentAction == AccessibilityService.SERVICE_INTERFACE && + !enabledAccessibilityServicesLiveData.isInitialized + ) { return } - if (intentAction == InputMethod.SERVICE_INTERFACE && - !enabledInputMethodsLiveData.isInitialized) { + if ( + intentAction == InputMethod.SERVICE_INTERFACE && + !enabledInputMethodsLiveData.isInitialized + ) { return } - if (intentAction == NotificationListenerService.SERVICE_INTERFACE && - !enabledNotificationListenersLiveData.isInitialized) { + if ( + intentAction == NotificationListenerService.SERVICE_INTERFACE && + !enabledNotificationListenersLiveData.isInitialized + ) { return } - if (intentAction == WallpaperService.SERVICE_INTERFACE && - !selectedWallpaperServiceLiveData.isInitialized) { + if ( + intentAction == WallpaperService.SERVICE_INTERFACE && + !selectedWallpaperServiceLiveData.isInitialized + ) { return } - if (intentAction == VoiceInteractionService.SERVICE_INTERFACE && - !selectedVoiceInteractionServiceLiveData.isInitialized) { + if ( + intentAction == VoiceInteractionService.SERVICE_INTERFACE && + !selectedVoiceInteractionServiceLiveData.isInitialized + ) { return } - if (intentAction == AutofillService.SERVICE_INTERFACE && - !selectedAutofillServiceLiveData.isInitialized) { + if ( + intentAction == AutofillService.SERVICE_INTERFACE && + !selectedAutofillServiceLiveData.isInitialized + ) { return } - if (intentAction == DreamService.SERVICE_INTERFACE && - !enabledDreamServicesLiveData.isInitialized) { + if ( + intentAction == DreamService.SERVICE_INTERFACE && + !enabledDreamServicesLiveData.isInitialized + ) { return } - if (intentAction == PrintService.SERVICE_INTERFACE && - !disabledPrintServicesLiveData.isInitialized) { + if ( + intentAction == PrintService.SERVICE_INTERFACE && + !disabledPrintServicesLiveData.isInitialized + ) { return } - if (intentAction == DevicePolicyManager.ACTION_DEVICE_ADMIN_SERVICE && - !enabledDeviceAdminsLiveDataLiveData.isInitialized) { + if ( + intentAction == DevicePolicyManager.ACTION_DEVICE_ADMIN_SERVICE && + !enabledDeviceAdminsLiveDataLiveData.isInitialized + ) { return } - val packageNames = getUserContext(app, user).packageManager + val packageNames = + getUserContext(app, user) + .packageManager .queryIntentServices( - Intent(intentAction), - PackageManager.GET_SERVICES or PackageManager.GET_META_DATA) + Intent(intentAction), + PackageManager.GET_SERVICES or PackageManager.GET_META_DATA + ) .mapNotNull { resolveInfo -> if (resolveInfo?.serviceInfo?.permission != permission) { return@mapNotNull null @@ -171,17 +175,19 @@ class ServiceLiveData( val packageName = resolveInfo.serviceInfo?.packageName if (!isServiceEnabled(packageName)) { if (DEBUG_HIBERNATION_POLICY) { - DumpableLog.i(LOG_TAG, - "Not exempting $packageName - not an active $name " + - "for u${user.identifier}") + DumpableLog.i( + LOG_TAG, + "Not exempting $packageName - not an active $name " + + "for u${user.identifier}" + ) } return@mapNotNull null } packageName - }.toSet() + } + .toSet() if (DEBUG_HIBERNATION_POLICY) { - DumpableLog.i(LOG_TAG, - "Detected ${name}s: $packageNames") + DumpableLog.i(LOG_TAG, "Detected ${name}s: $packageNames") } postValue(packageNames) @@ -241,13 +247,17 @@ class ServiceLiveData( * <p> Key value is a (string service name, required permission, user) triple, value is its * corresponding LiveData. */ - companion object : DataRepositoryForPackage<Triple<String, String, UserHandle>, - ServiceLiveData>() { + companion object : + DataRepositoryForPackage<Triple<String, String, UserHandle>, ServiceLiveData>() { private const val LOG_TAG = "ServiceLiveData" override fun newValue(key: Triple<String, String, UserHandle>): ServiceLiveData { - return ServiceLiveData(PermissionControllerApplication.get(), - key.first, key.second, key.third) + return ServiceLiveData( + PermissionControllerApplication.get(), + key.first, + key.second, + key.third + ) } } } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/SinglePermGroupPackagesUiInfoLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/SinglePermGroupPackagesUiInfoLiveData.kt index a46882c04..b2348a17a 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/data/SinglePermGroupPackagesUiInfoLiveData.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/data/SinglePermGroupPackagesUiInfoLiveData.kt @@ -19,8 +19,8 @@ package com.android.permissioncontroller.permission.data import android.app.Application import android.os.UserHandle import com.android.permissioncontroller.PermissionControllerApplication -import com.android.permissioncontroller.permission.utils.PermissionMapping import com.android.permissioncontroller.permission.model.livedatatypes.AppPermGroupUiInfo +import com.android.permissioncontroller.permission.utils.PermissionMapping /** * LiveData for the UI info for all packages in a single permission group. Tracks which packages @@ -30,26 +30,21 @@ import com.android.permissioncontroller.permission.model.livedatatypes.AppPermGr * @param app The current application * @param permGroupName The name of the permission group this LiveData represents */ -class SinglePermGroupPackagesUiInfoLiveData private constructor( - private val app: Application, - private val permGroupName: String -) : SmartUpdateMediatorLiveData<Map<Pair<String, UserHandle>, AppPermGroupUiInfo>>() { +class SinglePermGroupPackagesUiInfoLiveData +private constructor(private val app: Application, private val permGroupName: String) : + SmartUpdateMediatorLiveData<Map<Pair<String, UserHandle>, AppPermGroupUiInfo>>() { private val permGroupLiveData = PermGroupLiveData[permGroupName] private val isCustomGroup = !PermissionMapping.getPlatformPermissionGroups().contains(permGroupName) - private val permGroupPackagesLiveData = PermGroupsPackagesLiveData.get( - customGroups = isCustomGroup) + private val permGroupPackagesLiveData = + PermGroupsPackagesLiveData.get(customGroups = isCustomGroup) - /** - * Map<Pair<package name, UserHandle>, UI data LiveData> - */ - private val appPermGroupLiveDatas = mutableMapOf<Pair<String, UserHandle>, - AppPermGroupUiInfoLiveData>() + /** Map<Pair<package name, UserHandle>, UI data LiveData> */ + private val appPermGroupLiveDatas = + mutableMapOf<Pair<String, UserHandle>, AppPermGroupUiInfoLiveData>() - /** - * Map<Pair<packageName, userHandle>, UI data>. - */ + /** Map<Pair<packageName, userHandle>, UI data>. */ private val shownPackages = mutableMapOf<Pair<String, UserHandle>, AppPermGroupUiInfo>() init { @@ -60,9 +55,7 @@ class SinglePermGroupPackagesUiInfoLiveData private constructor( } } - addSource(permGroupPackagesLiveData) { - update() - } + addSource(permGroupPackagesLiveData) { update() } } override fun onUpdate() { @@ -71,9 +64,7 @@ class SinglePermGroupPackagesUiInfoLiveData private constructor( addAndRemoveAppPermGroupLiveDatas(thisPermGroupPackages.toList()) if (thisPermGroupPackages.isEmpty()) { - permGroupLiveData.value?.groupInfo?.let { - value = emptyMap() - } + permGroupLiveData.value?.groupInfo?.let { value = emptyMap() } } } } @@ -83,48 +74,46 @@ class SinglePermGroupPackagesUiInfoLiveData private constructor( AppPermGroupUiInfoLiveData[key.first, permGroupName, key.second] } - val (_, removed) = setSourcesToDifference(pkgs, appPermGroupLiveDatas, getLiveData) { key -> - val appPermGroupUiInfoLiveData = appPermGroupLiveDatas[key] - val appPermGroupUiInfo = appPermGroupUiInfoLiveData?.value - shownPackages.remove(key) - - if (appPermGroupUiInfo == null) { - if (appPermGroupUiInfoLiveData != null && - appPermGroupUiInfoLiveData.isInitialized) { - removeSource(appPermGroupUiInfoLiveData) - appPermGroupLiveDatas.remove(key) + val (_, removed) = + setSourcesToDifference(pkgs, appPermGroupLiveDatas, getLiveData) { key -> + val appPermGroupUiInfoLiveData = appPermGroupLiveDatas[key] + val appPermGroupUiInfo = appPermGroupUiInfoLiveData?.value + shownPackages.remove(key) + + if (appPermGroupUiInfo == null) { + if ( + appPermGroupUiInfoLiveData != null && + appPermGroupUiInfoLiveData.isInitialized + ) { + removeSource(appPermGroupUiInfoLiveData) + appPermGroupLiveDatas.remove(key) + } + } else { + shownPackages[key] = appPermGroupUiInfo } - } else { - shownPackages[key] = appPermGroupUiInfo - } - if (appPermGroupLiveDatas.all { entry -> entry.value.isInitialized }) { - permGroupLiveData.value?.groupInfo?.let { - value = shownPackages.toMap() + if (appPermGroupLiveDatas.all { entry -> entry.value.isInitialized }) { + permGroupLiveData.value?.groupInfo?.let { value = shownPackages.toMap() } } } - } for (removedKey in removed) { shownPackages.remove(removedKey) } if (appPermGroupLiveDatas.all { entry -> entry.value.isInitialized }) { - permGroupLiveData.value?.groupInfo?.let { - value = shownPackages.toMap() - } + permGroupLiveData.value?.groupInfo?.let { value = shownPackages.toMap() } } } /** * Repository for SinglePermGroupPackagesUiInfoLiveData objects. + * * <p> Key value is a string permission group name, value is its corresponding LiveData. */ - companion object : DataRepository<String, - SinglePermGroupPackagesUiInfoLiveData>() { + companion object : DataRepository<String, SinglePermGroupPackagesUiInfoLiveData>() { override fun newValue(key: String): SinglePermGroupPackagesUiInfoLiveData { - return SinglePermGroupPackagesUiInfoLiveData(PermissionControllerApplication.get(), - key) + return SinglePermGroupPackagesUiInfoLiveData(PermissionControllerApplication.get(), key) } } -}
\ No newline at end of file +} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/SmartAsyncMediatorLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/SmartAsyncMediatorLiveData.kt index b7491a7a4..1cc248956 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/data/SmartAsyncMediatorLiveData.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/data/SmartAsyncMediatorLiveData.kt @@ -36,14 +36,12 @@ abstract class SmartAsyncMediatorLiveData<T>( ) : SmartUpdateMediatorLiveData<T>(isStaticVal) { private var currentJob: Job? = null - @Volatile - private var jobQueued = false - @Volatile - private var jobRunning = false + @Volatile private var jobQueued = false + @Volatile private var jobRunning = false /** - * The main function which will load data. It should periodically check isCancelled to see if - * it should stop working. If data is loaded, it should call "postValue". + * The main function which will load data. It should periodically check isCancelled to see if it + * should stop working. If data is loaded, it should call "postValue". */ abstract suspend fun loadDataAndPostValue(job: Job) @@ -67,9 +65,7 @@ abstract class SmartAsyncMediatorLiveData<T>( jobRunning = false if (jobQueued) { jobQueued = false - GlobalScope.launch(Main.immediate) { - updateAsync() - } + GlobalScope.launch(Main.immediate) { updateAsync() } } } } @@ -95,4 +91,4 @@ abstract class SmartAsyncMediatorLiveData<T>( } } } -}
\ No newline at end of file +} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/SmartUpdateMediatorLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/SmartUpdateMediatorLiveData.kt index d7fe4fb2e..9cc100e38 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/data/SmartUpdateMediatorLiveData.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/data/SmartUpdateMediatorLiveData.kt @@ -31,8 +31,8 @@ import kotlinx.coroutines.launch /** * A MediatorLiveData which tracks how long it has been inactive, compares new values before setting - * its value (avoiding unnecessary updates), and can calculate the set difference between a list - * and a map (used when determining whether or not to add a LiveData as a source). + * its value (avoiding unnecessary updates), and can calculate the set difference between a list and + * a map (used when determining whether or not to add a LiveData as a source). * * @param isStaticVal Whether or not this LiveData value is expected to change */ @@ -46,8 +46,8 @@ abstract class SmartUpdateMediatorLiveData<T>(private val isStaticVal: Boolean = /** * Boolean, whether or not the value of this uiDataLiveData has been explicitly set yet. - * Differentiates between "null value because liveData is new" and "null value because - * liveData is invalid" + * Differentiates between "null value because liveData is new" and "null value because liveData + * is invalid" */ var isInitialized = false private set @@ -105,8 +105,7 @@ abstract class SmartUpdateMediatorLiveData<T>(private val isStaticVal: Boolean = onUpdate() } - @MainThread - protected abstract fun onUpdate() + @MainThread protected abstract fun onUpdate() override var timeWentInactive: Long? = System.nanoTime() @@ -116,7 +115,6 @@ abstract class SmartUpdateMediatorLiveData<T>(private val isStaticVal: Boolean = * * @param valOne The first T to be compared * @param valTwo The second T to be compared - * * @return True if the two values are different, false otherwise */ protected open fun valueNotEqual(valOne: T?, valTwo: T?): Boolean { @@ -124,8 +122,11 @@ abstract class SmartUpdateMediatorLiveData<T>(private val isStaticVal: Boolean = } override fun <S : Any?> addSource(source: LiveData<S>, onChanged: Observer<in S>) { - addSourceWithStackTraceAttribution(source, onChanged, - IllegalStateException().getStackTrace()) + addSourceWithStackTraceAttribution( + source, + onChanged, + IllegalStateException().getStackTrace() + ) } private fun <S : Any?> addSourceWithStackTraceAttribution( @@ -167,8 +168,7 @@ abstract class SmartUpdateMediatorLiveData<T>(private val isStaticVal: Boolean = * @param have The map of livedatas we currently have as sources * @param getLiveDataFun A function to turn a key into a liveData * @param onUpdateFun An optional function which will update differently based on different - * LiveDatas. If blank, will simply call update. - * + * LiveDatas. If blank, will simply call update. * @return a pair of (all keys added, all keys removed) */ fun <K, V : LiveData<*>> setSourcesToDifference( @@ -176,7 +176,7 @@ abstract class SmartUpdateMediatorLiveData<T>(private val isStaticVal: Boolean = have: MutableMap<K, V>, getLiveDataFun: (K) -> V, onUpdateFun: ((K) -> Unit)? = null - ): Pair<Set<K>, Set<K>>{ + ): Pair<Set<K>, Set<K>> { // Ensure the map is correct when method returns val (toAdd, toRemove) = KotlinUtils.getMapAndListDifferences(desired, have) for (key in toAdd) { @@ -201,13 +201,14 @@ abstract class SmartUpdateMediatorLiveData<T>(private val isStaticVal: Boolean = val liveData = getLiveDataFun(key) // Should be a no op, but there is a slight possibility it isn't have[key] = liveData - val observer = Observer<Any?> { - if (onUpdateFun != null) { - onUpdateFun(key) - } else { - update() + val observer = + Observer<Any?> { + if (onUpdateFun != null) { + onUpdateFun(key) + } else { + update() + } } - } addSourceWithStackTraceAttribution(liveData, observer, stackTrace) } } @@ -218,8 +219,11 @@ abstract class SmartUpdateMediatorLiveData<T>(private val isStaticVal: Boolean = timeWentInactive = null // If this is not an async livedata, and we have sources, and all sources are non-stale, // force update our value - if (sources.isNotEmpty() && sources.all { !it.isStale } && - this !is SmartAsyncMediatorLiveData<T>) { + if ( + sources.isNotEmpty() && + sources.all { !it.isStale } && + this !is SmartAsyncMediatorLiveData<T> + ) { update() } super.onActive() @@ -247,6 +251,7 @@ abstract class SmartUpdateMediatorLiveData<T>(private val isStaticVal: Boolean = update() } }, - isInitialized = { isInitialized && (staleOk || !isStale) }) + isInitialized = { isInitialized && (staleOk || !isStale) } + ) } } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/StandardPermGroupNamesLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/StandardPermGroupNamesLiveData.kt index 3b3b76171..a3b1799de 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/data/StandardPermGroupNamesLiveData.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/data/StandardPermGroupNamesLiveData.kt @@ -19,9 +19,7 @@ package com.android.permissioncontroller.permission.data import androidx.lifecycle.LiveData import com.android.permissioncontroller.permission.utils.PermissionMapping -/** - * A LiveData which tracks Platform Permission Group names. - */ +/** A LiveData which tracks Platform Permission Group names. */ object StandardPermGroupNamesLiveData : LiveData<List<String>>() { init { diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/UnusedPackagesLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/UnusedPackagesLiveData.kt index b97c27501..89bb93dbd 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/data/UnusedPackagesLiveData.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/data/UnusedPackagesLiveData.kt @@ -28,11 +28,12 @@ import com.android.permissioncontroller.hibernation.lastTimePackageUsed import com.android.permissioncontroller.permission.utils.Utils /** - * Gets all unused packages from an existing live data that have not been opened in a few months - * and the permission groups that have been revoked for them, if any. This will let us removed used - * apps from the Unused Apps screen. + * Gets all unused packages from an existing live data that have not been opened in a few months and + * the permission groups that have been revoked for them, if any. This will let us removed used apps + * from the Unused Apps screen. * * @param sourceLiveData the live data for packages to base this list of unused apps on + * * ```(packageName, user) -> [groupName]``` */ class UnusedPackagesLiveData( @@ -45,15 +46,9 @@ class UnusedPackagesLiveData( private var usageStatsLiveData = UsageStatsLiveData[unusedThreshold] init { - addSource(usageStatsLiveData) { - update() - } - addSource(AutoRevokedPackagesLiveData) { - update() - } - addSource(sourceLiveData) { - update() - } + addSource(usageStatsLiveData) { update() } + addSource(AutoRevokedPackagesLiveData) { update() } + addSource(sourceLiveData) { update() } DeviceConfig.addOnPropertiesChangedListener( NAMESPACE_PERMISSIONS, PermissionControllerApplication.get().mainExecutor, @@ -63,9 +58,7 @@ class UnusedPackagesLiveData( removeSource(usageStatsLiveData) unusedThreshold = getUnusedThresholdMs() usageStatsLiveData = UsageStatsLiveData[unusedThreshold] - addSource(usageStatsLiveData) { - update() - } + addSource(usageStatsLiveData) { update() } } } } @@ -73,9 +66,11 @@ class UnusedPackagesLiveData( } override fun onUpdate() { - if (!usageStatsLiveData.isInitialized || - !AutoRevokedPackagesLiveData.isInitialized || - !sourceLiveData.isInitialized) { + if ( + !usageStatsLiveData.isInitialized || + !AutoRevokedPackagesLiveData.isInitialized || + !sourceLiveData.isInitialized + ) { return } @@ -92,8 +87,10 @@ class UnusedPackagesLiveData( for ((user, stats) in usageStatsLiveData.value!!) { for (stat in stats) { val userPackage = stat.packageName to user - if (userPackage in autoRevokedPackages && - (now - stat.lastTimePackageUsed()) < unusedThreshold) { + if ( + userPackage in autoRevokedPackages && + (now - stat.lastTimePackageUsed()) < unusedThreshold + ) { unusedPackages.remove(userPackage) } } @@ -111,4 +108,4 @@ fun getUnusedPackages(): UnusedPackagesLiveData { } else { unusedAutoRevokePackagesLiveData } -}
\ No newline at end of file +} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/UsageStatsLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/UsageStatsLiveData.kt index b7a44d1d3..72ff21a0a 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/data/UsageStatsLiveData.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/data/UsageStatsLiveData.kt @@ -30,19 +30,18 @@ import kotlinx.coroutines.Job * * @param app The current application * @param searchTimeMs The length of time, in milliseconds, that this LiveData will track. The time - * will start when the liveData is loaded, and extend backwards searchTimeMs milliseconds. + * will start when the liveData is loaded, and extend backwards searchTimeMs milliseconds. * @param interval The interval to measure in. Default is monthly. */ -class UsageStatsLiveData private constructor( +class UsageStatsLiveData +private constructor( private val app: Application, private val searchTimeMs: Long, private val interval: Int = INTERVAL_MONTHLY ) : SmartAsyncMediatorLiveData<Map<UserHandle, List<UsageStats>>>() { init { - addSource(UsersLiveData) { - update() - } + addSource(UsersLiveData) { update() } } override suspend fun loadDataAndPostValue(job: Job) { @@ -58,8 +57,8 @@ class UsageStatsLiveData private constructor( if (Utils.isUserDisabledOrWorkProfile(user)) { continue } - val statsManager = Utils.getUserContext(app, user).getSystemService( - UsageStatsManager::class.java)!! + val statsManager = + Utils.getUserContext(app, user).getSystemService(UsageStatsManager::class.java)!! statsManager.queryUsageStats(interval, now - searchTimeMs, now)?.let { stats -> userMap[user] = stats } @@ -77,4 +76,4 @@ class UsageStatsLiveData private constructor( return get(interval to INTERVAL_MONTHLY) } } -}
\ No newline at end of file +} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/UserPackageInfosLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/UserPackageInfosLiveData.kt index 02285809c..884772f37 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/data/UserPackageInfosLiveData.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/data/UserPackageInfosLiveData.kt @@ -85,11 +85,13 @@ private constructor(private val app: Application, private val user: UserHandle) ) } else if (SdkLevel.isAtLeastS()) { app.applicationContext.packageManager.getInstalledPackagesAsUser( - GET_PERMISSIONS or GET_ATTRIBUTIONS or MATCH_ALL, user.identifier + GET_PERMISSIONS or GET_ATTRIBUTIONS or MATCH_ALL, + user.identifier ) } else { app.applicationContext.packageManager.getInstalledPackagesAsUser( - GET_PERMISSIONS or MATCH_ALL, user.identifier + GET_PERMISSIONS or MATCH_ALL, + user.identifier ) } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/UserSensitivityLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/UserSensitivityLiveData.kt index c138dc36d..fe4517173 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/data/UserSensitivityLiveData.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/data/UserSensitivityLiveData.kt @@ -23,25 +23,25 @@ import android.content.pm.PackageManager import android.os.Process import android.os.Process.INVALID_UID import android.os.UserHandle - import com.android.permissioncontroller.PermissionControllerApplication -import com.android.permissioncontroller.permission.utils.PermissionMapping import com.android.permissioncontroller.permission.model.livedatatypes.UidSensitivityState import com.android.permissioncontroller.permission.utils.KotlinUtils +import com.android.permissioncontroller.permission.utils.PermissionMapping import com.android.permissioncontroller.permission.utils.Utils -import kotlinx.coroutines.Job import java.lang.IllegalArgumentException +import kotlinx.coroutines.Job /** - * Live data of the user sensitivity of either one uid, or all uids that belong to a user. - * Maps <uid, user sensitive state> + * Live data of the user sensitivity of either one uid, or all uids that belong to a user. Maps + * <uid, user sensitive state> * * @param app The current application * @param uid The uid whose user sensitivity we would like to observer, or INVALID_UID if we want - * all uids for a user + * all uids for a user * @param user The user for whom we want the uid/s */ -class UserSensitivityLiveData private constructor( +class UserSensitivityLiveData +private constructor( private val app: Application, private val uid: Int, private val user: UserHandle @@ -60,12 +60,8 @@ class UserSensitivityLiveData private constructor( } if (getAllUids) { - addSource(userPackageInfosLiveData) { - update() - } - addSource(LauncherPackagesLiveData) { - update() - } + addSource(userPackageInfosLiveData) { update() } + addSource(LauncherPackagesLiveData) { update() } } else { update() } @@ -76,10 +72,10 @@ class UserSensitivityLiveData private constructor( if (!getAllUids) { val uidHasPackages = getAndObservePackageLiveDatas() - if (!uidHasPackages || packageLiveDatas.all { - it.value.isInitialized && - it.value.value == null - }) { + if ( + !uidHasPackages || + packageLiveDatas.all { it.value.isInitialized && it.value.value == null } + ) { packageLiveDatas.clear() invalidateSingle(uid to user) postValue(null) @@ -88,11 +84,12 @@ class UserSensitivityLiveData private constructor( return } } - val pkgs = if (getAllUids) { - userPackageInfosLiveData.value ?: return - } else { - packageLiveDatas.mapNotNull { it.value.value } - } + val pkgs = + if (getAllUids) { + userPackageInfosLiveData.value ?: return + } else { + packageLiveDatas.mapNotNull { it.value.value } + } if (job.isCancelled) { return } @@ -105,17 +102,19 @@ class UserSensitivityLiveData private constructor( for (pkg in pkgs) { // sensitivityState for one uid - val userSensitiveState = sensitiveStatePerUid.getOrPut(pkg.uid) { - UidSensitivityState(mutableSetOf(), mutableMapOf()) - } + val userSensitiveState = + sensitiveStatePerUid.getOrPut(pkg.uid) { + UidSensitivityState(mutableSetOf(), mutableMapOf()) + } userSensitiveState.packages.add(pkg) - val pkgHasLauncherIcon = if (getAllUids) { - // The launcher packages set will only be null when it is uninitialized. - LauncherPackagesLiveData.value?.contains(pkg.packageName) ?: return - } else { - KotlinUtils.packageHasLaunchIntent(context, pkg.packageName) - } + val pkgHasLauncherIcon = + if (getAllUids) { + // The launcher packages set will only be null when it is uninitialized. + LauncherPackagesLiveData.value?.contains(pkg.packageName) ?: return + } else { + KotlinUtils.packageHasLaunchIntent(context, pkg.packageName) + } val pkgIsSystemApp = pkg.appFlags and ApplicationInfo.FLAG_SYSTEM != 0 // Iterate through all runtime perms, setting their keys for (perm in pkg.requestedPermissions.intersect(runtimePerms)) { @@ -125,18 +124,20 @@ class UserSensitivityLiveData private constructor( * - the permission is not pre-granted, or * - the package is not a system app (i.e. not preinstalled) */ - var flags = if (pkgIsSystemApp && !pkgHasLauncherIcon) { - val permGrantedByDefault = pm.getPermissionFlags(perm, pkg.packageName, - user) and PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT != 0 - - if (permGrantedByDefault) { - 0 + var flags = + if (pkgIsSystemApp && !pkgHasLauncherIcon) { + val permGrantedByDefault = + pm.getPermissionFlags(perm, pkg.packageName, user) and + PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT != 0 + + if (permGrantedByDefault) { + 0 + } else { + PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED + } } else { - PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED + Utils.FLAGS_ALWAYS_USER_SENSITIVE } - } else { - Utils.FLAGS_ALWAYS_USER_SENSITIVE - } /* * If two packages share a UID there can be two cases: @@ -147,11 +148,12 @@ class UserSensitivityLiveData private constructor( */ val previousFlags = userSensitiveState.permStates[perm] if (previousFlags != null) { - flags = if (pkg.uid < Process.FIRST_APPLICATION_UID) { - flags and previousFlags - } else { - flags or previousFlags - } + flags = + if (pkg.uid < Process.FIRST_APPLICATION_UID) { + flags and previousFlags + } else { + flags or previousFlags + } } userSensitiveState.permStates[perm] = flags @@ -173,13 +175,17 @@ class UserSensitivityLiveData private constructor( /** * Repository for a UserSensitivityLiveData - * <p> Key value is a pair of int uid (INVALID_UID for all uids), and UserHandle, - * value is its corresponding LiveData. + * + * <p> Key value is a pair of int uid (INVALID_UID for all uids), and UserHandle, value is its + * corresponding LiveData. */ companion object : DataRepository<Pair<Int, UserHandle>, UserSensitivityLiveData>() { override fun newValue(key: Pair<Int, UserHandle>): UserSensitivityLiveData { - return UserSensitivityLiveData(PermissionControllerApplication.get(), key.first, - key.second) + return UserSensitivityLiveData( + PermissionControllerApplication.get(), + key.first, + key.second + ) } /** @@ -187,7 +193,6 @@ class UserSensitivityLiveData private constructor( * throw an exception if the uid is INVALID_UID. * * @param uid The uid for which we want the liveData - * * @return The liveData associated with the given UID */ operator fun get(uid: Int): UserSensitivityLiveData { @@ -201,7 +206,6 @@ class UserSensitivityLiveData private constructor( * Gets a liveData for a user, which will track all uids under * * @param user The user for whom we want the liveData - * * @return The liveData associated with that user, for all uids */ operator fun get(user: UserHandle): UserSensitivityLiveData { diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/UsersLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/UsersLiveData.kt index 0e78ec5f6..0fe3f6007 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/data/UsersLiveData.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/data/UsersLiveData.kt @@ -23,31 +23,26 @@ import android.content.Intent import android.content.IntentFilter import android.os.UserHandle import android.os.UserManager - import com.android.permissioncontroller.PermissionControllerApplication -import com.android.permissioncontroller.permission.utils.Utils /** * Live data of the users of the current profile group. * - * * Data source: system server */ object UsersLiveData : SmartUpdateMediatorLiveData<List<UserHandle>>() { - @SuppressLint("StaticFieldLeak") - private val app = PermissionControllerApplication.get() + @SuppressLint("StaticFieldLeak") private val app = PermissionControllerApplication.get() - /** Monitors changes to the users on this device */ - private val mUserMonitor = object : BroadcastReceiver() { - override fun onReceive(context: Context, intent: Intent) { - onUpdate() + /** Monitors changes to the users on this device */ + private val mUserMonitor = + object : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent) { + onUpdate() + } } - } - /** - * Update the encapsulated data with the current list of users. - */ + /** Update the encapsulated data with the current list of users. */ override fun onUpdate() { value = app.getSystemService(UserManager::class.java)!!.userProfiles } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/v31/AllLightPackageOpsLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/v31/AllLightPackageOpsLiveData.kt index 6483c9f9c..74b29043d 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/data/v31/AllLightPackageOpsLiveData.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/data/v31/AllLightPackageOpsLiveData.kt @@ -117,7 +117,9 @@ class AllLightPackageOpsLiveData(app: Application) : .filter { UserHandle.getUserHandleForUid(it.uid) in allProfilesInCurrentUser } .associateBy( { Pair(it.packageName, UserHandle.getUserHandleForUid(it.uid)) }, - { LightPackageOps(opNames, it) })) + { LightPackageOps(opNames, it) } + ) + ) } override fun onOpChanged(op: String?, packageName: String?) { diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/v33/RecentPermissionDecisionsLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/v33/RecentPermissionDecisionsLiveData.kt index 4c18d6987..ec4f936b7 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/data/v33/RecentPermissionDecisionsLiveData.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/data/v33/RecentPermissionDecisionsLiveData.kt @@ -20,8 +20,8 @@ import android.os.Build import androidx.annotation.RequiresApi import androidx.annotation.VisibleForTesting import com.android.permissioncontroller.permission.data.SmartAsyncMediatorLiveData -import com.android.permissioncontroller.permission.service.v33.PermissionDecisionStorageImpl import com.android.permissioncontroller.permission.service.PermissionEventStorage +import com.android.permissioncontroller.permission.service.v33.PermissionDecisionStorageImpl import kotlinx.coroutines.Job /** Gets all recent permission decisions made by the user. */ @@ -39,8 +39,6 @@ class RecentPermissionDecisionsLiveData( // no need to subscribe to decision changes, since those will also be bubbled up through // package info changes - recentDecisionsStorage.loadEvents().also { - postValue(it) - } + recentDecisionsStorage.loadEvents().also { postValue(it) } } -}
\ No newline at end of file +} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/v34/AppDataSharingUpdatesLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/v34/AppDataSharingUpdatesLiveData.kt index d5fd59242..7844a172f 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/data/v34/AppDataSharingUpdatesLiveData.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/data/v34/AppDataSharingUpdatesLiveData.kt @@ -40,14 +40,16 @@ class AppDataSharingUpdatesLiveData(val app: Application) : DeviceConfig.getLong( DeviceConfig.NAMESPACE_PRIVACY, PROPERTY_DATA_SHARING_UPDATE_PERIOD_MILLIS, - Duration.ofDays(DEFAULT_DATA_SHARING_UPDATE_PERIOD_DAYS).toMillis()) + Duration.ofDays(DEFAULT_DATA_SHARING_UPDATE_PERIOD_DAYS).toMillis() + ) val file = AppsSafetyLabelHistoryPersistence.getSafetyLabelHistoryFile(app.applicationContext) val appSafetyLabelDiffsFromPersistence = AppsSafetyLabelHistoryPersistence.getAppSafetyLabelDiffs( Instant.now().atZone(ZoneId.systemDefault()).toInstant().minusMillis(updatePeriod), - file) + file + ) val updatesFromPersistence = appSafetyLabelDiffsFromPersistence.mapNotNull { it.buildUpdateIfSignificantChange() } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/v34/LightInstallSourceInfoLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/v34/LightInstallSourceInfoLiveData.kt index bbc62dfc9..716d8dfe5 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/data/v34/LightInstallSourceInfoLiveData.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/data/v34/LightInstallSourceInfoLiveData.kt @@ -74,7 +74,9 @@ private constructor( try { val installSourceInfo = getInstallSourceInfo(packageName) LightInstallSourceInfo( - installSourceInfo.packageSource, installSourceInfo.initiatingPackageName) + installSourceInfo.packageSource, + installSourceInfo.initiatingPackageName + ) } catch (e: PackageManager.NameNotFoundException) { Log.w(LOG_TAG, "InstallSourceInfo for $packageName not found") invalidateSingle(packageName to user) @@ -101,7 +103,10 @@ private constructor( override fun newValue(key: Pair<String, UserHandle>): LightInstallSourceInfoLiveData { return LightInstallSourceInfoLiveData( - PermissionControllerApplication.get(), key.first, key.second) + PermissionControllerApplication.get(), + key.first, + key.second + ) } } } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/v34/SafetyLabelInfoLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/v34/SafetyLabelInfoLiveData.kt index 6229218d4..5c63de6ce 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/data/v34/SafetyLabelInfoLiveData.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/data/v34/SafetyLabelInfoLiveData.kt @@ -115,7 +115,8 @@ private constructor( } return SafetyLabel.getSafetyLabelFromMetadata( - userContext.packageManager.getAppMetadata(packageName)) + userContext.packageManager.getAppMetadata(packageName) + ) } companion object : @@ -124,7 +125,10 @@ private constructor( override fun newValue(key: Pair<String, UserHandle>): SafetyLabelInfoLiveData { return SafetyLabelInfoLiveData( - PermissionControllerApplication.get(), key.first, key.second) + PermissionControllerApplication.get(), + key.first, + key.second + ) } } } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/HibernationSettingState.kt b/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/HibernationSettingState.kt index 7b5b7994a..fd9c49f3d 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/HibernationSettingState.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/HibernationSettingState.kt @@ -23,25 +23,23 @@ import android.permission.PermissionControllerManager.HIBERNATION_ELIGIBILITY_EX * Tracks the setting state of hibernation and auto revoke for a package * * @param hibernationEligibility state saying whether the package is eligible for hibernation. See - * [HIBERNATION_ELIGIBILITY_ELIGIBLE]. + * [HIBERNATION_ELIGIBILITY_ELIGIBLE]. * @param revocableGroupNames A list of which permission groups of this package are eligible for - * auto-revoke. A permission group is auto-revocable if it does not contain a default granted - * permission. + * auto-revoke. A permission group is auto-revocable if it does not contain a default granted + * permission. */ data class HibernationSettingState( val hibernationEligibility: Int, val revocableGroupNames: List<String> ) { - /** - * Whether package will hibernate if it is unused. - */ + /** Whether package will hibernate if it is unused. */ fun isEligibleForHibernation(): Boolean { return hibernationEligibility == HIBERNATION_ELIGIBILITY_ELIGIBLE } /** - * Whether the package is exempt from hibernation by the system. This means the app can never - * be hibernated, and the user setting to exempt it is disabled. + * Whether the package is exempt from hibernation by the system. This means the app can never be + * hibernated, and the user setting to exempt it is disabled. */ fun isExemptBySystem(): Boolean { return hibernationEligibility == HIBERNATION_ELIGIBILITY_EXEMPT_BY_SYSTEM diff --git a/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/LightAppPermGroup.kt b/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/LightAppPermGroup.kt index 3c87f0b7a..a82387820 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/LightAppPermGroup.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/LightAppPermGroup.kt @@ -28,12 +28,12 @@ import android.os.UserHandle * @param packageInfo Information about the package * @param permGroupInfo Information about the permission group * @param allPermissions The permissions in the permission group that the package requests - * (including restricted ones). + * (including restricted ones). * @param hasInstallToRuntimeSplit If this group contains a permission that was previously an - * install permission, but is currently a runtime permission + * install permission, but is currently a runtime permission * @param specialLocationGrant If this package is the location provider, or the extra location - * package, then the grant state of the group is not determined by the grant state of individual - * permissions, but by other system properties + * package, then the grant state of the group is not determined by the grant state of individual + * permissions, but by other system properties */ data class LightAppPermGroup( val packageInfo: LightPackageInfo, @@ -48,25 +48,17 @@ data class LightAppPermGroup( perms: Map<String, LightPermission> ) : this(pI, pGI, perms, false, null) - /** - * All unrestricted permissions. Usually restricted permissions are ignored - */ + /** All unrestricted permissions. Usually restricted permissions are ignored */ val permissions: Map<String, LightPermission> = - allPermissions.filter { (_, permission) -> !permission.isRestricted } + allPermissions.filter { (_, permission) -> !permission.isRestricted } - /** - * The package name of this group - */ + /** The package name of this group */ val packageName = packageInfo.packageName - /** - * The permission group name of this group - */ + /** The permission group name of this group */ val permGroupName = permGroupInfo.name - /** - * The current userHandle of this AppPermGroup. - */ + /** The current userHandle of this AppPermGroup. */ val userHandle: UserHandle = UserHandle.getUserHandleForUid(packageInfo.uid) /** @@ -75,34 +67,36 @@ data class LightAppPermGroup( */ val backgroundPermNames = permissions.mapNotNull { it.value.backgroundPermission } - /** - * All foreground permissions in the permission group which are requested by the package. - */ - val foregroundPermNames get() = permissions.mapNotNull { (name, _) -> - if (name !in backgroundPermNames) name else null - } - - val foreground = AppPermSubGroup(permissions.filter { it.key in foregroundPermNames }, - packageInfo, specialLocationGrant) - - val background = AppPermSubGroup(permissions.filter { it.key in backgroundPermNames }, - packageInfo, specialLocationGrant) - - /** - * Whether or not this App Permission Group has a permission which has a background mode - */ + /** All foreground permissions in the permission group which are requested by the package. */ + val foregroundPermNames + get() = + permissions.mapNotNull { (name, _) -> if (name !in backgroundPermNames) name else null } + + val foreground = + AppPermSubGroup( + permissions.filter { it.key in foregroundPermNames }, + packageInfo, + specialLocationGrant + ) + + val background = + AppPermSubGroup( + permissions.filter { it.key in backgroundPermNames }, + packageInfo, + specialLocationGrant + ) + + /** Whether or not this App Permission Group has a permission which has a background mode */ val hasPermWithBackgroundMode = backgroundPermNames.isNotEmpty() - /** - * Whether or not this App Permission Group requests a background permission - */ + /** Whether or not this App Permission Group requests a background permission */ val hasBackgroundGroup = backgroundPermNames.any { permissions.contains(it) } /** * Whether this App Permission Group's background and foreground permissions are fixed by policy */ - val isPolicyFullyFixed = foreground.isPolicyFixed && (!hasBackgroundGroup || - background.isPolicyFixed) + val isPolicyFullyFixed = + foreground.isPolicyFixed && (!hasBackgroundGroup || background.isPolicyFixed) /** * Whether this App Permission Group's background permissions are fixed by the system or policy @@ -114,107 +108,91 @@ data class LightAppPermGroup( */ val isForegroundFixed = foreground.isPolicyFixed || foreground.isSystemFixed - /** - * Whether or not this group supports runtime permissions - */ + /** Whether or not this group supports runtime permissions */ val supportsRuntimePerms = packageInfo.targetSdkVersion >= Build.VERSION_CODES.M /** * Whether this App Permission Group is one-time. 2 cases: * 1. If the perm group is not LOCATION, check if any of the permissions is one-time and none of - * the granted permissions are not one-time. + * the granted permissions are not one-time. * 2. If the perm group is LOCATION, check if ACCESS_COARSE_LOCATION is one-time. */ - val isOneTime = (permGroupName != Manifest.permission_group.LOCATION && + val isOneTime = + (permGroupName != Manifest.permission_group.LOCATION && permissions.any { it.value.isOneTime } && permissions.none { !it.value.isOneTime && it.value.isGrantedIncludingAppOp }) || (permGroupName == Manifest.permission_group.LOCATION && - permissions[ACCESS_COARSE_LOCATION]?.isOneTime == true) + permissions[ACCESS_COARSE_LOCATION]?.isOneTime == true) - /** - * Whether any permissions in this group are granted by default (pregrant) - */ + /** Whether any permissions in this group are granted by default (pregrant) */ val isGrantedByDefault = foreground.isGrantedByDefault || background.isGrantedByDefault - /** - * Whether any permissions in this group are granted by being a role holder - */ + /** Whether any permissions in this group are granted by being a role holder */ val isGrantedByRole = foreground.isGrantedByRole || background.isGrantedByRole - /** - * Whether any of the permission (foreground/background) is fixed by the system - */ + /** Whether any of the permission (foreground/background) is fixed by the system */ val isSystemFixed = foreground.isSystemFixed || background.isSystemFixed - /** - * Whether any of the permission (foreground/background) in this group requires a review - */ + /** Whether any of the permission (foreground/background) in this group requires a review */ val isReviewRequired = foreground.isReviewRequired || background.isReviewRequired - /** - * Whether any of the permission (foreground/background) is granted in this permission group - */ + /** Whether any of the permission (foreground/background) is granted in this permission group */ var isGranted = foreground.isGranted || background.isGranted - /** - * Whether any permissions in this group are user sensitive - */ + /** Whether any permissions in this group are user sensitive */ val isUserSensitive = permissions.any { it.value.isUserSensitive } - /** - * Whether any permissions in this group are revoke-when-requested - */ + /** Whether any permissions in this group are revoke-when-requested */ val isRevokeWhenRequested = permissions.any { it.value.isRevokeWhenRequested } - /** - * Whether any of this App Permission Groups permissions are fixed by the user - */ + /** Whether any of this App Permission Groups permissions are fixed by the user */ val isUserFixed = foreground.isUserFixed || background.isUserFixed - /** - * Whether any of this App Permission Group's permissions are set by the user - */ + /** Whether any of this App Permission Group's permissions are set by the user */ val isUserSet = foreground.isUserSet || background.isUserSet /** - * A subset of the AppPermissionGroup, representing either the background or foreground permissions - * of the full group. + * A subset of the AppPermissionGroup, representing either the background or foreground + * permissions of the full group. * - * @param permissions The permissions contained within this subgroup, a subset of those contained - * in the full group + * @param permissions The permissions contained within this subgroup, a subset of those + * contained in the full group * @param specialLocationGrant Whether this is a special location package */ - data class AppPermSubGroup internal constructor( + data class AppPermSubGroup + internal constructor( private val permissions: Map<String, LightPermission>, private val packageInfo: LightPackageInfo, private val specialLocationGrant: Boolean? ) { - /** - * Whether any of this App Permission SubGroup's permissions are granted - */ + /** Whether any of this App Permission SubGroup's permissions are granted */ val isGranted = specialLocationGrant ?: permissions.any { it.value.isGrantedIncludingAppOp } /** * Whether this App Permission SubGroup should be treated as granted. This means either: * 1) At least one permission was granted excluding auto-granted permissions (i.e., granted - * during install time with flag RevokeWhenRequested.) Or, + * during install time with flag RevokeWhenRequested.) Or, * 2) All permissions were auto-granted (all permissions are all granted and all - * RevokeWhenRequested.) - */ - val isGrantedExcludingRWROrAllRWR = specialLocationGrant ?: (permissions - .any { it.value.isGrantedIncludingAppOp && !it.value.isRevokeWhenRequested } || - permissions.all { it.value.isGrantedIncludingAppOp && it.value.isRevokeWhenRequested }) - - /** - * Whether any of this App Permission SubGroup's permissions are granted by default + * RevokeWhenRequested.) */ + val isGrantedExcludingRWROrAllRWR = + specialLocationGrant + ?: (permissions.any { + it.value.isGrantedIncludingAppOp && !it.value.isRevokeWhenRequested + } || + permissions.all { + it.value.isGrantedIncludingAppOp && it.value.isRevokeWhenRequested + }) + + /** Whether any of this App Permission SubGroup's permissions are granted by default */ val isGrantedByDefault = permissions.any { it.value.isGrantedByDefault } /** - * Whether at least one of this App Permission SubGroup's permissions is one-time and - * none of the granted permissions are not one-time. + * Whether at least one of this App Permission SubGroup's permissions is one-time and none + * of the granted permissions are not one-time. */ - val isOneTime = permissions.any { it.value.isOneTime } && + val isOneTime = + permissions.any { it.value.isOneTime } && permissions.none { it.value.isGrantedIncludingAppOp && !it.value.isOneTime } /** @@ -222,24 +200,16 @@ data class LightAppPermGroup( */ val isPolicyFixed = permissions.any { it.value.isPolicyFixed } - /** - * Whether any of this App Permission Subgroup's permissions are fixed by the system - */ + /** Whether any of this App Permission Subgroup's permissions are fixed by the system */ val isSystemFixed = permissions.any { it.value.isSystemFixed } - /** - * Whether any of this App Permission Subgroup's permissions are fixed by the user - */ + /** Whether any of this App Permission Subgroup's permissions are fixed by the user */ val isUserFixed = permissions.any { it.value.isUserFixed } - /** - * Whether any of this App Permission Subgroup's permissions are set by the user - */ + /** Whether any of this App Permission Subgroup's permissions are set by the user */ val isUserSet = permissions.any { it.value.isUserSet } - /** - * whether review is required or not for the permission group - */ + /** whether review is required or not for the permission group */ val isReviewRequired = permissions.any { it.value.isReviewRequired } /** @@ -251,10 +221,9 @@ data class LightAppPermGroup( private val hasInstantPerm = permissions.any { (_, perm) -> perm.isInstantPerm } - /** - * Whether or not any permissions in this App Permission Subgroup can be granted - */ - val isGrantable = (!packageInfo.isInstantApp || hasInstantPerm) && + /** Whether or not any permissions in this App Permission Subgroup can be granted */ + val isGrantable = + (!packageInfo.isInstantApp || hasInstantPerm) && (packageInfo.targetSdkVersion >= Build.VERSION_CODES.M || hasPreRuntimePerm) } -}
\ No newline at end of file +} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/LightPackageInfo.kt b/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/LightPackageInfo.kt index 0f6b6c000..b50cf72d0 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/LightPackageInfo.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/LightPackageInfo.kt @@ -69,15 +69,17 @@ data class LightPackageInfo( pI.firstInstallTime, pI.lastUpdateTime, if (SdkLevel.isAtLeastS()) pI.applicationInfo.areAttributionsUserVisible() else false, - if (SdkLevel.isAtLeastS()) buildAttributionTagsToLabelsMap(pI.attributions) else emptyMap()) + if (SdkLevel.isAtLeastS()) buildAttributionTagsToLabelsMap(pI.attributions) else emptyMap() + ) /** Permissions which are granted according to the [requestedPermissionsFlags] */ val grantedPermissions: List<String> get() { val grantedPermissions = mutableListOf<String>() for (i in 0 until requestedPermissions.size) { - if ((requestedPermissionsFlags[i] and PackageInfo.REQUESTED_PERMISSION_GRANTED) != - 0) { + if ( + (requestedPermissionsFlags[i] and PackageInfo.REQUESTED_PERMISSION_GRANTED) != 0 + ) { grantedPermissions.add(requestedPermissions[i]) } } @@ -89,9 +91,8 @@ data class LightPackageInfo( * often. * * @param app The current application, which will be used to get the ApplicationInfo - * * @return The ApplicationInfo corresponding to this package, with this UID, or null, if no such - * package exists + * package exists */ fun getApplicationInfo(app: Application): ApplicationInfo? { try { @@ -112,7 +113,9 @@ data class LightPackageInfo( try { val userContext = Utils.getUserContext(app, UserHandle.getUserHandleForUid(uid)) return userContext.packageManager.getPackageInfo( - packageName, PackageManager.GET_PERMISSIONS) + packageName, + PackageManager.GET_PERMISSIONS + ) } catch (e: PackageManager.NameNotFoundException) {} return null } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/LightPermGroupInfo.kt b/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/LightPermGroupInfo.kt index 6aff2f3c9..7abf5ff1a 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/LightPermGroupInfo.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/LightPermGroupInfo.kt @@ -41,21 +41,22 @@ data class LightPermGroupInfo( val isSinglePermGroup: Boolean ) { - constructor(pII: PackageItemInfo) : this(pII.name, pII.packageName, pII.labelRes, pII.icon, - 0, pII is PermissionInfo) + constructor( + pII: PackageItemInfo + ) : this(pII.name, pII.packageName, pII.labelRes, pII.icon, 0, pII is PermissionInfo) - constructor(pGI: PermissionGroupInfo) : this(pGI.name, pGI.packageName, pGI.labelRes, pGI.icon, - pGI.descriptionRes, false) + constructor( + pGI: PermissionGroupInfo + ) : this(pGI.name, pGI.packageName, pGI.labelRes, pGI.icon, pGI.descriptionRes, false) /** * Gets the PackageItemInfo for this permission group from the system. * * @param app The current application, which will be used to get the PackageItemInfo - * - * @return The PackageItemInfo corresponding to this permission group, or null, if no - * such group exists + * @return The PackageItemInfo corresponding to this permission group, or null, if no such group + * exists */ fun toPackageItemInfo(app: Application): PackageItemInfo? { return Utils.getGroupInfo(name, app.applicationContext) } -}
\ No newline at end of file +} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/LightPermInfo.kt b/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/LightPermInfo.kt index 3954b7472..c1d271098 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/LightPermInfo.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/LightPermInfo.kt @@ -40,23 +40,29 @@ data class LightPermInfo( val protectionFlags: Int, val flags: Int ) { - constructor (permInfo: PermissionInfo): this(permInfo.name, permInfo.packageName, - permInfo.group, permInfo.backgroundPermission, permInfo.protection, - permInfo.protectionFlags, permInfo.flags) + constructor( + permInfo: PermissionInfo + ) : this( + permInfo.name, + permInfo.packageName, + permInfo.group, + permInfo.backgroundPermission, + permInfo.protection, + permInfo.protectionFlags, + permInfo.flags + ) /** * Gets the PermissionInfo for this permission from the system. * * @param app The current application, which will be used to get the PermissionInfo - * - * @return The PermissionInfo corresponding to this permission, or null, if no - * such permission exists + * @return The PermissionInfo corresponding to this permission, or null, if no such permission + * exists */ fun toPermissionInfo(app: Application): PermissionInfo? { try { return app.packageManager.getPermissionInfo(name, 0) - } catch (e: PackageManager.NameNotFoundException) { - } + } catch (e: PackageManager.NameNotFoundException) {} return null } -}
\ No newline at end of file +} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/LightPermission.kt b/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/LightPermission.kt index fd7d82dfc..7492ea6e0 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/LightPermission.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/LightPermission.kt @@ -28,11 +28,11 @@ import com.android.permissioncontroller.permission.utils.Utils * * @param pkgInfo The package requesting the permission * @param permInfo The permissionInfo this represents - * @param isGrantedIncludingAppOp Whether or not this permission is functionally granted. - * A non-granted app op but granted permission is counted as not granted + * @param isGrantedIncludingAppOp Whether or not this permission is functionally granted. A + * non-granted app op but granted permission is counted as not granted * @param flags The PermissionController flags for this permission * @param foregroundPerms The foreground permission names corresponding to this permission, if this - * permission is a background permission + * permission is a background permission */ data class LightPermission( val pkgInfo: LightPackageInfo, @@ -47,14 +47,13 @@ data class LightPermission( permInfo: LightPermInfo, permState: PermState, foregroundPerms: List<String>? - ) : - this(pkgInfo, permInfo, permState.granted, permState.permFlags, foregroundPerms) + ) : this(pkgInfo, permInfo, permState.granted, permState.permFlags, foregroundPerms) /** The name of this permission */ val name = permInfo.name /** The background permission name of this permission, if it exists */ val backgroundPermission: String? = permInfo.backgroundPermission - /** If this is a background permission **/ + /** If this is a background permission */ val isBackgroundPermission = foregroundPerms?.isNotEmpty() ?: false /** Whether this permission is fixed by policy */ val isPolicyFixed = flags and PackageManager.FLAG_PERMISSION_POLICY_FIXED != 0 @@ -76,9 +75,10 @@ data class LightPermission( val isImplicit: Boolean by lazy { var implicit = false for ((permName, permFlags) in - pkgInfo.requestedPermissions.zip(pkgInfo.requestedPermissionsFlags)) { - if (permName == permInfo.name && - (permFlags and PackageInfo.REQUESTED_PERMISSION_IMPLICIT) != 0 + pkgInfo.requestedPermissions.zip(pkgInfo.requestedPermissionsFlags)) { + if ( + permName == permInfo.name && + (permFlags and PackageInfo.REQUESTED_PERMISSION_IMPLICIT) != 0 ) { implicit = true break @@ -96,23 +96,25 @@ data class LightPermission( /** Whether this permission is set to be revoked upon being requested */ val isRevokeWhenRequested = flags and PackageManager.FLAG_PERMISSION_REVOKE_WHEN_REQUESTED != 0 /** Whether this permission is user sensitive in its current grant state */ - val isUserSensitive = !isRuntimePlatformPermission(permInfo.name) || + val isUserSensitive = + !isRuntimePlatformPermission(permInfo.name) || (isGrantedIncludingAppOp && - (flags and PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED) != 0) || + (flags and PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED) != 0) || (!isGrantedIncludingAppOp && - (flags and PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED) != 0) + (flags and PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED) != 0) /** Whether the permission is restricted */ - val isRestricted = when { - (permInfo.flags and PermissionInfo.FLAG_HARD_RESTRICTED) != 0 -> { - flags and Utils.FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT == 0 - } - (permInfo.flags and PermissionInfo.FLAG_SOFT_RESTRICTED) != 0 -> { - !SoftRestrictedPermissionPolicy.shouldShow(pkgInfo, permInfo.name, flags) - } - else -> { - false + val isRestricted = + when { + (permInfo.flags and PermissionInfo.FLAG_HARD_RESTRICTED) != 0 -> { + flags and Utils.FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT == 0 + } + (permInfo.flags and PermissionInfo.FLAG_SOFT_RESTRICTED) != 0 -> { + !SoftRestrictedPermissionPolicy.shouldShow(pkgInfo, permInfo.name, flags) + } + else -> { + false + } } - } /** Whether the permission is auto revoked */ val isAutoRevoked = flags and PackageManager.FLAG_PERMISSION_AUTO_REVOKED != 0 /** diff --git a/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/PermGroup.kt b/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/PermGroup.kt index c5079a950..e6913f57e 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/PermGroup.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/PermGroup.kt @@ -17,8 +17,8 @@ package com.android.permissioncontroller.permission.model.livedatatypes /** - * A permission Group, represented as a PackageItemInfo groupInfo, and a map of permission name - * to PermissionInfo objects. + * A permission Group, represented as a PackageItemInfo groupInfo, and a map of permission name to + * PermissionInfo objects. * * @param groupInfo information about the permission group * @param permissionInfos the Permissions in this group diff --git a/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/PermGroupPackagesUiInfo.kt b/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/PermGroupPackagesUiInfo.kt index e11e895a9..ba4aa0a20 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/PermGroupPackagesUiInfo.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/PermGroupPackagesUiInfo.kt @@ -22,17 +22,17 @@ package com.android.permissioncontroller.permission.model.livedatatypes * * @param name The name of the permission group whose UI data this represents * @param nonSystemTotal The total number of non-system applications that request permissions in - * this group + * this group * @param nonSystemGranted The total number of non-system applications that request permissions in - * this group, and have at least one permission in this group granted. + * this group, and have at least one permission in this group granted. * @param nonSystemUserSetOrPreGranted The total number of non-system applications that request - * permissions in this group, and have at least one permission in this group granted, or one - * permission denied by the user - * @param systemGranted The total number of system applications that request permissions in - * this group, and have at least one permission in this group granted. - * @param systemUserSetOrPreGranted The total number of system applications that request - * permissions in this group, and have at least one permission in this group granted, or one - * permission denied by the user + * permissions in this group, and have at least one permission in this group granted, or one + * permission denied by the user + * @param systemGranted The total number of system applications that request permissions in this + * group, and have at least one permission in this group granted. + * @param systemUserSetOrPreGranted The total number of system applications that request permissions + * in this group, and have at least one permission in this group granted, or one permission denied + * by the user */ data class PermGroupPackagesUiInfo( val name: String, diff --git a/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/UidSensitivityState.kt b/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/UidSensitivityState.kt index 3d0be0c0a..d8cdf01d6 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/UidSensitivityState.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/UidSensitivityState.kt @@ -22,8 +22,8 @@ package com.android.permissioncontroller.permission.model.livedatatypes * * @param packages A LightPackageInfo for every package with this uid * @param permStates A map <requested permission name, use sensitive state>, with the state being a - * combination of FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED and - * FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED + * combination of FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED and + * FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED */ data class UidSensitivityState( val packages: MutableSet<LightPackageInfo>, diff --git a/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/v31/LightHistoricalPackageOps.kt b/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/v31/LightHistoricalPackageOps.kt index 4c2051f9c..d5451c208 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/v31/LightHistoricalPackageOps.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/v31/LightHistoricalPackageOps.kt @@ -82,7 +82,10 @@ data class LightHistoricalPackageOps( this.getDiscreteAccesses(permissionToOpNames.value)?.let { appPermissionDiscreteAccesses.add( AppPermissionDiscreteAccesses( - AppPermissionId(packageName, userHandle, permissionToOpNames.key), it)) + AppPermissionId(packageName, userHandle, permissionToOpNames.key), + it + ) + ) } } @@ -117,7 +120,9 @@ data class LightHistoricalPackageOps( mutableMapOf() } attributedAppPermissionDiscreteAccesses[appPermissionId]?.put( - attributedHistoricalOps.tag ?: NO_ATTRIBUTION_TAG, discAccessData) + attributedHistoricalOps.tag ?: NO_ATTRIBUTION_TAG, + discAccessData + ) } } } @@ -152,7 +157,9 @@ data class LightHistoricalPackageOps( DiscreteAccess( opEntry.getLastAccessTime(DISCRETE_ACCESS_OP_FLAGS), opEntry.getLastDuration(DISCRETE_ACCESS_OP_FLAGS), - opEntry.getLastProxyInfo(DISCRETE_ACCESS_OP_FLAGS))) + opEntry.getLastProxyInfo(DISCRETE_ACCESS_OP_FLAGS) + ) + ) } } @@ -187,7 +194,9 @@ data class LightHistoricalPackageOps( DiscreteAccess( attributedOpEntry.getLastAccessTime(DISCRETE_ACCESS_OP_FLAGS), attributedOpEntry.getLastDuration(DISCRETE_ACCESS_OP_FLAGS), - attributedOpEntry.getLastProxyInfo(DISCRETE_ACCESS_OP_FLAGS))) + attributedOpEntry.getLastProxyInfo(DISCRETE_ACCESS_OP_FLAGS) + ) + ) } } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/v31/LightPackageOps.kt b/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/v31/LightPackageOps.kt index dde4857e2..b65fda5ea 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/v31/LightPackageOps.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/v31/LightPackageOps.kt @@ -44,7 +44,8 @@ data class LightPackageOps( ) : this( packageOps.packageName, UserHandle.getUserHandleForUid(packageOps.uid), - createLastPermissionGroupAccessTimesMap(ops, packageOps)) + createLastPermissionGroupAccessTimesMap(ops, packageOps) + ) /** Companion object for [LightPackageOps]. */ companion object { @@ -70,7 +71,8 @@ data class LightPackageOps( lastAccessTimeMs[permissionGroupOfOp] = maxOf( lastAccessTimeMs[permissionGroupOfOp] ?: -1, - opEntry.getLastAccessTime(OPS_LAST_ACCESS_FLAGS)) + opEntry.getLastAccessTime(OPS_LAST_ACCESS_FLAGS) + ) } return lastAccessTimeMs diff --git a/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/v34/LightInstallSourceInfo.kt b/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/v34/LightInstallSourceInfo.kt index e75c1eadf..85f77b823 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/v34/LightInstallSourceInfo.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/v34/LightInstallSourceInfo.kt @@ -36,12 +36,12 @@ class LightInstallSourceInfo { // default source of unspecified. All other sources should be explicitly set to another // PACKAGE_SOURCE_ value val isStoreInstalled = - initiatingPackageName != null && - (packageSource == PACKAGE_SOURCE_STORE || - packageSource == PACKAGE_SOURCE_UNSPECIFIED) + initiatingPackageName != null && + (packageSource == PACKAGE_SOURCE_STORE || + packageSource == PACKAGE_SOURCE_UNSPECIFIED) - isPreloadedApp = initiatingPackageName == null && - packageSource == PACKAGE_SOURCE_UNSPECIFIED + isPreloadedApp = + initiatingPackageName == null && packageSource == PACKAGE_SOURCE_UNSPECIFIED supportsSafetyLabel = isStoreInstalled || isPreloadedApp } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/v34/SafetyLabelInfo.kt b/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/v34/SafetyLabelInfo.kt index 7128e3069..2107e9944 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/v34/SafetyLabelInfo.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/v34/SafetyLabelInfo.kt @@ -28,7 +28,7 @@ import com.android.permissioncontroller.permission.model.livedatatypes.v34.Light class SafetyLabelInfo( val safetyLabel: SafetyLabel?, val installSourceInfo: LightInstallSourceInfo - ) { +) { companion object { /** Default definition of unavailable or no safety label found */ val UNAVAILABLE = SafetyLabelInfo(null, INSTALL_SOURCE_UNAVAILABLE) diff --git a/PermissionController/src/com/android/permissioncontroller/permission/service/AutoRevokePermissions.kt b/PermissionController/src/com/android/permissioncontroller/permission/service/AutoRevokePermissions.kt index 52e89e972..4b0192ba6 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/service/AutoRevokePermissions.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/service/AutoRevokePermissions.kt @@ -32,7 +32,6 @@ import com.android.permissioncontroller.PermissionControllerStatsLog import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__AUTO_UNUSED_APP_PERMISSION_REVOKED import com.android.permissioncontroller.hibernation.getUnusedThresholdMs -import com.android.permissioncontroller.permission.utils.PermissionMapping import com.android.permissioncontroller.permission.data.AutoRevokedPackagesLiveData import com.android.permissioncontroller.permission.data.LightAppPermGroupLiveData import com.android.permissioncontroller.permission.data.PackagePermissionsLiveData @@ -40,18 +39,18 @@ import com.android.permissioncontroller.permission.data.get import com.android.permissioncontroller.permission.model.livedatatypes.LightAppPermGroup import com.android.permissioncontroller.permission.model.livedatatypes.LightPackageInfo import com.android.permissioncontroller.permission.utils.KotlinUtils +import com.android.permissioncontroller.permission.utils.PermissionMapping import com.android.permissioncontroller.permission.utils.application import com.android.permissioncontroller.permission.utils.forEachInParallel import com.android.permissioncontroller.permission.utils.updatePermissionFlags -import kotlinx.coroutines.Dispatchers.Main import java.util.concurrent.atomic.AtomicBoolean +import kotlinx.coroutines.Dispatchers.Main private const val LOG_TAG = "AutoRevokePermissions" const val DEBUG_AUTO_REVOKE = true -private val EXEMPT_PERMISSIONS = listOf( - Manifest.permission.ACTIVITY_RECOGNITION, - Manifest.permission.POST_NOTIFICATIONS) +val AUTO_REVOKE_EXEMPT_PERMISSIONS = + listOf(Manifest.permission.ACTIVITY_RECOGNITION, Manifest.permission.POST_NOTIFICATIONS) private val SERVER_LOG_ID = PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__AUTO_UNUSED_APP_PERMISSION_REVOKED @@ -79,10 +78,10 @@ suspend fun revokeAppPermissions( continue } - val pkgPermChanges = PermissionChangeStorageImpl.getInstance().loadEvents() - .associateBy { it.packageName } + val pkgPermChanges = + PermissionChangeStorageImpl.getInstance().loadEvents().associateBy { it.packageName } // For each autorevoke-eligible app... - userApps.forEachInParallel(Main) forEachInParallelOuter@ { pkg: LightPackageInfo -> + userApps.forEachInParallel(Main) forEachInParallelOuter@{ pkg: LightPackageInfo -> if (pkg.grantedPermissions.isEmpty()) { return@forEachInParallelOuter } @@ -91,15 +90,18 @@ suspend fun revokeAppPermissions( val now = System.currentTimeMillis() if (pkgPermChange != null && now - pkgPermChange.eventTime < getUnusedThresholdMs()) { if (DEBUG_AUTO_REVOKE) { - DumpableLog.i(LOG_TAG, "Not revoking because permissions were changed " + - "recently for package $packageName") + DumpableLog.i( + LOG_TAG, + "Not revoking because permissions were changed " + + "recently for package $packageName" + ) } return@forEachInParallelOuter } val targetSdk = pkg.targetSdkVersion val pkgPermGroups: Map<String, List<String>> = - PackagePermissionsLiveData[packageName, user] - .getInitializedValue() ?: return@forEachInParallelOuter + PackagePermissionsLiveData[packageName, user].getInitializedValue() + ?: return@forEachInParallelOuter // Determine which permGroups are revocable val revocableGroups = mutableSetOf<String>() @@ -111,26 +113,30 @@ suspend fun revokeAppPermissions( continue } val group: LightAppPermGroup = - LightAppPermGroupLiveData[packageName, groupName, user] - .getInitializedValue() ?: continue + LightAppPermGroupLiveData[packageName, groupName, user].getInitializedValue() + ?: continue val fixed = group.isBackgroundFixed || group.isForegroundFixed - val granted = group.permissions.any { (_, perm) -> - perm.isGrantedIncludingAppOp && perm.name !in EXEMPT_PERMISSIONS - } - if (!fixed && granted && - !group.isGrantedByDefault && - !group.isGrantedByRole && - !group.isRevokeWhenRequested && - group.isUserSensitive) { + val granted = + group.permissions.any { (_, perm) -> + perm.isGrantedIncludingAppOp && perm.name !in AUTO_REVOKE_EXEMPT_PERMISSIONS + } + if ( + !fixed && + granted && + !group.isGrantedByDefault && + !group.isGrantedByRole && + !group.isRevokeWhenRequested && + group.isUserSensitive + ) { revocableGroups.add(groupName) } } // Mark any groups that split from an install-time permission as unrevocable for (fromPerm in - pkgPermGroups[PackagePermissionsLiveData.NON_RUNTIME_NORMAL_PERMS] ?: emptyList()) { + pkgPermGroups[PackagePermissionsLiveData.NON_RUNTIME_NORMAL_PERMS] ?: emptyList()) { for (toGroup in - splitPermissionIndex.getPermToGroupSplitsFrom(fromPerm, targetSdk)) { + splitPermissionIndex.getPermToGroupSplitsFrom(fromPerm, targetSdk)) { revocableGroups.remove(toGroup) } } @@ -139,11 +145,11 @@ suspend fun revokeAppPermissions( for (groupName in pkgPermGroups.keys) { if (!revocableGroups.contains(groupName)) { for (fromGroup in - splitPermissionIndex.getGroupToGroupSplitsTo(groupName, targetSdk)) { + splitPermissionIndex.getGroupToGroupSplitsTo(groupName, targetSdk)) { revocableGroups.remove(fromGroup) } for (toGroup in - splitPermissionIndex.getGroupToGroupSplitsFrom(groupName, targetSdk)) { + splitPermissionIndex.getGroupToGroupSplitsFrom(groupName, targetSdk)) { revocableGroups.remove(toGroup) } } @@ -153,72 +159,92 @@ suspend fun revokeAppPermissions( val anyPermsRevoked = AtomicBoolean(false) pkgPermGroups.entries .filter { revocableGroups.contains(it.key) } - .forEachInParallel(Main) forEachInParallelInner@ { (groupName, _) -> - val group: LightAppPermGroup = - LightAppPermGroupLiveData[packageName, groupName, user] - .getInitializedValue()!! + .forEachInParallel(Main) forEachInParallelInner@{ (groupName, _) -> + val group: LightAppPermGroup = + LightAppPermGroupLiveData[packageName, groupName, user] + .getInitializedValue()!! - val revocablePermissions = group.permissions.keys.toList() + val revocablePermissions = group.permissions.keys.toList() - if (revocablePermissions.isEmpty()) { - return@forEachInParallelInner - } - - if (DEBUG_AUTO_REVOKE) { - DumpableLog.i(LOG_TAG, - "revokeUnused $packageName - $revocablePermissions") - } + if (revocablePermissions.isEmpty()) { + return@forEachInParallelInner + } - val uid = group.packageInfo.uid - for (permName in revocablePermissions) { - PermissionControllerStatsLog.write( - PERMISSION_GRANT_REQUEST_RESULT_REPORTED, - sessionId, uid, packageName, permName, false, SERVER_LOG_ID, - /* permission_rationale_shown = */ false) - } + if (DEBUG_AUTO_REVOKE) { + DumpableLog.i(LOG_TAG, "revokeUnused $packageName - $revocablePermissions") + } - if (DEBUG_AUTO_REVOKE) { - DumpableLog.i(LOG_TAG, "revoking $packageName - $revocablePermissions") - DumpableLog.i(LOG_TAG, "State pre revocation: ${group.allPermissions}") - } - anyPermsRevoked.compareAndSet(false, true) + val uid = group.packageInfo.uid + for (permName in revocablePermissions) { + PermissionControllerStatsLog.write( + PERMISSION_GRANT_REQUEST_RESULT_REPORTED, + sessionId, + uid, + packageName, + permName, + false, + SERVER_LOG_ID, + /* permission_rationale_shown = */ false + ) + } - val bgRevokedState = KotlinUtils.revokeBackgroundRuntimePermissions( - context.application, group, - userFixed = false, oneTime = false, - filterPermissions = revocablePermissions) - if (DEBUG_AUTO_REVOKE) { - DumpableLog.i(LOG_TAG, - "Bg state post revocation: ${bgRevokedState.allPermissions}") - } - val fgRevokedState = KotlinUtils.revokeForegroundRuntimePermissions( - context.application, group, - userFixed = false, oneTime = false, - filterPermissions = revocablePermissions) - if (DEBUG_AUTO_REVOKE) { - DumpableLog.i(LOG_TAG, - "Fg state post revocation: ${fgRevokedState.allPermissions}") - } + if (DEBUG_AUTO_REVOKE) { + DumpableLog.i(LOG_TAG, "revoking $packageName - $revocablePermissions") + DumpableLog.i(LOG_TAG, "State pre revocation: ${group.allPermissions}") + } + anyPermsRevoked.compareAndSet(false, true) + + val bgRevokedState = + KotlinUtils.revokeBackgroundRuntimePermissions( + context.application, + group, + userFixed = false, + oneTime = false, + filterPermissions = revocablePermissions + ) + if (DEBUG_AUTO_REVOKE) { + DumpableLog.i( + LOG_TAG, + "Bg state post revocation: ${bgRevokedState.allPermissions}" + ) + } + val fgRevokedState = + KotlinUtils.revokeForegroundRuntimePermissions( + context.application, + group, + userFixed = false, + oneTime = false, + filterPermissions = revocablePermissions + ) + if (DEBUG_AUTO_REVOKE) { + DumpableLog.i( + LOG_TAG, + "Fg state post revocation: ${fgRevokedState.allPermissions}" + ) + } - for (permission in revocablePermissions) { - context.packageManager.updatePermissionFlags( - permission, packageName, user, - FLAG_PERMISSION_AUTO_REVOKED to true, - FLAG_PERMISSION_USER_SET to false) + for (permission in revocablePermissions) { + context.packageManager.updatePermissionFlags( + permission, + packageName, + user, + FLAG_PERMISSION_AUTO_REVOKED to true, + FLAG_PERMISSION_USER_SET to false + ) + } } - } if (anyPermsRevoked.get()) { - synchronized(revokedApps) { - revokedApps.add(packageName to user) - } + synchronized(revokedApps) { revokedApps.add(packageName to user) } } } if (DEBUG_AUTO_REVOKE) { synchronized(revokedApps) { - DumpableLog.i(LOG_TAG, - "Done auto-revoke for user ${user.identifier} - revoked $revokedApps") + DumpableLog.i( + LOG_TAG, + "Done auto-revoke for user ${user.identifier} - revoked $revokedApps" + ) } } } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/service/BackupHelper.java b/PermissionController/src/com/android/permissioncontroller/permission/service/BackupHelper.java index 9082b6931..bea110f70 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/service/BackupHelper.java +++ b/PermissionController/src/com/android/permissioncontroller/permission/service/BackupHelper.java @@ -136,8 +136,8 @@ public class BackupHelper { case END_TAG: numOpenTags--; break; - default: - // ignore + case END_DOCUMENT: + return; } } } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/service/BasePermissionEventStorage.kt b/PermissionController/src/com/android/permissioncontroller/permission/service/BasePermissionEventStorage.kt index 840b7e483..8bbb41c56 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/service/BasePermissionEventStorage.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/service/BasePermissionEventStorage.kt @@ -22,17 +22,14 @@ import android.util.AtomicFile import android.util.Log import com.android.permissioncontroller.DumpableLog import com.android.permissioncontroller.permission.data.PermissionEvent -import org.xmlpull.v1.XmlPullParserException import java.io.File import java.io.FileOutputStream import java.io.IOException import java.io.InputStream import java.io.OutputStream +import org.xmlpull.v1.XmlPullParserException -/** - * Thread-safe implementation of [PermissionEventStorage] using an XML file as the - * database. - */ +/** Thread-safe implementation of [PermissionEventStorage] using an XML file as the database. */ abstract class BasePermissionEventStorage<T : PermissionEvent>( private val context: Context, jobScheduler: JobScheduler = context.getSystemService(JobScheduler::class.java)!! @@ -75,9 +72,7 @@ abstract class BasePermissionEventStorage<T : PermissionEvent>( } override suspend fun clearEvents() { - synchronized(fileLock) { - dbFile.delete() - } + synchronized(fileLock) { dbFile.delete() } } override suspend fun removeOldData(): Boolean { @@ -85,12 +80,15 @@ abstract class BasePermissionEventStorage<T : PermissionEvent>( val existingEvents = readData() val originalCount = existingEvents.size - val newEvents = existingEvents.filter { - (System.currentTimeMillis() - it.eventTime) <= getMaxDataAgeMs() - } + val newEvents = + existingEvents.filter { + (System.currentTimeMillis() - it.eventTime) <= getMaxDataAgeMs() + } - DumpableLog.d(LOG_TAG, - "${originalCount - newEvents.size} old permission events removed") + DumpableLog.d( + LOG_TAG, + "${originalCount - newEvents.size} old permission events removed" + ) return writeData(newEvents) } @@ -109,20 +107,19 @@ abstract class BasePermissionEventStorage<T : PermissionEvent>( synchronized(fileLock) { val existingEvents = readData() - val newEvents = existingEvents.map { - it.copyWithTimeDelta(diffSystemTimeMillis) - } + val newEvents = existingEvents.map { it.copyWithTimeDelta(diffSystemTimeMillis) } return writeData(newEvents) } } private fun writeData(events: List<T>): Boolean { - val stream: FileOutputStream = try { - dbFile.startWrite() - } catch (e: IOException) { - Log.e(LOG_TAG, "Failed to save db file", e) - return false - } + val stream: FileOutputStream = + try { + dbFile.startWrite() + } catch (e: IOException) { + Log.e(LOG_TAG, "Failed to save db file", e) + return false + } try { serialize(stream, events) dbFile.finishWrite(stream) @@ -167,23 +164,15 @@ abstract class BasePermissionEventStorage<T : PermissionEvent>( @Throws(XmlPullParserException::class, IOException::class) abstract fun parse(inputStream: InputStream): List<T> - /** - * Returns file name for database. - */ + /** Returns file name for database. */ abstract fun getDatabaseFileName(): String - /** - * Returns max time that data should be persisted before being removed. - */ + /** Returns max time that data should be persisted before being removed. */ abstract fun getMaxDataAgeMs(): Long - /** - * Returns true if the two events have the same primary key for the database store. - */ + /** Returns true if the two events have the same primary key for the database store. */ abstract fun hasTheSamePrimaryKey(first: T, second: T): Boolean - /** - * Copies the event with the time delta applied to the [PermissionEvent.eventTime]. - */ + /** Copies the event with the time delta applied to the [PermissionEvent.eventTime]. */ abstract fun T.copyWithTimeDelta(timeDelta: Long): T } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/service/CheckLifecycleRegistry.kt b/PermissionController/src/com/android/permissioncontroller/permission/service/CheckLifecycleRegistry.kt index 506fa0ef0..678caa168 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/service/CheckLifecycleRegistry.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/service/CheckLifecycleRegistry.kt @@ -29,17 +29,13 @@ class CheckLifecycleRegistry(provider: LifecycleOwner) : LifecycleRegistry(provi if (Looper.myLooper() != Looper.getMainLooper()) { throw IllegalStateException("Lifecycle running on non main thread") } - synchronized(observerLock) { - super.addObserver(observer) - } + synchronized(observerLock) { super.addObserver(observer) } } override fun removeObserver(observer: LifecycleObserver) { if (Looper.myLooper() != Looper.getMainLooper()) { throw IllegalStateException("Lifecycle running on non main thread") } - synchronized(observerLock) { - super.removeObserver(observer) - } + synchronized(observerLock) { super.removeObserver(observer) } } -}
\ No newline at end of file +} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/service/ExemptRestrictedPermission.kt b/PermissionController/src/com/android/permissioncontroller/permission/service/ExemptRestrictedPermission.kt index dbf844b27..33e579eef 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/service/ExemptRestrictedPermission.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/service/ExemptRestrictedPermission.kt @@ -8,8 +8,8 @@ import android.os.Process import android.os.UserHandle /** - * For manually exempting a restricted permission. - * STOPSHIP This functionality should not be in the final release. + * For manually exempting a restricted permission. STOPSHIP This functionality should not be in the + * final release. */ class ExemptRestrictedPermission : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { @@ -21,7 +21,10 @@ class ExemptRestrictedPermission : BroadcastReceiver() { // Use upgrade flag. If the permission needs to be manually exempted then it probably // should have been done on upgrade. - userContext.packageManager.addWhitelistedRestrictedPermission(packageName, permission, - PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE) + userContext.packageManager.addWhitelistedRestrictedPermission( + packageName, + permission, + PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE + ) } -}
\ No newline at end of file +} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/service/PermissionChangeStorageImpl.kt b/PermissionController/src/com/android/permissioncontroller/permission/service/PermissionChangeStorageImpl.kt index bdcf833fc..5a49b7ebe 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/service/PermissionChangeStorageImpl.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/service/PermissionChangeStorageImpl.kt @@ -25,12 +25,6 @@ import com.android.permissioncontroller.PermissionControllerApplication import com.android.permissioncontroller.hibernation.getUnusedThresholdMs import com.android.permissioncontroller.permission.data.PermissionChange import com.android.permissioncontroller.permission.utils.Utils -import kotlinx.coroutines.DelicateCoroutinesApi -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.launch -import org.xmlpull.v1.XmlPullParser -import org.xmlpull.v1.XmlPullParserException import java.io.IOException import java.io.InputStream import java.io.OutputStream @@ -39,6 +33,12 @@ import java.text.ParseException import java.text.SimpleDateFormat import java.util.Date import java.util.Locale +import kotlinx.coroutines.DelicateCoroutinesApi +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.launch +import org.xmlpull.v1.XmlPullParser +import org.xmlpull.v1.XmlPullParserException /** * Implementation of [BasePermissionEventStorage] for storing [PermissionChange] events for long @@ -52,14 +52,10 @@ class PermissionChangeStorageImpl( // We don't use namespaces private val ns: String? = null - /** - * The format for how dates are stored. - */ + /** The format for how dates are stored. */ private val dateFormat = SimpleDateFormat("yyyy-MM-dd", Locale.US) - /** - * Exact format if [PROPERTY_PERMISSION_CHANGES_STORE_EXACT_TIME] is true - */ + /** Exact format if [PROPERTY_PERMISSION_CHANGES_STORE_EXACT_TIME] is true */ private val exactTimeFormat = SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS", Locale.US) companion object { @@ -67,9 +63,7 @@ class PermissionChangeStorageImpl( private const val DB_VERSION = 1 - /** - * Config store file name for general shared store file. - */ + /** Config store file name for general shared store file. */ private const val STORE_FILE_NAME = "permission_changes.xml" private const val TAG_PERMISSION_CHANGES = "permission-changes" @@ -79,13 +73,10 @@ class PermissionChangeStorageImpl( private const val ATTR_PACKAGE_NAME = "package-name" private const val ATTR_EVENT_TIME = "event-time" - @Volatile - private var INSTANCE: PermissionEventStorage<PermissionChange>? = null + @Volatile private var INSTANCE: PermissionEventStorage<PermissionChange>? = null fun getInstance(): PermissionEventStorage<PermissionChange> = - INSTANCE ?: synchronized(this) { - INSTANCE ?: createInstance().also { INSTANCE = it } - } + INSTANCE ?: synchronized(this) { INSTANCE ?: createInstance().also { INSTANCE = it } } private fun createInstance(): PermissionEventStorage<PermissionChange> { return PermissionChangeStorageImpl(PermissionControllerApplication.get()) @@ -142,9 +133,7 @@ class PermissionChangeStorageImpl( val storesExactTime = storesExactTime() val truncateToDay = didStoreExactTime != storesExactTime && !storesExactTime while (parser.next() != XmlPullParser.END_TAG) { - readPermissionChange(parser, format, truncateToDay)?.let { - entries.add(it) - } + readPermissionChange(parser, format, truncateToDay)?.let { entries.add(it) } } return entries } @@ -160,9 +149,11 @@ class PermissionChangeStorageImpl( try { val packageName = parser.getAttributeValueNullSafe(ns, ATTR_PACKAGE_NAME) val changeDate = parser.getAttributeValueNullSafe(ns, ATTR_EVENT_TIME) - var changeTime = format.parse(changeDate)?.time - ?: throw IllegalArgumentException( - "Could not parse date $changeDate on package $packageName") + var changeTime = + format.parse(changeDate)?.time + ?: throw IllegalArgumentException( + "Could not parse date $changeDate on package $packageName" + ) if (truncateToDay) { changeTime = dateFormat.parse(dateFormat.format(Date(changeTime)))!!.time } @@ -184,7 +175,8 @@ class PermissionChangeStorageImpl( private fun XmlPullParser.getAttributeValueNullSafe(namespace: String?, name: String): String { return this.getAttributeValue(namespace, name) ?: throw XmlPullParserException( - "Could not find attribute: namespace $namespace, name $name") + "Could not find attribute: namespace $namespace, name $name" + ) } override fun getDatabaseFileName(): String { @@ -204,11 +196,12 @@ class PermissionChangeStorageImpl( return this.copy(eventTime = this.eventTime + timeDelta) } - /** - * Should only be true in tests and never true in prod. - */ + /** Should only be true in tests and never true in prod. */ private fun storesExactTime(): Boolean { - return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PERMISSIONS, - Utils.PROPERTY_PERMISSION_CHANGES_STORE_EXACT_TIME, /* defaultValue= */ false) + return DeviceConfig.getBoolean( + DeviceConfig.NAMESPACE_PERMISSIONS, + Utils.PROPERTY_PERMISSION_CHANGES_STORE_EXACT_TIME, + /* defaultValue= */ false + ) } } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/service/PermissionControllerServiceModel.kt b/PermissionController/src/com/android/permissioncontroller/permission/service/PermissionControllerServiceModel.kt index 49a465898..05fee9797 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/service/PermissionControllerServiceModel.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/service/PermissionControllerServiceModel.kt @@ -28,7 +28,6 @@ import androidx.lifecycle.Observer import androidx.lifecycle.Transformations import com.android.permissioncontroller.DumpableLog import com.android.permissioncontroller.PermissionControllerProto.PermissionControllerDumpProto -import com.android.permissioncontroller.permission.utils.PermissionMapping import com.android.permissioncontroller.permission.data.AppPermGroupUiInfoLiveData import com.android.permissioncontroller.permission.data.HibernationSettingStateLiveData import com.android.permissioncontroller.permission.data.PackagePermissionsLiveData @@ -39,14 +38,15 @@ import com.android.permissioncontroller.permission.data.getUnusedPackages import com.android.permissioncontroller.permission.model.livedatatypes.AppPermGroupUiInfo import com.android.permissioncontroller.permission.model.livedatatypes.AppPermGroupUiInfo.PermGrantState import com.android.permissioncontroller.permission.model.livedatatypes.LightPackageInfo +import com.android.permissioncontroller.permission.utils.PermissionMapping import com.android.permissioncontroller.permission.utils.Utils +import java.util.function.IntConsumer import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.Dispatchers.Main import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.async import kotlinx.coroutines.launch import kotlinx.coroutines.withTimeout -import java.util.function.IntConsumer /** * A model for the PermissionControllerServiceImpl. Handles the data gathering for some methods of @@ -57,10 +57,10 @@ class PermissionControllerServiceModel(private val service: PermissionController private val observedLiveDatas = mutableListOf<LiveData<*>>() /** - * *Must* be used instead of LiveData.observe, in order to allow the lifecycle state to - * be set to "started" correctly. If the liveData was inactive, create a no op observer, which - * will survive until the service goes inactive. Will remove the provided observer after one - * update (one non-stale update, in the case of a SmartUpdateMediatorLiveData). + * *Must* be used instead of LiveData.observe, in order to allow the lifecycle state to be set + * to "started" correctly. If the liveData was inactive, create a no op observer, which will + * survive until the service goes inactive. Will remove the provided observer after one update + * (one non-stale update, in the case of a SmartUpdateMediatorLiveData). * * @param liveData The livedata we wish to observe * @param onChangedFun The function we wish to be called upon livedata updates @@ -72,14 +72,13 @@ class PermissionControllerServiceModel(private val service: PermissionController onChangedFun: (t: T?) -> Unit ) { GlobalScope.launch(Main.immediate) { - if (service.lifecycle.currentState != Lifecycle.State.STARTED) { service.setLifecycleToStarted() } if (!liveData.hasActiveObservers()) { observedLiveDatas.add(liveData) - liveData.observe(service, Observer { }) + liveData.observe(service, Observer {}) } if (forceUpdate && liveData is SmartUpdateMediatorLiveData<T>) { @@ -87,27 +86,28 @@ class PermissionControllerServiceModel(private val service: PermissionController } var updated = false - val observer = object : Observer<T> { - override fun onChanged(data: T) { - if (updated) { - return - } - if ((liveData is SmartUpdateMediatorLiveData<T> && !liveData.isStale) || - liveData !is SmartUpdateMediatorLiveData<T>) { - onChangedFun(data) - liveData.removeObserver(this) - updated = true + val observer = + object : Observer<T> { + override fun onChanged(data: T) { + if (updated) { + return + } + if ( + (liveData is SmartUpdateMediatorLiveData<T> && !liveData.isStale) || + liveData !is SmartUpdateMediatorLiveData<T> + ) { + onChangedFun(data) + liveData.removeObserver(this) + updated = true + } } } - } liveData.observe(service, observer) } } - /** - * Stop observing all currently observed liveDatas - */ + /** Stop observing all currently observed liveDatas */ fun removeObservers() { GlobalScope.launch(Main.immediate) { for (liveData in observedLiveDatas) { @@ -133,17 +133,16 @@ class PermissionControllerServiceModel(private val service: PermissionController ) { val packageInfosLiveData = UserPackageInfosLiveData[Process.myUserHandle()] observeAndCheckForLifecycleState(packageInfosLiveData) { packageInfos -> - onPackagesLoadedForCountPermissionApps(permissionNames, flags, callback, - packageInfos) + onPackagesLoadedForCountPermissionApps(permissionNames, flags, callback, packageInfos) } } /** - * Called upon receiving a list of packages which we want to filter by a list of permissions - * and flags. Observes the AppPermGroupUiInfoLiveData for every app, and, upon receiving a - * non-stale update, adds it to the count if it matches the permission list and flags. Will - * only use the first non-stale update, so if an app is updated after this update, but before - * execution is complete, the changes will not be reflected until the method is called again. + * Called upon receiving a list of packages which we want to filter by a list of permissions and + * flags. Observes the AppPermGroupUiInfoLiveData for every app, and, upon receiving a non-stale + * update, adds it to the count if it matches the permission list and flags. Will only use the + * first non-stale update, so if an app is updated after this update, but before execution is + * complete, the changes will not be reflected until the method is called again. * * @param permissionNames The list of permission names whose apps we want to count * @param flags Flags specifying if we want to count system apps, and count only granted apps @@ -167,11 +166,12 @@ class PermissionControllerServiceModel(private val service: PermissionController // Store the group of all installed, runtime permissions in permissionNames val permToGroup = mutableMapOf<String, String?>() for (permName in permissionNames) { - val permInfo = try { - service.packageManager.getPermissionInfo(permName, 0) - } catch (e: PackageManager.NameNotFoundException) { - continue - } + val permInfo = + try { + service.packageManager.getPermissionInfo(permName, 0) + } catch (e: PackageManager.NameNotFoundException) { + continue + } if (Utils.isPermissionDangerousInstalledNotRemoved(permInfo)) { permToGroup[permName] = PermissionMapping.getGroupOfPermission(permInfo) @@ -184,8 +184,10 @@ class PermissionControllerServiceModel(private val service: PermissionController val packageUiLiveDatas = mutableSetOf<AppPermGroupUiInfoLiveData>() for (permName in permToGroup.keys) { if (requestedPermissions.contains(permName)) { - packageUiLiveDatas.add(AppPermGroupUiInfoLiveData[packageName, - permToGroup[permName]!!, Process.myUserHandle()]) + packageUiLiveDatas.add( + AppPermGroupUiInfoLiveData[ + packageName, permToGroup[permName]!!, Process.myUserHandle()] + ) } } if (packageUiLiveDatas.isNotEmpty()) { @@ -211,8 +213,9 @@ class PermissionControllerServiceModel(private val service: PermissionController numPermAppsChecked++ if (uiInfo != null && uiInfo.shouldShow && (!uiInfo.isSystem || countSystem)) { - val granted = uiInfo.permGrantState != PermGrantState.PERMS_DENIED && - uiInfo.permGrantState != PermGrantState.PERMS_ASK + val granted = + uiInfo.permGrantState != PermGrantState.PERMS_DENIED && + uiInfo.permGrantState != PermGrantState.PERMS_ASK if (granted || !countOnlyGranted && !packageAdded) { // The permission might not be granted, but some permissions of the // group are granted. In this case the permission is granted silently @@ -244,8 +247,7 @@ class PermissionControllerServiceModel(private val service: PermissionController packageName: String, callback: Consumer<List<Pair<String, AppPermGroupUiInfo>>> ) { - val packageGroupsLiveData = PackagePermissionsLiveData[packageName, - Process.myUserHandle()] + val packageGroupsLiveData = PackagePermissionsLiveData[packageName, Process.myUserHandle()] observeAndCheckForLifecycleState(packageGroupsLiveData) { groups -> val groupNames = groups?.keys?.toMutableList() ?: mutableListOf() groupNames.remove(PackagePermissionsLiveData.NON_RUNTIME_NORMAL_PERMS) @@ -260,8 +262,8 @@ class PermissionControllerServiceModel(private val service: PermissionController // live datas, because this method is used primarily for UI, and there is inherent // delay when calling this method, due to binder calls, so some staleness is // acceptable - val uiInfoLiveData = AppPermGroupUiInfoLiveData[packageName, groupName, - Process.myUserHandle()] + val uiInfoLiveData = + AppPermGroupUiInfoLiveData[packageName, groupName, Process.myUserHandle()] observeAndCheckForLifecycleState(uiInfoLiveData, forceUpdate = true) { uiInfo -> numLiveDatasUpdated++ @@ -285,31 +287,25 @@ class PermissionControllerServiceModel(private val service: PermissionController * * @param callback The callback our result will be returned to */ - fun onCountUnusedApps( - callback: IntConsumer - ) { - val unusedAppsCount = Transformations.map(getUnusedPackages()) { - it?.size ?: 0 - } + fun onCountUnusedApps(callback: IntConsumer) { + val unusedAppsCount = Transformations.map(getUnusedPackages()) { it?.size ?: 0 } observeAndCheckForLifecycleState(unusedAppsCount) { count -> callback.accept(count ?: 0) } } /** - * Gets whether the package is eligible for hibernation. The logic is the same logic used by - * the app hibernation job when determining which apps to hibernate. + * Gets whether the package is eligible for hibernation. The logic is the same logic used by the + * app hibernation job when determining which apps to hibernate. * * @param packageName The package to check eligibility for * @param callback The callback the result will be returned to */ - fun onGetHibernationEligibility( - packageName: String, - callback: IntConsumer - ) { + fun onGetHibernationEligibility(packageName: String, callback: IntConsumer) { val user = Process.myUserHandle() val hibernationSettingLiveData = HibernationSettingStateLiveData[packageName, user] observeAndCheckForLifecycleState(hibernationSettingLiveData) { hibernationSettingState -> callback.accept( - hibernationSettingState?.hibernationEligibility ?: HIBERNATION_ELIGIBILITY_UNKNOWN) + hibernationSettingState?.hibernationEligibility ?: HIBERNATION_ELIGIBILITY_UNKNOWN + ) } } @@ -323,9 +319,7 @@ class PermissionControllerServiceModel(private val service: PermissionController return withTimeout(9000) { val dumpedLogs = GlobalScope.async(IO) { DumpableLog.get() } - PermissionControllerDumpProto.newBuilder() - .addAllLogs(dumpedLogs.await()) - .build() + PermissionControllerDumpProto.newBuilder().addAllLogs(dumpedLogs.await()).build() } } } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/service/PermissionEventCleanupJobService.kt b/PermissionController/src/com/android/permissioncontroller/permission/service/PermissionEventCleanupJobService.kt index d22b63e9a..45501cd25 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/service/PermissionEventCleanupJobService.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/service/PermissionEventCleanupJobService.kt @@ -26,15 +26,13 @@ import android.provider.DeviceConfig import com.android.permissioncontroller.Constants import com.android.permissioncontroller.DumpableLog import com.android.permissioncontroller.permission.utils.Utils +import java.util.concurrent.TimeUnit import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.Job import kotlinx.coroutines.launch -import java.util.concurrent.TimeUnit -/** - * A job to clean up old permission events. - */ +/** A job to clean up old permission events. */ class PermissionEventCleanupJobService : JobService() { companion object { @@ -43,13 +41,15 @@ class PermissionEventCleanupJobService : JobService() { fun scheduleOldDataCleanupIfNecessary(context: Context, jobScheduler: JobScheduler) { if (isNewJobScheduleRequired(jobScheduler)) { - val jobInfo = JobInfo.Builder( - Constants.OLD_PERMISSION_EVENT_CLEANUP_JOB_ID, - ComponentName(context, PermissionEventCleanupJobService::class.java)) - .setPeriodic(getClearOldEventsCheckFrequencyMs()) - // persist this job across boots - .setPersisted(true) - .build() + val jobInfo = + JobInfo.Builder( + Constants.OLD_PERMISSION_EVENT_CLEANUP_JOB_ID, + ComponentName(context, PermissionEventCleanupJobService::class.java) + ) + .setPeriodic(getClearOldEventsCheckFrequencyMs()) + // persist this job across boots + .setPersisted(true) + .build() val status = jobScheduler.schedule(jobInfo) if (status != JobScheduler.RESULT_SUCCESS) { DumpableLog.e(LOG_TAG, "Could not schedule job: $status") @@ -64,8 +64,8 @@ class PermissionEventCleanupJobService : JobService() { */ private fun isNewJobScheduleRequired(jobScheduler: JobScheduler): Boolean { var scheduleNewJob = false - val existingJob: JobInfo? = jobScheduler - .getPendingJob(Constants.OLD_PERMISSION_EVENT_CLEANUP_JOB_ID) + val existingJob: JobInfo? = + jobScheduler.getPendingJob(Constants.OLD_PERMISSION_EVENT_CLEANUP_JOB_ID) when { existingJob == null -> { DumpableLog.i(LOG_TAG, "No existing job, scheduling a new one") @@ -83,9 +83,11 @@ class PermissionEventCleanupJobService : JobService() { } private fun getClearOldEventsCheckFrequencyMs() = - DeviceConfig.getLong(DeviceConfig.NAMESPACE_PERMISSIONS, + DeviceConfig.getLong( + DeviceConfig.NAMESPACE_PERMISSIONS, Utils.PROPERTY_PERMISSION_EVENTS_CHECK_OLD_FREQUENCY_MILLIS, - DEFAULT_CLEAR_OLD_EVENTS_CHECK_FREQUENCY) + DEFAULT_CLEAR_OLD_EVENTS_CHECK_FREQUENCY + ) } var job: Job? = null @@ -98,15 +100,16 @@ class PermissionEventCleanupJobService : JobService() { return false } jobStartTime = System.currentTimeMillis() - job = GlobalScope.launch(Dispatchers.IO) { - for (storage in storages) { - val success = storage.removeOldData() - if (!success) { - DumpableLog.e(LOG_TAG, "Failed to remove old data for $storage") + job = + GlobalScope.launch(Dispatchers.IO) { + for (storage in storages) { + val success = storage.removeOldData() + if (!success) { + DumpableLog.e(LOG_TAG, "Failed to remove old data for $storage") + } } + jobFinished(params, false) } - jobFinished(params, false) - } return true } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/service/PermissionEventStorage.kt b/PermissionController/src/com/android/permissioncontroller/permission/service/PermissionEventStorage.kt index 67a1cb4a4..fb73bcf75 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/service/PermissionEventStorage.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/service/PermissionEventStorage.kt @@ -18,9 +18,7 @@ package com.android.permissioncontroller.permission.service import com.android.permissioncontroller.permission.data.PermissionEvent -/** - * Persistent storage for retrieving persisted permission event data. - */ +/** Persistent storage for retrieving persisted permission event data. */ interface PermissionEventStorage<T : PermissionEvent> { /** * Persist a permission event for retrieval later. @@ -36,9 +34,7 @@ interface PermissionEventStorage<T : PermissionEvent> { */ suspend fun loadEvents(): List<T> - /** - * Clear all events. - */ + /** Clear all events. */ suspend fun clearEvents() /** @@ -60,9 +56,9 @@ interface PermissionEventStorage<T : PermissionEvent> { * Update event timestamps based on the delta in system time. * * @param diffSystemTimeMillis the difference between the current and old system times. Positive - * values mean that the time has changed in the future and negative means the time was changed - * into the past. + * values mean that the time has changed in the future and negative means the time was changed + * into the past. * @return whether the storage was successful */ suspend fun updateEventsBySystemTimeDelta(diffSystemTimeMillis: Long): Boolean -}
\ No newline at end of file +} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/service/PermissionEventStorageImpls.kt b/PermissionController/src/com/android/permissioncontroller/permission/service/PermissionEventStorageImpls.kt index de6a0d9e2..33dc128f6 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/service/PermissionEventStorageImpls.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/service/PermissionEventStorageImpls.kt @@ -21,18 +21,13 @@ import com.android.permissioncontroller.PermissionControllerApplication import com.android.permissioncontroller.permission.data.PermissionEvent import com.android.permissioncontroller.permission.service.v33.PermissionDecisionStorageImpl -/** - * Singleton of all supported [PermissionEventStorage] on the device. - */ +/** Singleton of all supported [PermissionEventStorage] on the device. */ class PermissionEventStorageImpls { companion object { - @Volatile - private var INSTANCE: List<PermissionEventStorage<out PermissionEvent>>? = null + @Volatile private var INSTANCE: List<PermissionEventStorage<out PermissionEvent>>? = null fun getInstance(): List<PermissionEventStorage<out PermissionEvent>> = - INSTANCE ?: synchronized(this) { - INSTANCE ?: createInstance().also { INSTANCE = it } - } + INSTANCE ?: synchronized(this) { INSTANCE ?: createInstance().also { INSTANCE = it } } @SuppressLint("NewApi") private fun createInstance(): List<PermissionEventStorage<out PermissionEvent>> { diff --git a/PermissionController/src/com/android/permissioncontroller/permission/service/PermissionStorageTimeChangeReceiver.kt b/PermissionController/src/com/android/permissioncontroller/permission/service/PermissionStorageTimeChangeReceiver.kt index 43970dd13..ccb3acbad 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/service/PermissionStorageTimeChangeReceiver.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/service/PermissionStorageTimeChangeReceiver.kt @@ -26,10 +26,10 @@ import com.android.permissioncontroller.DumpableLog import com.android.permissioncontroller.permission.data.PermissionEvent import com.android.permissioncontroller.permission.utils.SystemTimeSource import com.android.permissioncontroller.permission.utils.TimeSource +import kotlin.math.abs import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch -import kotlin.math.abs /** * [BroadcastReceiver] to update the persisted timestamps when the date changes. Receives broadcasts @@ -51,8 +51,7 @@ class PermissionStorageTimeChangeReceiver( * Key for the last known system time from the system. First initialized after boot * complete. */ - @VisibleForTesting - const val PREF_KEY_SYSTEM_TIME_SNAPSHOT = "system_time_snapshot" + @VisibleForTesting const val PREF_KEY_SYSTEM_TIME_SNAPSHOT = "system_time_snapshot" /** * Key for the last known elapsed time since boot. First initialized after boot complete. @@ -60,12 +59,9 @@ class PermissionStorageTimeChangeReceiver( @VisibleForTesting const val PREF_KEY_ELAPSED_REALTIME_SNAPSHOT = "elapsed_realtime_snapshot" - @VisibleForTesting - const val SNAPSHOT_UNINITIALIZED = -1L + @VisibleForTesting const val SNAPSHOT_UNINITIALIZED = -1L - /** - * The millisecond threshold for a time delta to be considered a time change. - */ + /** The millisecond threshold for a time delta to be considered a time change. */ private const val TIME_CHANGE_THRESHOLD_MILLIS = 60 * 1000L } @@ -75,8 +71,11 @@ class PermissionStorageTimeChangeReceiver( } when (val action = intent.action) { Intent.ACTION_BOOT_COMPLETED -> { - persistTimeSnapshots(context, timeSource.currentTimeMillis(), - timeSource.elapsedRealtime()) + persistTimeSnapshots( + context, + timeSource.currentTimeMillis(), + timeSource.elapsedRealtime() + ) } Intent.ACTION_TIME_CHANGED -> { checkForTimeChanged(context) @@ -90,15 +89,16 @@ class PermissionStorageTimeChangeReceiver( private fun checkForTimeChanged(context: Context) { val systemTimeSnapshot = getSystemTimeSnapshot(context) val realtimeSnapshot = getElapsedRealtimeSnapshot(context) - if (realtimeSnapshot == SNAPSHOT_UNINITIALIZED || - systemTimeSnapshot == SNAPSHOT_UNINITIALIZED) { + if ( + realtimeSnapshot == SNAPSHOT_UNINITIALIZED || + systemTimeSnapshot == SNAPSHOT_UNINITIALIZED + ) { DumpableLog.e(LOG_TAG, "Snapshots not initialized") return } val actualSystemTime = timeSource.currentTimeMillis() val actualRealtime = timeSource.elapsedRealtime() - val expectedSystemTime = (actualRealtime - realtimeSnapshot) + - systemTimeSnapshot + val expectedSystemTime = (actualRealtime - realtimeSnapshot) + systemTimeSnapshot val diffSystemTime = actualSystemTime - expectedSystemTime if (abs(diffSystemTime) > TIME_CHANGE_THRESHOLD_MILLIS) { DumpableLog.d(LOG_TAG, "Time changed by ${diffSystemTime / 1000} seconds") @@ -131,17 +131,21 @@ class PermissionStorageTimeChangeReceiver( } private fun getSystemTimeSnapshot(context: Context): Long { - return context.sharedPreferences.getLong(PREF_KEY_SYSTEM_TIME_SNAPSHOT, - SNAPSHOT_UNINITIALIZED) + return context.sharedPreferences.getLong( + PREF_KEY_SYSTEM_TIME_SNAPSHOT, + SNAPSHOT_UNINITIALIZED + ) } private fun getElapsedRealtimeSnapshot(context: Context): Long { - return context.sharedPreferences.getLong(PREF_KEY_ELAPSED_REALTIME_SNAPSHOT, - SNAPSHOT_UNINITIALIZED) + return context.sharedPreferences.getLong( + PREF_KEY_ELAPSED_REALTIME_SNAPSHOT, + SNAPSHOT_UNINITIALIZED + ) } val Context.sharedPreferences: SharedPreferences get() { return PreferenceManager.getDefaultSharedPreferences(this) } -}
\ No newline at end of file +} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/service/PersistedStoragePackageUninstalledReceiver.kt b/PermissionController/src/com/android/permissioncontroller/permission/service/PersistedStoragePackageUninstalledReceiver.kt index 37fa2b36d..383e52dfc 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/service/PersistedStoragePackageUninstalledReceiver.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/service/PersistedStoragePackageUninstalledReceiver.kt @@ -29,8 +29,8 @@ import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch /** - * [BroadcastReceiver] to clear user decision information when a package has its data cleared or - * is fully removed. + * [BroadcastReceiver] to clear user decision information when a package has its data cleared or is + * fully removed. */ class PersistedStoragePackageUninstalledReceiver( @VisibleForTesting @@ -48,8 +48,10 @@ class PersistedStoragePackageUninstalledReceiver( return } val action = intent.action - if (!(action == Intent.ACTION_PACKAGE_DATA_CLEARED || - action == Intent.ACTION_PACKAGE_FULLY_REMOVED)) { + if ( + !(action == Intent.ACTION_PACKAGE_DATA_CLEARED || + action == Intent.ACTION_PACKAGE_FULLY_REMOVED) + ) { return } intent.data?.let { diff --git a/PermissionController/src/com/android/permissioncontroller/permission/service/RuntimePermissionsUpgradeController.kt b/PermissionController/src/com/android/permissioncontroller/permission/service/RuntimePermissionsUpgradeController.kt index 11299def9..d8fc48bbc 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/service/RuntimePermissionsUpgradeController.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/service/RuntimePermissionsUpgradeController.kt @@ -49,20 +49,19 @@ import com.android.permissioncontroller.permission.utils.application import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch -/** - * This class handles upgrading the runtime permissions database - */ +/** This class handles upgrading the runtime permissions database */ internal object RuntimePermissionsUpgradeController { private val LOG_TAG = RuntimePermissionsUpgradeController::class.java.simpleName // The latest version of the runtime permissions database - private val LATEST_VERSION = if (SdkLevel.isAtLeastU()) { - 11 - } else if (SdkLevel.isAtLeastT()) { - 10 - } else { - 9 - } + private val LATEST_VERSION = + if (SdkLevel.isAtLeastU()) { + 11 + } else if (SdkLevel.isAtLeastT()) { + 10 + } else { + 9 + } fun upgradeIfNeeded(context: Context, onComplete: Runnable) { val permissionManager = context.getSystemService(PermissionManager::class.java) @@ -72,9 +71,13 @@ internal object RuntimePermissionsUpgradeController { GlobalScope.launch(IPC) { val upgradedVersion = onUpgradeLocked(context, currentVersion) if (upgradedVersion != LATEST_VERSION) { - Log.wtf("PermissionControllerService", "warning: upgrading permission database" + - " to version $LATEST_VERSION left it at $currentVersion instead; this is " + - "probably a bug. Did you update LATEST_VERSION?", Throwable()) + Log.wtf( + "PermissionControllerService", + "warning: upgrading permission database" + + " to version $LATEST_VERSION left it at $currentVersion instead; this is " + + "probably a bug. Did you update LATEST_VERSION?", + Throwable() + ) throw RuntimeException("db upgrade error") } @@ -90,7 +93,6 @@ internal object RuntimePermissionsUpgradeController { * * @param permissionInfos permissions to exempt * @param pkgs packages to exempt - * * @return the exemptions to apply */ private fun getExemptions( @@ -110,9 +112,8 @@ internal object RuntimePermissionsUpgradeController { } /** - * You must perform all necessary mutations to bring the runtime permissions - * database from the old to the new version. When you add a new upgrade step - * you *must* update LATEST_VERSION. + * You must perform all necessary mutations to bring the runtime permissions database from the + * old to the new version. When you add a new upgrade step you *must* update LATEST_VERSION. * * <p> NOTE: Relies upon the fact that the system will attempt to upgrade every version after * currentVersion in order, without skipping any versions. Should this become the case, this @@ -121,10 +122,7 @@ internal object RuntimePermissionsUpgradeController { * @param context The current context * @param currentVersion The current version of the permission database */ - private suspend fun onUpgradeLocked( - context: Context, - currentVersion: Int - ): Int { + private suspend fun onUpgradeLocked(context: Context, currentVersion: Int): Int { var sdkUpgradedFromP = false var isNewUser = false @@ -143,188 +141,232 @@ internal object RuntimePermissionsUpgradeController { // All data needed by this method. // // All data is loaded once and then not updated. - val upgradeDataProvider = object : SmartUpdateMediatorLiveData<UpgradeData>() { - /** Provides all preinstalled packages in the system */ - private val preinstalledPkgInfoProvider = + val upgradeDataProvider = + object : SmartUpdateMediatorLiveData<UpgradeData>() { + /** Provides all preinstalled packages in the system */ + private val preinstalledPkgInfoProvider = PreinstalledUserPackageInfosLiveData[myUserHandle()] - /** Provides all platform runtime permission infos */ - private val platformRuntimePermissionInfoProviders = + /** Provides all platform runtime permission infos */ + private val platformRuntimePermissionInfoProviders = mutableListOf<LightPermInfoLiveData>() - /** {@link #platformRuntimePermissionInfoProvider} that already provided a result */ - private val platformRuntimePermissionInfoProvidersDone = + /** {@link #platformRuntimePermissionInfoProvider} that already provided a result */ + private val platformRuntimePermissionInfoProvidersDone = mutableSetOf<LightPermInfoLiveData>() - /** Provides all packages in the system */ - private val pkgInfoProvider = UserPackageInfosLiveData[myUserHandle()] + /** Provides all packages in the system */ + private val pkgInfoProvider = UserPackageInfosLiveData[myUserHandle()] - /** Provides all {@link LightAppPermGroup} this upgrade needs */ - private var permGroupProviders: MutableSet<LightAppPermGroupLiveData>? = null + /** Provides all {@link LightAppPermGroup} this upgrade needs */ + private var permGroupProviders: MutableSet<LightAppPermGroupLiveData>? = null - /** {@link #permGroupProviders} that already provided a result */ - private val permGroupProvidersDone = mutableSetOf<LightAppPermGroupLiveData>() + /** {@link #permGroupProviders} that already provided a result */ + private val permGroupProvidersDone = mutableSetOf<LightAppPermGroupLiveData>() - init { - // First step: Load packages + perm infos - addSource(pkgInfoProvider) { pkgInfos -> - if (pkgInfos != null) { - removeSource(pkgInfoProvider) + init { + // First step: Load packages + perm infos + addSource(pkgInfoProvider) { pkgInfos -> + if (pkgInfos != null) { + removeSource(pkgInfoProvider) - addSource(preinstalledPkgInfoProvider) { preinstalledPkgInfos -> - if (preinstalledPkgInfos != null) { - removeSource(preinstalledPkgInfoProvider) + addSource(preinstalledPkgInfoProvider) { preinstalledPkgInfos -> + if (preinstalledPkgInfos != null) { + removeSource(preinstalledPkgInfoProvider) - update() + update() + } } } } - } - for (platformRuntimePermission in getRuntimePlatformPermissionNames()) { - val permProvider = LightPermInfoLiveData[platformRuntimePermission] - platformRuntimePermissionInfoProviders.add(permProvider) + for (platformRuntimePermission in getRuntimePlatformPermissionNames()) { + val permProvider = LightPermInfoLiveData[platformRuntimePermission] + platformRuntimePermissionInfoProviders.add(permProvider) - addSource(permProvider) { permInfo -> - if (permInfo != null) { - platformRuntimePermissionInfoProvidersDone.add(permProvider) - removeSource(permProvider) + addSource(permProvider) { permInfo -> + if (permInfo != null) { + platformRuntimePermissionInfoProvidersDone.add(permProvider) + removeSource(permProvider) - update() + update() + } } } } - } - override fun onUpdate() { - if (permGroupProviders == null && pkgInfoProvider.value != null) { - // Second step: Trigger load of app-perm-groups + override fun onUpdate() { + if (permGroupProviders == null && pkgInfoProvider.value != null) { + // Second step: Trigger load of app-perm-groups - permGroupProviders = mutableSetOf() + permGroupProviders = mutableSetOf() - // Only load app-perm-groups needed for this upgrade - if (needBackgroundAppPermGroups || needAccessMediaAppPermGroups || - needGrantedExternalStorage || needGrantedReadMediaVisual) { - for ((pkgName, _, requestedPerms, requestedPermFlags) in + // Only load app-perm-groups needed for this upgrade + if ( + needBackgroundAppPermGroups || + needAccessMediaAppPermGroups || + needGrantedExternalStorage || + needGrantedReadMediaVisual + ) { + for ((pkgName, _, requestedPerms, requestedPermFlags) in pkgInfoProvider.value!!) { - var requestsAccessMediaLocation = false - var hasGrantedExternalStorage = false - var hasGrantedReadMediaVisual = false - - for ((perm, flags) in requestedPerms.zip(requestedPermFlags)) { - if (needBackgroundAppPermGroups && - perm == permission.ACCESS_BACKGROUND_LOCATION) { - permGroupProviders!!.add(LightAppPermGroupLiveData[pkgName, - permission_group.LOCATION, myUserHandle()]) - } - - if (needAccessMediaAppPermGroups || needGrantedExternalStorage || - needGrantedReadMediaVisual) { - if (needAccessMediaAppPermGroups && - perm == permission.ACCESS_MEDIA_LOCATION) { - requestsAccessMediaLocation = true + var requestsAccessMediaLocation = false + var hasGrantedExternalStorage = false + var hasGrantedReadMediaVisual = false + + for ((perm, flags) in requestedPerms.zip(requestedPermFlags)) { + if ( + needBackgroundAppPermGroups && + perm == permission.ACCESS_BACKGROUND_LOCATION + ) { + permGroupProviders!!.add( + LightAppPermGroupLiveData[ + pkgName, permission_group.LOCATION, myUserHandle()] + ) } - val isGranted = - flags and PackageInfo.REQUESTED_PERMISSION_GRANTED != 0 - if (perm == permission.READ_EXTERNAL_STORAGE && isGranted) { - hasGrantedExternalStorage = true - } - if (PermissionMapping.getGroupOfPlatformPermission(perm) - == permission_group.READ_MEDIA_VISUAL && isGranted) { - hasGrantedReadMediaVisual = true + if ( + needAccessMediaAppPermGroups || + needGrantedExternalStorage || + needGrantedReadMediaVisual + ) { + if ( + needAccessMediaAppPermGroups && + perm == permission.ACCESS_MEDIA_LOCATION + ) { + requestsAccessMediaLocation = true + } + + val isGranted = + flags and PackageInfo.REQUESTED_PERMISSION_GRANTED != 0 + if (perm == permission.READ_EXTERNAL_STORAGE && isGranted) { + hasGrantedExternalStorage = true + } + if ( + PermissionMapping.getGroupOfPlatformPermission(perm) == + permission_group.READ_MEDIA_VISUAL && isGranted + ) { + hasGrantedReadMediaVisual = true + } } } - } - val accessMediaLocationPermGroup = - if (SdkLevel.isAtLeastT()) - permission_group.READ_MEDIA_VISUAL - else - permission_group.STORAGE - - if (hasGrantedExternalStorage) { - if (needGrantedExternalStorage) { - permGroupProviders!!.add(LightAppPermGroupLiveData[pkgName, - permission_group.STORAGE, myUserHandle()]) - if (SdkLevel.isAtLeastT()) { - permGroupProviders!!.add(LightAppPermGroupLiveData[pkgName, - permission_group.READ_MEDIA_VISUAL, myUserHandle()]) - permGroupProviders!!.add(LightAppPermGroupLiveData[pkgName, - permission_group.READ_MEDIA_AURAL, myUserHandle()]) + val accessMediaLocationPermGroup = + if (SdkLevel.isAtLeastT()) permission_group.READ_MEDIA_VISUAL + else permission_group.STORAGE + + if (hasGrantedExternalStorage) { + if (needGrantedExternalStorage) { + permGroupProviders!!.add( + LightAppPermGroupLiveData[ + pkgName, permission_group.STORAGE, myUserHandle()] + ) + if (SdkLevel.isAtLeastT()) { + permGroupProviders!!.add( + LightAppPermGroupLiveData[ + pkgName, + permission_group.READ_MEDIA_VISUAL, + myUserHandle()] + ) + permGroupProviders!!.add( + LightAppPermGroupLiveData[ + pkgName, + permission_group.READ_MEDIA_AURAL, + myUserHandle()] + ) + } + } else if (requestsAccessMediaLocation) { + permGroupProviders!!.add( + LightAppPermGroupLiveData[ + pkgName, + accessMediaLocationPermGroup, + myUserHandle()] + ) } - } else if (requestsAccessMediaLocation) { - permGroupProviders!!.add(LightAppPermGroupLiveData[pkgName, - accessMediaLocationPermGroup, myUserHandle()]) } - } - if (hasGrantedReadMediaVisual && needGrantedReadMediaVisual) { - permGroupProviders!!.add(LightAppPermGroupLiveData[pkgName, - permission_group.READ_MEDIA_VISUAL, myUserHandle()]) + if (hasGrantedReadMediaVisual && needGrantedReadMediaVisual) { + permGroupProviders!!.add( + LightAppPermGroupLiveData[ + pkgName, + permission_group.READ_MEDIA_VISUAL, + myUserHandle()] + ) + } } } - } - // Wait until groups are loaded and then trigger third step - for (permGroupProvider in permGroupProviders!!) { - addSource(permGroupProvider) { group -> - if (group != null) { - permGroupProvidersDone.add(permGroupProvider) - removeSource(permGroupProvider) + // Wait until groups are loaded and then trigger third step + for (permGroupProvider in permGroupProviders!!) { + addSource(permGroupProvider) { group -> + if (group != null) { + permGroupProvidersDone.add(permGroupProvider) + removeSource(permGroupProvider) - update() + update() + } } } - } - // If no group need to be loaded, directly switch to third step - if (permGroupProviders!!.isEmpty()) { - update() - } - } else if (permGroupProviders != null && - permGroupProvidersDone.size == permGroupProviders!!.size && - preinstalledPkgInfoProvider.value != null && - platformRuntimePermissionInfoProviders.size - == platformRuntimePermissionInfoProvidersDone.size) { - // Third step: All packages, perm infos and perm groups are loaded, set value - - val bgGroups = mutableListOf<LightAppPermGroup>() - val storageGroups = mutableListOf<LightAppPermGroup>() - - for (group in permGroupProviders!!.mapNotNull { it.value }) { - when (group.permGroupName) { - permission_group.LOCATION -> { - bgGroups.add(group) - } - permission_group.STORAGE -> { - storageGroups.add(group) - } - permission_group.READ_MEDIA_AURAL -> { - storageGroups.add(group) - } - permission_group.READ_MEDIA_VISUAL -> { - storageGroups.add(group) + // If no group need to be loaded, directly switch to third step + if (permGroupProviders!!.isEmpty()) { + update() + } + } else if ( + permGroupProviders != null && + permGroupProvidersDone.size == permGroupProviders!!.size && + preinstalledPkgInfoProvider.value != null && + platformRuntimePermissionInfoProviders.size == + platformRuntimePermissionInfoProvidersDone.size + ) { + // Third step: All packages, perm infos and perm groups are loaded, set + // value + + val bgGroups = mutableListOf<LightAppPermGroup>() + val storageGroups = mutableListOf<LightAppPermGroup>() + + for (group in permGroupProviders!!.mapNotNull { it.value }) { + when (group.permGroupName) { + permission_group.LOCATION -> { + bgGroups.add(group) + } + permission_group.STORAGE -> { + storageGroups.add(group) + } + permission_group.READ_MEDIA_AURAL -> { + storageGroups.add(group) + } + permission_group.READ_MEDIA_VISUAL -> { + storageGroups.add(group) + } } } - } - val restrictedPermissions = mutableSetOf<String>() - for (permInfoLiveDt in platformRuntimePermissionInfoProviders) { - val permInfo = permInfoLiveDt.value!! + val restrictedPermissions = mutableSetOf<String>() + for (permInfoLiveDt in platformRuntimePermissionInfoProviders) { + val permInfo = permInfoLiveDt.value!! + + if ( + permInfo.flags and + (PermissionInfo.FLAG_HARD_RESTRICTED or + PermissionInfo.FLAG_SOFT_RESTRICTED) == 0 + ) { + continue + } - if (permInfo.flags and (PermissionInfo.FLAG_HARD_RESTRICTED or - PermissionInfo.FLAG_SOFT_RESTRICTED) == 0) { - continue + restrictedPermissions.add(permInfo.name) } - restrictedPermissions.add(permInfo.name) + value = + UpgradeData( + preinstalledPkgInfoProvider.value!!, + restrictedPermissions, + pkgInfoProvider.value!!, + bgGroups, + storageGroups + ) } - - value = UpgradeData(preinstalledPkgInfoProvider.value!!, restrictedPermissions, - pkgInfoProvider.value!!, bgGroups, storageGroups) } } - } // Trigger loading of data and wait until data is loaded val upgradeData = upgradeDataProvider.getInitializedValue(forceUpdate = true) @@ -334,14 +376,18 @@ internal object RuntimePermissionsUpgradeController { // always exempting them. For non-OTA updates the installer should do the exemption. // If a restricted permission can't be exempted by the installer then it should be filtered // out here. - val preinstalledAppExemptions = getExemptions( - upgradeData.restrictedPermissions, - upgradeData.preinstalledPkgs) + val preinstalledAppExemptions = + getExemptions(upgradeData.restrictedPermissions, upgradeData.preinstalledPkgs) - val (newVersion, upgradeExemptions, grants) = onUpgradeLockedDataLoaded(currentVersion, - upgradeData.pkgs, upgradeData.restrictedPermissions, - upgradeData.bgGroups, upgradeData.storageGroups, - isDeviceUpgrading) + val (newVersion, upgradeExemptions, grants) = + onUpgradeLockedDataLoaded( + currentVersion, + upgradeData.pkgs, + upgradeData.restrictedPermissions, + upgradeData.bgGroups, + upgradeData.storageGroups, + isDeviceUpgrading + ) // Do not run in parallel. Measurements have shown that this is slower than sequential for (exemption in (preinstalledAppExemptions union upgradeExemptions)) { @@ -387,9 +433,10 @@ internal object RuntimePermissionsUpgradeController { if (currentVersion == 0) { Log.i(LOG_TAG, "Grandfathering SMS and CallLog permissions") - val permissions = restrictedPermissions intersect + val permissions = + restrictedPermissions intersect (getPlatformPermissionNamesOfGroup(permission_group.SMS) + - getPlatformPermissionNamesOfGroup(permission_group.CALL_LOG)) + getPlatformPermissionNamesOfGroup(permission_group.CALL_LOG)) exemptions.addAll(getExemptions(permissions, pkgs)) @@ -409,8 +456,7 @@ internal object RuntimePermissionsUpgradeController { if (currentVersion == 3) { Log.i(LOG_TAG, "Grandfathering location background permissions") - val bgLocExemptions = getExemptions(setOf(permission.ACCESS_BACKGROUND_LOCATION), - pkgs) + val bgLocExemptions = getExemptions(setOf(permission.ACCESS_BACKGROUND_LOCATION), pkgs) // Adjust bgApps as if the exemption was applied for ((pkgName, _) in bgLocExemptions) { @@ -419,13 +465,22 @@ internal object RuntimePermissionsUpgradeController { val allPermissionsWithxemption = bgApp.allPermissions.toMutableMap() allPermissionsWithxemption[permission.ACCESS_BACKGROUND_LOCATION] = - LightPermission(perm.pkgInfo, perm.permInfo, perm.isGrantedIncludingAppOp, + LightPermission( + perm.pkgInfo, + perm.permInfo, + perm.isGrantedIncludingAppOp, perm.flags or FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT, - perm.foregroundPerms) - - bgAppsWithExemption[pkgName] = LightAppPermGroup(bgApp.packageInfo, - bgApp.permGroupInfo, allPermissionsWithxemption, - bgApp.hasInstallToRuntimeSplit, bgApp.specialLocationGrant) + perm.foregroundPerms + ) + + bgAppsWithExemption[pkgName] = + LightAppPermGroup( + bgApp.packageInfo, + bgApp.permGroupInfo, + allPermissionsWithxemption, + bgApp.hasInstallToRuntimeSplit, + bgApp.specialLocationGrant + ) } exemptions.addAll(bgLocExemptions) @@ -441,7 +496,8 @@ internal object RuntimePermissionsUpgradeController { if (currentVersion == 5) { Log.i(LOG_TAG, "Grandfathering Storage permissions") - val permissions = restrictedPermissions intersect + val permissions = + restrictedPermissions intersect getPlatformPermissionNamesOfGroup(permission_group.STORAGE) // We don't want to allow modification of storage post install, so put it @@ -455,18 +511,23 @@ internal object RuntimePermissionsUpgradeController { if (sdkUpgradedFromP) { Log.i(LOG_TAG, "Expanding location permissions") for (appPermGroup in bgAppsWithExemption.values) { - if (appPermGroup.foreground.isGranted && - appPermGroup.hasBackgroundGroup && - !appPermGroup.background.isUserSet && - !appPermGroup.background.isSystemFixed && - !appPermGroup.background.isPolicyFixed && - !appPermGroup.background.isUserFixed) { + if ( + appPermGroup.foreground.isGranted && + appPermGroup.hasBackgroundGroup && + !appPermGroup.background.isUserSet && + !appPermGroup.background.isSystemFixed && + !appPermGroup.background.isPolicyFixed && + !appPermGroup.background.isUserFixed + ) { grants.add(Grant(true, appPermGroup)) } } } else { - Log.i(LOG_TAG, "Not expanding location permissions as this is not an upgrade " + - "from Android P") + Log.i( + LOG_TAG, + "Not expanding location permissions as this is not an upgrade " + + "from Android P" + ) } currentVersion = 7 @@ -477,18 +538,25 @@ internal object RuntimePermissionsUpgradeController { Log.i(LOG_TAG, "Expanding read storage to access media location") for (appPermGroup in storageAndMediaAppPermGroups) { - val perm = appPermGroup.permissions[permission.ACCESS_MEDIA_LOCATION] - ?: continue - - if (!perm.isUserSet && !perm.isSystemFixed && !perm.isPolicyFixed && - !perm.isGrantedIncludingAppOp) { - grants.add(Grant(false, appPermGroup, - listOf(permission.ACCESS_MEDIA_LOCATION))) + val perm = + appPermGroup.permissions[permission.ACCESS_MEDIA_LOCATION] ?: continue + + if ( + !perm.isUserSet && + !perm.isSystemFixed && + !perm.isPolicyFixed && + !perm.isGrantedIncludingAppOp + ) { + grants.add( + Grant(false, appPermGroup, listOf(permission.ACCESS_MEDIA_LOCATION)) + ) } } } else { - Log.i(LOG_TAG, "Not expanding read storage to access media location as this is " + - "a new user") + Log.i( + LOG_TAG, + "Not expanding read storage to access media location as this is " + "a new user" + ) } currentVersion = 8 @@ -502,35 +570,47 @@ internal object RuntimePermissionsUpgradeController { if (currentVersion == 9 && SdkLevel.isAtLeastT()) { if (isNewUser) { - Log.i(LOG_TAG, "Not migrating STORAGE permissions to READ_MEDIA permissions as" + - " this is a new user") + Log.i( + LOG_TAG, + "Not migrating STORAGE permissions to READ_MEDIA permissions as" + + " this is a new user" + ) } else if (!isDeviceUpgrading) { - Log.i(LOG_TAG, "Not migrating STORAGE permissions to READ_MEDIA permissions as" + - " this device is not performing an upgrade") + Log.i( + LOG_TAG, + "Not migrating STORAGE permissions to READ_MEDIA permissions as" + + " this device is not performing an upgrade" + ) } else { Log.i(LOG_TAG, "Migrating STORAGE permissions to READ_MEDIA permissions") // Upon upgrading to platform 33, for all targetSdk>=33 apps, do the following: // If STORAGE is granted, and the user has not set READ_MEDIA_AURAL or // READ_MEDIA_VISUAL, grant READ_MEDIA_AURAL and READ_MEDIA_VISUAL - val storageAppPermGroups = storageAndMediaAppPermGroups.filter { - it.packageInfo.targetSdkVersion >= Build.VERSION_CODES.TIRAMISU && - it.permGroupInfo.name == permission_group.STORAGE && - it.isGranted && it.isUserSet - } + val storageAppPermGroups = + storageAndMediaAppPermGroups.filter { + it.packageInfo.targetSdkVersion >= Build.VERSION_CODES.TIRAMISU && + it.permGroupInfo.name == permission_group.STORAGE && + it.isGranted && + it.isUserSet + } for (storageAppPermGroup in storageAppPermGroups) { val pkgName = storageAppPermGroup.packageInfo.packageName - val auralAppPermGroup = storageAndMediaAppPermGroups.firstOrNull { - it.packageInfo.packageName == pkgName && - it.permGroupInfo.name == permission_group.READ_MEDIA_AURAL && - !it.isUserSet && !it.isUserFixed - } - val visualAppPermGroup = storageAndMediaAppPermGroups.firstOrNull { - it.packageInfo.packageName == pkgName && - it.permGroupInfo.name == permission_group.READ_MEDIA_VISUAL && - !it.permissions.filter { it.key != permission.ACCESS_MEDIA_LOCATION } - .any { it.value.isUserSet || it.value.isUserFixed } - } + val auralAppPermGroup = + storageAndMediaAppPermGroups.firstOrNull { + it.packageInfo.packageName == pkgName && + it.permGroupInfo.name == permission_group.READ_MEDIA_AURAL && + !it.isUserSet && + !it.isUserFixed + } + val visualAppPermGroup = + storageAndMediaAppPermGroups.firstOrNull { + it.packageInfo.packageName == pkgName && + it.permGroupInfo.name == permission_group.READ_MEDIA_VISUAL && + !it.permissions + .filter { it.key != permission.ACCESS_MEDIA_LOCATION } + .any { it.value.isUserSet || it.value.isUserFixed } + } if (auralAppPermGroup != null) { grants.add(Grant(false, auralAppPermGroup)) @@ -547,16 +627,19 @@ internal object RuntimePermissionsUpgradeController { // On U, if the app is granted READ_MEDIA_VISUAL, expand the grant to // READ_MEDIA_VISUAL_USER_SELECTED if (isDeviceUpgrading && !isNewUser) { - Log.i(LOG_TAG, "Grandfathering READ_MEDIA_VISUAL_USER_SELECTED to apps already " + - "granted visual permissions") - val visualAppPermGroups = storageAndMediaAppPermGroups.filter { - it.packageInfo.targetSdkVersion >= Build.VERSION_CODES.TIRAMISU && - it.permGroupInfo.name == permission_group.READ_MEDIA_VISUAL && - it.isGranted && it.isUserSet - } - visualAppPermGroups.forEach { - grants.add(Grant(false, it)) - } + Log.i( + LOG_TAG, + "Grandfathering READ_MEDIA_VISUAL_USER_SELECTED to apps already " + + "granted visual permissions" + ) + val visualAppPermGroups = + storageAndMediaAppPermGroups.filter { + it.packageInfo.targetSdkVersion >= Build.VERSION_CODES.TIRAMISU && + it.permGroupInfo.name == permission_group.READ_MEDIA_VISUAL && + it.isGranted && + it.isUserSet + } + visualAppPermGroups.forEach { grants.add(Grant(false, it)) } } currentVersion = 11 } @@ -566,9 +649,7 @@ internal object RuntimePermissionsUpgradeController { return Triple(currentVersion, exemptions, grants) } - /** - * All data needed by {@link #onUpgradeLocked} - */ + /** All data needed by {@link #onUpgradeLocked} */ private data class UpgradeData( /** Preinstalled packages */ val preinstalledPkgs: List<LightPackageInfo>, @@ -581,15 +662,11 @@ internal object RuntimePermissionsUpgradeController { * {@link #onUpgradeLockedDataLoaded} */ val bgGroups: List<LightAppPermGroup>, - /** - * Storage groups that need to be inspected by {@link #onUpgradeLockedDataLoaded} - */ + /** Storage groups that need to be inspected by {@link #onUpgradeLockedDataLoaded} */ val storageGroups: List<LightAppPermGroup>, ) - /** - * A restricted permission of an app that should be exempted - */ + /** A restricted permission of an app that should be exempted */ private data class RestrictionExemption( /** Name of package to exempt */ val pkgName: String, @@ -608,9 +685,7 @@ internal object RuntimePermissionsUpgradeController { } } - /** - * A permission group of an app that should get granted - */ + /** A permission group of an app that should get granted */ private data class Grant( /** Should the grant be for the foreground or background permissions */ private val isBackground: Boolean, @@ -626,17 +701,21 @@ internal object RuntimePermissionsUpgradeController { */ fun applyToPlatform(context: Context) { if (isBackground) { - val newGroup = grantBackgroundRuntimePermissions(context.application, group, - permissions) + val newGroup = + grantBackgroundRuntimePermissions(context.application, group, permissions) - logRuntimePermissionUpgradeResult(newGroup, - permissions intersect newGroup.backgroundPermNames) + logRuntimePermissionUpgradeResult( + newGroup, + permissions intersect newGroup.backgroundPermNames + ) } else { - val newGroup = grantForegroundRuntimePermissions(context.application, group, - permissions) + val newGroup = + grantForegroundRuntimePermissions(context.application, group, permissions) - logRuntimePermissionUpgradeResult(newGroup, - permissions intersect newGroup.foregroundPermNames) + logRuntimePermissionUpgradeResult( + newGroup, + permissions intersect newGroup.foregroundPermNames + ) } } @@ -654,10 +733,21 @@ internal object RuntimePermissionsUpgradeController { val packageName = permissionGroup.packageName for (permName in filterPermissions) { val permission = permissionGroup.permissions[permName] ?: continue - PermissionControllerStatsLog.write(RUNTIME_PERMISSIONS_UPGRADE_RESULT, - permission.name, uid, packageName) - Log.v(LOG_TAG, "Runtime permission upgrade logged for permissionName=" + - permission.name + " uid=" + uid + " packageName=" + packageName) + PermissionControllerStatsLog.write( + RUNTIME_PERMISSIONS_UPGRADE_RESULT, + permission.name, + uid, + packageName + ) + Log.v( + LOG_TAG, + "Runtime permission upgrade logged for permissionName=" + + permission.name + + " uid=" + + uid + + " packageName=" + + packageName + ) } } } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/service/SplitPermissionIndex.kt b/PermissionController/src/com/android/permissioncontroller/permission/service/SplitPermissionIndex.kt index 115200b2f..eb3f2e9af 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/service/SplitPermissionIndex.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/service/SplitPermissionIndex.kt @@ -40,12 +40,14 @@ class SplitPermissionIndex() { val oldPermGroup = PermissionMapping.getGroupOfPlatformPermission(oldPerm) val newPermGroup = PermissionMapping.getGroupOfPlatformPermission(newPerm) if (newPermGroup != null) { - permToGroupSplits.add(SplitPermissionIndexEntry( - oldPerm, splitPerm.targetSdk, newPermGroup)) + permToGroupSplits.add( + SplitPermissionIndexEntry(oldPerm, splitPerm.targetSdk, newPermGroup) + ) } if (oldPermGroup != null && newPermGroup != null) { - groupToGroupSplits.add(SplitPermissionIndexEntry( - oldPermGroup, splitPerm.targetSdk, newPermGroup)) + groupToGroupSplits.add( + SplitPermissionIndexEntry(oldPermGroup, splitPerm.targetSdk, newPermGroup) + ) } } } @@ -53,9 +55,7 @@ class SplitPermissionIndex() { this.groupToGroupSplits = groupToGroupSplits } - /** - * Given a permission, return which groups split *from* it for the given targetSdk. - */ + /** Given a permission, return which groups split *from* it for the given targetSdk. */ fun getPermToGroupSplitsFrom(oldPermission: String, targetSdk: Int): List<String> { return permToGroupSplits .filter { it.oldPerm == oldPermission && it.targetSdk < targetSdk } @@ -63,9 +63,7 @@ class SplitPermissionIndex() { .toList() } - /** - * Given a permission group, return which groups split *from* it for the given targetSdk. - */ + /** Given a permission group, return which groups split *from* it for the given targetSdk. */ fun getGroupToGroupSplitsFrom(oldPermissionGroup: String, targetSdk: Int): List<String> { return groupToGroupSplits .filter { it.oldPerm == oldPermissionGroup && it.targetSdk < targetSdk } @@ -73,9 +71,7 @@ class SplitPermissionIndex() { .toList() } - /** - * Given a permission group, return which permissions split *to* it for the given targetSdk. - */ + /** Given a permission group, return which permissions split *to* it for the given targetSdk. */ fun getGroupToGroupSplitsTo(newPermissionGroup: String, targetSdk: Int): List<String> { return groupToGroupSplits .filter { it.newPerm == newPermissionGroup && it.targetSdk < targetSdk } @@ -88,4 +84,4 @@ class SplitPermissionIndex() { val targetSdk: Int, val newPerm: String ) -}
\ No newline at end of file +} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/service/v33/PermissionDecisionStorageImpl.kt b/PermissionController/src/com/android/permissioncontroller/permission/service/v33/PermissionDecisionStorageImpl.kt index 578b74783..18c40e0e4 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/service/v33/PermissionDecisionStorageImpl.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/service/v33/PermissionDecisionStorageImpl.kt @@ -29,11 +29,6 @@ import com.android.permissioncontroller.permission.data.v33.PermissionDecision import com.android.permissioncontroller.permission.service.BasePermissionEventStorage import com.android.permissioncontroller.permission.service.PermissionEventStorage import com.android.permissioncontroller.permission.utils.Utils -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.launch -import org.xmlpull.v1.XmlPullParser -import org.xmlpull.v1.XmlPullParserException import java.io.IOException import java.io.InputStream import java.io.OutputStream @@ -43,10 +38,13 @@ import java.text.SimpleDateFormat import java.util.Date import java.util.Locale import java.util.concurrent.TimeUnit +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.launch +import org.xmlpull.v1.XmlPullParser +import org.xmlpull.v1.XmlPullParserException -/** - * Implementation of [BasePermissionEventStorage] for storing [PermissionDecision] events. - */ +/** Implementation of [BasePermissionEventStorage] for storing [PermissionDecision] events. */ @RequiresApi(Build.VERSION_CODES.TIRAMISU) class PermissionDecisionStorageImpl( context: Context, @@ -56,9 +54,7 @@ class PermissionDecisionStorageImpl( // We don't use namespaces private val ns: String? = null - /** - * The format for how dates are stored. - */ + /** The format for how dates are stored. */ private val dateFormat = SimpleDateFormat("yyyy-MM-dd", Locale.US) companion object { @@ -66,9 +62,7 @@ class PermissionDecisionStorageImpl( private const val DB_VERSION = 1 - /** - * Config store file name for general shared store file. - */ + /** Config store file name for general shared store file. */ private const val STORE_FILE_NAME = "recent_permission_decisions.xml" private const val TAG_RECENT_PERMISSION_DECISIONS = "recent-permission-decisions" @@ -81,13 +75,10 @@ class PermissionDecisionStorageImpl( private val DEFAULT_MAX_DATA_AGE_MS = TimeUnit.DAYS.toMillis(7) - @Volatile - private var INSTANCE: PermissionEventStorage<PermissionDecision>? = null + @Volatile private var INSTANCE: PermissionEventStorage<PermissionDecision>? = null fun getInstance(): PermissionEventStorage<PermissionDecision> = - INSTANCE ?: synchronized(this) { - INSTANCE ?: createInstance().also { INSTANCE = it } - } + INSTANCE ?: synchronized(this) { INSTANCE ?: createInstance().also { INSTANCE = it } } private fun createInstance(): PermissionEventStorage<PermissionDecision> { return PermissionDecisionStorageImpl(PermissionControllerApplication.get()) @@ -101,9 +92,15 @@ class PermissionDecisionStorageImpl( ) { if (isRecordPermissionsSupported(context)) { GlobalScope.launch(Dispatchers.IO) { - getInstance().storeEvent( - PermissionDecision(packageName, System.currentTimeMillis(), permGroupName, - isGranted)) + getInstance() + .storeEvent( + PermissionDecision( + packageName, + System.currentTimeMillis(), + permGroupName, + isGranted + ) + ) } } } @@ -148,9 +145,7 @@ class PermissionDecisionStorageImpl( parser.require(XmlPullParser.START_TAG, ns, TAG_RECENT_PERMISSION_DECISIONS) while (parser.next() != XmlPullParser.END_TAG) { - readPermissionDecision(parser)?.let { - entries.add(it) - } + readPermissionDecision(parser)?.let { entries.add(it) } } return entries } @@ -163,9 +158,11 @@ class PermissionDecisionStorageImpl( val packageName = parser.getAttributeValueNullSafe(ns, ATTR_PACKAGE_NAME) val permissionGroup = parser.getAttributeValueNullSafe(ns, ATTR_PERMISSION_GROUP) val decisionDate = parser.getAttributeValueNullSafe(ns, ATTR_DECISION_TIME) - val decisionTime = dateFormat.parse(decisionDate)?.time - ?: throw IllegalArgumentException( - "Could not parse date $decisionDate on package $packageName") + val decisionTime = + dateFormat.parse(decisionDate)?.time + ?: throw IllegalArgumentException( + "Could not parse date $decisionDate on package $packageName" + ) val isGranted = parser.getAttributeValueNullSafe(ns, ATTR_IS_GRANTED).toBoolean() decision = PermissionDecision(packageName, decisionTime, permissionGroup, isGranted) } catch (e: XmlPullParserException) { @@ -185,7 +182,8 @@ class PermissionDecisionStorageImpl( private fun XmlPullParser.getAttributeValueNullSafe(namespace: String?, name: String): String { return this.getAttributeValue(namespace, name) ?: throw XmlPullParserException( - "Could not find attribute: namespace $namespace, name $name") + "Could not find attribute: namespace $namespace, name $name" + ) } override fun getDatabaseFileName(): String { @@ -193,9 +191,11 @@ class PermissionDecisionStorageImpl( } override fun getMaxDataAgeMs(): Long { - return DeviceConfig.getLong(DeviceConfig.NAMESPACE_PERMISSIONS, + return DeviceConfig.getLong( + DeviceConfig.NAMESPACE_PERMISSIONS, Utils.PROPERTY_PERMISSION_DECISIONS_MAX_DATA_AGE_MILLIS, - DEFAULT_MAX_DATA_AGE_MS) + DEFAULT_MAX_DATA_AGE_MS + ) } override fun hasTheSamePrimaryKey( diff --git a/PermissionController/src/com/android/permissioncontroller/permission/service/v34/SafetyLabelChangesJobService.kt b/PermissionController/src/com/android/permissioncontroller/permission/service/v34/SafetyLabelChangesJobService.kt index 9231dc17b..b627664d4 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/service/v34/SafetyLabelChangesJobService.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/service/v34/SafetyLabelChangesJobService.kt @@ -56,10 +56,10 @@ import com.android.permissioncontroller.PermissionControllerStatsLog.APP_DATA_SH import com.android.permissioncontroller.PermissionControllerStatsLog.APP_DATA_SHARING_UPDATES_NOTIFICATION_INTERACTION__ACTION__DISMISSED import com.android.permissioncontroller.PermissionControllerStatsLog.APP_DATA_SHARING_UPDATES_NOTIFICATION_INTERACTION__ACTION__NOTIFICATION_SHOWN import com.android.permissioncontroller.R -import com.android.permissioncontroller.permission.data.v34.LightInstallSourceInfoLiveData import com.android.permissioncontroller.permission.data.LightPackageInfoLiveData import com.android.permissioncontroller.permission.data.SinglePermGroupPackagesUiInfoLiveData import com.android.permissioncontroller.permission.data.v34.AppDataSharingUpdatesLiveData +import com.android.permissioncontroller.permission.data.v34.LightInstallSourceInfoLiveData import com.android.permissioncontroller.permission.model.livedatatypes.AppPermGroupUiInfo import com.android.permissioncontroller.permission.model.livedatatypes.AppPermGroupUiInfo.PermGrantState.PERMS_ALLOWED_ALWAYS import com.android.permissioncontroller.permission.model.livedatatypes.AppPermGroupUiInfo.PermGrantState.PERMS_ALLOWED_FOREGROUND_ONLY @@ -419,9 +419,7 @@ class SafetyLabelChangesJobService : JobService() { // preinstalled apps. private suspend fun getAllStoreInstalledPackagesRequestingLocation(): Set<Pair<String, UserHandle>> = - getAllPackagesRequestingLocation() - .filter { isSafetyLabelSupported(it) } - .toSet() + getAllPackagesRequestingLocation().filter { isSafetyLabelSupported(it) }.toSet() private suspend fun getAllPackagesRequestingLocation(): Set<Pair<String, UserHandle>> = SinglePermGroupPackagesUiInfoLiveData[Manifest.permission_group.LOCATION] @@ -439,7 +437,7 @@ class SafetyLabelChangesJobService : JobService() { private suspend fun isSafetyLabelSupported(packageUser: Pair<String, UserHandle>): Boolean { val lightInstallSourceInfo = - LightInstallSourceInfoLiveData[packageUser].getInitializedValue() + LightInstallSourceInfoLiveData[packageUser].getInitializedValue() return lightInstallSourceInfo.supportsSafetyLabel } @@ -531,8 +529,9 @@ class SafetyLabelChangesJobService : JobService() { createNotificationChannel(context, notificationManager) val (appLabel, smallIcon, color) = KotlinUtils.getSafetyCenterNotificationResources(this) - val smallIconCompat = IconCompat.createFromIcon(smallIcon) - ?: IconCompat.createWithResource(this, R.drawable.ic_info) + val smallIconCompat = + IconCompat.createFromIcon(smallIcon) + ?: IconCompat.createWithResource(this, R.drawable.ic_info) val title = context.getString(R.string.safety_label_changes_notification_title) val text = context.getString(R.string.safety_label_changes_notification_desc) var notificationBuilder = diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/Category.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/Category.kt index 61336cdce..5daf26883 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/Category.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/Category.kt @@ -24,5 +24,6 @@ enum class Category(val categoryName: String) { ALLOWED("allowed"), ALLOWED_FOREGROUND("allowed_foreground"), ASK("ask"), - DENIED("denied") + DENIED("denied"), + STORAGE_FOOTER("storage_footer_category"), } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/GrantPermissionsActivity.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/GrantPermissionsActivity.java index c173146eb..a6e521138 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/GrantPermissionsActivity.java +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/GrantPermissionsActivity.java @@ -546,8 +546,11 @@ public class GrantPermissionsActivity extends SettingsActivity getWindow().setDimAmount(mOriginalDimAmount); if (mRootView.getVisibility() == View.GONE) { - InputMethodManager manager = getSystemService(InputMethodManager.class); - manager.hideSoftInputFromWindow(mRootView.getWindowToken(), 0); + if (mIsSystemTriggered) { + // We don't want the keyboard obscuring system-triggered dialogs + InputMethodManager manager = getSystemService(InputMethodManager.class); + manager.hideSoftInputFromWindow(mRootView.getWindowToken(), 0); + } mRootView.setVisibility(View.VISIBLE); } } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/RemovablePref.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/RemovablePref.kt index 2525c191c..df4e7947a 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/RemovablePref.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/RemovablePref.kt @@ -16,17 +16,11 @@ package com.android.permissioncontroller.permission.ui -/** - * Preference with a clickable UI component for removal. - */ +/** Preference with a clickable UI component for removal. */ interface RemovablePref { - /** - * Sets the action to run when the remove UI component is clicked. - */ + /** Sets the action to run when the remove UI component is clicked. */ fun setRemoveClickRunnable(runnable: Runnable) - /** - * Set whether the UI component for removal should be enabled or not. - */ + /** Set whether the UI component for removal should be enabled or not. */ fun setRemoveComponentEnabled(enabled: Boolean) -}
\ No newline at end of file +} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/UnusedAppsFragment.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/UnusedAppsFragment.kt index 68fb493eb..69aef1a4f 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/UnusedAppsFragment.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/UnusedAppsFragment.kt @@ -52,12 +52,14 @@ import com.android.permissioncontroller.permission.utils.KotlinUtils import java.text.Collator /** - * A fragment displaying all applications that are unused as well as the option to remove them - * and to open them. + * A fragment displaying all applications that are unused as well as the option to remove them and + * to open them. */ -class UnusedAppsFragment<PF, UnusedAppPref> : Fragment() - where PF : PreferenceFragmentCompat, PF : UnusedAppsFragment.Parent<UnusedAppPref>, - UnusedAppPref : Preference, UnusedAppPref : RemovablePref { +class UnusedAppsFragment<PF, UnusedAppPref> : Fragment() where +PF : PreferenceFragmentCompat, +PF : UnusedAppsFragment.Parent<UnusedAppPref>, +UnusedAppPref : Preference, +UnusedAppPref : RemovablePref { private lateinit var viewModel: UnusedAppsViewModel private lateinit var collator: Collator @@ -72,9 +74,11 @@ class UnusedAppsFragment<PF, UnusedAppPref> : Fragment() private val LOG_TAG = UnusedAppsFragment::class.java.simpleName @JvmStatic - fun <PF, UnusedAppPref> newInstance(): UnusedAppsFragment<PF, UnusedAppPref> - where PF : PreferenceFragmentCompat, PF : UnusedAppsFragment.Parent<UnusedAppPref>, - UnusedAppPref : Preference, UnusedAppPref : RemovablePref { + fun <PF, UnusedAppPref> newInstance(): UnusedAppsFragment<PF, UnusedAppPref> where + PF : PreferenceFragmentCompat, + PF : UnusedAppsFragment.Parent<UnusedAppPref>, + UnusedAppPref : Preference, + UnusedAppPref : RemovablePref { return UnusedAppsFragment() } @@ -82,7 +86,6 @@ class UnusedAppsFragment<PF, UnusedAppPref> : Fragment() * Create the args needed for this fragment * * @param sessionId The current session Id - * * @return A bundle containing the session Id */ @JvmStatic @@ -101,29 +104,35 @@ class UnusedAppsFragment<PF, UnusedAppPref> : Fragment() val preferenceFragment: PF = requirePreferenceFragment() isFirstLoad = true - collator = Collator.getInstance( - context!!.getResources().getConfiguration().getLocales().get(0)) + collator = + Collator.getInstance(context!!.getResources().getConfiguration().getLocales().get(0)) sessionId = arguments!!.getLong(EXTRA_SESSION_ID, INVALID_SESSION_ID) val factory = UnusedAppsViewModelFactory(activity!!.application, sessionId) viewModel = ViewModelProvider(this, factory).get(UnusedAppsViewModel::class.java) - viewModel.unusedPackageCategoriesLiveData.observe(this, Observer { - it?.let { pkgs -> - updatePackages(pkgs) - preferenceFragment.setLoadingState(loading = false, animate = true) + viewModel.unusedPackageCategoriesLiveData.observe( + this, + Observer { + it?.let { pkgs -> + updatePackages(pkgs) + preferenceFragment.setLoadingState(loading = false, animate = true) + } } - }) + ) activity?.getActionBar()?.setDisplayHomeAsUpEnabled(true) if (!viewModel.unusedPackageCategoriesLiveData.isInitialized) { val handler = Handler(Looper.getMainLooper()) - handler.postDelayed({ - if (!viewModel.unusedPackageCategoriesLiveData.isInitialized) { - preferenceFragment.setLoadingState(loading = true, animate = true) - } else { - updatePackages(viewModel.unusedPackageCategoriesLiveData.value!!) - } - }, SHOW_LOAD_DELAY_MS) + handler.postDelayed( + { + if (!viewModel.unusedPackageCategoriesLiveData.isInitialized) { + preferenceFragment.setLoadingState(loading = true, animate = true) + } else { + updatePackages(viewModel.unusedPackageCategoriesLiveData.value!!) + } + }, + SHOW_LOAD_DELAY_MS + ) } else { updatePackages(viewModel.unusedPackageCategoriesLiveData.value!!) } @@ -150,15 +159,15 @@ class UnusedAppsFragment<PF, UnusedAppPref> : Fragment() return requireParentFragment() as PF } - /** - * Create [PreferenceScreen] in the parent fragment. - */ + /** Create [PreferenceScreen] in the parent fragment. */ private fun createPreferenceScreen() { val preferenceFragment: PF = requirePreferenceFragment() - val preferenceScreen = preferenceFragment.preferenceManager.inflateFromResource( - context!!, - R.xml.unused_app_categories, - /* rootPreferences= */ null) + val preferenceScreen = + preferenceFragment.preferenceManager.inflateFromResource( + context!!, + R.xml.unused_app_categories, + /* rootPreferences= */ null + ) for (period in allPeriods) { val periodCat = PreferenceCategory(context!!) @@ -208,8 +217,10 @@ class UnusedAppsFragment<PF, UnusedAppPref> : Fragment() val category = preferenceScreen.findPreference<PreferenceCategory>(period.name)!! val months = period.months category.title = - MessageFormat.format(getString(R.string.last_opened_category_title), - mapOf("count" to months)) + MessageFormat.format( + getString(R.string.last_opened_category_title), + mapOf("count" to months) + ) category.isVisible = packages.isNotEmpty() if (packages.isNotEmpty()) { allCategoriesEmpty = false @@ -221,39 +232,53 @@ class UnusedAppsFragment<PF, UnusedAppPref> : Fragment() var pref = category.findPreference<UnusedAppPref>(key) if (pref == null) { - pref = removedPrefs[key] ?: preferenceFragment.createUnusedAppPref( - activity!!.application, pkgName, user) + pref = + removedPrefs[key] + ?: preferenceFragment.createUnusedAppPref( + activity!!.application, + pkgName, + user + ) pref.key = key pref.title = KotlinUtils.getPackageLabel(activity!!.application, pkgName, user) } - pref.setRemoveClickRunnable { - viewModel.requestUninstallApp(this, pkgName, user) - } + pref.setRemoveClickRunnable { viewModel.requestUninstallApp(this, pkgName, user) } pref.setRemoveComponentEnabled(!isSystemApp) - pref.onPreferenceClickListener = Preference.OnPreferenceClickListener { _ -> - viewModel.navigateToAppInfo(pkgName, user, sessionId) - true - } + pref.onPreferenceClickListener = + Preference.OnPreferenceClickListener { _ -> + viewModel.navigateToAppInfo(pkgName, user, sessionId) + true + } val mostImportant = getMostImportantGroup(revokedPerms) val importantLabel = KotlinUtils.getPermGroupLabel(context!!, mostImportant) - pref.summary = when { - revokedPerms.isEmpty() -> null - revokedPerms.size == 1 -> getString(R.string.auto_revoked_app_summary_one, - importantLabel) - revokedPerms.size == 2 -> { - val otherLabel = if (revokedPerms[0] == mostImportant) { - KotlinUtils.getPermGroupLabel(context!!, revokedPerms[1]) - } else { - KotlinUtils.getPermGroupLabel(context!!, revokedPerms[0]) + pref.summary = + when { + revokedPerms.isEmpty() -> null + revokedPerms.size == 1 -> + getString(R.string.auto_revoked_app_summary_one, importantLabel) + revokedPerms.size == 2 -> { + val otherLabel = + if (revokedPerms[0] == mostImportant) { + KotlinUtils.getPermGroupLabel(context!!, revokedPerms[1]) + } else { + KotlinUtils.getPermGroupLabel(context!!, revokedPerms[0]) + } + getString( + R.string.auto_revoked_app_summary_two, + importantLabel, + otherLabel + ) } - getString(R.string.auto_revoked_app_summary_two, importantLabel, otherLabel) + else -> + getString( + R.string.auto_revoked_app_summary_many, + importantLabel, + "${revokedPerms.size - 1}" + ) } - else -> getString(R.string.auto_revoked_app_summary_many, importantLabel, - "${revokedPerms.size - 1}") - } category.addPreference(pref) KotlinUtils.sortPreferenceGroup(category, this::comparePreference, false) } @@ -267,13 +292,19 @@ class UnusedAppsFragment<PF, UnusedAppPref> : Fragment() } Log.i(LOG_TAG, "sessionId: $sessionId Showed Auto Revoke Page") for (period in allPeriods) { - Log.i(LOG_TAG, "sessionId: $sessionId $period unused: " + - "${categorizedPackages[period]}") + Log.i( + LOG_TAG, + "sessionId: $sessionId $period unused: " + "${categorizedPackages[period]}" + ) for (revokedPackageInfo in categorizedPackages[period]!!) { for (groupName in revokedPackageInfo.revokedGroups) { val isNewlyRevoked = period.isNewlyUnused() - viewModel.logAppView(revokedPackageInfo.packageName, - revokedPackageInfo.user, groupName, isNewlyRevoked) + viewModel.logAppView( + revokedPackageInfo.packageName, + revokedPackageInfo.user, + groupName, + isNewlyRevoked + ) } } } @@ -281,8 +312,7 @@ class UnusedAppsFragment<PF, UnusedAppPref> : Fragment() } private fun comparePreference(lhs: Preference, rhs: Preference): Int { - var result = collator.compare(lhs.title.toString(), - rhs.title.toString()) + var result = collator.compare(lhs.title.toString(), rhs.title.toString()) if (result == 0) { result = lhs.key.compareTo(rhs.key) } @@ -324,23 +354,23 @@ class UnusedAppsFragment<PF, UnusedAppPref> : Fragment() val fragment = parentFragment as UnusedAppsFragment<*, *> val packageName = arguments!!.getString(Intent.EXTRA_PACKAGE_NAME)!! val user = arguments!!.getParcelable<UserHandle>(Intent.EXTRA_USER)!! - val b = AlertDialog.Builder(context!!) - .setMessage(R.string.app_disable_dlg_text) - .setPositiveButton(R.string.app_disable_dlg_positive) { _, _ -> - fragment.viewModel.disableApp(packageName, user) - } - .setNegativeButton(R.string.cancel, null) + val b = + AlertDialog.Builder(context!!) + .setMessage(R.string.app_disable_dlg_text) + .setPositiveButton(R.string.app_disable_dlg_positive) { _, _ -> + fragment.viewModel.disableApp(packageName, user) + } + .setNegativeButton(R.string.cancel, null) val d: Dialog = b.create() d.setCanceledOnTouchOutside(true) return d } } - /** - * Interface that the parent fragment must implement. - */ - interface Parent<UnusedAppPref> where UnusedAppPref : Preference, - UnusedAppPref : RemovablePref { + /** Interface that the parent fragment must implement. */ + interface Parent<UnusedAppPref> where + UnusedAppPref : Preference, + UnusedAppPref : RemovablePref { /** * Set the title of the current settings page. diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoAppPermissionFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoAppPermissionFragment.java index 2de936469..d3a89c3ed 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoAppPermissionFragment.java +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoAppPermissionFragment.java @@ -418,7 +418,7 @@ public class AutoAppPermissionFragment extends AutoSettingsFrameFragment // TODO(b/229024576): This code is duplicated, refactor ConfirmDialog for easier // NFF sharing boolean isGrantFileAccess = getArguments().getSerializable(CHANGE_REQUEST) - == ChangeRequest.GRANT_All_FILE_ACCESS; + == ChangeRequest.GRANT_ALL_FILE_ACCESS; boolean isGrantStorageSupergroup = getArguments().getSerializable(CHANGE_REQUEST) == ChangeRequest.GRANT_STORAGE_SUPERGROUP; int positiveButtonStringResId = R.string.grant_dialog_button_deny_anyway; diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoDividerPreference.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoDividerPreference.kt index aa3c7ffd7..7bb4ccb05 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoDividerPreference.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoDividerPreference.kt @@ -20,9 +20,7 @@ import android.util.AttributeSet import androidx.preference.Preference import com.android.permissioncontroller.R -/** - * Non-interactive preference that displays a horizontal divider. - */ +/** Non-interactive preference that displays a horizontal divider. */ class AutoDividerPreference : Preference { constructor( context: Context?, diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoPermissionAppsFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoPermissionAppsFragment.java index 29fcb43b2..3b8419bcf 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoPermissionAppsFragment.java +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoPermissionAppsFragment.java @@ -22,6 +22,7 @@ import static com.android.permissioncontroller.permission.ui.Category.ALLOWED; import static com.android.permissioncontroller.permission.ui.Category.ALLOWED_FOREGROUND; import static com.android.permissioncontroller.permission.ui.Category.ASK; import static com.android.permissioncontroller.permission.ui.Category.DENIED; +import static com.android.permissioncontroller.permission.ui.Category.STORAGE_FOOTER; import static com.android.permissioncontroller.permission.ui.ManagePermissionsActivity.EXTRA_CALLER_NAME; import android.content.Context; @@ -52,15 +53,15 @@ import com.android.permissioncontroller.permission.utils.KotlinUtils; import com.android.permissioncontroller.permission.utils.Utils; import com.android.settingslib.utils.applications.AppUtils; +import kotlin.Pair; +import kotlin.Triple; + import java.text.Collator; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Random; -import kotlin.Pair; -import kotlin.Triple; - /** Shows the list of applications which have (or do not have) the given permission. */ public class AutoPermissionAppsFragment extends AutoSettingsFrameFragment implements PermissionUsages.PermissionsUsagesChangeCallback { @@ -172,6 +173,10 @@ public class AutoPermissionAppsFragment extends AutoSettingsFrameFragment implem } // Hide allowed foreground label by default, to avoid briefly showing it before updating findPreference(ALLOWED_FOREGROUND.getCategoryName()).setVisible(false); + + // Hide storage footer category + findPreference(STORAGE_FOOTER.getCategoryName()).setVisible(false); + Context context = getPreferenceManager().getContext(); if (context == null || getActivity() == null || categories == null) { diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoReviewPermissionDecisionsFragment.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoReviewPermissionDecisionsFragment.kt index 92917a342..99f5c85e4 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoReviewPermissionDecisionsFragment.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoReviewPermissionDecisionsFragment.kt @@ -53,20 +53,19 @@ class AutoReviewPermissionDecisionsFragment : AutoSettingsFrameFragment() { private const val LOG_TAG = "AutoReviewPermissionDecisionsFragment" private const val MAX_DECISIONS = 3 - /** - * Creates a new instance of [AutoReviewPermissionDecisionsFragment]. - */ + /** Creates a new instance of [AutoReviewPermissionDecisionsFragment]. */ fun newInstance( sessionId: Long, userHandle: UserHandle, source: String? ): AutoReviewPermissionDecisionsFragment { return AutoReviewPermissionDecisionsFragment().apply { - arguments = Bundle().apply { - putLong(Constants.EXTRA_SESSION_ID, sessionId) - putParcelable(Intent.EXTRA_USER, userHandle) - putString(EXTRA_SOURCE, source) - } + arguments = + Bundle().apply { + putLong(Constants.EXTRA_SESSION_ID, sessionId) + putParcelable(Intent.EXTRA_USER, userHandle) + putString(EXTRA_SOURCE, source) + } } } } @@ -95,21 +94,23 @@ class AutoReviewPermissionDecisionsFragment : AutoSettingsFrameFragment() { } user = requireArguments().getParcelable<UserHandle>(Intent.EXTRA_USER)!! sessionId = requireArguments().getLong(Constants.EXTRA_SESSION_ID) - if (requireArguments().containsKey(EXTRA_SOURCE) && - (requireArguments().getString(EXTRA_SOURCE) == EXTRA_SOURCE_NOTIFICATION)) { + if ( + requireArguments().containsKey(EXTRA_SOURCE) && + (requireArguments().getString(EXTRA_SOURCE) == EXTRA_SOURCE_NOTIFICATION) + ) { logDecisionReminderNotificationClicked() } - val factory = ReviewPermissionDecisionsViewModelFactory( - requireActivity().getApplication()!!, user) - viewModel = ViewModelProvider(this, - factory)[ReviewPermissionDecisionsViewModel::class.java] + val factory = + ReviewPermissionDecisionsViewModelFactory(requireActivity().getApplication()!!, user) + viewModel = ViewModelProvider(this, factory)[ReviewPermissionDecisionsViewModel::class.java] addPrivacyDashboardPreference() addPermissionManagerPreference() preferenceScreen.addPreference(AutoDividerPreference(context)) - recentPermissionsGroup = PreferenceCategory(context!!).apply { - title = getString(R.string.review_permission_decisions) - } + recentPermissionsGroup = + PreferenceCategory(context!!).apply { + title = getString(R.string.review_permission_decisions) + } preferenceScreen.addPreference(recentPermissionsGroup) viewModel.recentPermissionDecisionsLiveData.observe(this) { recentDecisions -> @@ -138,34 +139,43 @@ class AutoReviewPermissionDecisionsFragment : AutoSettingsFrameFragment() { } private fun addPrivacyDashboardPreference() { - val preference = CarUiPreference(context).apply { - title = getString(R.string.permission_usage_title) - summary = getString(R.string.auto_permission_usage_summary) - onPreferenceClickListener = Preference.OnPreferenceClickListener { _ -> - val intent = Intent(Intent.ACTION_REVIEW_PERMISSION_USAGE).apply { - putExtra(Constants.EXTRA_SESSION_ID, sessionId) - } - startActivity(intent) - true + val preference = + CarUiPreference(context).apply { + title = getString(R.string.permission_usage_title) + summary = getString(R.string.auto_permission_usage_summary) + onPreferenceClickListener = + Preference.OnPreferenceClickListener { _ -> + val intent = + Intent(Intent.ACTION_REVIEW_PERMISSION_USAGE).apply { + putExtra(Constants.EXTRA_SESSION_ID, sessionId) + } + startActivity(intent) + true + } } - } preferenceScreen.addPreference(preference) } private fun addPermissionManagerPreference() { - val preference = CarUiPreference(context).apply { - title = getString(R.string.app_permission_manager) - summary = getString(R.string.auto_permission_manager_summary) - onPreferenceClickListener = Preference.OnPreferenceClickListener { _ -> - val intent = Intent(Intent.ACTION_MANAGE_PERMISSIONS).apply { - putExtra(Intent.EXTRA_USER, user) - putExtra(ManagePermissionsActivity.EXTRA_CALLER_NAME, javaClass.name) - putExtra(Constants.EXTRA_SESSION_ID, sessionId) - } - startActivity(intent) - true + val preference = + CarUiPreference(context).apply { + title = getString(R.string.app_permission_manager) + summary = getString(R.string.auto_permission_manager_summary) + onPreferenceClickListener = + Preference.OnPreferenceClickListener { _ -> + val intent = + Intent(Intent.ACTION_MANAGE_PERMISSIONS).apply { + putExtra(Intent.EXTRA_USER, user) + putExtra( + ManagePermissionsActivity.EXTRA_CALLER_NAME, + javaClass.name + ) + putExtra(Constants.EXTRA_SESSION_ID, sessionId) + } + startActivity(intent) + true + } } - } preferenceScreen.addPreference(preference) } @@ -175,46 +185,57 @@ class AutoReviewPermissionDecisionsFragment : AutoSettingsFrameFragment() { ) { for (i in 0 until min(recentDecisions.size, MAX_DECISIONS)) { val recentDecision = recentDecisions[i] - val decisionPreference = CarUiPreference(context).apply { - icon = viewModel.getAppIcon(recentDecision.packageName) - title = viewModel.createPreferenceTitle(recentDecision) - summary = viewModel.createSummaryText(recentDecision) - onPreferenceClickListener = Preference.OnPreferenceClickListener { - viewModel.createManageAppPermissionIntent(recentDecision).also { - startActivity(it) - } - logPermissionDecisionClicked(recentDecision.packageName, - recentDecision.permissionGroupName) - true + val decisionPreference = + CarUiPreference(context).apply { + icon = viewModel.getAppIcon(recentDecision.packageName) + title = viewModel.createPreferenceTitle(recentDecision) + summary = viewModel.createSummaryText(recentDecision) + onPreferenceClickListener = + Preference.OnPreferenceClickListener { + viewModel.createManageAppPermissionIntent(recentDecision).also { + startActivity(it) + } + logPermissionDecisionClicked( + recentDecision.packageName, + recentDecision.permissionGroupName + ) + true + } } - } preferenceGroup.addPreference(decisionPreference) } } private fun addViewAllPreference(preferenceGroup: PreferenceGroup) { val viewAllIcon = requireContext().getDrawable(R.drawable.car_ic_apps) - val preference = CarUiPreference(context).apply { - icon = Utils.applyTint(context, viewAllIcon, android.R.attr.colorControlNormal) - title = getString(R.string.review_permission_decisions_view_all) - onPreferenceClickListener = Preference.OnPreferenceClickListener { - val frag = AutoReviewPermissionDecisionsViewAllFragment.newInstance(sessionId, - user) - getParentFragmentManager().beginTransaction() - .replace(android.R.id.content, frag) - .addToBackStack(null) - .commit() - logViewAllClicked() - true + val preference = + CarUiPreference(context).apply { + icon = Utils.applyTint(context, viewAllIcon, android.R.attr.colorControlNormal) + title = getString(R.string.review_permission_decisions_view_all) + onPreferenceClickListener = + Preference.OnPreferenceClickListener { + val frag = + AutoReviewPermissionDecisionsViewAllFragment.newInstance( + sessionId, + user + ) + getParentFragmentManager() + .beginTransaction() + .replace(android.R.id.content, frag) + .addToBackStack(null) + .commit() + logViewAllClicked() + true + } } - } preferenceGroup.addPreference(preference) } private fun addNoRecentDecisionsPreference(preferenceGroup: PreferenceGroup) { - val preference = CarUiPreference(context).apply { - title = getString(R.string.review_permission_decisions_empty) - } + val preference = + CarUiPreference(context).apply { + title = getString(R.string.review_permission_decisions_empty) + } preferenceGroup.addPreference(preference) } @@ -224,7 +245,8 @@ class AutoReviewPermissionDecisionsFragment : AutoSettingsFrameFragment() { sessionId, RECENT_PERMISSION_DECISIONS_INTERACTED__ACTION__SCREEN_VIEWED, 0, - null) + null + ) } private fun logViewAllClicked() { @@ -233,7 +255,8 @@ class AutoReviewPermissionDecisionsFragment : AutoSettingsFrameFragment() { sessionId, RECENT_PERMISSION_DECISIONS_INTERACTED__ACTION__VIEW_ALL_CLICKED, 0, - null) + null + ) } private fun logPermissionDecisionClicked(packageName: String, permissionGroupName: String) { @@ -243,12 +266,15 @@ class AutoReviewPermissionDecisionsFragment : AutoSettingsFrameFragment() { sessionId, RECENT_PERMISSION_DECISIONS_INTERACTED__ACTION__REVIEW_DECISION, uid, - permissionGroupName) + permissionGroupName + ) } private fun logDecisionReminderNotificationClicked() { PermissionControllerStatsLog.write( PermissionControllerStatsLog.PERMISSION_REMINDER_NOTIFICATION_INTERACTED, - sessionId, PERMISSION_REMINDER_NOTIFICATION_INTERACTED__RESULT__NOTIFICATION_CLICKED) + sessionId, + PERMISSION_REMINDER_NOTIFICATION_INTERACTED__RESULT__NOTIFICATION_CLICKED + ) } } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoReviewPermissionDecisionsViewAllFragment.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoReviewPermissionDecisionsViewAllFragment.kt index 9f9471fdf..11cca4693 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoReviewPermissionDecisionsViewAllFragment.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoReviewPermissionDecisionsViewAllFragment.kt @@ -39,18 +39,17 @@ class AutoReviewPermissionDecisionsViewAllFragment : AutoSettingsFrameFragment() companion object { private const val LOG_TAG = "AutoReviewPermissionDecisionsViewAllFragment" - /** - * Creates a new instance of [AutoReviewPermissionDecisionsViewAllFragment]. - */ + /** Creates a new instance of [AutoReviewPermissionDecisionsViewAllFragment]. */ fun newInstance( sessionId: Long, userHandle: UserHandle ): AutoReviewPermissionDecisionsViewAllFragment { return AutoReviewPermissionDecisionsViewAllFragment().apply { - arguments = Bundle().apply { - putLong(Constants.EXTRA_SESSION_ID, sessionId) - putParcelable(Intent.EXTRA_USER, userHandle) - } + arguments = + Bundle().apply { + putLong(Constants.EXTRA_SESSION_ID, sessionId) + putParcelable(Intent.EXTRA_USER, userHandle) + } } } } @@ -71,10 +70,9 @@ class AutoReviewPermissionDecisionsViewAllFragment : AutoSettingsFrameFragment() return } user = requireArguments().getParcelable<UserHandle>(Intent.EXTRA_USER)!! - val factory = ReviewPermissionDecisionsViewModelFactory( - requireActivity().getApplication()!!, user) - viewModel = ViewModelProvider(this, - factory)[ReviewPermissionDecisionsViewModel::class.java] + val factory = + ReviewPermissionDecisionsViewModelFactory(requireActivity().getApplication()!!, user) + viewModel = ViewModelProvider(this, factory)[ReviewPermissionDecisionsViewModel::class.java] viewModel.recentPermissionDecisionsLiveData.observe(this) { recentDecisions -> onRecentDecisionsChanged(recentDecisions) } @@ -88,17 +86,19 @@ class AutoReviewPermissionDecisionsViewAllFragment : AutoSettingsFrameFragment() private fun onRecentDecisionsChanged(recentDecisions: List<PermissionDecision>) { preferenceScreen.removeAll() for (recentDecision in recentDecisions) { - val decisionPreference = CarUiPreference(context).apply { - icon = viewModel.getAppIcon(recentDecision.packageName) - title = viewModel.createPreferenceTitle(recentDecision) - summary = viewModel.createSummaryText(recentDecision) - onPreferenceClickListener = Preference.OnPreferenceClickListener { - viewModel.createManageAppPermissionIntent(recentDecision).also { - startActivity(it) - } - false + val decisionPreference = + CarUiPreference(context).apply { + icon = viewModel.getAppIcon(recentDecision.packageName) + title = viewModel.createPreferenceTitle(recentDecision) + summary = viewModel.createSummaryText(recentDecision) + onPreferenceClickListener = + Preference.OnPreferenceClickListener { + viewModel.createManageAppPermissionIntent(recentDecision).also { + startActivity(it) + } + false + } } - } preferenceScreen.addPreference(decisionPreference) } } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoUnusedAppsFragment.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoUnusedAppsFragment.kt index 61f77c81a..d798291e0 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoUnusedAppsFragment.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoUnusedAppsFragment.kt @@ -30,16 +30,14 @@ import com.android.permissioncontroller.hibernation.isHibernationEnabled import com.android.permissioncontroller.permission.ui.UnusedAppsFragment import com.android.permissioncontroller.permission.ui.UnusedAppsFragment.Companion.INFO_MSG_CATEGORY -/** - * Auto wrapper, with customizations, around [UnusedAppsFragment]. - */ -class AutoUnusedAppsFragment : AutoSettingsFrameFragment(), - UnusedAppsFragment.Parent<AutoUnusedAppsPreference> { +/** Auto wrapper, with customizations, around [UnusedAppsFragment]. */ +class AutoUnusedAppsFragment : + AutoSettingsFrameFragment(), UnusedAppsFragment.Parent<AutoUnusedAppsPreference> { companion object { private const val UNUSED_PREFERENCE_KEY = "unused_pref_row_key" - /** Create a new instance of this fragment. */ + /** Create a new instance of this fragment. */ @JvmStatic fun newInstance(): AutoUnusedAppsFragment { return AutoUnusedAppsFragment() @@ -53,15 +51,12 @@ class AutoUnusedAppsFragment : AutoSettingsFrameFragment(), override fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) if (savedInstanceState == null) { - val fragment: - UnusedAppsFragment<AutoUnusedAppsFragment, AutoUnusedAppsPreference> = + val fragment: UnusedAppsFragment<AutoUnusedAppsFragment, AutoUnusedAppsPreference> = UnusedAppsFragment.newInstance() fragment.arguments = arguments // child fragment does not have its own UI - it will add to the preferences of this // parent fragment - childFragmentManager.beginTransaction() - .add(fragment, null) - .commit() + childFragmentManager.beginTransaction().add(fragment, null).commit() } // initially focus on focus parking view and then shift focus to recyclerview once it has @@ -76,10 +71,12 @@ class AutoUnusedAppsFragment : AutoSettingsFrameFragment(), if (isHibernationEnabled()) { preference.summary = getString(R.string.unused_apps_page_summary) } else { - preference.summary = """ + preference.summary = + """ ${getString(R.string.auto_revoked_apps_page_summary)} ${getString(R.string.auto_revoke_open_app_message)} - """.trimIndent() + """ + .trimIndent() } preference.setIcon(R.drawable.ic_info_outline) preference.isSelectable = false @@ -104,9 +101,9 @@ class AutoUnusedAppsFragment : AutoSettingsFrameFragment(), override fun setEmptyState(empty: Boolean) { val infoMsgCategory = - preferenceScreen.findPreference<PreferenceCategory>(INFO_MSG_CATEGORY)!! + preferenceScreen.findPreference<PreferenceCategory>(INFO_MSG_CATEGORY)!! val noUnusedAppsPreference: Preference? = - infoMsgCategory.findPreference<Preference>(UNUSED_PREFERENCE_KEY) + infoMsgCategory.findPreference<Preference>(UNUSED_PREFERENCE_KEY) if (empty && noUnusedAppsPreference == null) { infoMsgCategory.addPreference(createNoUnusedAppsPreference()) } else if (noUnusedAppsPreference != null) { diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoUnusedAppsPreference.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoUnusedAppsPreference.kt index 9835a90f4..f1b65b38f 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoUnusedAppsPreference.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoUnusedAppsPreference.kt @@ -52,4 +52,4 @@ class AutoUnusedAppsPreference( override fun setRemoveComponentEnabled(enabled: Boolean) { setSecondaryActionEnabled(enabled) } -}
\ No newline at end of file +} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/dashboard/AutoPermissionHistoryPreference.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/dashboard/AutoPermissionHistoryPreference.kt index 7ea400127..08460178c 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/dashboard/AutoPermissionHistoryPreference.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/dashboard/AutoPermissionHistoryPreference.kt @@ -23,8 +23,8 @@ import androidx.annotation.RequiresApi import androidx.preference.Preference.OnPreferenceClickListener import com.android.car.ui.preference.CarUiPreference import com.android.permissioncontroller.R -import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageDetailsViewModel import com.android.permissioncontroller.permission.ui.legacy.PermissionUsageDetailsViewModelLegacy +import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageDetailsViewModel /** Preference that displays a permission usage for an app. */ @RequiresApi(Build.VERSION_CODES.S) @@ -40,7 +40,8 @@ class AutoPermissionHistoryPreference( context.getString( R.string.auto_permission_usage_timeline_summary, DateFormat.getTimeFormat(context).format(historyPreferenceData.accessEndTime), - historyPreferenceData.summaryText) + historyPreferenceData.summaryText + ) } else { DateFormat.getTimeFormat(context).format(historyPreferenceData.accessEndTime) } @@ -60,7 +61,9 @@ class AutoPermissionHistoryPreference( accessEndTime = historyPreferenceData.accessEndTime, accessStartTime = historyPreferenceData.accessStartTime, showingAttribution = historyPreferenceData.showingAttribution, - attributionTags = historyPreferenceData.attributionTags)) + attributionTags = historyPreferenceData.attributionTags + ) + ) true } } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/dashboard/AutoPermissionUsageFragment.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/dashboard/AutoPermissionUsageFragment.kt index d4a2a073e..c11129514 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/dashboard/AutoPermissionUsageFragment.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/dashboard/AutoPermissionUsageFragment.kt @@ -38,11 +38,11 @@ import com.android.permissioncontroller.permission.model.livedatatypes.PermGroup import com.android.permissioncontroller.permission.model.v31.AppPermissionUsage import com.android.permissioncontroller.permission.model.v31.PermissionUsages import com.android.permissioncontroller.permission.model.v31.PermissionUsages.PermissionsUsagesChangeCallback -import com.android.permissioncontroller.permission.ui.model.ManagePermissionsViewModel -import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageControlPreferenceUtils import com.android.permissioncontroller.permission.ui.legacy.PermissionUsageViewModelFactoryLegacy import com.android.permissioncontroller.permission.ui.legacy.PermissionUsageViewModelLegacy import com.android.permissioncontroller.permission.ui.legacy.PermissionUsageViewModelLegacy.PermissionGroupWithUsageCount +import com.android.permissioncontroller.permission.ui.model.ManagePermissionsViewModel +import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageControlPreferenceUtils import com.android.permissioncontroller.permission.utils.Utils @RequiresApi(Build.VERSION_CODES.S) @@ -95,7 +95,9 @@ class AutoPermissionUsageFragment : AutoSettingsFrameFragment(), PermissionsUsag PermissionUsageViewModelLegacy::class.java] managePermissionsViewModel.standardPermGroupsLiveData.observe( - this, this::onPermissionGroupsChanged) + this, + this::onPermissionGroupsChanged + ) setLoading(true) reloadData() } @@ -119,7 +121,8 @@ class AutoPermissionUsageFragment : AutoSettingsFrameFragment(), PermissionsUsag PermissionControllerStatsLog.write( PERMISSION_USAGE_FRAGMENT_INTERACTION, sessionId, - PERMISSION_USAGE_FRAGMENT_INTERACTION__ACTION__SHOW_SYSTEM_CLICKED) + PERMISSION_USAGE_FRAGMENT_INTERACTION__ACTION__SHOW_SYSTEM_CLICKED + ) } showSystem = !showSystem updateAction() @@ -143,7 +146,10 @@ class AutoPermissionUsageFragment : AutoSettingsFrameFragment(), PermissionsUsag /** Reloads the data to show. */ private fun reloadData() { usageViewModel.loadPermissionUsages( - requireActivity().getLoaderManager(), permissionUsages, this) + requireActivity().getLoaderManager(), + permissionUsages, + this + ) if (finishedInitialLoad) { setLoading(false) } @@ -165,7 +171,11 @@ class AutoPermissionUsageFragment : AutoSettingsFrameFragment(), PermissionsUsag val permissionUsagesUiData = usageViewModel.buildPermissionUsagesUiData( - appPermissionUsages, show7Days, showSystem, requireContext()) + appPermissionUsages, + show7Days, + showSystem, + requireContext() + ) val permissionApps = permissionUsagesUiData.permissionApps val displayShowSystemToggle = permissionUsagesUiData.displayShowSystemToggle @@ -213,7 +223,8 @@ class AutoPermissionUsageFragment : AutoSettingsFrameFragment(), PermissionsUsag count, showSystem, sessionId, - show7Days) + show7Days + ) getPreferenceScreen().addPreference(permissionUsagePreference) } finishedInitialLoad = true diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionFragment.java index cab0de15e..7fa51dd8a 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionFragment.java +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionFragment.java @@ -103,6 +103,7 @@ public class AppPermissionFragment extends SettingsWithLargeHeader implements AppPermissionViewModel.ConfirmDialogShowingFragment { private static final String LOG_TAG = "AppPermissionFragment"; private static final long POST_DELAY_MS = 20; + private static final long EDIT_PHOTOS_BUTTON_ANIMATION_LENGTH_MS = 200L; static final String GRANT_CATEGORY = "grant_category"; @@ -117,6 +118,9 @@ public class AppPermissionFragment extends SettingsWithLargeHeader private @NonNull RadioButton mSelectPhotosButton; private @NonNull RadioButton mDenyButton; private @NonNull RadioButton mDenyForegroundButton; + private @NonNull ImageView mEditPhotosButton; + private @NonNull View mSelectPhotosLayout; + private @NonNull View mEditPhotosDivider; private @NonNull View mLocationAccuracy; private @NonNull Switch mLocationAccuracySwitch; private @NonNull View mDivider; @@ -128,6 +132,8 @@ public class AppPermissionFragment extends SettingsWithLargeHeader private @NonNull UserHandle mUser; private boolean mIsStorageGroup; private boolean mIsInitialLoad; + // This prevents the user from clicking the photo picker button multiple times in succession + private boolean mPhotoPickerTriggered; private long mSessionId; private @NonNull String mPackageLabel; @@ -209,7 +215,6 @@ public class AppPermissionFragment extends SettingsWithLargeHeader if (mIsStorageGroup) { mViewModel.getFullStorageStateLiveData().observe(this, this::setSpecialStorageState); } - mViewModel.registerPhotoPickerResultIfNeeded(this); mRoleManager = Utils.getSystemServiceSafe(getContext(), RoleManager.class); } @@ -261,12 +266,15 @@ public class AppPermissionFragment extends SettingsWithLargeHeader mSelectPhotosButton = root.requireViewById(R.id.select_radio_button); mDenyButton = root.requireViewById(R.id.deny_radio_button); mDenyForegroundButton = root.requireViewById(R.id.deny_foreground_radio_button); + mDivider = root.requireViewById(R.id.two_target_divider); mWidgetFrame = root.requireViewById(R.id.widget_frame); mPermissionDetails = root.requireViewById(R.id.permission_details); mLocationAccuracy = root.requireViewById(R.id.location_accuracy); mLocationAccuracySwitch = root.requireViewById(R.id.location_accuracy_switch); - + mSelectPhotosLayout = root.requireViewById(R.id.radio_select_layout); + mEditPhotosButton = root.requireViewById(R.id.edit_selected_button); + mEditPhotosDivider = root.requireViewById(R.id.edit_photos_divider); mNestedScrollView = root.requireViewById(R.id.nested_scroll_view); if (mViewModel.getButtonStateLiveData().getValue() != null) { @@ -280,6 +288,9 @@ public class AppPermissionFragment extends SettingsWithLargeHeader mDenyButton.setVisibility(View.GONE); mDenyForegroundButton.setVisibility(View.GONE); mLocationAccuracy.setVisibility(View.GONE); + mSelectPhotosLayout.setVisibility(View.GONE); + mEditPhotosDivider.setAlpha(0f); + mEditPhotosButton.setAlpha(0f); } if (mViewModel.getFullStorageStateLiveData().isInitialized() && mIsStorageGroup) { @@ -302,6 +313,12 @@ public class AppPermissionFragment extends SettingsWithLargeHeader return root; } + public void onResume() { + super.onResume(); + // If we're returning to the fragment, photo picker hasn't been triggered + mPhotoPickerTriggered = false; + } + private void showPermissionRationaleDialog(boolean showPermissionRationale) { if (!showPermissionRationale) { mAppPermissionRationaleContainer.setVisibility(View.GONE); @@ -380,7 +397,7 @@ public class AppPermissionFragment extends SettingsWithLargeHeader }); mAllowAlwaysButton.setOnClickListener((v) -> { if (mIsStorageGroup) { - showConfirmDialog(ChangeRequest.GRANT_All_FILE_ACCESS, + showConfirmDialog(ChangeRequest.GRANT_ALL_FILE_ACCESS, R.string.special_file_access_dialog, -1, false); } else { mViewModel.requestChange(false, this, this, ChangeRequest.GRANT_BOTH, @@ -412,6 +429,13 @@ public class AppPermissionFragment extends SettingsWithLargeHeader mViewModel.requestChange(false, this, this, ChangeRequest.PHOTOS_SELECTED, buttonPressed); }); + mEditPhotosButton.setOnClickListener((v) -> { + ButtonState selectState = states.get(ButtonType.SELECT_PHOTOS); + if (selectState != null && selectState.isChecked() && !mPhotoPickerTriggered) { + mPhotoPickerTriggered = true; + mViewModel.openPhotoPicker(this); + } + }); mDenyButton.setOnClickListener((v) -> { if (mViewModel.getFullStorageStateLiveData().getValue() != null && !mViewModel.getFullStorageStateLiveData().getValue().isLegacy()) { @@ -485,6 +509,21 @@ public class AppPermissionFragment extends SettingsWithLargeHeader if (mIsInitialLoad) { button.jumpDrawablesToCurrentState(); } + + if (button == mSelectPhotosButton) { + mSelectPhotosLayout.setVisibility(visible); + float endOpacity = state.isChecked() ? 1f : 0f; + // On initial load, do not show the fade in/out animation + if (mIsInitialLoad) { + mEditPhotosDivider.setAlpha(endOpacity); + mEditPhotosButton.setAlpha(endOpacity); + return; + } + mEditPhotosButton.animate().alpha(endOpacity) + .setDuration(EDIT_PHOTOS_BUTTON_ANIMATION_LENGTH_MS); + mEditPhotosDivider.animate().alpha(endOpacity) + .setDuration(EDIT_PHOTOS_BUTTON_ANIMATION_LENGTH_MS); + } } private void setSpecialStorageState(FullStoragePackageState storageState, View v) { @@ -630,7 +669,7 @@ public class AppPermissionFragment extends SettingsWithLargeHeader // NFF sharing AppPermissionFragment fragment = (AppPermissionFragment) getParentFragment(); boolean isGrantFileAccess = getArguments().getSerializable(CHANGE_REQUEST) - == ChangeRequest.GRANT_All_FILE_ACCESS; + == ChangeRequest.GRANT_ALL_FILE_ACCESS; int positiveButtonStringResId = R.string.grant_dialog_button_deny_anyway; if (isGrantFileAccess) { positiveButtonStringResId = R.string.grant_dialog_button_allow; diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionGroupsFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionGroupsFragment.java index a8b79bfde..5b24b52ec 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionGroupsFragment.java +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionGroupsFragment.java @@ -23,6 +23,7 @@ import static com.android.permissioncontroller.PermissionControllerStatsLog.APP_ import static com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSIONS_FRAGMENT_VIEWED__CATEGORY__ALLOWED_FOREGROUND; import static com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSIONS_FRAGMENT_VIEWED__CATEGORY__DENIED; import static com.android.permissioncontroller.hibernation.HibernationPolicyKt.isHibernationEnabled; +import static com.android.permissioncontroller.permission.ui.Category.STORAGE_FOOTER; import static com.android.permissioncontroller.permission.ui.handheld.UtilsKt.pressBack; import static java.util.concurrent.TimeUnit.DAYS; @@ -318,6 +319,9 @@ public final class AppPermissionGroupsFragment extends SettingsWithLargeHeader i findPreference(Category.ALLOWED_FOREGROUND.getCategoryName()).setVisible(false); + // Hide storage footer category + findPreference(STORAGE_FOOTER.getCategoryName()).setVisible(false); + long sessionId = getArguments().getLong(EXTRA_SESSION_ID, INVALID_SESSION_ID); for (Category grantCategory : groupMap.keySet()) { diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/FooterPreference.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/FooterPreference.kt index 7864abbb2..278243f09 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/FooterPreference.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/FooterPreference.kt @@ -28,10 +28,10 @@ import com.android.permissioncontroller.R * placement. */ class FooterPreference : Preference { - constructor(c: Context): super(c) - constructor(c: Context, a: AttributeSet): super(c, a) - constructor(c: Context, a: AttributeSet, attr: Int): super(c, a, attr) - constructor(c: Context, a: AttributeSet, attr: Int, res: Int): super(c, a, attr, res) + constructor(c: Context) : super(c) + constructor(c: Context, a: AttributeSet) : super(c, a) + constructor(c: Context, a: AttributeSet, attr: Int) : super(c, a, attr) + constructor(c: Context, a: AttributeSet, attr: Int, res: Int) : super(c, a, attr, res) init { layoutResource = R.layout.footer_preference diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/GrantPermissionsViewHandlerImpl.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/GrantPermissionsViewHandlerImpl.kt index decbfe590..394c2d113 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/GrantPermissionsViewHandlerImpl.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/GrantPermissionsViewHandlerImpl.kt @@ -83,10 +83,14 @@ class GrantPermissionsViewHandlerImpl( private val resultListener: ResultListener ) : GrantPermissionsViewHandler, OnClickListener { - private val LOCATION_ACCURACY_DIALOGS = listOf(DIALOG_WITH_BOTH_LOCATIONS, - DIALOG_WITH_FINE_LOCATION_ONLY, DIALOG_WITH_COARSE_LOCATION_ONLY) - private val LOCATION_ACCURACY_IMAGE_DIAMETER = mActivity.resources.getDimension( - R.dimen.location_accuracy_image_size) + private val LOCATION_ACCURACY_DIALOGS = + listOf( + DIALOG_WITH_BOTH_LOCATIONS, + DIALOG_WITH_FINE_LOCATION_ONLY, + DIALOG_WITH_COARSE_LOCATION_ONLY + ) + private val LOCATION_ACCURACY_IMAGE_DIAMETER = + mActivity.resources.getDimension(R.dimen.location_accuracy_image_size) // Configuration of the current dialog private var groupName: String? = null @@ -124,8 +128,10 @@ class GrantPermissionsViewHandlerImpl( arguments.putParcelable(ARG_GROUP_ICON, groupIcon) arguments.putCharSequence(ARG_GROUP_MESSAGE, groupMessage) arguments.putCharSequence(ARG_GROUP_DETAIL_MESSAGE, detailMessage) - arguments.putCharSequence(ARG_GROUP_PERMISSION_RATIONALE_MESSAGE, - permissionRationaleMessage) + arguments.putCharSequence( + ARG_GROUP_PERMISSION_RATIONALE_MESSAGE, + permissionRationaleMessage + ) arguments.putBooleanArray(ARG_DIALOG_BUTTON_VISIBILITIES, buttonVisibilities) arguments.putBooleanArray(ARG_DIALOG_LOCATION_VISIBILITIES, locationVisibilities) arguments.putInt(ARG_DIALOG_SELECTED_PRECISION, selectedPrecision) @@ -141,8 +147,9 @@ class GrantPermissionsViewHandlerImpl( permissionRationaleMessage = savedInstanceState.getCharSequence(ARG_GROUP_PERMISSION_RATIONALE_MESSAGE) setButtonVisibilities(savedInstanceState.getBooleanArray(ARG_DIALOG_BUTTON_VISIBILITIES)) - setLocationVisibilities(savedInstanceState.getBooleanArray( - ARG_DIALOG_LOCATION_VISIBILITIES)) + setLocationVisibilities( + savedInstanceState.getBooleanArray(ARG_DIALOG_LOCATION_VISIBILITIES) + ) selectedPrecision = savedInstanceState.getInt(ARG_DIALOG_SELECTED_PRECISION) updateAll() @@ -187,21 +194,22 @@ class GrantPermissionsViewHandlerImpl( // Grow or shrink the content container to size of new content val growShrinkToNewContentSize = ChangeBounds() growShrinkToNewContentSize.duration = ANIMATION_DURATION_MILLIS - growShrinkToNewContentSize.interpolator = AnimationUtils.loadInterpolator(mActivity, - android.R.interpolator.fast_out_slow_in) + growShrinkToNewContentSize.interpolator = + AnimationUtils.loadInterpolator(mActivity, android.R.interpolator.fast_out_slow_in) TransitionManager.beginDelayedTransition(rootView, growShrinkToNewContentSize) } override fun createView(): View { - val useMaterial3PermissionGrantDialog = mActivity.resources - .getBoolean(R.bool.config_useMaterial3PermissionGrantDialog) - val rootView = if (useMaterial3PermissionGrantDialog || SdkLevel.isAtLeastT()) { - LayoutInflater.from(mActivity) - .inflate(R.layout.grant_permissions_material3, null) as ViewGroup - } else { - LayoutInflater.from(mActivity) - .inflate(R.layout.grant_permissions, null) as ViewGroup - } + val useMaterial3PermissionGrantDialog = + mActivity.resources.getBoolean(R.bool.config_useMaterial3PermissionGrantDialog) + val rootView = + if (useMaterial3PermissionGrantDialog || SdkLevel.isAtLeastT()) { + LayoutInflater.from(mActivity).inflate(R.layout.grant_permissions_material3, null) + as ViewGroup + } else { + LayoutInflater.from(mActivity).inflate(R.layout.grant_permissions, null) + as ViewGroup + } this.rootView = rootView // Uses the vertical gravity of the PermissionGrantSingleton style to position the window @@ -257,14 +265,15 @@ class GrantPermissionsViewHandlerImpl( private fun getLottieDrawable(@RawRes rawResId: Int): LottieDrawable { val composition = LottieCompositionFactory.fromRawResSync(mActivity, rawResId).value!! val scale = LOCATION_ACCURACY_IMAGE_DIAMETER / composition.bounds.width() - val drawable = object : LottieDrawable() { - override fun getIntrinsicHeight(): Int { - return (super.getIntrinsicHeight() * scale).toInt() - } - override fun getIntrinsicWidth(): Int { - return (super.getIntrinsicWidth() * scale).toInt() + val drawable = + object : LottieDrawable() { + override fun getIntrinsicHeight(): Int { + return (super.getIntrinsicHeight() * scale).toInt() + } + override fun getIntrinsicWidth(): Int { + return (super.getIntrinsicWidth() * scale).toInt() + } } - } drawable.composition = composition return drawable } @@ -282,21 +291,23 @@ class GrantPermissionsViewHandlerImpl( private fun setButtonVisibilities(visibilities: BooleanArray?) { for (i in buttonVisibilities.indices) { - buttonVisibilities[i] = if (visibilities != null && i < visibilities.size) { - visibilities[i] - } else { - false - } + buttonVisibilities[i] = + if (visibilities != null && i < visibilities.size) { + visibilities[i] + } else { + false + } } } private fun setLocationVisibilities(visibilities: BooleanArray?) { for (i in locationVisibilities.indices) { - locationVisibilities[i] = if (visibilities != null && i < visibilities.size) { - visibilities[i] - } else { - false - } + locationVisibilities[i] = + if (visibilities != null && i < visibilities.size) { + visibilities[i] + } else { + false + } } } @@ -329,29 +340,38 @@ class GrantPermissionsViewHandlerImpl( private fun updateButtons() { for (i in 0 until BUTTON_RES_ID_TO_NUM.size()) { val pos = BUTTON_RES_ID_TO_NUM.valueAt(i) - buttons[pos]?.visibility = if (buttonVisibilities[pos]) { - View.VISIBLE - } else { - View.GONE - } + buttons[pos]?.visibility = + if (buttonVisibilities[pos]) { + View.VISIBLE + } else { + View.GONE + } if (pos == ALLOW_FOREGROUND_BUTTON && buttonVisibilities[pos]) { - if (locationVisibilities[LOCATION_ACCURACY_LAYOUT] && - locationVisibilities[DIALOG_WITH_FINE_LOCATION_ONLY]) { - buttons[pos]?.text = mActivity.resources.getString( - R.string.grant_dialog_button_change_to_precise_location) + if ( + locationVisibilities[LOCATION_ACCURACY_LAYOUT] && + locationVisibilities[DIALOG_WITH_FINE_LOCATION_ONLY] + ) { + buttons[pos]?.text = + mActivity.resources.getString( + R.string.grant_dialog_button_change_to_precise_location + ) } else { - buttons[pos]?.text = mActivity.resources.getString( - R.string.grant_dialog_button_allow_foreground) + buttons[pos]?.text = + mActivity.resources.getString(R.string.grant_dialog_button_allow_foreground) } } if ((pos == DENY_BUTTON || pos == DENY_AND_DONT_ASK_AGAIN_BUTTON)) { - if (locationVisibilities[LOCATION_ACCURACY_LAYOUT] && - locationVisibilities[DIALOG_WITH_FINE_LOCATION_ONLY]) { - buttons[pos]?.text = mActivity.resources.getString( - R.string.grant_dialog_button_keey_approximate_location) + if ( + locationVisibilities[LOCATION_ACCURACY_LAYOUT] && + locationVisibilities[DIALOG_WITH_FINE_LOCATION_ONLY] + ) { + buttons[pos]?.text = + mActivity.resources.getString( + R.string.grant_dialog_button_keey_approximate_location + ) } else { - buttons[pos]?.text = mActivity.resources.getString( - R.string.grant_dialog_button_deny) + buttons[pos]?.text = + mActivity.resources.getString(R.string.grant_dialog_button_deny) } } buttons[pos]?.requestLayout() @@ -365,11 +385,12 @@ class GrantPermissionsViewHandlerImpl( } locationViews[LOCATION_ACCURACY_LAYOUT]?.visibility = View.VISIBLE for (i in LOCATION_ACCURACY_DIALOGS) { - locationViews[i]?.visibility = if (locationVisibilities[i]) { - View.VISIBLE - } else { - View.GONE - } + locationViews[i]?.visibility = + if (locationVisibilities[i]) { + View.VISIBLE + } else { + View.GONE + } } if (locationVisibilities[DIALOG_WITH_BOTH_LOCATIONS]) { coarseRadioButton?.visibility = View.VISIBLE @@ -388,11 +409,13 @@ class GrantPermissionsViewHandlerImpl( } } else if (locationVisibilities[DIALOG_WITH_COARSE_LOCATION_ONLY]) { (locationViews[DIALOG_WITH_COARSE_LOCATION_ONLY] as ImageView).setImageDrawable( - coarseOnDrawable) + coarseOnDrawable + ) coarseOnDrawable?.start() } else if (locationVisibilities[DIALOG_WITH_FINE_LOCATION_ONLY]) { (locationViews[DIALOG_WITH_FINE_LOCATION_ONLY] as ImageView).setImageDrawable( - fineOnDrawable) + fineOnDrawable + ) fineOnDrawable?.start() } } else { @@ -408,10 +431,18 @@ class GrantPermissionsViewHandlerImpl( if (isFineSelected) { coarseOnDrawable?.stop() fineOffDrawable?.stop() - coarseRadioButton?.setCompoundDrawablesWithIntrinsicBounds(null, coarseOffDrawable, - null, null) - fineRadioButton?.setCompoundDrawablesWithIntrinsicBounds(null, fineOnDrawable, - null, null) + coarseRadioButton?.setCompoundDrawablesWithIntrinsicBounds( + null, + coarseOffDrawable, + null, + null + ) + fineRadioButton?.setCompoundDrawablesWithIntrinsicBounds( + null, + fineOnDrawable, + null, + null + ) coarseOffDrawable?.start() fineOnDrawable?.start() fineRadioButton?.setTypeface(null, Typeface.BOLD) @@ -419,10 +450,18 @@ class GrantPermissionsViewHandlerImpl( } else { coarseOffDrawable?.stop() fineOnDrawable?.stop() - coarseRadioButton?.setCompoundDrawablesWithIntrinsicBounds(null, coarseOnDrawable, - null, null) - fineRadioButton?.setCompoundDrawablesWithIntrinsicBounds(null, fineOffDrawable, - null, null) + coarseRadioButton?.setCompoundDrawablesWithIntrinsicBounds( + null, + coarseOnDrawable, + null, + null + ) + fineRadioButton?.setCompoundDrawablesWithIntrinsicBounds( + null, + fineOffDrawable, + null, + null + ) fineOffDrawable?.start() coarseOnDrawable?.start() coarseRadioButton?.setTypeface(null, Typeface.BOLD) @@ -471,8 +510,8 @@ class GrantPermissionsViewHandlerImpl( R.id.permission_location_accuracy_radio_coarse -> affectedForegroundPermissions = listOf(ACCESS_COARSE_LOCATION) R.id.permission_location_accuracy_radio_fine -> - affectedForegroundPermissions = listOf(ACCESS_FINE_LOCATION, - ACCESS_COARSE_LOCATION) + affectedForegroundPermissions = + listOf(ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION) } } else if (locationVisibilities[DIALOG_WITH_FINE_LOCATION_ONLY]) { affectedForegroundPermissions = listOf(ACCESS_FINE_LOCATION) @@ -481,52 +520,94 @@ class GrantPermissionsViewHandlerImpl( } when (BUTTON_RES_ID_TO_NUM.get(id, -1)) { - ALLOW_ALL_BUTTON, ALLOW_BUTTON -> { + ALLOW_ALL_BUTTON, + ALLOW_BUTTON -> { view.performAccessibilityAction( - AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS, null) - resultListener.onPermissionGrantResult(groupName, affectedForegroundPermissions, - GRANTED_ALWAYS) + AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS, + null + ) + resultListener.onPermissionGrantResult( + groupName, + affectedForegroundPermissions, + GRANTED_ALWAYS + ) } ALLOW_FOREGROUND_BUTTON -> { view.performAccessibilityAction( - AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS, null) - resultListener.onPermissionGrantResult(groupName, affectedForegroundPermissions, - GRANTED_FOREGROUND_ONLY) + AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS, + null + ) + resultListener.onPermissionGrantResult( + groupName, + affectedForegroundPermissions, + GRANTED_FOREGROUND_ONLY + ) } ALLOW_ALWAYS_BUTTON -> { view.performAccessibilityAction( - AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS, null) - resultListener.onPermissionGrantResult(groupName, affectedForegroundPermissions, - GRANTED_ALWAYS) + AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS, + null + ) + resultListener.onPermissionGrantResult( + groupName, + affectedForegroundPermissions, + GRANTED_ALWAYS + ) } ALLOW_ONE_TIME_BUTTON -> { view.performAccessibilityAction( - AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS, null) - resultListener.onPermissionGrantResult(groupName, affectedForegroundPermissions, - GRANTED_ONE_TIME) + AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS, + null + ) + resultListener.onPermissionGrantResult( + groupName, + affectedForegroundPermissions, + GRANTED_ONE_TIME + ) } ALLOW_SELECTED_BUTTON -> { view.performAccessibilityAction( - AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS, null) - resultListener.onPermissionGrantResult(groupName, affectedForegroundPermissions, - GRANTED_USER_SELECTED) + AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS, + null + ) + resultListener.onPermissionGrantResult( + groupName, + affectedForegroundPermissions, + GRANTED_USER_SELECTED + ) } DONT_ALLOW_MORE_SELECTED_BUTTON -> { - resultListener.onPermissionGrantResult(groupName, affectedForegroundPermissions, - DENIED_MORE) + resultListener.onPermissionGrantResult( + groupName, + affectedForegroundPermissions, + DENIED_MORE + ) } - DENY_BUTTON, NO_UPGRADE_BUTTON, NO_UPGRADE_OT_BUTTON -> { + DENY_BUTTON, + NO_UPGRADE_BUTTON, + NO_UPGRADE_OT_BUTTON -> { view.performAccessibilityAction( - AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS, null) - resultListener.onPermissionGrantResult(groupName, affectedForegroundPermissions, - DENIED) + AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS, + null + ) + resultListener.onPermissionGrantResult( + groupName, + affectedForegroundPermissions, + DENIED + ) } - DENY_AND_DONT_ASK_AGAIN_BUTTON, NO_UPGRADE_AND_DONT_ASK_AGAIN_BUTTON, + DENY_AND_DONT_ASK_AGAIN_BUTTON, + NO_UPGRADE_AND_DONT_ASK_AGAIN_BUTTON, NO_UPGRADE_OT_AND_DONT_ASK_AGAIN_BUTTON -> { view.performAccessibilityAction( - AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS, null) - resultListener.onPermissionGrantResult(groupName, affectedForegroundPermissions, - DENIED_DO_NOT_ASK_AGAIN) + AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS, + null + ) + resultListener.onPermissionGrantResult( + groupName, + affectedForegroundPermissions, + DENIED_DO_NOT_ASK_AGAIN + ) } } } @@ -567,39 +648,57 @@ class GrantPermissionsViewHandlerImpl( init { BUTTON_RES_ID_TO_NUM.put(R.id.permission_allow_button, ALLOW_BUTTON) - BUTTON_RES_ID_TO_NUM.put(R.id.permission_allow_foreground_only_button, - ALLOW_FOREGROUND_BUTTON) + BUTTON_RES_ID_TO_NUM.put( + R.id.permission_allow_foreground_only_button, + ALLOW_FOREGROUND_BUTTON + ) BUTTON_RES_ID_TO_NUM.put(R.id.permission_deny_button, DENY_BUTTON) - BUTTON_RES_ID_TO_NUM.put(R.id.permission_deny_and_dont_ask_again_button, - DENY_AND_DONT_ASK_AGAIN_BUTTON) - BUTTON_RES_ID_TO_NUM.put(R.id.permission_allow_one_time_button, - ALLOW_ONE_TIME_BUTTON) - BUTTON_RES_ID_TO_NUM.put(R.id.permission_no_upgrade_button, - NO_UPGRADE_BUTTON) - BUTTON_RES_ID_TO_NUM.put(R.id.permission_no_upgrade_and_dont_ask_again_button, - NO_UPGRADE_AND_DONT_ASK_AGAIN_BUTTON) - BUTTON_RES_ID_TO_NUM.put(R.id.permission_no_upgrade_one_time_button, - NO_UPGRADE_OT_BUTTON) - BUTTON_RES_ID_TO_NUM.put(R.id.permission_no_upgrade_one_time_and_dont_ask_again_button, - NO_UPGRADE_OT_AND_DONT_ASK_AGAIN_BUTTON) - BUTTON_RES_ID_TO_NUM.put(R.id.permission_allow_all_button, - ALLOW_ALL_BUTTON) - BUTTON_RES_ID_TO_NUM.put(R.id.permission_allow_selected_button, - ALLOW_SELECTED_BUTTON) - BUTTON_RES_ID_TO_NUM.put(R.id.permission_dont_allow_more_selected_button, - DONT_ALLOW_MORE_SELECTED_BUTTON) + BUTTON_RES_ID_TO_NUM.put( + R.id.permission_deny_and_dont_ask_again_button, + DENY_AND_DONT_ASK_AGAIN_BUTTON + ) + BUTTON_RES_ID_TO_NUM.put(R.id.permission_allow_one_time_button, ALLOW_ONE_TIME_BUTTON) + BUTTON_RES_ID_TO_NUM.put(R.id.permission_no_upgrade_button, NO_UPGRADE_BUTTON) + BUTTON_RES_ID_TO_NUM.put( + R.id.permission_no_upgrade_and_dont_ask_again_button, + NO_UPGRADE_AND_DONT_ASK_AGAIN_BUTTON + ) + BUTTON_RES_ID_TO_NUM.put( + R.id.permission_no_upgrade_one_time_button, + NO_UPGRADE_OT_BUTTON + ) + BUTTON_RES_ID_TO_NUM.put( + R.id.permission_no_upgrade_one_time_and_dont_ask_again_button, + NO_UPGRADE_OT_AND_DONT_ASK_AGAIN_BUTTON + ) + BUTTON_RES_ID_TO_NUM.put(R.id.permission_allow_all_button, ALLOW_ALL_BUTTON) + BUTTON_RES_ID_TO_NUM.put(R.id.permission_allow_selected_button, ALLOW_SELECTED_BUTTON) + BUTTON_RES_ID_TO_NUM.put( + R.id.permission_dont_allow_more_selected_button, + DONT_ALLOW_MORE_SELECTED_BUTTON + ) LOCATION_RES_ID_TO_NUM.put(R.id.permission_location_accuracy, LOCATION_ACCURACY_LAYOUT) - LOCATION_RES_ID_TO_NUM.put(R.id.permission_location_accuracy_radio_fine, - FINE_RADIO_BUTTON) - LOCATION_RES_ID_TO_NUM.put(R.id.permission_location_accuracy_radio_coarse, - COARSE_RADIO_BUTTON) - LOCATION_RES_ID_TO_NUM.put(R.id.permission_location_accuracy_radio_group, - DIALOG_WITH_BOTH_LOCATIONS) - LOCATION_RES_ID_TO_NUM.put(R.id.permission_location_accuracy_fine_only, - DIALOG_WITH_FINE_LOCATION_ONLY) - LOCATION_RES_ID_TO_NUM.put(R.id.permission_location_accuracy_coarse_only, - DIALOG_WITH_COARSE_LOCATION_ONLY) + LOCATION_RES_ID_TO_NUM.put( + R.id.permission_location_accuracy_radio_fine, + FINE_RADIO_BUTTON + ) + LOCATION_RES_ID_TO_NUM.put( + R.id.permission_location_accuracy_radio_coarse, + COARSE_RADIO_BUTTON + ) + LOCATION_RES_ID_TO_NUM.put( + R.id.permission_location_accuracy_radio_group, + DIALOG_WITH_BOTH_LOCATIONS + ) + LOCATION_RES_ID_TO_NUM.put( + R.id.permission_location_accuracy_fine_only, + DIALOG_WITH_FINE_LOCATION_ONLY + ) + LOCATION_RES_ID_TO_NUM.put( + R.id.permission_location_accuracy_coarse_only, + DIALOG_WITH_COARSE_LOCATION_ONLY + ) } } } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/HandheldUnusedAppsFragment.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/HandheldUnusedAppsFragment.kt index e2fdfc86e..37ac50bb8 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/HandheldUnusedAppsFragment.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/HandheldUnusedAppsFragment.kt @@ -28,14 +28,12 @@ import com.android.permissioncontroller.hibernation.isHibernationEnabled import com.android.permissioncontroller.permission.ui.UnusedAppsFragment import com.android.permissioncontroller.permission.ui.UnusedAppsFragment.Companion.INFO_MSG_CATEGORY -/** - * Handheld wrapper, with customizations, around [UnusedAppsFragment]. - */ -class HandheldUnusedAppsFragment : PermissionsFrameFragment(), - UnusedAppsFragment.Parent<UnusedAppPreference> { +/** Handheld wrapper, with customizations, around [UnusedAppsFragment]. */ +class HandheldUnusedAppsFragment : + PermissionsFrameFragment(), UnusedAppsFragment.Parent<UnusedAppPreference> { companion object { - /** Create a new instance of this fragment. */ + /** Create a new instance of this fragment. */ @JvmStatic fun newInstance(): HandheldUnusedAppsFragment { return HandheldUnusedAppsFragment() @@ -55,15 +53,12 @@ class HandheldUnusedAppsFragment : PermissionsFrameFragment(), override fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) if (savedInstanceState == null) { - val fragment: - UnusedAppsFragment<HandheldUnusedAppsFragment, UnusedAppPreference> = + val fragment: UnusedAppsFragment<HandheldUnusedAppsFragment, UnusedAppPreference> = UnusedAppsFragment.newInstance() fragment.arguments = arguments // child fragment does not have its own UI - it will add to the preferences of this // parent fragment - childFragmentManager.beginTransaction() - .add(fragment, null) - .commit() + childFragmentManager.beginTransaction().add(fragment, null).commit() } } @@ -113,7 +108,7 @@ class HandheldUnusedAppsFragment : PermissionsFrameFragment(), override fun setEmptyState(empty: Boolean) { val infoMsgCategory = - preferenceScreen.findPreference<PreferenceCategory>(INFO_MSG_CATEGORY)!! + preferenceScreen.findPreference<PreferenceCategory>(INFO_MSG_CATEGORY)!! infoMsgCategory.isVisible = !empty } -}
\ No newline at end of file +} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/HandheldUnusedAppsWrapperFragment.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/HandheldUnusedAppsWrapperFragment.kt index 44a9f3d08..7565e6d17 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/HandheldUnusedAppsWrapperFragment.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/HandheldUnusedAppsWrapperFragment.kt @@ -24,12 +24,10 @@ import android.view.ViewGroup import com.android.permissioncontroller.R import com.android.settingslib.collapsingtoolbar.CollapsingToolbarBaseFragment -/** - * Wrapper over HandheldUnusedAppsFragment - */ +/** Wrapper over HandheldUnusedAppsFragment */ class HandheldUnusedAppsWrapperFragment : CollapsingToolbarBaseFragment() { companion object { - /** Create a new instance of this fragment. */ + /** Create a new instance of this fragment. */ @JvmStatic fun newInstance(): HandheldUnusedAppsWrapperFragment { return HandheldUnusedAppsWrapperFragment() @@ -48,15 +46,16 @@ class HandheldUnusedAppsWrapperFragment : CollapsingToolbarBaseFragment() { override fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) - var preferenceFragment = childFragmentManager - .findFragmentById(R.id.preference_fragment_container) + var preferenceFragment = + childFragmentManager.findFragmentById(R.id.preference_fragment_container) as HandheldUnusedAppsFragment? if (preferenceFragment == null) { preferenceFragment = HandheldUnusedAppsFragment.newInstance() preferenceFragment.arguments = arguments - childFragmentManager.beginTransaction() - .add(R.id.preference_fragment_container, preferenceFragment) - .commit() + childFragmentManager + .beginTransaction() + .add(R.id.preference_fragment_container, preferenceFragment) + .commit() } } } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionAppsFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionAppsFragment.java index 220507426..6bcf926d3 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionAppsFragment.java +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionAppsFragment.java @@ -21,6 +21,7 @@ import static com.android.permissioncontroller.permission.ui.Category.ALLOWED; import static com.android.permissioncontroller.permission.ui.Category.ALLOWED_FOREGROUND; import static com.android.permissioncontroller.permission.ui.Category.ASK; import static com.android.permissioncontroller.permission.ui.Category.DENIED; +import static com.android.permissioncontroller.permission.ui.Category.STORAGE_FOOTER; import static com.android.permissioncontroller.permission.ui.handheld.UtilsKt.pressBack; import android.Manifest; @@ -62,15 +63,15 @@ import com.android.settingslib.HelpUtils; import com.android.settingslib.utils.applications.AppUtils; import com.android.settingslib.widget.FooterPreference; +import kotlin.Pair; +import kotlin.Triple; + import java.text.Collator; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Random; -import kotlin.Pair; -import kotlin.Triple; - /** * Show and manage apps which request a single permission group. * @@ -87,7 +88,6 @@ public final class PermissionAppsFragment extends SettingsWithLargeHeader implem private static final String STORAGE_ALLOWED_FULL = "allowed_storage_full"; private static final String STORAGE_ALLOWED_SCOPED = "allowed_storage_scoped"; private static final String BLOCKED_SENSOR_PREF_KEY = "sensor_card"; - private static final String STORAGE_FOOTER_CATEGORY_KEY = "storage_footer_category"; private static final String STORAGE_FOOTER_PREFERENCE_KEY = "storage_footer_preference"; private static final int SHOW_LOAD_DELAY_MS = 200; @@ -303,7 +303,7 @@ public final class PermissionAppsFragment extends SettingsWithLargeHeader implem private void addStorageFooterSeeAllFilesAccess() { PreferenceScreen screen = getPreferenceScreen(); Context context = screen.getPreferenceManager().getContext(); - PreferenceCategory preferenceCategory = findPreference(STORAGE_FOOTER_CATEGORY_KEY); + PreferenceCategory preferenceCategory = findPreference(STORAGE_FOOTER.getCategoryName()); Preference existingPreference = findPreference(STORAGE_FOOTER_PREFERENCE_KEY); if (preferenceCategory == null || existingPreference != null) { @@ -502,6 +502,13 @@ public final class PermissionAppsFragment extends SettingsWithLargeHeader implem if (SdkLevel.isAtLeastT() && Manifest.permission_group.STORAGE.equals(mPermGroupName)) { addStorageFooterSeeAllFilesAccess(); + } else { + // Hide storage footer category + PreferenceCategory storageFooterPreferenceCategory = + findPreference(STORAGE_FOOTER.getCategoryName()); + if (storageFooterPreferenceCategory != null) { + storageFooterPreferenceCategory.setVisible(false); + } } mViewModel.setCreationLogged(true); diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/SmartIconLoadPackagePermissionPreference.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/SmartIconLoadPackagePermissionPreference.kt index 27cbd8c15..f7178bdbb 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/SmartIconLoadPackagePermissionPreference.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/SmartIconLoadPackagePermissionPreference.kt @@ -30,16 +30,17 @@ import com.android.permissioncontroller.R import com.android.permissioncontroller.permission.utils.KotlinUtils /** - * A Preference representing a package for a user, which loads and displays its icon only upon - * being bound to a viewHolder. This lets us synchronously load package icons and labels, while - * still displaying the PermissionAppsFragment instantly. + * A Preference representing a package for a user, which loads and displays its icon only upon being + * bound to a viewHolder. This lets us synchronously load package icons and labels, while still + * displaying the PermissionAppsFragment instantly. * * @param app The current application * @param packageName The name of the package whose icon this preference will retrieve * @param user The user whose package icon will be retrieved * @param context The current context */ -open class SmartIconLoadPackagePermissionPreference constructor( +open class SmartIconLoadPackagePermissionPreference +constructor( private val app: Application, private val packageName: String, private val user: UserHandle, diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/UnusedAppPreference.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/UnusedAppPreference.kt index dfab55ed7..02eb6c090 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/UnusedAppPreference.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/UnusedAppPreference.kt @@ -50,9 +50,7 @@ class UnusedAppPreference( super.onBindViewHolder(holder) val removeButton = holder.findViewById(R.id.uninstall_button) as ImageButton - removeButton.setOnClickListener { - removeRunnable?.run() - } + removeButton.setOnClickListener { removeRunnable?.run() } removeButton.isEnabled = removeButtonEnabled } @@ -63,4 +61,4 @@ class UnusedAppPreference( override fun setRemoveComponentEnabled(enabled: Boolean) { removeButtonEnabled = enabled } -}
\ No newline at end of file +} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/Utils.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/Utils.kt index e2acb498c..f6a387e9d 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/Utils.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/Utils.kt @@ -19,12 +19,10 @@ package com.android.permissioncontroller.permission.ui.handheld import androidx.fragment.app.Fragment import androidx.navigation.fragment.findNavController -/** - * Press back and close the activity if this is the last fragment. - */ +/** Press back and close the activity if this is the last fragment. */ fun Fragment.pressBack() { val wasBackExecuted = findNavController().popBackStack() if (!wasBackExecuted) { activity?.let { it.finishAfterTransition() } } -}
\ No newline at end of file +} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v31/DashboardUtils.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v31/DashboardUtils.kt index 5b92dd36d..b36d5174c 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v31/DashboardUtils.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v31/DashboardUtils.kt @@ -49,7 +49,6 @@ fun shouldShowSubattributionInPermissionsDashboard(): Boolean { * * @param context the context. * @param lastAccessTime the time in milliseconds. - * * @return a string representing the time or date of the given time or null if the time is 0. */ fun getAbsoluteTimeString(context: Context, lastAccessTime: Long): String? { @@ -64,14 +63,13 @@ fun getAbsoluteTimeString(context: Context, lastAccessTime: Long): String? { } /** - * Build a string representing the time of the most recent permission usage if it happened on - * the current day and the date otherwise. + * Build a string representing the time of the most recent permission usage if it happened on the + * current day and the date otherwise. * * @param context the context. * @param groupUsage the permission usage. - * - * @return a string representing the time or date of the most recent usage or null if there are - * no usages. + * @return a string representing the time or date of the most recent usage or null if there are no + * usages. */ @RequiresApi(Build.VERSION_CODES.S) fun getAbsoluteLastUsageString(context: Context, groupUsage: GroupUsage?): String? { @@ -83,8 +81,7 @@ fun getAbsoluteLastUsageString(context: Context, groupUsage: GroupUsage?): Strin /** * Build a string representing the duration of a permission usage. * - * @return a string representing the duration of this app's usage or null if there are no - * usages. + * @return a string representing the duration of this app's usage or null if there are no usages. */ @RequiresApi(Build.VERSION_CODES.S) fun getUsageDurationString(context: Context, groupUsage: GroupUsage?): String? { @@ -94,9 +91,9 @@ fun getUsageDurationString(context: Context, groupUsage: GroupUsage?): String? { } /** - * Build a string representing the number of milliseconds passed in. It rounds to the nearest - * unit. For example, given a duration of 3500 and an English locale, this can return - * "3 seconds". + * Build a string representing the number of milliseconds passed in. It rounds to the nearest unit. + * For example, given a duration of 3500 and an English locale, this can return "3 seconds". + * * @param context The context. * @param duration The number of milliseconds. * @return a string representing the given number of milliseconds. @@ -104,37 +101,63 @@ fun getUsageDurationString(context: Context, groupUsage: GroupUsage?): String? { fun getTimeDiffStr(context: Context, duration: Long): String { val timeDiffAndUnit = calculateTimeDiffAndUnit(duration) return when (timeDiffAndUnit.second) { - SECONDS -> StringUtils.getIcuPluralsString(context, - R.string.seconds, timeDiffAndUnit.first.toInt()) - MINUTES -> StringUtils.getIcuPluralsString(context, - R.string.minutes, timeDiffAndUnit.first.toInt()) - HOURS -> StringUtils.getIcuPluralsString(context, - R.string.hours, timeDiffAndUnit.first.toInt()) - else -> StringUtils.getIcuPluralsString(context, - R.string.days, timeDiffAndUnit.first.toInt()) + SECONDS -> + StringUtils.getIcuPluralsString( + context, + R.string.seconds, + timeDiffAndUnit.first.toInt() + ) + MINUTES -> + StringUtils.getIcuPluralsString( + context, + R.string.minutes, + timeDiffAndUnit.first.toInt() + ) + HOURS -> + StringUtils.getIcuPluralsString(context, R.string.hours, timeDiffAndUnit.first.toInt()) + else -> + StringUtils.getIcuPluralsString(context, R.string.days, timeDiffAndUnit.first.toInt()) } } /** * Build a string representing the duration used of milliseconds passed in. + * * @return a string representing the duration used in the nearest unit. ex: Used for 3 mins */ fun getDurationUsedStr(context: Context, duration: Long): String { val timeDiffAndUnit = calculateTimeDiffAndUnit(duration) return when (timeDiffAndUnit.second) { - SECONDS -> StringUtils.getIcuPluralsString(context, - R.string.duration_used_seconds, timeDiffAndUnit.first.toInt()) - MINUTES -> StringUtils.getIcuPluralsString(context, - R.string.duration_used_minutes, timeDiffAndUnit.first.toInt()) - HOURS -> StringUtils.getIcuPluralsString(context, - R.string.duration_used_hours, timeDiffAndUnit.first.toInt()) - else -> StringUtils.getIcuPluralsString(context, - R.string.duration_used_days, timeDiffAndUnit.first.toInt()) + SECONDS -> + StringUtils.getIcuPluralsString( + context, + R.string.duration_used_seconds, + timeDiffAndUnit.first.toInt() + ) + MINUTES -> + StringUtils.getIcuPluralsString( + context, + R.string.duration_used_minutes, + timeDiffAndUnit.first.toInt() + ) + HOURS -> + StringUtils.getIcuPluralsString( + context, + R.string.duration_used_hours, + timeDiffAndUnit.first.toInt() + ) + else -> + StringUtils.getIcuPluralsString( + context, + R.string.duration_used_days, + timeDiffAndUnit.first.toInt() + ) } } /** * Given the duration in milliseconds, calculate the time of that duration in the nearest unit. + * * @return a Pair of the <duration in the nearest unit, the nearest unit> */ fun calculateTimeDiffAndUnit(duration: Long): Pair<Long, Int> { @@ -159,7 +182,6 @@ fun calculateTimeDiffAndUnit(duration: Long): Pair<Long, Int> { * Check whether the given time (in milliseconds) is in the current day. * * @param time the time in milliseconds - * * @return whether the given time is in the current day. */ private fun isToday(time: Long): Boolean { diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v31/PermissionHistoryPreference.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v31/PermissionHistoryPreference.java index bcca75fcc..5c20ef9df 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v31/PermissionHistoryPreference.java +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v31/PermissionHistoryPreference.java @@ -77,7 +77,7 @@ public class PermissionHistoryPreference extends Preference { public PermissionHistoryPreference(@NonNull Context context, @NonNull UserHandle userHandle, @NonNull String pkgName, - @NonNull Drawable appIcon, + @Nullable Drawable appIcon, @NonNull String preferenceTitle, @NonNull String permissionGroup, @NonNull long accessStartTime, diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v31/PermissionUsageDetailsFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v31/PermissionUsageDetailsFragment.java index 15ee31a54..5d4343639 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v31/PermissionUsageDetailsFragment.java +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v31/PermissionUsageDetailsFragment.java @@ -368,15 +368,9 @@ public class PermissionUsageDetailsFragment extends SettingsWithLargeHeader { new PermissionHistoryPreference( getContext(), appPermissionAccessUiInfo.getUserHandle(), - appPermissionAccessUiInfo.getPkgName(), - KotlinUtils.INSTANCE.getBadgedPackageIcon( - mViewModel.getApplication(), - appPermissionAccessUiInfo.getPkgName(), - appPermissionAccessUiInfo.getUserHandle()), - KotlinUtils.INSTANCE.getPackageLabel( - mViewModel.getApplication(), - appPermissionAccessUiInfo.getPkgName(), - appPermissionAccessUiInfo.getUserHandle()), + appPermissionAccessUiInfo.getPackageName(), + appPermissionAccessUiInfo.getBadgedPackageIcon(), + appPermissionAccessUiInfo.getPackageLabel(), appPermissionAccessUiInfo.getPermissionGroup(), appPermissionAccessUiInfo.getAccessStartTime(), appPermissionAccessUiInfo.getAccessEndTime(), diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v34/AppDataSharingUpdatesFooterPreference.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v34/AppDataSharingUpdatesFooterPreference.kt index 88b5ebe87..e74022291 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v34/AppDataSharingUpdatesFooterPreference.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v34/AppDataSharingUpdatesFooterPreference.kt @@ -76,7 +76,8 @@ class AppDataSharingUpdatesFooterPreference : Preference { }, 0, footerLink.length, - 0) + 0 + ) footerLinkView?.let { it.visibility = if (onFooterLinkClick == null) View.GONE else View.VISIBLE it.text = footerLinkText diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v34/AppDataSharingUpdatesFragment.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v34/AppDataSharingUpdatesFragment.kt index 2db9bc4b4..b58de90a5 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v34/AppDataSharingUpdatesFragment.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v34/AppDataSharingUpdatesFragment.kt @@ -98,7 +98,8 @@ class AppDataSharingUpdatesFragment : PermissionsFrameFragment() { val updatesCategory = preferenceScreen.findPreference<PreferenceCategory>( LAST_PERIOD_UPDATES_PREFERENCE_CATEGORY_ID - ) ?: return + ) + ?: return val preferencesToRemove = mutableSetOf<Preference>() for (i in 0 until (updatesCategory.preferenceCount)) { @@ -192,11 +193,12 @@ class AppDataSharingUpdatesFragment : PermissionsFrameFragment() { it.isVisible = true } - val onFooterLinkClick = if (viewModel.canLinkToHelpCenter(requireActivity())) { - View.OnClickListener { viewModel.openSafetyLabelsHelpCenterPage(requireActivity()) } - } else { - null - } + val onFooterLinkClick = + if (viewModel.canLinkToHelpCenter(requireActivity())) { + View.OnClickListener { viewModel.openSafetyLabelsHelpCenterPage(requireActivity()) } + } else { + null + } footerPreference?.let { it.footerMessage = getString(R.string.data_sharing_updates_footer_message) it.footerLink = getString(R.string.learn_about_data_sharing) @@ -231,11 +233,14 @@ class AppDataSharingUpdatesFragment : PermissionsFrameFragment() { footerPreference?.let { it.footerMessage = getString(R.string.data_sharing_updates_footer_message) it.footerLink = getString(R.string.learn_about_data_sharing) - it.onFooterLinkClick = if (viewModel.canLinkToHelpCenter(requireActivity())) { - View.OnClickListener { viewModel.openSafetyLabelsHelpCenterPage(requireActivity()) } - } else { - null - } + it.onFooterLinkClick = + if (viewModel.canLinkToHelpCenter(requireActivity())) { + View.OnClickListener { + viewModel.openSafetyLabelsHelpCenterPage(requireActivity()) + } + } else { + null + } it.isVisible = true } } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v34/PermissionRationaleViewHandlerImpl.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v34/PermissionRationaleViewHandlerImpl.kt index 3998ca141..93e6b6336 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v34/PermissionRationaleViewHandlerImpl.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v34/PermissionRationaleViewHandlerImpl.kt @@ -122,14 +122,14 @@ class PermissionRationaleViewHandlerImpl( // Grow or shrink the content container to size of new content val growShrinkToNewContentSize = ChangeBounds() growShrinkToNewContentSize.duration = ANIMATION_DURATION_MILLIS - growShrinkToNewContentSize.interpolator = AnimationUtils.loadInterpolator(mActivity, - android.R.interpolator.fast_out_slow_in) + growShrinkToNewContentSize.interpolator = + AnimationUtils.loadInterpolator(mActivity, android.R.interpolator.fast_out_slow_in) TransitionManager.beginDelayedTransition(rootView, growShrinkToNewContentSize) } override fun createView(): View { - val rootView = LayoutInflater.from(mActivity) - .inflate(R.layout.permission_rationale, null) as ViewGroup + val rootView = + LayoutInflater.from(mActivity).inflate(R.layout.permission_rationale, null) as ViewGroup // Uses the vertical gravity of the PermissionGrantSingleton style to position the window val gravity = @@ -160,13 +160,15 @@ class PermissionRationaleViewHandlerImpl( val settingsSectionView: ViewGroup? = rootView.findViewById(R.id.settings_section) settingsSectionView?.visibility = View.GONE } - backButton = rootView.findViewById<Button>(R.id.back_button)!!.apply { - setOnClickListener(this@PermissionRationaleViewHandlerImpl) - - // Load the text color from the activity theme rather than the Material Design theme - val textColor = getColorStateListForAttr(mActivity, android.R.attr.textColorPrimary)!! - setTextColor(textColor) - } + backButton = + rootView.findViewById<Button>(R.id.back_button)!!.apply { + setOnClickListener(this@PermissionRationaleViewHandlerImpl) + + // Load the text color from the activity theme rather than the Material Design theme + val textColor = + getColorStateListForAttr(mActivity, android.R.attr.textColorPrimary)!! + setTextColor(textColor) + } this.rootView = rootView diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/legacy/PermissionUsageDetailsViewModelLegacy.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/legacy/PermissionUsageDetailsViewModelLegacy.kt index e219153f3..14aff67ba 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/legacy/PermissionUsageDetailsViewModelLegacy.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/legacy/PermissionUsageDetailsViewModelLegacy.kt @@ -44,8 +44,8 @@ import com.android.permissioncontroller.permission.utils.KotlinUtils import com.android.permissioncontroller.permission.utils.KotlinUtils.getPackageLabel import com.android.permissioncontroller.permission.utils.PermissionMapping import com.android.permissioncontroller.permission.utils.StringUtils -import com.android.permissioncontroller.permission.utils.v31.SubattributionUtils import com.android.permissioncontroller.permission.utils.Utils +import com.android.permissioncontroller.permission.utils.v31.SubattributionUtils import java.time.Instant import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit.DAYS @@ -93,7 +93,8 @@ class PermissionUsageDetailsViewModelLegacy( /* getUiInfo= */ false, /* getNonPlatformPermissions= */ false, /* callback= */ callback, - /* sync= */ false) + /* sync= */ false + ) } /** @@ -116,19 +117,26 @@ class PermissionUsageDetailsViewModelLegacy( } val startTime = (System.currentTimeMillis() - showPermissionUsagesDuration).coerceAtLeast( - Instant.EPOCH.toEpochMilli()) + Instant.EPOCH.toEpochMilli() + ) val appPermissionTimelineUsages: List<AppPermissionTimelineUsage> = extractAppPermissionTimelineUsagesForGroup(appPermissionUsages, permissionGroup) val shouldDisplayShowSystemToggle = shouldDisplayShowSystemToggle(appPermissionTimelineUsages) val permissionApps: List<PermissionApp> = getPermissionAppsWithRecentDiscreteUsage( - appPermissionTimelineUsages, showSystem, startTime) + appPermissionTimelineUsages, + showSystem, + startTime + ) val appPermissionUsageEntries = buildDiscreteAccessClusterData(appPermissionTimelineUsages, showSystem, startTime) return PermissionUsageDetailsUiData( - permissionApps, shouldDisplayShowSystemToggle, appPermissionUsageEntries) + permissionApps, + shouldDisplayShowSystemToggle, + appPermissionUsageEntries + ) } private fun getHistoryPreferenceData( @@ -141,14 +149,14 @@ class PermissionUsageDetailsViewModelLegacy( getDurationSummary(discreteAccessClusterData, accessTimeList, context) val proxyLabel = getProxyPackageLabel(discreteAccessClusterData) val subattributionLabel = getSubattributionLabel(discreteAccessClusterData) - val showingSubattribution = - subattributionLabel != null && subattributionLabel.isNotEmpty() + val showingSubattribution = subattributionLabel != null && subattributionLabel.isNotEmpty() val summary = buildUsageSummary(durationSummaryLabel, proxyLabel, subattributionLabel, context) return HistoryPreferenceData( UserHandle.getUserHandleForUid( - discreteAccessClusterData.appPermissionTimelineUsage.permissionApp.uid), + discreteAccessClusterData.appPermissionTimelineUsage.permissionApp.uid + ), discreteAccessClusterData.appPermissionTimelineUsage.permissionApp.packageName, discreteAccessClusterData.appPermissionTimelineUsage.permissionApp.icon, discreteAccessClusterData.appPermissionTimelineUsage.permissionApp.label, @@ -158,7 +166,8 @@ class PermissionUsageDetailsViewModelLegacy( summary, showingSubattribution, discreteAccessClusterData.appPermissionTimelineUsage.attributionTags, - sessionId) + sessionId + ) } /** @@ -180,7 +189,8 @@ class PermissionUsageDetailsViewModelLegacy( .map { appPermissionUsage -> getAppPermissionTimelineUsages( appPermissionUsage.app, - appPermissionUsage.groupUsages.firstOrNull { it.group.name == group }) + appPermissionUsage.groupUsages.firstOrNull { it.group.name == group } + ) } .flatten() @@ -221,7 +231,10 @@ class PermissionUsageDetailsViewModelLegacy( .map { appPermissionTimelineUsages -> val accessDataList = extractRecentDiscreteAccessData( - appPermissionTimelineUsages.timelineUsage, showSystem, startTime) + appPermissionTimelineUsages.timelineUsage, + showSystem, + startTime + ) if (accessDataList.size <= 1) { return@map accessDataList.map { @@ -235,7 +248,9 @@ class PermissionUsageDetailsViewModelLegacy( .sortedWith( compareBy( { -it.discreteAccessDataList.first().accessTimeMs }, - { it.appPermissionTimelineUsage.permissionApp.label })) + { it.appPermissionTimelineUsage.permissionApp.label } + ) + ) .toList() /** @@ -253,11 +268,15 @@ class PermissionUsageDetailsViewModelLegacy( for (discreteAccessData in discreteAccessDataList) { if (currentDiscreteAccessDataList.isEmpty()) { currentDiscreteAccessDataList.add(discreteAccessData) - } else if (!canAccessBeAddedToCluster( - discreteAccessData, currentDiscreteAccessDataList)) { + } else if ( + !canAccessBeAddedToCluster(discreteAccessData, currentDiscreteAccessDataList) + ) { clusterDataList.add( DiscreteAccessClusterData( - appPermissionTimelineUsage, currentDiscreteAccessDataList.toMutableList())) + appPermissionTimelineUsage, + currentDiscreteAccessDataList.toMutableList() + ) + ) currentDiscreteAccessDataList.clear() currentDiscreteAccessDataList.add(discreteAccessData) } else { @@ -266,8 +285,8 @@ class PermissionUsageDetailsViewModelLegacy( } if (currentDiscreteAccessDataList.isNotEmpty()) { clusterDataList.add( - DiscreteAccessClusterData( - appPermissionTimelineUsage, currentDiscreteAccessDataList)) + DiscreteAccessClusterData(appPermissionTimelineUsage, currentDiscreteAccessDataList) + ) } return clusterDataList } @@ -282,8 +301,9 @@ class PermissionUsageDetailsViewModelLegacy( showSystem: Boolean, startTime: Long ): List<DiscreteAccessData> { - return if (timelineUsages.hasDiscreteData() && - (showSystem || !timelineUsages.group.isSystem())) { + return if ( + timelineUsages.hasDiscreteData() && (showSystem || !timelineUsages.group.isSystem()) + ) { getRecentDiscreteAccessData(timelineUsages, startTime) .sortedWith(compareBy { -it.accessTimeMs }) .toList() @@ -377,7 +397,8 @@ class PermissionUsageDetailsViewModelLegacy( getPackageLabel( PermissionControllerApplication.get(), it.proxy!!.packageName!!, - UserHandle.getUserHandleForUid(it.proxy.uid)) + UserHandle.getUserHandleForUid(it.proxy.uid) + ) } /** Returns the attribution label for the permission access, if any. */ @@ -406,10 +427,14 @@ class PermissionUsageDetailsViewModelLegacy( R.string.history_preference_subtext_3, subTextStrings[0], subTextStrings[1], - subTextStrings[2]) + subTextStrings[2] + ) 2 -> context.getString( - R.string.history_preference_subtext_2, subTextStrings[0], subTextStrings[1]) + R.string.history_preference_subtext_2, + subTextStrings[0], + subTextStrings[1] + ) 1 -> subTextStrings[0] else -> null } @@ -434,7 +459,8 @@ class PermissionUsageDetailsViewModelLegacy( } return listOf( - AppPermissionTimelineUsage(permissionGroup, app, groupUsage, Resources.ID_NULL)) + AppPermissionTimelineUsage(permissionGroup, app, groupUsage, Resources.ID_NULL) + ) } /** Extracts to a set all the permission groups declared by the platform. */ @@ -448,15 +474,20 @@ class PermissionUsageDetailsViewModelLegacy( /** Initialize all relevant [TimeFilterItemMs] values. */ private fun initializeTimeFilterItems(context: Context) { mTimeFilterItemMs.add( - TimeFilterItemMs(Long.MAX_VALUE, context.getString(R.string.permission_usage_any_time))) + TimeFilterItemMs(Long.MAX_VALUE, context.getString(R.string.permission_usage_any_time)) + ) mTimeFilterItemMs.add( TimeFilterItemMs( DAYS.toMillis(7), - StringUtils.getIcuPluralsString(context, R.string.permission_usage_last_n_days, 7))) + StringUtils.getIcuPluralsString(context, R.string.permission_usage_last_n_days, 7) + ) + ) mTimeFilterItemMs.add( TimeFilterItemMs( DAYS.toMillis(1), - StringUtils.getIcuPluralsString(context, R.string.permission_usage_last_n_days, 1))) + StringUtils.getIcuPluralsString(context, R.string.permission_usage_last_n_days, 1) + ) + ) // TODO: theianchen add code for filtering by time here. } @@ -554,7 +585,11 @@ class PermissionUsageDetailsViewModelFactoryLegacy( override fun <T : ViewModel> create(modelClass: Class<T>): T { @Suppress("UNCHECKED_CAST") return PermissionUsageDetailsViewModelLegacy( - application, roleManager, filterGroup, sessionId) + application, + roleManager, + filterGroup, + sessionId + ) as T } } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/legacy/PermissionUsageViewModelLegacy.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/legacy/PermissionUsageViewModelLegacy.kt index d0e751f7d..3032dece5 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/legacy/PermissionUsageViewModelLegacy.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/legacy/PermissionUsageViewModelLegacy.kt @@ -57,7 +57,8 @@ class PermissionUsageViewModelLegacy(val roleManager: RoleManager) : ViewModel() mapOf( Manifest.permission_group.LOCATION to 0, Manifest.permission_group.CAMERA to 1, - Manifest.permission_group.MICROPHONE to 2) + Manifest.permission_group.MICROPHONE to 2 + ) private const val DEFAULT_ORDER = 3 } @@ -79,7 +80,8 @@ class PermissionUsageViewModelLegacy(val roleManager: RoleManager) : ViewModel() false /*getUiInfo*/, false /*getNonPlatformPermissions*/, callback /*callback*/, - false /*sync*/) + false /*sync*/ + ) } /** @@ -108,10 +110,16 @@ class PermissionUsageViewModelLegacy(val roleManager: RoleManager) : ViewModel() val permissionApps = filteredAppPermissionUsages.getRecentPermissionApps(startTime) val orderedPermissionGroupsWithUsage = filteredAppPermissionUsages.buildOrderedPermissionGroupsWithUsageCount( - context, startTime, showSystem) + context, + startTime, + showSystem + ) return PermissionUsagesUiData( - permissionApps, displayShowSystemToggle, orderedPermissionGroupsWithUsage) + permissionApps, + displayShowSystemToggle, + orderedPermissionGroupsWithUsage + ) } /** @@ -150,7 +158,9 @@ class PermissionUsageViewModelLegacy(val roleManager: RoleManager) : ViewModel() .sortedWith( compareBy( { PERMISSION_GROUP_ORDER.getOrDefault(it.permGroup, DEFAULT_ORDER) }, - { getPermGroupLabel(context, it.permGroup).toString() })) + { getPermGroupLabel(context, it.permGroup).toString() } + ) + ) } /** Extracts [PermissionApp] where there has been recent permission usage. */ @@ -163,7 +173,8 @@ class PermissionUsageViewModelLegacy(val roleManager: RoleManager) : ViewModel() .filter { !EXEMPTED_PERMISSION_GROUPS.contains(it.group.name) } .any { it.lastAccessTime >= startTime || it.lastAccessTime == 0L } } - .map { it.app }) + .map { it.app } + ) } /** diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/AllAppPermissionsViewModel.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/AllAppPermissionsViewModel.kt index 226208041..3e651dd9f 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/AllAppPermissionsViewModel.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/AllAppPermissionsViewModel.kt @@ -33,41 +33,34 @@ import com.android.permissioncontroller.permission.utils.Utils * * @param packageName The name of the package this viewModel is representing * @param user The user of the package this viewModel is representing - * @param filterGroup An optional single group that should be shown, no other groups will be - * shown + * @param filterGroup An optional single group that should be shown, no other groups will be shown */ -class AllAppPermissionsViewModel( - packageName: String, - user: UserHandle, - filterGroup: String? -) : ViewModel() { +class AllAppPermissionsViewModel(packageName: String, user: UserHandle, filterGroup: String?) : + ViewModel() { - val allPackagePermissionsLiveData = AllPackagePermissionsLiveData(packageName, user, - filterGroup) + val allPackagePermissionsLiveData = + AllPackagePermissionsLiveData(packageName, user, filterGroup) class AllPackagePermissionsLiveData( packageName: String, user: UserHandle, private val filterGroup: String? - ) : SmartUpdateMediatorLiveData<@kotlin.jvm.JvmSuppressWildcards - Map<String, List<String>>>() { + ) : SmartUpdateMediatorLiveData<@kotlin.jvm.JvmSuppressWildcards Map<String, List<String>>>() { - private val packagePermsLiveData = - PackagePermissionsLiveData[packageName, user] + private val packagePermsLiveData = PackagePermissionsLiveData[packageName, user] private val packageInfoLiveData = LightPackageInfoLiveData[packageName, user] init { - addSource(packagePermsLiveData) { - update() - } - addSource(packageInfoLiveData) { - update() - } + addSource(packagePermsLiveData) { update() } + addSource(packageInfoLiveData) { update() } } override fun onUpdate() { - if (!packagePermsLiveData.isInitialized || packagePermsLiveData.isStale || - !packageInfoLiveData.isInitialized) { + if ( + !packagePermsLiveData.isInitialized || + packagePermsLiveData.isStale || + !packageInfoLiveData.isInitialized + ) { return } val permissions = packagePermsLiveData.value @@ -77,12 +70,17 @@ class AllAppPermissionsViewModel( return } - value = permissions - .filter { filterGroup == null || it.key == filterGroup } - .filter { (it.key != Manifest.permission_group.STORAGE || - Utils.shouldShowStorage(packageInfo)) } - .filter { (!Utils.isHealthPermissionGroup(it.key) || - Utils.shouldShowHealthPermission(packageInfo, it.key))} + value = + permissions + .filter { filterGroup == null || it.key == filterGroup } + .filter { + (it.key != Manifest.permission_group.STORAGE || + Utils.shouldShowStorage(packageInfo)) + } + .filter { + (!Utils.isHealthPermissionGroup(it.key) || + Utils.shouldShowHealthPermission(packageInfo, it.key)) + } } } } @@ -93,8 +91,7 @@ class AllAppPermissionsViewModel( * @param app The current application * @param packageName The name of the package this viewModel is representing * @param user The user of the package this viewModel is representing - * @param filterGroup An optional single group that should be shown, no other groups will be - * shown + * @param filterGroup An optional single group that should be shown, no other groups will be shown */ class AllAppPermissionsViewModelFactory( private val packageName: String, diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/AppPermissionGroupsViewModel.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/AppPermissionGroupsViewModel.kt index 741c93aab..5ecab1527 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/AppPermissionGroupsViewModel.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/AppPermissionGroupsViewModel.kt @@ -95,135 +95,165 @@ class AppPermissionGroupsViewModel( val isSystem: Boolean = false, val subtitle: PermSubtitle ) { - constructor(groupName: String, isSystem: Boolean) : - this(groupName, isSystem, PermSubtitle.NONE) + constructor( + groupName: String, + isSystem: Boolean + ) : this(groupName, isSystem, PermSubtitle.NONE) } // Auto-revoke and hibernation share the same settings val autoRevokeLiveData = HibernationSettingStateLiveData[packageName, user] - private val packagePermsLiveData = - PackagePermissionsLiveData[packageName, user] + private val packagePermsLiveData = PackagePermissionsLiveData[packageName, user] private val appPermGroupUiInfoLiveDatas = mutableMapOf<String, AppPermGroupUiInfoLiveData>() private val fullStoragePermsLiveData = FullStoragePermissionAppsLiveData /** - * LiveData whose data is a map of grant category (either allowed or denied) to a list - * of permission group names that match the key, and two booleans representing if this is a - * system group, and a subtitle resource ID, if applicable. + * LiveData whose data is a map of grant category (either allowed or denied) to a list of + * permission group names that match the key, and two booleans representing if this is a system + * group, and a subtitle resource ID, if applicable. */ - val packagePermGroupsLiveData = object : SmartUpdateMediatorLiveData<@JvmSuppressWildcards - Map<Category, List<GroupUiInfo>>>() { - - init { - addSource(packagePermsLiveData) { - update() - } - addSource(fullStoragePermsLiveData) { - update() - } - addSource(autoRevokeLiveData) { - removeSource(autoRevokeLiveData) + val packagePermGroupsLiveData = + object : + SmartUpdateMediatorLiveData<@JvmSuppressWildcards Map<Category, List<GroupUiInfo>>>() { + + init { + addSource(packagePermsLiveData) { update() } + addSource(fullStoragePermsLiveData) { update() } + addSource(autoRevokeLiveData) { + removeSource(autoRevokeLiveData) + update() + } update() } - update() - } - override fun onUpdate() { - val groups = packagePermsLiveData.value?.keys?.filter { it != NON_RUNTIME_NORMAL_PERMS } - if (groups == null && packagePermsLiveData.isInitialized) { - value = null - return - } else if (groups == null || (Manifest.permission_group.STORAGE in groups && - !fullStoragePermsLiveData.isInitialized) || !autoRevokeLiveData.isInitialized) { - return - } - - val getLiveData = { groupName: String -> - AppPermGroupUiInfoLiveData[packageName, groupName, user] - } - setSourcesToDifference(groups, appPermGroupUiInfoLiveDatas, getLiveData) + override fun onUpdate() { + val groups = + packagePermsLiveData.value?.keys?.filter { it != NON_RUNTIME_NORMAL_PERMS } + if (groups == null && packagePermsLiveData.isInitialized) { + value = null + return + } else if ( + groups == null || + (Manifest.permission_group.STORAGE in groups && + !fullStoragePermsLiveData.isInitialized) || + !autoRevokeLiveData.isInitialized + ) { + return + } - if (!appPermGroupUiInfoLiveDatas.all { it.value.isInitialized }) { - return - } + val getLiveData = { groupName: String -> + AppPermGroupUiInfoLiveData[packageName, groupName, user] + } + setSourcesToDifference(groups, appPermGroupUiInfoLiveDatas, getLiveData) - val groupGrantStates = mutableMapOf<Category, - MutableList<GroupUiInfo>>() - groupGrantStates[Category.ALLOWED] = mutableListOf() - groupGrantStates[Category.ASK] = mutableListOf() - groupGrantStates[Category.DENIED] = mutableListOf() + if (!appPermGroupUiInfoLiveDatas.all { it.value.isInitialized }) { + return + } - val fullStorageState = fullStoragePermsLiveData.value?.find { pkg -> - pkg.packageName == packageName && pkg.user == user - } + val groupGrantStates = mutableMapOf<Category, MutableList<GroupUiInfo>>() + groupGrantStates[Category.ALLOWED] = mutableListOf() + groupGrantStates[Category.ASK] = mutableListOf() + groupGrantStates[Category.DENIED] = mutableListOf() - for (groupName in groups) { - val isSystem = PermissionMapping.getPlatformPermissionGroups().contains(groupName) - appPermGroupUiInfoLiveDatas[groupName]?.value?.let { uiInfo -> - if (SdkLevel.isAtLeastT() && !uiInfo.shouldShow) { - return@let + val fullStorageState = + fullStoragePermsLiveData.value?.find { pkg -> + pkg.packageName == packageName && pkg.user == user } - if (groupName == Manifest.permission_group.STORAGE && - (fullStorageState?.isGranted == true && !fullStorageState.isLegacy)) { - groupGrantStates[Category.ALLOWED]!!.add( - GroupUiInfo(groupName, isSystem, PermSubtitle.ALL_FILES)) - return@let - } - when (uiInfo.permGrantState) { - PermGrantState.PERMS_ALLOWED -> { - val subtitle = if (groupName == Manifest.permission_group.STORAGE) { - if (SdkLevel.isAtLeastT()) { - PermSubtitle.NONE - } else { - if (fullStorageState?.isLegacy == true) { - PermSubtitle.ALL_FILES + + for (groupName in groups) { + val isSystem = + PermissionMapping.getPlatformPermissionGroups().contains(groupName) + appPermGroupUiInfoLiveDatas[groupName]?.value?.let { uiInfo -> + if (SdkLevel.isAtLeastT() && !uiInfo.shouldShow) { + return@let + } + if ( + groupName == Manifest.permission_group.STORAGE && + (fullStorageState?.isGranted == true && !fullStorageState.isLegacy) + ) { + groupGrantStates[Category.ALLOWED]!!.add( + GroupUiInfo(groupName, isSystem, PermSubtitle.ALL_FILES) + ) + return@let + } + when (uiInfo.permGrantState) { + PermGrantState.PERMS_ALLOWED -> { + val subtitle = + if (groupName == Manifest.permission_group.STORAGE) { + if (SdkLevel.isAtLeastT()) { + PermSubtitle.NONE + } else { + if (fullStorageState?.isLegacy == true) { + PermSubtitle.ALL_FILES + } else { + PermSubtitle.MEDIA_ONLY + } + } } else { - PermSubtitle.MEDIA_ONLY + PermSubtitle.NONE } - } - } else { - PermSubtitle.NONE + groupGrantStates[Category.ALLOWED]!!.add( + GroupUiInfo(groupName, isSystem, subtitle) + ) } - groupGrantStates[Category.ALLOWED]!!.add( - GroupUiInfo(groupName, isSystem, subtitle)) + PermGrantState.PERMS_ALLOWED_ALWAYS -> + groupGrantStates[Category.ALLOWED]!!.add( + GroupUiInfo(groupName, isSystem, PermSubtitle.BACKGROUND) + ) + PermGrantState.PERMS_ALLOWED_FOREGROUND_ONLY -> + groupGrantStates[Category.ALLOWED]!!.add( + GroupUiInfo(groupName, isSystem, PermSubtitle.FOREGROUND_ONLY) + ) + PermGrantState.PERMS_DENIED -> + groupGrantStates[Category.DENIED]!!.add( + GroupUiInfo(groupName, isSystem) + ) + PermGrantState.PERMS_ASK -> + groupGrantStates[Category.ASK]!!.add( + GroupUiInfo(groupName, isSystem) + ) } - PermGrantState.PERMS_ALLOWED_ALWAYS -> groupGrantStates[ - Category.ALLOWED]!!.add(GroupUiInfo(groupName, isSystem, - PermSubtitle.BACKGROUND)) - PermGrantState.PERMS_ALLOWED_FOREGROUND_ONLY -> groupGrantStates[ - Category.ALLOWED]!!.add(GroupUiInfo(groupName, isSystem, - PermSubtitle.FOREGROUND_ONLY)) - PermGrantState.PERMS_DENIED -> groupGrantStates[Category.DENIED]!!.add( - GroupUiInfo(groupName, isSystem)) - PermGrantState.PERMS_ASK -> groupGrantStates[Category.ASK]!!.add( - GroupUiInfo(groupName, isSystem)) } } - } - value = groupGrantStates + value = groupGrantStates + } } - } // TODO 206455664: remove once issue is identified fun logLiveDataState() { - Log.i(LOG_TAG, "Overall liveData isStale: ${packagePermGroupsLiveData.isStale}, " + + Log.i( + LOG_TAG, + "Overall liveData isStale: ${packagePermGroupsLiveData.isStale}, " + "isInitialized: ${packagePermGroupsLiveData.isInitialized}, " + - "value: ${packagePermGroupsLiveData.value}") - Log.i(LOG_TAG, "AutoRevoke liveData isStale: ${autoRevokeLiveData.isStale}, " + + "value: ${packagePermGroupsLiveData.value}" + ) + Log.i( + LOG_TAG, + "AutoRevoke liveData isStale: ${autoRevokeLiveData.isStale}, " + "isInitialized: ${autoRevokeLiveData.isInitialized}, " + - "value: ${autoRevokeLiveData.value}") - Log.i(LOG_TAG, "PackagePerms liveData isStale: ${packagePermsLiveData.isStale}, " + + "value: ${autoRevokeLiveData.value}" + ) + Log.i( + LOG_TAG, + "PackagePerms liveData isStale: ${packagePermsLiveData.isStale}, " + "isInitialized: ${packagePermsLiveData.isInitialized}, " + - "value: ${packagePermsLiveData.value}") - Log.i(LOG_TAG, "FullStorage liveData isStale: ${fullStoragePermsLiveData.isStale}, " + + "value: ${packagePermsLiveData.value}" + ) + Log.i( + LOG_TAG, + "FullStorage liveData isStale: ${fullStoragePermsLiveData.isStale}, " + "isInitialized: ${fullStoragePermsLiveData.isInitialized}, " + - "value size: ${fullStoragePermsLiveData.value?.size}") + "value size: ${fullStoragePermsLiveData.value?.size}" + ) for ((group, liveData) in appPermGroupUiInfoLiveDatas) { - Log.i(LOG_TAG, "$group ui liveData isStale: ${liveData.isStale}, " + + Log.i( + LOG_TAG, + "$group ui liveData isStale: ${liveData.isStale}, " + "isInitialized: ${liveData.isInitialized}, " + - "value size: ${liveData.value}") + "value size: ${liveData.value}" + ) } } @@ -233,26 +263,33 @@ class AppPermissionGroupsViewModel( val lightPackageInfo = LightPackageInfoLiveData[packageName, user].getInitializedValue() if (lightPackageInfo != null) { - Log.i(LOG_TAG, "sessionId $sessionId setting auto revoke enabled to $enabled for" + - "$packageName $user") - val tag = if (enabled) { - APP_PERMISSION_GROUPS_FRAGMENT_AUTO_REVOKE_ACTION__ACTION__SWITCH_ENABLED - } else { - APP_PERMISSION_GROUPS_FRAGMENT_AUTO_REVOKE_ACTION__ACTION__SWITCH_DISABLED - } + Log.i( + LOG_TAG, + "sessionId $sessionId setting auto revoke enabled to $enabled for" + + "$packageName $user" + ) + val tag = + if (enabled) { + APP_PERMISSION_GROUPS_FRAGMENT_AUTO_REVOKE_ACTION__ACTION__SWITCH_ENABLED + } else { + APP_PERMISSION_GROUPS_FRAGMENT_AUTO_REVOKE_ACTION__ACTION__SWITCH_DISABLED + } PermissionControllerStatsLog.write( - APP_PERMISSION_GROUPS_FRAGMENT_AUTO_REVOKE_ACTION, sessionId, - lightPackageInfo.uid, packageName, tag) - - val mode = if (enabled) { - MODE_ALLOWED - } else { - MODE_IGNORED - } + APP_PERMISSION_GROUPS_FRAGMENT_AUTO_REVOKE_ACTION, + sessionId, + lightPackageInfo.uid, + packageName, + tag + ) + + val mode = + if (enabled) { + MODE_ALLOWED + } else { + MODE_IGNORED + } aom.setUidMode(OPSTR_AUTO_REVOKE_PERMISSIONS_IF_UNUSED, lightPackageInfo.uid, mode) - if (isHibernationEnabled() && - SdkLevel.isAtLeastSv2() && - !enabled) { + if (isHibernationEnabled() && SdkLevel.isAtLeastSv2() && !enabled) { // Only unhibernate on S_V2+ to have consistent toggle behavior w/ Settings val ahm = app.getSystemService(AppHibernationManager::class.java)!! ahm.setHibernatingForUser(packageName, false) @@ -281,13 +318,17 @@ class AppPermissionGroupsViewModel( return } - val aggregateDataFilterBeginDays = if (KotlinUtils.is7DayToggleEnabled()) - AGGREGATE_DATA_FILTER_BEGIN_DAYS_7 else AGGREGATE_DATA_FILTER_BEGIN_DAYS_1 + val aggregateDataFilterBeginDays = + if (KotlinUtils.is7DayToggleEnabled()) AGGREGATE_DATA_FILTER_BEGIN_DAYS_7 + else AGGREGATE_DATA_FILTER_BEGIN_DAYS_1 accessTime.clear() - val filterTimeBeginMillis = max(System.currentTimeMillis() - - TimeUnit.DAYS.toMillis(aggregateDataFilterBeginDays.toLong()), - Instant.EPOCH.toEpochMilli()) + val filterTimeBeginMillis = + max( + System.currentTimeMillis() - + TimeUnit.DAYS.toMillis(aggregateDataFilterBeginDays.toLong()), + Instant.EPOCH.toEpochMilli() + ) val numApps: Int = appPermissionUsages.size for (appIndex in 0 until numApps) { val appUsage: AppPermissionUsage = appPermissionUsages[appIndex] @@ -307,96 +348,128 @@ class AppPermissionGroupsViewModel( // We might have another AppPermissionUsage entry that's of the same packageName // but with a different uid. In that case, we want to grab the max lastAccessTime // as the last usage to show. - lastAccessTime = Math.max( + lastAccessTime = + Math.max( accessTime.getOrDefault(groupName, Instant.EPOCH.toEpochMilli()), - lastAccessTime) + lastAccessTime + ) accessTime[groupName] = lastAccessTime } } } - fun getPreferenceSummary(groupInfo: GroupUiInfo, context: Context, lastAccessTime: Long?): - String { - val summaryTimestamp = Utils - .getPermissionLastAccessSummaryTimestamp( - lastAccessTime, context, groupInfo.groupName) + fun getPreferenceSummary( + groupInfo: GroupUiInfo, + context: Context, + lastAccessTime: Long? + ): String { + val summaryTimestamp = + Utils.getPermissionLastAccessSummaryTimestamp( + lastAccessTime, + context, + groupInfo.groupName + ) @AppPermsLastAccessType val lastAccessType: Int = summaryTimestamp.second return when (groupInfo.subtitle) { PermSubtitle.BACKGROUND -> when (lastAccessType) { - Utils.LAST_24H_CONTENT_PROVIDER -> context.getString( - R.string.app_perms_content_provider_24h_background) - Utils.LAST_7D_CONTENT_PROVIDER -> context.getString( - R.string.app_perms_content_provider_7d_background) - Utils.LAST_24H_SENSOR_TODAY -> context.getString( + Utils.LAST_24H_CONTENT_PROVIDER -> + context.getString(R.string.app_perms_content_provider_24h_background) + Utils.LAST_7D_CONTENT_PROVIDER -> + context.getString(R.string.app_perms_content_provider_7d_background) + Utils.LAST_24H_SENSOR_TODAY -> + context.getString( R.string.app_perms_24h_access_background, - summaryTimestamp.first) - Utils.LAST_24H_SENSOR_YESTERDAY -> context.getString( + summaryTimestamp.first + ) + Utils.LAST_24H_SENSOR_YESTERDAY -> + context.getString( R.string.app_perms_24h_access_yest_background, - summaryTimestamp.first) - Utils.LAST_7D_SENSOR -> context.getString( + summaryTimestamp.first + ) + Utils.LAST_7D_SENSOR -> + context.getString( R.string.app_perms_7d_access_background, - summaryTimestamp.third, summaryTimestamp.first) - Utils.NOT_IN_LAST_7D -> context.getString( - R.string.permission_subtitle_background) - else -> context.getString( - R.string.permission_subtitle_background) + summaryTimestamp.third, + summaryTimestamp.first + ) + Utils.NOT_IN_LAST_7D -> + context.getString(R.string.permission_subtitle_background) + else -> context.getString(R.string.permission_subtitle_background) } PermSubtitle.MEDIA_ONLY -> when (lastAccessType) { - Utils.LAST_24H_CONTENT_PROVIDER -> context.getString( - R.string.app_perms_content_provider_24h_media_only) - Utils.LAST_7D_CONTENT_PROVIDER -> context.getString( - R.string.app_perms_content_provider_7d_media_only) - Utils.LAST_24H_SENSOR_TODAY -> context.getString( + Utils.LAST_24H_CONTENT_PROVIDER -> + context.getString(R.string.app_perms_content_provider_24h_media_only) + Utils.LAST_7D_CONTENT_PROVIDER -> + context.getString(R.string.app_perms_content_provider_7d_media_only) + Utils.LAST_24H_SENSOR_TODAY -> + context.getString( R.string.app_perms_24h_access_media_only, - summaryTimestamp.first) - Utils.LAST_24H_SENSOR_YESTERDAY -> context.getString( + summaryTimestamp.first + ) + Utils.LAST_24H_SENSOR_YESTERDAY -> + context.getString( R.string.app_perms_24h_access_yest_media_only, - summaryTimestamp.first) - Utils.LAST_7D_SENSOR -> context.getString( + summaryTimestamp.first + ) + Utils.LAST_7D_SENSOR -> + context.getString( R.string.app_perms_7d_access_media_only, - summaryTimestamp.third, summaryTimestamp.first) - Utils.NOT_IN_LAST_7D -> context.getString( - R.string.permission_subtitle_media_only) + summaryTimestamp.third, + summaryTimestamp.first + ) + Utils.NOT_IN_LAST_7D -> + context.getString(R.string.permission_subtitle_media_only) else -> context.getString(R.string.permission_subtitle_media_only) } PermSubtitle.ALL_FILES -> when (lastAccessType) { - Utils.LAST_24H_CONTENT_PROVIDER -> context.getString( - R.string.app_perms_content_provider_24h_all_files) - Utils.LAST_7D_CONTENT_PROVIDER -> context.getString( - R.string.app_perms_content_provider_7d_all_files) - Utils.LAST_24H_SENSOR_TODAY -> context.getString( + Utils.LAST_24H_CONTENT_PROVIDER -> + context.getString(R.string.app_perms_content_provider_24h_all_files) + Utils.LAST_7D_CONTENT_PROVIDER -> + context.getString(R.string.app_perms_content_provider_7d_all_files) + Utils.LAST_24H_SENSOR_TODAY -> + context.getString( R.string.app_perms_24h_access_all_files, - summaryTimestamp.first) - Utils.LAST_24H_SENSOR_YESTERDAY -> context.getString( + summaryTimestamp.first + ) + Utils.LAST_24H_SENSOR_YESTERDAY -> + context.getString( R.string.app_perms_24h_access_yest_all_files, - summaryTimestamp.first) - Utils.LAST_7D_SENSOR -> context.getString( + summaryTimestamp.first + ) + Utils.LAST_7D_SENSOR -> + context.getString( R.string.app_perms_7d_access_all_files, - summaryTimestamp.third, summaryTimestamp.first) - Utils.NOT_IN_LAST_7D -> context.getString( - R.string.permission_subtitle_all_files) + summaryTimestamp.third, + summaryTimestamp.first + ) + Utils.NOT_IN_LAST_7D -> + context.getString(R.string.permission_subtitle_all_files) else -> context.getString(R.string.permission_subtitle_all_files) } else -> // PermSubtitle.FOREGROUND_ONLY should fall into this as well when (lastAccessType) { - Utils.LAST_24H_CONTENT_PROVIDER -> context.getString( - R.string.app_perms_content_provider_24h) - Utils.LAST_7D_CONTENT_PROVIDER -> context.getString( - R.string.app_perms_content_provider_7d) - Utils.LAST_24H_SENSOR_TODAY -> context.getString( - R.string.app_perms_24h_access, - summaryTimestamp.first) - Utils.LAST_24H_SENSOR_YESTERDAY -> context.getString( + Utils.LAST_24H_CONTENT_PROVIDER -> + context.getString(R.string.app_perms_content_provider_24h) + Utils.LAST_7D_CONTENT_PROVIDER -> + context.getString(R.string.app_perms_content_provider_7d) + Utils.LAST_24H_SENSOR_TODAY -> + context.getString(R.string.app_perms_24h_access, summaryTimestamp.first) + Utils.LAST_24H_SENSOR_YESTERDAY -> + context.getString( R.string.app_perms_24h_access_yest, - summaryTimestamp.first) - Utils.LAST_7D_SENSOR -> context.getString( + summaryTimestamp.first + ) + Utils.LAST_7D_SENSOR -> + context.getString( R.string.app_perms_7d_access, - summaryTimestamp.third, summaryTimestamp.first) + summaryTimestamp.third, + summaryTimestamp.first + ) Utils.NOT_IN_LAST_7D -> "" else -> "" } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/AppPermissionViewModel.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/AppPermissionViewModel.kt index 99b40d8a7..cc29acbd7 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/AppPermissionViewModel.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/AppPermissionViewModel.kt @@ -20,6 +20,7 @@ package com.android.permissioncontroller.permission.ui.model import android.Manifest import android.Manifest.permission.ACCESS_COARSE_LOCATION import android.Manifest.permission.ACCESS_FINE_LOCATION +import android.Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED import android.Manifest.permission_group.READ_MEDIA_VISUAL import android.annotation.SuppressLint import android.app.Activity @@ -28,19 +29,14 @@ import android.app.AppOpsManager.MODE_ALLOWED import android.app.AppOpsManager.MODE_ERRORED import android.app.AppOpsManager.OPSTR_MANAGE_EXTERNAL_STORAGE import android.app.Application -import android.content.Context import android.content.Intent import android.os.Build import android.os.Bundle import android.os.UserHandle -import android.provider.MediaStore import android.util.Log -import androidx.activity.result.ActivityResultLauncher -import androidx.activity.result.contract.ActivityResultContract import androidx.annotation.ChecksSdkIntAtLeast import androidx.annotation.RequiresApi import androidx.annotation.StringRes -import androidx.core.util.Consumer import androidx.fragment.app.Fragment import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel @@ -56,14 +52,13 @@ import com.android.permissioncontroller.R import com.android.permissioncontroller.permission.data.FullStoragePermissionAppsLiveData import com.android.permissioncontroller.permission.data.FullStoragePermissionAppsLiveData.FullStoragePackageState import com.android.permissioncontroller.permission.data.LightAppPermGroupLiveData -import com.android.permissioncontroller.permission.data.v34.SafetyLabelInfoLiveData import com.android.permissioncontroller.permission.data.SmartUpdateMediatorLiveData import com.android.permissioncontroller.permission.data.get +import com.android.permissioncontroller.permission.data.v34.SafetyLabelInfoLiveData import com.android.permissioncontroller.permission.model.livedatatypes.LightAppPermGroup import com.android.permissioncontroller.permission.model.livedatatypes.LightPermission import com.android.permissioncontroller.permission.service.PermissionChangeStorageImpl import com.android.permissioncontroller.permission.service.v33.PermissionDecisionStorageImpl -import com.android.permissioncontroller.permission.ui.v33.AdvancedConfirmDialogArgs import com.android.permissioncontroller.permission.ui.model.AppPermissionViewModel.ButtonType.ALLOW import com.android.permissioncontroller.permission.ui.model.AppPermissionViewModel.ButtonType.ALLOW_ALWAYS import com.android.permissioncontroller.permission.ui.model.AppPermissionViewModel.ButtonType.ALLOW_FOREGROUND @@ -73,12 +68,14 @@ import com.android.permissioncontroller.permission.ui.model.AppPermissionViewMod import com.android.permissioncontroller.permission.ui.model.AppPermissionViewModel.ButtonType.DENY_FOREGROUND import com.android.permissioncontroller.permission.ui.model.AppPermissionViewModel.ButtonType.LOCATION_ACCURACY import com.android.permissioncontroller.permission.ui.model.AppPermissionViewModel.ButtonType.SELECT_PHOTOS +import com.android.permissioncontroller.permission.ui.v33.AdvancedConfirmDialogArgs import com.android.permissioncontroller.permission.ui.v34.PermissionRationaleActivity import com.android.permissioncontroller.permission.ui.v34.PermissionRationaleActivity.EXTRA_SHOULD_SHOW_SETTINGS_SECTION import com.android.permissioncontroller.permission.utils.KotlinUtils import com.android.permissioncontroller.permission.utils.KotlinUtils.getDefaultPrecision import com.android.permissioncontroller.permission.utils.KotlinUtils.isLocationAccuracyEnabled import com.android.permissioncontroller.permission.utils.KotlinUtils.isPhotoPickerPromptEnabled +import com.android.permissioncontroller.permission.utils.KotlinUtils.openPhotoPickerForApp import com.android.permissioncontroller.permission.utils.LocationUtils import com.android.permissioncontroller.permission.utils.PermissionMapping import com.android.permissioncontroller.permission.utils.PermissionMapping.getPartialStorageGrantPermissionsForGroup @@ -112,7 +109,6 @@ class AppPermissionViewModel( companion object { private val LOG_TAG = AppPermissionViewModel::class.java.simpleName private const val DEVICE_PROFILE_ROLE_PREFIX = "android.app.role" - const val PHOTO_PICKER_REQUEST_CODE = 1 } interface ConfirmDialogShowingFragment { @@ -134,15 +130,16 @@ class AppPermissionViewModel( GRANT_BOTH(GRANT_FOREGROUND.value or GRANT_BACKGROUND.value), REVOKE_BOTH(REVOKE_FOREGROUND.value or REVOKE_BACKGROUND.value), GRANT_FOREGROUND_ONLY(GRANT_FOREGROUND.value or REVOKE_BACKGROUND.value), - GRANT_All_FILE_ACCESS(1 shl 4), + GRANT_ALL_FILE_ACCESS(1 shl 4), GRANT_FINE_LOCATION(1 shl 5), REVOKE_FINE_LOCATION(1 shl 6), GRANT_STORAGE_SUPERGROUP(1 shl 7), REVOKE_STORAGE_SUPERGROUP(1 shl 8), GRANT_STORAGE_SUPERGROUP_CONFIRMED( - GRANT_STORAGE_SUPERGROUP.value or GRANT_FOREGROUND.value), + GRANT_STORAGE_SUPERGROUP.value or GRANT_FOREGROUND.value + ), REVOKE_STORAGE_SUPERGROUP_CONFIRMED(REVOKE_STORAGE_SUPERGROUP.value or REVOKE_BOTH.value), - PHOTOS_SELECTED( 1 shl 9); + PHOTOS_SELECTED(1 shl 9); infix fun andValue(other: ChangeRequest): Int { return value and other.value @@ -158,89 +155,86 @@ class AppPermissionViewModel( DENY(5), DENY_FOREGROUND(6), LOCATION_ACCURACY(7), - SELECT_PHOTOS( 8); + SELECT_PHOTOS(8) } private val isStorageAndLessThanT = permGroupName == Manifest.permission_group.STORAGE && !SdkLevel.isAtLeastT() private var hasConfirmedRevoke = false private var lightAppPermGroup: LightAppPermGroup? = null - private var photoPickerLauncher: ActivityResultLauncher<Unit>? = null - private var photoPickerResultConsumer: Consumer<Int>? = null private val mediaStorageSupergroupPermGroups = mutableMapOf<String, LightAppPermGroup>() /* Whether the current ViewModel is Location permission with both Coarse and Fine */ private var shouldShowLocationAccuracy: Boolean? = null - /** - * A livedata which determines which detail string, if any, should be shown - */ + /** A livedata which determines which detail string, if any, should be shown */ val detailResIdLiveData = MutableLiveData<Pair<Int, Int?>>() - /** - * A livedata which stores the device admin, if there is one - */ + /** A livedata which stores the device admin, if there is one */ val showAdminSupportLiveData = MutableLiveData<RestrictedLockUtils.EnforcedAdmin>() - /** - * A livedata for determining the display state of safety label information - */ - val showPermissionRationaleLiveData = object : SmartUpdateMediatorLiveData<Boolean>() { - private val safetyLabelInfoLiveData = if (SdkLevel.isAtLeastU()) { - SafetyLabelInfoLiveData[packageName, user] - } else { - null - } + /** A livedata for determining the display state of safety label information */ + val showPermissionRationaleLiveData = + object : SmartUpdateMediatorLiveData<Boolean>() { + private val safetyLabelInfoLiveData = + if (SdkLevel.isAtLeastU()) { + SafetyLabelInfoLiveData[packageName, user] + } else { + null + } - init { - if (safetyLabelInfoLiveData != null && - PermissionMapping.isSafetyLabelAwarePermissionGroup(permGroupName)) { - addSource(safetyLabelInfoLiveData) { update() } - } else { - value = false + init { + if ( + safetyLabelInfoLiveData != null && + PermissionMapping.isSafetyLabelAwarePermissionGroup(permGroupName) + ) { + addSource(safetyLabelInfoLiveData) { update() } + } else { + value = false + } } - } - override fun onUpdate() { - if (safetyLabelInfoLiveData != null && safetyLabelInfoLiveData.isStale) { - return - } + override fun onUpdate() { + if (safetyLabelInfoLiveData != null && safetyLabelInfoLiveData.isStale) { + return + } - val safetyLabel = safetyLabelInfoLiveData?.value?.safetyLabel - if (safetyLabel == null) { - value = false - return - } + val safetyLabel = safetyLabelInfoLiveData?.value?.safetyLabel + if (safetyLabel == null) { + value = false + return + } - value = SafetyLabelUtils.getSafetyLabelSharingPurposesForGroup( - safetyLabel, permGroupName).any() + value = + SafetyLabelUtils.getSafetyLabelSharingPurposesForGroup( + safetyLabel, + permGroupName + ) + .any() + } } - } - /** - * A livedata which determines which detail string, if any, should be shown - */ - val fullStorageStateLiveData = object : SmartUpdateMediatorLiveData<FullStoragePackageState>() { - init { - if (isStorageAndLessThanT) { - addSource(FullStoragePermissionAppsLiveData) { - update() + /** A livedata which determines which detail string, if any, should be shown */ + val fullStorageStateLiveData = + object : SmartUpdateMediatorLiveData<FullStoragePackageState>() { + init { + if (isStorageAndLessThanT) { + addSource(FullStoragePermissionAppsLiveData) { update() } + } else { + value = null } - } else { - value = null } - } - override fun onUpdate() { - for (state in FullStoragePermissionAppsLiveData.value ?: return) { - if (state.packageName == packageName && state.user == user) { - value = state - return + override fun onUpdate() { + for (state in FullStoragePermissionAppsLiveData.value ?: return) { + if (state.packageName == packageName && state.user == user) { + value = state + return + } } + value = null + return } - value = null - return } - } data class ButtonState( var isChecked: Boolean, @@ -251,182 +245,201 @@ class AppPermissionViewModel( constructor() : this(false, true, false, null) } - /** - * A livedata which computes the state of the radio buttons - */ - val buttonStateLiveData = object : - SmartUpdateMediatorLiveData<@JvmSuppressWildcards Map<ButtonType, ButtonState>>() { + /** A livedata which computes the state of the radio buttons */ + val buttonStateLiveData = + object : SmartUpdateMediatorLiveData<@JvmSuppressWildcards Map<ButtonType, ButtonState>>() { - private val appPermGroupLiveData = LightAppPermGroupLiveData[packageName, permGroupName, - user] - private val mediaStorageSupergroupLiveData = - mutableMapOf<String, LightAppPermGroupLiveData>() + private val appPermGroupLiveData = + LightAppPermGroupLiveData[packageName, permGroupName, user] + private val mediaStorageSupergroupLiveData = + mutableMapOf<String, LightAppPermGroupLiveData>() - init { + init { - addSource(appPermGroupLiveData) { appPermGroup -> - lightAppPermGroup = appPermGroup - if (permGroupName in PermissionMapping.STORAGE_SUPERGROUP_PERMISSIONS) { - onMediaPermGroupUpdate(permGroupName, appPermGroup) - } - if (appPermGroupLiveData.isInitialized && appPermGroup == null) { - value = null - } else if (appPermGroup != null) { - if (isStorageAndLessThanT && !fullStorageStateLiveData.isInitialized) { - return@addSource + addSource(appPermGroupLiveData) { appPermGroup -> + lightAppPermGroup = appPermGroup + if (permGroupName in PermissionMapping.STORAGE_SUPERGROUP_PERMISSIONS) { + onMediaPermGroupUpdate(permGroupName, appPermGroup) + } + if (appPermGroupLiveData.isInitialized && appPermGroup == null) { + value = null + } else if (appPermGroup != null) { + if (isStorageAndLessThanT && !fullStorageStateLiveData.isInitialized) { + return@addSource + } + update() } - update() } - } - if (isStorageAndLessThanT) { - addSource(fullStorageStateLiveData) { - update() + if (isStorageAndLessThanT) { + addSource(fullStorageStateLiveData) { update() } } - } - if (permGroupName in PermissionMapping.STORAGE_SUPERGROUP_PERMISSIONS) { - for (permGroupName in PermissionMapping.STORAGE_SUPERGROUP_PERMISSIONS) { - val liveData = LightAppPermGroupLiveData[packageName, permGroupName, user] - mediaStorageSupergroupLiveData[permGroupName] = liveData - } - for (permGroupName in mediaStorageSupergroupLiveData.keys) { - val liveData = mediaStorageSupergroupLiveData[permGroupName]!! - addSource(liveData) { permGroup -> - onMediaPermGroupUpdate(permGroupName, permGroup) + if (permGroupName in PermissionMapping.STORAGE_SUPERGROUP_PERMISSIONS) { + for (permGroupName in PermissionMapping.STORAGE_SUPERGROUP_PERMISSIONS) { + val liveData = LightAppPermGroupLiveData[packageName, permGroupName, user] + mediaStorageSupergroupLiveData[permGroupName] = liveData + } + for (permGroupName in mediaStorageSupergroupLiveData.keys) { + val liveData = mediaStorageSupergroupLiveData[permGroupName]!! + addSource(liveData) { permGroup -> + onMediaPermGroupUpdate(permGroupName, permGroup) + } } } - } - addSource(showPermissionRationaleLiveData) { - update() - } - } - - private fun onMediaPermGroupUpdate(permGroupName: String, permGroup: LightAppPermGroup?) { - if (permGroup == null) { - mediaStorageSupergroupPermGroups.remove(permGroupName) - value = null - } else { - mediaStorageSupergroupPermGroups[permGroupName] = permGroup - update() + addSource(showPermissionRationaleLiveData) { update() } } - } - override fun onUpdate() { - val group = appPermGroupLiveData.value ?: return - for (mediaGroupLiveData in mediaStorageSupergroupLiveData.values) { - if (!mediaGroupLiveData.isInitialized) { - return + private fun onMediaPermGroupUpdate( + permGroupName: String, + permGroup: LightAppPermGroup? + ) { + if (permGroup == null) { + mediaStorageSupergroupPermGroups.remove(permGroupName) + value = null + } else { + mediaStorageSupergroupPermGroups[permGroupName] = permGroup + update() } } - if (!showPermissionRationaleLiveData.isInitialized) { - return - } - - val admin = RestrictedLockUtils.getProfileOrDeviceOwner(app, user) - - val allowedState = ButtonState() - val allowedAlwaysState = ButtonState() - val allowedForegroundState = ButtonState() - val askOneTimeState = ButtonState() - val askState = ButtonState() - val deniedState = ButtonState() - val deniedForegroundState = ButtonState() - val selectState = ButtonState() - - askOneTimeState.isShown = group.foreground.isGranted && group.isOneTime - askState.isShown = PermissionMapping.supportsOneTimeGrant(permGroupName) && - !(group.foreground.isGranted && group.isOneTime) - deniedState.isShown = true - - if (group.hasPermWithBackgroundMode) { - // Background / Foreground / Deny case - allowedForegroundState.isShown = true - if (group.hasBackgroundGroup) { - allowedAlwaysState.isShown = true + override fun onUpdate() { + val group = appPermGroupLiveData.value ?: return + for (mediaGroupLiveData in mediaStorageSupergroupLiveData.values) { + if (!mediaGroupLiveData.isInitialized) { + return + } } - allowedAlwaysState.isChecked = group.background.isGranted && - group.foreground.isGranted && !group.background.isOneTime - allowedForegroundState.isChecked = group.foreground.isGranted && - (!group.background.isGranted || group.background.isOneTime) && - !group.foreground.isOneTime - askState.isChecked = !group.foreground.isGranted && group.isOneTime - askOneTimeState.isChecked = group.foreground.isGranted && group.isOneTime - askOneTimeState.isShown = askOneTimeState.isChecked - deniedState.isChecked = !group.foreground.isGranted && !group.isOneTime - if (applyFixToForegroundBackground(group, group.foreground.isSystemFixed, - group.background.isSystemFixed, allowedAlwaysState, - allowedForegroundState, askState, deniedState, - deniedForegroundState) || - applyFixToForegroundBackground(group, group.foreground.isPolicyFixed, - group.background.isPolicyFixed, allowedAlwaysState, - allowedForegroundState, askState, deniedState, - deniedForegroundState)) { - showAdminSupportLiveData.value = admin - val detailId = getDetailResIdForFixedByPolicyPermissionGroup(group, - admin != null) - if (detailId != 0) { - detailResIdLiveData.value = detailId to null - } - } else if (Utils.areGroupPermissionsIndividuallyControlled(app, permGroupName)) { - val detailId = getIndividualPermissionDetailResId(group) - detailResIdLiveData.value = detailId.first to detailId.second + if (!showPermissionRationaleLiveData.isInitialized) { + return } - } else if (KotlinUtils.isPhotoPickerPromptEnabled() && - group.permGroupName == READ_MEDIA_VISUAL && - group.packageInfo.targetSdkVersion >= Build.VERSION_CODES.TIRAMISU) { - // Allow / Select Photos / Deny case - allowedState.isShown = true + + val admin = RestrictedLockUtils.getProfileOrDeviceOwner(app, user) + + val allowedState = ButtonState() + val allowedAlwaysState = ButtonState() + val allowedForegroundState = ButtonState() + val askOneTimeState = ButtonState() + val askState = ButtonState() + val deniedState = ButtonState() + val deniedForegroundState = ButtonState() + val selectState = ButtonState() + + askOneTimeState.isShown = group.foreground.isGranted && group.isOneTime + askState.isShown = + PermissionMapping.supportsOneTimeGrant(permGroupName) && + !(group.foreground.isGranted && group.isOneTime) deniedState.isShown = true - selectState.isShown = true - deniedState.isChecked = !group.isGranted - selectState.isChecked = isPartialStorageGrant(group) - allowedState.isChecked = group.isGranted && !isPartialStorageGrant(group) - } else { - // Allow / Deny case - allowedState.isShown = true - - allowedState.isChecked = group.foreground.isGranted && !group.foreground.isOneTime - askState.isChecked = !group.foreground.isGranted && group.isOneTime - askOneTimeState.isChecked = group.foreground.isGranted && group.isOneTime - askOneTimeState.isShown = askOneTimeState.isChecked - deniedState.isChecked = !group.foreground.isGranted && !group.isOneTime - - if (group.foreground.isPolicyFixed || group.foreground.isSystemFixed) { - allowedState.isEnabled = false - askState.isEnabled = false - deniedState.isEnabled = false - showAdminSupportLiveData.value = admin - val detailId = getDetailResIdForFixedByPolicyPermissionGroup(group, - admin != null) - if (detailId != 0) { - detailResIdLiveData.value = detailId to null + if (group.hasPermWithBackgroundMode) { + // Background / Foreground / Deny case + allowedForegroundState.isShown = true + if (group.hasBackgroundGroup) { + allowedAlwaysState.isShown = true + } + + allowedAlwaysState.isChecked = + group.background.isGranted && + group.foreground.isGranted && + !group.background.isOneTime + allowedForegroundState.isChecked = + group.foreground.isGranted && + (!group.background.isGranted || group.background.isOneTime) && + !group.foreground.isOneTime + askState.isChecked = !group.foreground.isGranted && group.isOneTime + askOneTimeState.isChecked = group.foreground.isGranted && group.isOneTime + askOneTimeState.isShown = askOneTimeState.isChecked + deniedState.isChecked = !group.foreground.isGranted && !group.isOneTime + if ( + applyFixToForegroundBackground( + group, + group.foreground.isSystemFixed, + group.background.isSystemFixed, + allowedAlwaysState, + allowedForegroundState, + askState, + deniedState, + deniedForegroundState + ) || + applyFixToForegroundBackground( + group, + group.foreground.isPolicyFixed, + group.background.isPolicyFixed, + allowedAlwaysState, + allowedForegroundState, + askState, + deniedState, + deniedForegroundState + ) + ) { + showAdminSupportLiveData.value = admin + val detailId = + getDetailResIdForFixedByPolicyPermissionGroup(group, admin != null) + if (detailId != 0) { + detailResIdLiveData.value = detailId to null + } + } else if ( + Utils.areGroupPermissionsIndividuallyControlled(app, permGroupName) + ) { + val detailId = getIndividualPermissionDetailResId(group) + detailResIdLiveData.value = detailId.first to detailId.second + } + } else if ( + group.permGroupName == READ_MEDIA_VISUAL && + shouldShowPhotoPickerPromptForApp(group) + ) { + // Allow / Select Photos / Deny case + allowedState.isShown = true + deniedState.isShown = true + selectState.isShown = true + + deniedState.isChecked = !group.isGranted + selectState.isChecked = isPartialStorageGrant(group) + allowedState.isChecked = group.isGranted && !isPartialStorageGrant(group) + } else { + // Allow / Deny case + allowedState.isShown = true + + allowedState.isChecked = + group.foreground.isGranted && !group.foreground.isOneTime + askState.isChecked = !group.foreground.isGranted && group.isOneTime + askOneTimeState.isChecked = group.foreground.isGranted && group.isOneTime + askOneTimeState.isShown = askOneTimeState.isChecked + deniedState.isChecked = !group.foreground.isGranted && !group.isOneTime + + if (group.foreground.isPolicyFixed || group.foreground.isSystemFixed) { + allowedState.isEnabled = false + askState.isEnabled = false + deniedState.isEnabled = false + showAdminSupportLiveData.value = admin + val detailId = + getDetailResIdForFixedByPolicyPermissionGroup(group, admin != null) + if (detailId != 0) { + detailResIdLiveData.value = detailId to null + } + } + if (isForegroundGroupSpecialCase(permGroupName)) { + allowedForegroundState.isShown = true + allowedState.isShown = false + allowedForegroundState.isChecked = allowedState.isChecked + allowedForegroundState.isEnabled = allowedState.isEnabled } } - if (isForegroundGroupSpecialCase(permGroupName)) { - allowedForegroundState.isShown = true - allowedState.isShown = false - allowedForegroundState.isChecked = allowedState.isChecked - allowedForegroundState.isEnabled = allowedState.isEnabled + if (group.packageInfo.targetSdkVersion < Build.VERSION_CODES.M) { + // Pre-M app's can't ask for runtime permissions + askState.isShown = false + deniedState.isChecked = askState.isChecked || deniedState.isChecked + deniedForegroundState.isChecked = + askState.isChecked || deniedForegroundState.isChecked } - } - if (group.packageInfo.targetSdkVersion < Build.VERSION_CODES.M) { - // Pre-M app's can't ask for runtime permissions - askState.isShown = false - deniedState.isChecked = askState.isChecked || deniedState.isChecked - deniedForegroundState.isChecked = askState.isChecked || - deniedForegroundState.isChecked - } - val storageState = fullStorageStateLiveData.value - if (isStorageAndLessThanT && storageState?.isLegacy != true) { - val allowedAllFilesState = allowedAlwaysState - val allowedMediaOnlyState = allowedForegroundState - if (storageState != null) { + val storageState = fullStorageStateLiveData.value + if (isStorageAndLessThanT && storageState?.isLegacy != true) { + val allowedAllFilesState = allowedAlwaysState + val allowedMediaOnlyState = allowedForegroundState + if (storageState != null) { // Set up the tri state permission for storage allowedAllFilesState.isEnabled = allowedState.isEnabled allowedAllFilesState.isShown = true @@ -434,62 +447,63 @@ class AppPermissionViewModel( allowedAllFilesState.isChecked = true deniedState.isChecked = false } - } else { - allowedAllFilesState.isEnabled = false - allowedAllFilesState.isShown = false + } else { + allowedAllFilesState.isEnabled = false + allowedAllFilesState.isShown = false + } + allowedMediaOnlyState.isShown = true + allowedMediaOnlyState.isEnabled = allowedState.isEnabled + allowedMediaOnlyState.isChecked = + allowedState.isChecked && storageState?.isGranted != true + allowedState.isChecked = false + allowedState.isShown = false } - allowedMediaOnlyState.isShown = true - allowedMediaOnlyState.isEnabled = allowedState.isEnabled - allowedMediaOnlyState.isChecked = allowedState.isChecked && - storageState?.isGranted != true - allowedState.isChecked = false - allowedState.isShown = false - } - if (shouldShowLocationAccuracy == null) { - shouldShowLocationAccuracy = isLocationAccuracyEnabled() && - group.permissions.containsKey(ACCESS_FINE_LOCATION) - } - val locationAccuracyState = ButtonState(isFineLocationChecked(group), - true, false, null) - if (shouldShowLocationAccuracy == true && !deniedState.isChecked) { - locationAccuracyState.isShown = true - } - if (group.foreground.isSystemFixed || group.foreground.isPolicyFixed) { - locationAccuracyState.isEnabled = false - } + if (shouldShowLocationAccuracy == null) { + shouldShowLocationAccuracy = + isLocationAccuracyEnabled() && + group.permissions.containsKey(ACCESS_FINE_LOCATION) + } + val locationAccuracyState = + ButtonState(isFineLocationChecked(group), true, false, null) + if (shouldShowLocationAccuracy == true && !deniedState.isChecked) { + locationAccuracyState.isShown = true + } + if (group.foreground.isSystemFixed || group.foreground.isPolicyFixed) { + locationAccuracyState.isEnabled = false + } - if (value == null) { - logAppPermissionFragmentViewed() - } + if (value == null) { + logAppPermissionFragmentViewed() + } - value = mapOf( - ALLOW to allowedState, ALLOW_ALWAYS to allowedAlwaysState, - ALLOW_FOREGROUND to allowedForegroundState, ASK_ONCE to askOneTimeState, - ASK to askState, DENY to deniedState, DENY_FOREGROUND to deniedForegroundState, - LOCATION_ACCURACY to locationAccuracyState, SELECT_PHOTOS to selectState) + value = + mapOf( + ALLOW to allowedState, + ALLOW_ALWAYS to allowedAlwaysState, + ALLOW_FOREGROUND to allowedForegroundState, + ASK_ONCE to askOneTimeState, + ASK to askState, + DENY to deniedState, + DENY_FOREGROUND to deniedForegroundState, + LOCATION_ACCURACY to locationAccuracyState, + SELECT_PHOTOS to selectState + ) + } } - } - fun registerPhotoPickerResultIfNeeded(fragment: Fragment) { - if (permGroupName != READ_MEDIA_VISUAL) { - return + private fun shouldShowPhotoPickerPromptForApp(group: LightAppPermGroup): Boolean { + if ( + !isPhotoPickerPromptEnabled() || + group.packageInfo.targetSdkVersion < Build.VERSION_CODES.TIRAMISU + ) { + return false } - photoPickerLauncher = fragment.registerForActivityResult( - object : ActivityResultContract<Unit, Int>() { - override fun parseResult(resultCode: Int, intent: Intent?): Int { - return resultCode - } - - override fun createIntent(context: Context, input: Unit): Intent { - return Intent(MediaStore.ACTION_USER_SELECT_IMAGES_FOR_APP) - .putExtra(Intent.EXTRA_UID, lightAppPermGroup?.packageInfo?.uid) - .setType(KotlinUtils.getMimeTypeForPermissions( - lightAppPermGroup?.foregroundPermNames ?: emptyList())) - } - }) { result -> - photoPickerResultConsumer?.accept(result) + if (group.packageInfo.targetSdkVersion >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { + return true } + val userSelectedPerm = group.permissions[READ_MEDIA_VISUAL_USER_SELECTED] ?: return false + return !userSelectedPerm.isImplicit } private fun isFineLocationChecked(group: LightAppPermGroup): Boolean { @@ -501,11 +515,13 @@ class AppPermissionViewModel( // 2. Else if FINE or COARSE have the isSelectedLocationAccuracy flag set, then return // true if FINE isSelectedLocationAccuracy is set. // 3. Else, return default precision from device config. - return if (fineLocation.isGrantedIncludingAppOp || - coarseLocation.isGrantedIncludingAppOp) { + return if ( + fineLocation.isGrantedIncludingAppOp || coarseLocation.isGrantedIncludingAppOp + ) { fineLocation.isGrantedIncludingAppOp - } else if (fineLocation.isSelectedLocationAccuracy || - coarseLocation.isSelectedLocationAccuracy) { + } else if ( + fineLocation.isSelectedLocationAccuracy || coarseLocation.isSelectedLocationAccuracy + ) { fineLocation.isSelectedLocationAccuracy } else { getDefaultPrecision() @@ -517,7 +533,7 @@ class AppPermissionViewModel( // TODO evanseverson: Actually change mic/camera to be a foreground only permission private fun isForegroundGroupSpecialCase(permissionGroupName: String): Boolean { return permissionGroupName.equals(Manifest.permission_group.CAMERA) || - permissionGroupName.equals(Manifest.permission_group.MICROPHONE) + permissionGroupName.equals(Manifest.permission_group.MICROPHONE) } /** @@ -608,10 +624,12 @@ class AppPermissionViewModel( logAppPermissionFragmentActionReportedForPermissionGroup( /* changeId= */ Random().nextLong(), group, - APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__PERMISSION_RATIONALE) + APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__PERMISSION_RATIONALE + ) } - val intent = Intent(activity, PermissionRationaleActivity::class.java).apply { + val intent = + Intent(activity, PermissionRationaleActivity::class.java).apply { putExtra(Intent.EXTRA_PACKAGE_NAME, packageName) putExtra(Intent.EXTRA_PERMISSION_GROUP_NAME, groupName) putExtra(Constants.EXTRA_SESSION_ID, sessionId) @@ -622,6 +640,7 @@ class AppPermissionViewModel( /** * Navigate to either the App Permission Groups screen, or the Permission Apps Screen. + * * @param fragment The current fragment * @param action The action to be taken * @param args The arguments to pass to the fragment @@ -635,26 +654,29 @@ class AppPermissionViewModel( fragment.findNavController().navigateSafe(actionId, args) } + fun openPhotoPicker(fragment: Fragment) { + val appPermGroup = lightAppPermGroup ?: return + openPhotoPickerForApp(fragment.requireActivity(), appPermGroup.packageInfo.uid, + appPermGroup.foregroundPermNames, 0) + } + /** * Request to grant/revoke permissions group. * * Does <u>not</u> handle: - * - * * Individually granted permissions - * * Permission groups with background permissions + * * Individually granted permissions + * * Permission groups with background permissions * * <u>Does</u> handle: - * - * * Default grant permissions + * * Default grant permissions * * @param setOneTime Whether or not to set this permission as one time * @param fragment The fragment calling this method * @param defaultDeny The system which will show the default deny dialog. Usually the same as - * the fragment. + * the fragment. * @param changeRequest Which permission group (foreground/background/both) should be changed * @param buttonClicked button which was pressed to initiate the change, one of - * AppPermissionFragmentActionReported.button_pressed constants - * + * AppPermissionFragmentActionReported.button_pressed constants * @return The dialogue to show, if applicable, or if the request was processed. */ fun requestChange( @@ -685,8 +707,12 @@ class AppPermissionViewModel( if (changeRequest == ChangeRequest.REVOKE_FINE_LOCATION) { if (!group.isOneTime) { - val newGroup = KotlinUtils.revokeForegroundRuntimePermissions(app, group, - filterPermissions = listOf(ACCESS_FINE_LOCATION)) + val newGroup = + KotlinUtils.revokeForegroundRuntimePermissions( + app, + group, + filterPermissions = listOf(ACCESS_FINE_LOCATION) + ) logPermissionChanges(group, newGroup, buttonClicked) } KotlinUtils.setFlagsWhenLocationAccuracyChanged(app, group, false) @@ -696,10 +722,18 @@ class AppPermissionViewModel( if (changeRequest == ChangeRequest.PHOTOS_SELECTED) { val partialGrantPerms = getPartialStorageGrantPermissionsForGroup(group) val nonSelectedPerms = group.permissions.keys.filter { it !in partialGrantPerms } - var newGroup = KotlinUtils.revokeForegroundRuntimePermissions(app, group, - filterPermissions = nonSelectedPerms) - newGroup = KotlinUtils.grantForegroundRuntimePermissions(app, newGroup, - filterPermissions = partialGrantPerms.toList()) + var newGroup = + KotlinUtils.revokeForegroundRuntimePermissions( + app, + group, + filterPermissions = nonSelectedPerms + ) + newGroup = + KotlinUtils.grantForegroundRuntimePermissions( + app, + newGroup, + filterPermissions = partialGrantPerms.toList() + ) logPermissionChanges(group, newGroup, buttonClicked) return } @@ -713,27 +747,31 @@ class AppPermissionViewModel( var showCDMWarning = false if (shouldRevokeForeground && wasForegroundGranted) { - showDefaultDenyDialog = (group.foreground.isGrantedByDefault || + showDefaultDenyDialog = + (group.foreground.isGrantedByDefault || !group.supportsRuntimePerms || group.hasInstallToRuntimeSplit) - showGrantedByDefaultWarning = showGrantedByDefaultWarning || - group.foreground.isGrantedByDefault + showGrantedByDefaultWarning = + showGrantedByDefaultWarning || group.foreground.isGrantedByDefault showCDMWarning = showCDMWarning || group.foreground.isGrantedByRole } if (shouldRevokeBackground && wasBackgroundGranted) { - showDefaultDenyDialog = showDefaultDenyDialog || + showDefaultDenyDialog = + showDefaultDenyDialog || group.background.isGrantedByDefault || !group.supportsRuntimePerms || group.hasInstallToRuntimeSplit - showGrantedByDefaultWarning = showGrantedByDefaultWarning || - group.background.isGrantedByDefault + showGrantedByDefaultWarning = + showGrantedByDefaultWarning || group.background.isGrantedByDefault showCDMWarning = showCDMWarning || group.background.isGrantedByRole } if (showCDMWarning) { // Refine showCDMWarning to only trigger for apps holding a device profile role - val heldRoles = context.getSystemService(android.app.role.RoleManager::class.java) + val heldRoles = + context + .getSystemService(android.app.role.RoleManager::class.java) .getHeldRolesFromController(packageName) val heldProfiles = heldRoles.filter { it.startsWith(DEVICE_PROFILE_ROLE_PREFIX) } showCDMWarning = showCDMWarning && heldProfiles.isNotEmpty() @@ -743,14 +781,24 @@ class AppPermissionViewModel( if (group.permGroupName == Manifest.permission_group.STORAGE) { showDefaultDenyDialog = false } else if (changeRequest == ChangeRequest.GRANT_FOREGROUND) { - showMediaConfirmDialog(setOneTime, defaultDeny, - ChangeRequest.GRANT_STORAGE_SUPERGROUP, buttonClicked, group.permGroupName, - group.packageInfo.targetSdkVersion) + showMediaConfirmDialog( + setOneTime, + defaultDeny, + ChangeRequest.GRANT_STORAGE_SUPERGROUP, + buttonClicked, + group.permGroupName, + group.packageInfo.targetSdkVersion + ) return } else if (changeRequest == ChangeRequest.REVOKE_BOTH) { - showMediaConfirmDialog(setOneTime, defaultDeny, - ChangeRequest.REVOKE_STORAGE_SUPERGROUP, buttonClicked, group.permGroupName, - group.packageInfo.targetSdkVersion) + showMediaConfirmDialog( + setOneTime, + defaultDeny, + ChangeRequest.REVOKE_STORAGE_SUPERGROUP, + buttonClicked, + group.permGroupName, + group.packageInfo.targetSdkVersion + ) return } else { showDefaultDenyDialog = false @@ -758,20 +806,32 @@ class AppPermissionViewModel( } if (showDefaultDenyDialog && !hasConfirmedRevoke && showGrantedByDefaultWarning) { - defaultDeny.showConfirmDialog(changeRequest, R.string.system_warning, buttonClicked, - setOneTime) + defaultDeny.showConfirmDialog( + changeRequest, + R.string.system_warning, + buttonClicked, + setOneTime + ) return } if (showDefaultDenyDialog && !hasConfirmedRevoke) { - defaultDeny.showConfirmDialog(changeRequest, R.string.old_sdk_deny_warning, - buttonClicked, setOneTime) + defaultDeny.showConfirmDialog( + changeRequest, + R.string.old_sdk_deny_warning, + buttonClicked, + setOneTime + ) return } if (showCDMWarning) { - defaultDeny.showConfirmDialog(changeRequest, - R.string.cdm_profile_revoke_warning, buttonClicked, setOneTime) + defaultDeny.showConfirmDialog( + changeRequest, + R.string.cdm_profile_revoke_warning, + buttonClicked, + setOneTime + ) return } @@ -780,12 +840,20 @@ class AppPermissionViewModel( var newGroup = group2 val oldGroup = group2 - if (shouldRevokeBackground && group2.hasBackgroundGroup && - (wasBackgroundGranted || group2.background.isUserFixed || - group2.isOneTime != setOneTime)) { - newGroup = KotlinUtils - .revokeBackgroundRuntimePermissions(app, newGroup, oneTime = setOneTime, - forceRemoveRevokedCompat = shouldClearOneTimeRevokedCompat(newGroup)) + if ( + shouldRevokeBackground && + group2.hasBackgroundGroup && + (wasBackgroundGranted || + group2.background.isUserFixed || + group2.isOneTime != setOneTime) + ) { + newGroup = + KotlinUtils.revokeBackgroundRuntimePermissions( + app, + newGroup, + oneTime = setOneTime, + forceRemoveRevokedCompat = shouldClearOneTimeRevokedCompat(newGroup) + ) // only log if we have actually denied permissions, not if we switch from // "ask every time" to denied @@ -794,12 +862,17 @@ class AppPermissionViewModel( } } - if (shouldRevokeForeground && - (wasForegroundGranted || group2.isOneTime != setOneTime)) { - newGroup = KotlinUtils - .revokeForegroundRuntimePermissions(app, newGroup, userFixed = false, - oneTime = setOneTime, - forceRemoveRevokedCompat = shouldClearOneTimeRevokedCompat(newGroup)) + if ( + shouldRevokeForeground && (wasForegroundGranted || group2.isOneTime != setOneTime) + ) { + newGroup = + KotlinUtils.revokeForegroundRuntimePermissions( + app, + newGroup, + userFixed = false, + oneTime = setOneTime, + forceRemoveRevokedCompat = shouldClearOneTimeRevokedCompat(newGroup) + ) // only log if we have actually denied permissions, not if we switch from // "ask every time" to denied @@ -809,13 +882,16 @@ class AppPermissionViewModel( } if (shouldGrantForeground) { - newGroup = if (shouldShowLocationAccuracy == true && - !isFineLocationChecked(newGroup)) { - KotlinUtils.grantForegroundRuntimePermissions(app, newGroup, - filterPermissions = listOf(ACCESS_COARSE_LOCATION)) - } else { - KotlinUtils.grantForegroundRuntimePermissions(app, newGroup) - } + newGroup = + if (shouldShowLocationAccuracy == true && !isFineLocationChecked(newGroup)) { + KotlinUtils.grantForegroundRuntimePermissions( + app, + newGroup, + filterPermissions = listOf(ACCESS_COARSE_LOCATION) + ) + } else { + KotlinUtils.grantForegroundRuntimePermissions(app, newGroup) + } if (!wasForegroundGranted) { SafetyNetLogger.logPermissionToggled(newGroup) @@ -832,15 +908,14 @@ class AppPermissionViewModel( logPermissionChanges(oldGroup, newGroup, buttonClicked) - fullStorageStateLiveData.value?.let { - FullStoragePermissionAppsLiveData.recalculate() - } + fullStorageStateLiveData.value?.let { FullStoragePermissionAppsLiveData.recalculate() } } } private fun shouldClearOneTimeRevokedCompat(group: LightAppPermGroup): Boolean { - return isPhotoPickerPromptEnabled() && permGroupName == READ_MEDIA_VISUAL && - group.permissions.values.any { it.isCompatRevoked && it.isOneTime } + return isPhotoPickerPromptEnabled() && + permGroupName == READ_MEDIA_VISUAL && + group.permissions.values.any { it.isCompatRevoked && it.isOneTime } } @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.TIRAMISU) @@ -850,8 +925,10 @@ class AppPermissionViewModel( } private fun expandToSupergroup(group: LightAppPermGroup): List<LightAppPermGroup> { - val mediaSupergroup = PermissionMapping.STORAGE_SUPERGROUP_PERMISSIONS - .mapNotNull { mediaStorageSupergroupPermGroups[it] } + val mediaSupergroup = + PermissionMapping.STORAGE_SUPERGROUP_PERMISSIONS.mapNotNull { + mediaStorageSupergroupPermGroups[it] + } return if (expandsToStorageSupergroup(group)) { mediaSupergroup } else { @@ -860,21 +937,23 @@ class AppPermissionViewModel( } private fun getPermGroupIcon(permGroup: String) = - Utils.getGroupInfo(permGroup, app.applicationContext)?.icon ?: R.drawable.ic_empty_icon + Utils.getGroupInfo(permGroup, app.applicationContext)?.icon ?: R.drawable.ic_empty_icon private val storagePermGroupIcon = getPermGroupIcon(Manifest.permission_group.STORAGE) - private val auralPermGroupIcon = if (SdkLevel.isAtLeastT()) { - getPermGroupIcon(Manifest.permission_group.READ_MEDIA_AURAL) - } else { - R.drawable.ic_empty_icon - } + private val auralPermGroupIcon = + if (SdkLevel.isAtLeastT()) { + getPermGroupIcon(Manifest.permission_group.READ_MEDIA_AURAL) + } else { + R.drawable.ic_empty_icon + } - private val visualPermGroupIcon = if (SdkLevel.isAtLeastT()) { - getPermGroupIcon(Manifest.permission_group.READ_MEDIA_VISUAL) - } else { - R.drawable.ic_empty_icon - } + private val visualPermGroupIcon = + if (SdkLevel.isAtLeastT()) { + getPermGroupIcon(Manifest.permission_group.READ_MEDIA_VISUAL) + } else { + R.drawable.ic_empty_icon + } @RequiresApi(Build.VERSION_CODES.TIRAMISU) private fun showMediaConfirmDialog( @@ -885,56 +964,63 @@ class AppPermissionViewModel( groupName: String, targetSdk: Int ) { - val aural = groupName == Manifest.permission_group.READ_MEDIA_AURAL val visual = groupName == Manifest.permission_group.READ_MEDIA_VISUAL val allow = changeRequest === ChangeRequest.GRANT_STORAGE_SUPERGROUP val deny = changeRequest === ChangeRequest.REVOKE_STORAGE_SUPERGROUP - val (iconId, titleId, messageId) = when { - targetSdk < Build.VERSION_CODES.Q && aural && allow -> - Triple( - storagePermGroupIcon, - R.string.media_confirm_dialog_title_a_to_p_aural_allow, - R.string.media_confirm_dialog_message_a_to_p_aural_allow) - targetSdk < Build.VERSION_CODES.Q && aural && deny -> - Triple( - storagePermGroupIcon, - R.string.media_confirm_dialog_title_a_to_p_aural_deny, - R.string.media_confirm_dialog_message_a_to_p_aural_deny) - targetSdk < Build.VERSION_CODES.Q && visual && allow -> - Triple( - storagePermGroupIcon, - R.string.media_confirm_dialog_title_a_to_p_visual_allow, - R.string.media_confirm_dialog_message_a_to_p_visual_allow) - targetSdk < Build.VERSION_CODES.Q && visual && deny -> - Triple( - storagePermGroupIcon, - R.string.media_confirm_dialog_title_a_to_p_visual_deny, - R.string.media_confirm_dialog_message_a_to_p_visual_deny) - targetSdk <= Build.VERSION_CODES.S_V2 && aural && allow -> - Triple( - visualPermGroupIcon, - R.string.media_confirm_dialog_title_q_to_s_aural_allow, - R.string.media_confirm_dialog_message_q_to_s_aural_allow) - targetSdk <= Build.VERSION_CODES.S_V2 && aural && deny -> - Triple( - visualPermGroupIcon, - R.string.media_confirm_dialog_title_q_to_s_aural_deny, - R.string.media_confirm_dialog_message_q_to_s_aural_deny) - targetSdk <= Build.VERSION_CODES.S_V2 && visual && allow -> - Triple( - auralPermGroupIcon, - R.string.media_confirm_dialog_title_q_to_s_visual_allow, - R.string.media_confirm_dialog_message_q_to_s_visual_allow) - targetSdk <= Build.VERSION_CODES.S_V2 && visual && deny -> - Triple( - auralPermGroupIcon, - R.string.media_confirm_dialog_title_q_to_s_visual_deny, - R.string.media_confirm_dialog_message_q_to_s_visual_deny) - else -> - Triple(0, 0, 0) - } + val (iconId, titleId, messageId) = + when { + targetSdk < Build.VERSION_CODES.Q && aural && allow -> + Triple( + storagePermGroupIcon, + R.string.media_confirm_dialog_title_a_to_p_aural_allow, + R.string.media_confirm_dialog_message_a_to_p_aural_allow + ) + targetSdk < Build.VERSION_CODES.Q && aural && deny -> + Triple( + storagePermGroupIcon, + R.string.media_confirm_dialog_title_a_to_p_aural_deny, + R.string.media_confirm_dialog_message_a_to_p_aural_deny + ) + targetSdk < Build.VERSION_CODES.Q && visual && allow -> + Triple( + storagePermGroupIcon, + R.string.media_confirm_dialog_title_a_to_p_visual_allow, + R.string.media_confirm_dialog_message_a_to_p_visual_allow + ) + targetSdk < Build.VERSION_CODES.Q && visual && deny -> + Triple( + storagePermGroupIcon, + R.string.media_confirm_dialog_title_a_to_p_visual_deny, + R.string.media_confirm_dialog_message_a_to_p_visual_deny + ) + targetSdk <= Build.VERSION_CODES.S_V2 && aural && allow -> + Triple( + visualPermGroupIcon, + R.string.media_confirm_dialog_title_q_to_s_aural_allow, + R.string.media_confirm_dialog_message_q_to_s_aural_allow + ) + targetSdk <= Build.VERSION_CODES.S_V2 && aural && deny -> + Triple( + visualPermGroupIcon, + R.string.media_confirm_dialog_title_q_to_s_aural_deny, + R.string.media_confirm_dialog_message_q_to_s_aural_deny + ) + targetSdk <= Build.VERSION_CODES.S_V2 && visual && allow -> + Triple( + auralPermGroupIcon, + R.string.media_confirm_dialog_title_q_to_s_visual_allow, + R.string.media_confirm_dialog_message_q_to_s_visual_allow + ) + targetSdk <= Build.VERSION_CODES.S_V2 && visual && deny -> + Triple( + auralPermGroupIcon, + R.string.media_confirm_dialog_title_q_to_s_visual_deny, + R.string.media_confirm_dialog_message_q_to_s_visual_deny + ) + else -> Triple(0, 0, 0) + } if (iconId == 0 || titleId == 0 || messageId == 0) { throw UnsupportedOperationException() @@ -962,9 +1048,8 @@ class AppPermissionViewModel( * * @param changeRequest whether to change foreground, background, or both. * @param buttonPressed button pressed to initiate the change, one of - * AppPermissionFragmentActionReported.button_pressed constants + * AppPermissionFragmentActionReported.button_pressed constants * @param oneTime whether the change should show that the permission was selected as one-time - * */ fun onDenyAnyWay(changeRequest: ChangeRequest, buttonPressed: Int, oneTime: Boolean) { val unexpandedGroup = lightAppPermGroup ?: return @@ -977,16 +1062,17 @@ class AppPermissionViewModel( var newGroup = group val oldGroup = group - if (changeRequest andValue ChangeRequest.REVOKE_BACKGROUND != 0 && - group.hasBackgroundGroup) { + if ( + changeRequest andValue ChangeRequest.REVOKE_BACKGROUND != 0 && + group.hasBackgroundGroup + ) { newGroup = KotlinUtils.revokeBackgroundRuntimePermissions(app, newGroup, false, oneTime) if (wasBackgroundGranted) { SafetyNetLogger.logPermissionToggled(newGroup) } - hasDefaultPermissions = hasDefaultPermissions || - group.background.isGrantedByDefault + hasDefaultPermissions = hasDefaultPermissions || group.background.isGrantedByDefault } if (changeRequest andValue ChangeRequest.REVOKE_FOREGROUND != 0) { @@ -1003,9 +1089,7 @@ class AppPermissionViewModel( hasConfirmedRevoke = true } - fullStorageStateLiveData.value?.let { - FullStoragePermissionAppsLiveData.recalculate() - } + fullStorageStateLiveData.value?.let { FullStoragePermissionAppsLiveData.recalculate() } } } @@ -1017,11 +1101,12 @@ class AppPermissionViewModel( fun setAllFilesAccess(granted: Boolean) { val aom = app.getSystemService(AppOpsManager::class.java)!! val uid = lightAppPermGroup?.packageInfo?.uid ?: return - val mode = if (granted) { - MODE_ALLOWED - } else { - MODE_ERRORED - } + val mode = + if (granted) { + MODE_ALLOWED + } else { + MODE_ERRORED + } val fullStorageGrant = fullStorageStateLiveData.value?.isGranted if (fullStorageGrant != null && fullStorageGrant != granted) { aom.setUidMode(OPSTR_MANAGE_EXTERNAL_STORAGE, uid, mode) @@ -1039,8 +1124,9 @@ class AppPermissionViewModel( } private fun getIndividualPermissionDetailResId(group: LightAppPermGroup): Pair<Int, Int> { - return when (val numRevoked = - group.permissions.filter { !it.value.isGrantedIncludingAppOp }.size) { + return when ( + val numRevoked = group.permissions.filter { !it.value.isGrantedIncludingAppOp }.size + ) { 0 -> R.string.permission_revoked_none to numRevoked group.permissions.size -> R.string.permission_revoked_all to numRevoked else -> R.string.permission_revoked_count to numRevoked @@ -1055,8 +1141,8 @@ class AppPermissionViewModel( hasAdmin: Boolean ): Int { val isForegroundPolicyDenied = group.foreground.isPolicyFixed && !group.foreground.isGranted - val isPolicyFullyFixedWithGrantedOrNoBkg = group.isPolicyFullyFixed && - (group.background.isGranted || !group.hasBackgroundGroup) + val isPolicyFullyFixedWithGrantedOrNoBkg = + group.isPolicyFullyFixed && (group.background.isGranted || !group.hasBackgroundGroup) if (group.foreground.isSystemFixed || group.background.isSystemFixed) { return R.string.permission_summary_enabled_system_fixed } else if (hasAdmin) { @@ -1110,11 +1196,17 @@ class AppPermissionViewModel( for ((permName, permission) in oldGroup.permissions) { val newPermission = newGroup.permissions[permName] ?: continue - if (permission.isGrantedIncludingAppOp != newPermission.isGrantedIncludingAppOp || - permission.flags != newPermission.flags) { + if ( + permission.isGrantedIncludingAppOp != newPermission.isGrantedIncludingAppOp || + permission.flags != newPermission.flags + ) { logAppPermissionFragmentActionReported(changeId, newPermission, buttonPressed) - PermissionDecisionStorageImpl.recordPermissionDecision(app.applicationContext, - packageName, permGroupName, newPermission.isGrantedIncludingAppOp) + PermissionDecisionStorageImpl.recordPermissionDecision( + app.applicationContext, + packageName, + permGroupName, + newPermission.isGrantedIncludingAppOp + ) PermissionChangeStorageImpl.recordPermissionChange(packageName) } } @@ -1136,13 +1228,28 @@ class AppPermissionViewModel( buttonPressed: Int ) { val uid = KotlinUtils.getPackageUid(app, packageName, user) ?: return - PermissionControllerStatsLog.write(APP_PERMISSION_FRAGMENT_ACTION_REPORTED, sessionId, - changeId, uid, packageName, permission.permInfo.name, - permission.isGrantedIncludingAppOp, permission.flags, buttonPressed) - Log.v(LOG_TAG, "Permission changed via UI with sessionId=$sessionId changeId=" + - "$changeId uid=$uid packageName=$packageName permission=" + permission.permInfo.name + - " isGranted=" + permission.isGrantedIncludingAppOp + " permissionFlags=" + - permission.flags + " buttonPressed=$buttonPressed") + PermissionControllerStatsLog.write( + APP_PERMISSION_FRAGMENT_ACTION_REPORTED, + sessionId, + changeId, + uid, + packageName, + permission.permInfo.name, + permission.isGrantedIncludingAppOp, + permission.flags, + buttonPressed + ) + Log.v( + LOG_TAG, + "Permission changed via UI with sessionId=$sessionId changeId=" + + "$changeId uid=$uid packageName=$packageName permission=" + + permission.permInfo.name + + " isGranted=" + + permission.isGrantedIncludingAppOp + + " permissionFlags=" + + permission.flags + + " buttonPressed=$buttonPressed" + ) } /** Logs information about this AppPermissionGroup and view session */ @@ -1156,19 +1263,20 @@ class AppPermissionViewModel( uid, packageName, permGroupName, - permissionRationaleShown) + permissionRationaleShown + ) Log.v( LOG_TAG, "AppPermission fragment viewed with sessionId=$sessionId uid=$uid " + "packageName=$packageName permGroupName=$permGroupName " + - "permissionRationaleShown=$permissionRationaleShown") + "permissionRationaleShown=$permissionRationaleShown" + ) } /** - * A partial storage grant happens when: - * An app which doesn't support the photo picker has READ_MEDIA_VISUAL_USER_SELECTED granted, or - * An app which does support the photo picker has READ_MEDIA_VISUAL_USER_SELECTED and/or - * ACCESS_MEDIA_LOCATION granted + * A partial storage grant happens when: An app which doesn't support the photo picker has + * READ_MEDIA_VISUAL_USER_SELECTED granted, or An app which does support the photo picker has + * READ_MEDIA_VISUAL_USER_SELECTED and/or ACCESS_MEDIA_LOCATION granted */ private fun isPartialStorageGrant(group: LightAppPermGroup): Boolean { if (!isPhotoPickerPromptEnabled() || group.permGroupName != READ_MEDIA_VISUAL) { @@ -1177,9 +1285,10 @@ class AppPermissionViewModel( val partialPerms = getPartialStorageGrantPermissionsForGroup(group) - return group.isGranted && group.permissions.values.all { - it.name in partialPerms || (it.name !in partialPerms && !it.isGrantedIncludingAppOp) - } + return group.isGranted && + group.permissions.values.all { + it.name in partialPerms || (it.name !in partialPerms && !it.isGrantedIncludingAppOp) + } } } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/GrantPermissionsViewModel.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/GrantPermissionsViewModel.kt index 8a2216469..3f19db475 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/GrantPermissionsViewModel.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/GrantPermissionsViewModel.kt @@ -39,9 +39,7 @@ import android.health.connect.HealthPermissions.HEALTH_PERMISSION_GROUP import android.os.Build import android.os.Bundle import android.os.Process -import android.os.UserManager import android.permission.PermissionManager -import android.provider.MediaStore import android.util.Log import androidx.core.util.Consumer import androidx.lifecycle.ViewModel @@ -72,9 +70,9 @@ import com.android.permissioncontroller.auto.DrivingDecisionReminderService import com.android.permissioncontroller.permission.data.LightAppPermGroupLiveData import com.android.permissioncontroller.permission.data.LightPackageInfoLiveData import com.android.permissioncontroller.permission.data.PackagePermissionsLiveData -import com.android.permissioncontroller.permission.data.v34.SafetyLabelInfoLiveData import com.android.permissioncontroller.permission.data.SmartUpdateMediatorLiveData import com.android.permissioncontroller.permission.data.get +import com.android.permissioncontroller.permission.data.v34.SafetyLabelInfoLiveData import com.android.permissioncontroller.permission.model.AppPermissionGroup import com.android.permissioncontroller.permission.model.livedatatypes.LightAppPermGroup import com.android.permissioncontroller.permission.model.livedatatypes.LightPackageInfo @@ -118,25 +116,27 @@ import com.android.permissioncontroller.permission.ui.ManagePermissionsActivity import com.android.permissioncontroller.permission.ui.ManagePermissionsActivity.EXTRA_RESULT_PERMISSION_INTERACTED import com.android.permissioncontroller.permission.ui.ManagePermissionsActivity.EXTRA_RESULT_PERMISSION_RESULT import com.android.permissioncontroller.permission.ui.v34.PermissionRationaleActivity -import com.android.permissioncontroller.permission.utils.v31.AdminRestrictedPermissionsUtils import com.android.permissioncontroller.permission.utils.KotlinUtils import com.android.permissioncontroller.permission.utils.KotlinUtils.getDefaultPrecision import com.android.permissioncontroller.permission.utils.KotlinUtils.grantBackgroundRuntimePermissions import com.android.permissioncontroller.permission.utils.KotlinUtils.grantForegroundRuntimePermissions import com.android.permissioncontroller.permission.utils.KotlinUtils.isLocationAccuracyEnabled +import com.android.permissioncontroller.permission.utils.KotlinUtils.isPhotoPickerPromptEnabled import com.android.permissioncontroller.permission.utils.KotlinUtils.isPhotoPickerPromptSupported +import com.android.permissioncontroller.permission.utils.KotlinUtils.openPhotoPickerForApp import com.android.permissioncontroller.permission.utils.KotlinUtils.revokeBackgroundRuntimePermissions import com.android.permissioncontroller.permission.utils.KotlinUtils.revokeForegroundRuntimePermissions import com.android.permissioncontroller.permission.utils.PermissionMapping import com.android.permissioncontroller.permission.utils.PermissionMapping.getPartialStorageGrantPermissionsForGroup import com.android.permissioncontroller.permission.utils.SafetyNetLogger import com.android.permissioncontroller.permission.utils.Utils +import com.android.permissioncontroller.permission.utils.v31.AdminRestrictedPermissionsUtils import com.android.permissioncontroller.permission.utils.v34.SafetyLabelUtils /** - * ViewModel for the GrantPermissionsActivity. Tracks all permission groups that are affected by - * the permissions requested by the user, and generates a RequestInfo object for each group, if - * action is needed. It will not return any data if one of the requests is malformed. + * ViewModel for the GrantPermissionsActivity. Tracks all permission groups that are affected by the + * permissions requested by the user, and generates a RequestInfo object for each group, if action + * is needed. It will not return any data if one of the requests is malformed. * * @param app: The current application * @param packageName: The packageName permissions are being requested for @@ -155,9 +155,12 @@ class GrantPermissionsViewModel( private val user = Process.myUserHandle() private val packageInfoLiveData = LightPackageInfoLiveData[packageName, user] private val safetyLabelInfoLiveData = - if (SdkLevel.isAtLeastU() && requestedPermissions - .mapNotNull { PermissionMapping.getGroupOfPlatformPermission(it) } - .any { PermissionMapping.isSafetyLabelAwarePermissionGroup(it) }) { + if ( + SdkLevel.isAtLeastU() && + requestedPermissions + .mapNotNull { PermissionMapping.getGroupOfPlatformPermission(it) } + .any { PermissionMapping.isSafetyLabelAwarePermissionGroup(it) } + ) { SafetyLabelInfoLiveData[packageName, user] } else { null @@ -201,416 +204,492 @@ class GrantPermissionsViewModel( var activityResultCallback: Consumer<Intent>? = null - /** - * A LiveData which holds a list of the currently pending RequestInfos - */ - val requestInfosLiveData = object : - SmartUpdateMediatorLiveData<List<RequestInfo>>() { - private val LOG_TAG = GrantPermissionsViewModel::class.java.simpleName - private val packagePermissionsLiveData = PackagePermissionsLiveData[packageName, user] - - init { - addSource(packagePermissionsLiveData) { onPackageLoaded() } - addSource(packageInfoLiveData) { onPackageLoaded() } - if (safetyLabelInfoLiveData != null) { - addSource(safetyLabelInfoLiveData) { onPackageLoaded() } - } - - // Load package state, if available - onPackageLoaded() - } + /** A LiveData which holds a list of the currently pending RequestInfos */ + val requestInfosLiveData = + object : SmartUpdateMediatorLiveData<List<RequestInfo>>() { + private val LOG_TAG = GrantPermissionsViewModel::class.java.simpleName + private val packagePermissionsLiveData = PackagePermissionsLiveData[packageName, user] + + init { + addSource(packagePermissionsLiveData) { onPackageLoaded() } + addSource(packageInfoLiveData) { onPackageLoaded() } + if (safetyLabelInfoLiveData != null) { + addSource(safetyLabelInfoLiveData) { onPackageLoaded() } + } - private fun onPackageLoaded() { - if (packageInfoLiveData.isStale || - packagePermissionsLiveData.isStale || - (safetyLabelInfoLiveData != null && safetyLabelInfoLiveData.isStale)) { - return + // Load package state, if available + onPackageLoaded() } - val groups = packagePermissionsLiveData.value - val pI = packageInfoLiveData.value - if (groups == null || groups.isEmpty() || pI == null) { - Log.e(LOG_TAG, "Package $packageName not found") - value = null - return - } - packageInfo = pI + private fun onPackageLoaded() { + if ( + packageInfoLiveData.isStale || + packagePermissionsLiveData.isStale || + (safetyLabelInfoLiveData != null && safetyLabelInfoLiveData.isStale) + ) { + return + } - if (packageInfo.requestedPermissions.isEmpty() || - packageInfo.targetSdkVersion < Build.VERSION_CODES.M) { - Log.e(LOG_TAG, "Package $packageName has no requested permissions, or " + - "is a pre-M app") - value = null - return - } + val groups = packagePermissionsLiveData.value + val pI = packageInfoLiveData.value + if (groups == null || groups.isEmpty() || pI == null) { + Log.e(LOG_TAG, "Package $packageName not found") + value = null + return + } + packageInfo = pI + + if ( + packageInfo.requestedPermissions.isEmpty() || + packageInfo.targetSdkVersion < Build.VERSION_CODES.M + ) { + Log.e( + LOG_TAG, + "Package $packageName has no requested permissions, or " + "is a pre-M app" + ) + value = null + return + } - val allAffectedPermissions = requestedPermissions.toMutableSet() - for (requestedPerm in requestedPermissions) { - allAffectedPermissions.addAll(computeAffectedPermissions(requestedPerm, groups)) - } - unfilteredAffectedPermissions = allAffectedPermissions.toList() + val allAffectedPermissions = requestedPermissions.toMutableSet() + for (requestedPerm in requestedPermissions) { + allAffectedPermissions.addAll(computeAffectedPermissions(requestedPerm, groups)) + } + unfilteredAffectedPermissions = allAffectedPermissions.toList() - setAppPermGroupsLiveDatas(groups.toMutableMap().apply { - remove(PackagePermissionsLiveData.NON_RUNTIME_NORMAL_PERMS) - }) + setAppPermGroupsLiveDatas( + groups.toMutableMap().apply { + remove(PackagePermissionsLiveData.NON_RUNTIME_NORMAL_PERMS) + } + ) - for (splitPerm in app.getSystemService( - PermissionManager::class.java)!!.splitPermissions) { - splitPermissionTargetSdkMap[splitPerm.splitPermission] = splitPerm.targetSdk + for (splitPerm in + app.getSystemService(PermissionManager::class.java)!!.splitPermissions) { + splitPermissionTargetSdkMap[splitPerm.splitPermission] = splitPerm.targetSdk + } } - } - private fun setAppPermGroupsLiveDatas(groups: Map<String, List<String>>) { + private fun setAppPermGroupsLiveDatas(groups: Map<String, List<String>>) { - val requestedGroups = groups.filter { (_, perms) -> - perms.any { it in unfilteredAffectedPermissions } - } + val requestedGroups = + groups.filter { (_, perms) -> + perms.any { it in unfilteredAffectedPermissions } + } - if (requestedGroups.isEmpty()) { - Log.e(LOG_TAG, "None of " + - "$unfilteredAffectedPermissions in $groups") - value = null - return - } + if (requestedGroups.isEmpty()) { + Log.e(LOG_TAG, "None of " + "$unfilteredAffectedPermissions in $groups") + value = null + return + } - val getLiveDataFun = { groupName: String -> - LightAppPermGroupLiveData[packageName, groupName, user] + val getLiveDataFun = { groupName: String -> + LightAppPermGroupLiveData[packageName, groupName, user] + } + setSourcesToDifference(requestedGroups.keys, appPermGroupLiveDatas, getLiveDataFun) } - setSourcesToDifference(requestedGroups.keys, appPermGroupLiveDatas, getLiveDataFun) - } - override fun onUpdate() { - if (appPermGroupLiveDatas.any { it.value.isStale }) { - return - } - var newGroups = false - for ((groupName, groupLiveData) in appPermGroupLiveDatas) { - val appPermGroup = groupLiveData.value - if (appPermGroup == null || groupName in permGroupsToSkip) { - if (appPermGroup == null) { - Log.e(LOG_TAG, "Group $packageName $groupName invalid") - } - groupStates[groupName to true]?.state = STATE_SKIPPED - groupStates[groupName to false]?.state = STATE_SKIPPED - continue + override fun onUpdate() { + if (appPermGroupLiveDatas.any { it.value.isStale }) { + return } - - packageInfo = appPermGroup.packageInfo - - val states = groupStates.filter { it.key.first == groupName } - if (states.isNotEmpty()) { - for ((key, state) in states) { - val allAffectedGranted = state.affectedPermissions.all { perm -> - appPermGroup.permissions[perm]?.isGrantedIncludingAppOp == true && - appPermGroup.permissions[perm]?.isRevokeWhenRequested == false - } - if (allAffectedGranted) { - groupStates[key]!!.state = STATE_ALLOWED + var newGroups = false + for ((groupName, groupLiveData) in appPermGroupLiveDatas) { + val appPermGroup = groupLiveData.value + if (appPermGroup == null || groupName in permGroupsToSkip) { + if (appPermGroup == null) { + Log.e(LOG_TAG, "Group $packageName $groupName invalid") } + groupStates[groupName to true]?.state = STATE_SKIPPED + groupStates[groupName to false]?.state = STATE_SKIPPED + continue } - } else { - newGroups = true - } - } - - if (newGroups) { - groupStates = getRequiredGroupStates( - appPermGroupLiveDatas.mapNotNull { it.value.value }) - } - setRequestInfosFromGroupStates() - } - private fun setRequestInfosFromGroupStates() { - val requestInfos = mutableListOf<RequestInfo>() - for ((key, groupState) in groupStates) { - val groupInfo = groupState.group.permGroupInfo - val (groupName, isBackground) = key - if (groupState.state != STATE_UNKNOWN) { - continue - } - val fgState = groupStates[groupName to false] - val bgState = groupStates[groupName to true] - var needFgPermissions = false - var needBgPermissions = false - var isFgUserSet = false - var isBgUserSet = false - var minSdkForOrderedSplitPermissions = Build.VERSION_CODES.R - if (fgState?.group != null) { - val fgGroup = fgState.group - for (perm in fgState.affectedPermissions) { - minSdkForOrderedSplitPermissions = maxOf(minSdkForOrderedSplitPermissions, - splitPermissionTargetSdkMap.getOrDefault(perm, 0)) - if (fgGroup.permissions[perm]?.isGrantedIncludingAppOp == false) { - // If any of the requested permissions is not granted, - // needFgPermissions = true - needFgPermissions = true - // If any of the requested permission's UserSet is true and the - // permission is not granted, isFgUserSet = true. - if (fgGroup.permissions[perm]?.isUserSet == true) { - isFgUserSet = true + packageInfo = appPermGroup.packageInfo + + val states = groupStates.filter { it.key.first == groupName } + if (states.isNotEmpty()) { + for ((key, state) in states) { + val allAffectedGranted = + state.affectedPermissions.all { perm -> + appPermGroup.permissions[perm]?.isGrantedIncludingAppOp == + true && + appPermGroup.permissions[perm]?.isRevokeWhenRequested == + false + } + if (allAffectedGranted) { + groupStates[key]!!.state = STATE_ALLOWED } } + } else { + newGroups = true } } - if (bgState?.group?.background?.isGranted == false) { - needBgPermissions = true - isBgUserSet = bgState.group.background.isUserSet + + if (newGroups) { + groupStates = + getRequiredGroupStates(appPermGroupLiveDatas.mapNotNull { it.value.value }) } + setRequestInfosFromGroupStates() + } - val buttonVisibilities = MutableList(NEXT_BUTTON) { false } - buttonVisibilities[ALLOW_BUTTON] = true - buttonVisibilities[DENY_BUTTON] = true - buttonVisibilities[ALLOW_ONE_TIME_BUTTON] = - PermissionMapping.supportsOneTimeGrant(groupName) - var message = RequestMessage.FG_MESSAGE - // Whether or not to use the foreground, background, or no detail message. - // null == - var detailMessage = RequestMessage.NO_MESSAGE - - if (KotlinUtils.isPhotoPickerPromptEnabled() && - groupState.group.permGroupName == READ_MEDIA_VISUAL && - groupState.group.packageInfo.targetSdkVersion >= Build.VERSION_CODES.TIRAMISU) { - // If the USER_SELECTED permission is user fixed and granted, or the app is only - // requesting USER_SELECTED, direct straight to photo picker - val userPerm = groupState.group.permissions[READ_MEDIA_VISUAL_USER_SELECTED] - if ((userPerm?.isUserFixed == true && userPerm.isGrantedIncludingAppOp) || - groupState.affectedPermissions == listOf(READ_MEDIA_VISUAL_USER_SELECTED)) { - requestInfos.add(RequestInfo(groupInfo, openPhotoPicker = true)) + private fun setRequestInfosFromGroupStates() { + val requestInfos = mutableListOf<RequestInfo>() + for ((key, groupState) in groupStates) { + val groupInfo = groupState.group.permGroupInfo + val (groupName, isBackground) = key + if (groupState.state != STATE_UNKNOWN) { continue - } else if (isPartialStorageGrant(groupState.group)) { - // More photos dialog - message = RequestMessage.MORE_PHOTOS_MESSAGE - buttonVisibilities[DENY_AND_DONT_ASK_AGAIN_BUTTON] = false - buttonVisibilities[DENY_BUTTON] = false - buttonVisibilities[DONT_ALLOW_MORE_SELECTED_BUTTON] = true - } else { - // Standard photos dialog - buttonVisibilities[DENY_AND_DONT_ASK_AGAIN_BUTTON] = isFgUserSet - buttonVisibilities[DENY_BUTTON] = !isFgUserSet } - buttonVisibilities[ALLOW_SELECTED_BUTTON] = true - buttonVisibilities[ALLOW_BUTTON] = false - buttonVisibilities[ALLOW_ALL_BUTTON] = true - } else if (groupState.group.packageInfo.targetSdkVersion >= - minSdkForOrderedSplitPermissions) { - if (isBackground || Utils.hasPermWithBackgroundModeCompat(groupState.group)) { - if (needFgPermissions) { - if (needBgPermissions) { - if (groupState.group.permGroupName - .equals(Manifest.permission_group.CAMERA) || - groupState.group.permGroupName - .equals(Manifest.permission_group.MICROPHONE)) { - if (groupState.group.packageInfo.targetSdkVersion >= - Build.VERSION_CODES.S) { - Log.e(LOG_TAG, + val fgState = groupStates[groupName to false] + val bgState = groupStates[groupName to true] + var needFgPermissions = false + var needBgPermissions = false + var isFgUserSet = false + var isBgUserSet = false + var minSdkForOrderedSplitPermissions = Build.VERSION_CODES.R + if (fgState?.group != null) { + val fgGroup = fgState.group + for (perm in fgState.affectedPermissions) { + minSdkForOrderedSplitPermissions = + maxOf( + minSdkForOrderedSplitPermissions, + splitPermissionTargetSdkMap.getOrDefault(perm, 0) + ) + if (fgGroup.permissions[perm]?.isGrantedIncludingAppOp == false) { + // If any of the requested permissions is not granted, + // needFgPermissions = true + needFgPermissions = true + // If any of the requested permission's UserSet is true and the + // permission is not granted, isFgUserSet = true. + if (fgGroup.permissions[perm]?.isUserSet == true) { + isFgUserSet = true + } + } + } + } + if (bgState?.group?.background?.isGranted == false) { + needBgPermissions = true + isBgUserSet = bgState.group.background.isUserSet + } + + val buttonVisibilities = MutableList(NEXT_BUTTON) { false } + buttonVisibilities[ALLOW_BUTTON] = true + buttonVisibilities[DENY_BUTTON] = true + buttonVisibilities[ALLOW_ONE_TIME_BUTTON] = + PermissionMapping.supportsOneTimeGrant(groupName) + var message = RequestMessage.FG_MESSAGE + // Whether or not to use the foreground, background, or no detail message. + // null == + var detailMessage = RequestMessage.NO_MESSAGE + + if ( + groupState.group.permGroupName == READ_MEDIA_VISUAL && + shouldShowPhotoPickerPromptForApp(groupState.group) + ) { + // If the USER_SELECTED permission is user fixed and granted, or the app is + // only + // requesting USER_SELECTED, direct straight to photo picker + val userPerm = groupState.group.permissions[READ_MEDIA_VISUAL_USER_SELECTED] + if ( + (userPerm?.isUserFixed == true && userPerm.isGrantedIncludingAppOp) || + groupState.affectedPermissions == + listOf(READ_MEDIA_VISUAL_USER_SELECTED) + ) { + requestInfos.add(RequestInfo(groupInfo, openPhotoPicker = true)) + continue + } else if (isPartialStorageGrant(groupState.group)) { + // More photos dialog + message = RequestMessage.MORE_PHOTOS_MESSAGE + buttonVisibilities[DENY_AND_DONT_ASK_AGAIN_BUTTON] = false + buttonVisibilities[DENY_BUTTON] = false + buttonVisibilities[DONT_ALLOW_MORE_SELECTED_BUTTON] = true + } else { + // Standard photos dialog + buttonVisibilities[DENY_AND_DONT_ASK_AGAIN_BUTTON] = isFgUserSet + buttonVisibilities[DENY_BUTTON] = !isFgUserSet + } + buttonVisibilities[ALLOW_SELECTED_BUTTON] = true + buttonVisibilities[ALLOW_BUTTON] = false + buttonVisibilities[ALLOW_ALL_BUTTON] = true + } else if ( + groupState.group.packageInfo.targetSdkVersion >= + minSdkForOrderedSplitPermissions + ) { + if ( + isBackground || Utils.hasPermWithBackgroundModeCompat(groupState.group) + ) { + if (needFgPermissions) { + if (needBgPermissions) { + if ( + groupState.group.permGroupName.equals( + Manifest.permission_group.CAMERA + ) || + groupState.group.permGroupName.equals( + Manifest.permission_group.MICROPHONE + ) + ) { + if ( + groupState.group.packageInfo.targetSdkVersion >= + Build.VERSION_CODES.S + ) { + Log.e( + LOG_TAG, "For S apps, background permissions must be " + - "requested after foreground permissions are" + - " already granted") - value = null - return - } else { - // Case: sdk < S, BG&FG mic/camera permission requested - buttonVisibilities[ALLOW_BUTTON] = false - buttonVisibilities[ALLOW_FOREGROUND_BUTTON] = true - buttonVisibilities[DENY_BUTTON] = !isFgUserSet - buttonVisibilities[DENY_AND_DONT_ASK_AGAIN_BUTTON] = + "requested after foreground permissions are" + + " already granted" + ) + value = null + return + } else { + // Case: sdk < S, BG&FG mic/camera permission requested + buttonVisibilities[ALLOW_BUTTON] = false + buttonVisibilities[ALLOW_FOREGROUND_BUTTON] = true + buttonVisibilities[DENY_BUTTON] = !isFgUserSet + buttonVisibilities[DENY_AND_DONT_ASK_AGAIN_BUTTON] = isFgUserSet - if (needBgPermissions) { - // Case: sdk < R, BG/FG permission requesting both - message = RequestMessage.BG_MESSAGE - detailMessage = RequestMessage.BG_MESSAGE + if (needBgPermissions) { + // Case: sdk < R, BG/FG permission requesting both + message = RequestMessage.BG_MESSAGE + detailMessage = RequestMessage.BG_MESSAGE + } } + } else { + // Shouldn't be reached as background must be requested as a + // singleton + Log.e( + LOG_TAG, + "For R+ apps, background permissions must be " + + "requested after foreground permissions are already" + + " granted" + ) + value = null + return } } else { - // Shouldn't be reached as background must be requested as a - // singleton - Log.e(LOG_TAG, "For R+ apps, background permissions must be " + - "requested after foreground permissions are already" + - " granted") - value = null - return + buttonVisibilities[ALLOW_BUTTON] = false + buttonVisibilities[ALLOW_FOREGROUND_BUTTON] = true + buttonVisibilities[DENY_BUTTON] = !isFgUserSet + buttonVisibilities[DENY_AND_DONT_ASK_AGAIN_BUTTON] = isFgUserSet + } + } else if (needBgPermissions) { + // Case: sdk >= R, BG/FG permission requesting BG only + if ( + storedState != null && + storedState.containsKey( + getInstanceStateKey( + groupInfo.name, + groupState.isBackground + ) + ) + ) { + // If we're restoring state, and we had this groupInfo in our + // previous state, that means that we likely sent the user to + // settings already. Don't send the user back. + permGroupsToSkip.add(groupInfo.name) + groupState.state = STATE_SKIPPED + } else { + requestInfos.add( + RequestInfo(groupInfo, sendToSettingsImmediately = true) + ) } + continue } else { + // Not reached as the permissions should be auto-granted + value = null + return + } + } else { + // Case: sdk >= R, Requesting normal permission + buttonVisibilities[DENY_BUTTON] = !isFgUserSet + buttonVisibilities[DENY_AND_DONT_ASK_AGAIN_BUTTON] = isFgUserSet + } + } else { + if ( + isBackground || Utils.hasPermWithBackgroundModeCompat(groupState.group) + ) { + if (needFgPermissions) { + // Case: sdk < R, BG/FG permission requesting both or FG only buttonVisibilities[ALLOW_BUTTON] = false buttonVisibilities[ALLOW_FOREGROUND_BUTTON] = true buttonVisibilities[DENY_BUTTON] = !isFgUserSet buttonVisibilities[DENY_AND_DONT_ASK_AGAIN_BUTTON] = isFgUserSet - } - } else if (needBgPermissions) { - // Case: sdk >= R, BG/FG permission requesting BG only - if (storedState != null && storedState.containsKey(getInstanceStateKey( - groupInfo.name, groupState.isBackground))) { - // If we're restoring state, and we had this groupInfo in our - // previous state, that means that we likely sent the user to - // settings already. Don't send the user back. - permGroupsToSkip.add(groupInfo.name) - groupState.state = STATE_SKIPPED + if (needBgPermissions) { + // Case: sdk < R, BG/FG permission requesting both + message = RequestMessage.BG_MESSAGE + detailMessage = RequestMessage.BG_MESSAGE + } + } else if (needBgPermissions) { + // Case: sdk < R, BG/FG permission requesting BG only + if (!groupState.group.foreground.isGranted) { + Log.e( + LOG_TAG, + "Background permissions can't be requested " + + "solely before foreground permissions are granted." + ) + value = null + return + } + message = RequestMessage.UPGRADE_MESSAGE + detailMessage = RequestMessage.UPGRADE_MESSAGE + buttonVisibilities[ALLOW_BUTTON] = false + buttonVisibilities[DENY_BUTTON] = false + buttonVisibilities[ALLOW_ONE_TIME_BUTTON] = false + if (groupState.group.isOneTime) { + buttonVisibilities[NO_UPGRADE_OT_BUTTON] = !isBgUserSet + buttonVisibilities[NO_UPGRADE_OT_AND_DONT_ASK_AGAIN_BUTTON] = + isBgUserSet + } else { + buttonVisibilities[NO_UPGRADE_BUTTON] = !isBgUserSet + buttonVisibilities[NO_UPGRADE_AND_DONT_ASK_AGAIN_BUTTON] = + isBgUserSet + } } else { - requestInfos.add(RequestInfo( - groupInfo, sendToSettingsImmediately = true)) + // Not reached as the permissions should be auto-granted + value = null + return } - continue } else { - // Not reached as the permissions should be auto-granted - value = null - return - } - } else { - // Case: sdk >= R, Requesting normal permission - buttonVisibilities[DENY_BUTTON] = !isFgUserSet - buttonVisibilities[DENY_AND_DONT_ASK_AGAIN_BUTTON] = isFgUserSet - } - } else { - if (isBackground || Utils.hasPermWithBackgroundModeCompat(groupState.group)) { - if (needFgPermissions) { - // Case: sdk < R, BG/FG permission requesting both or FG only - buttonVisibilities[ALLOW_BUTTON] = false - buttonVisibilities[ALLOW_FOREGROUND_BUTTON] = true - buttonVisibilities[DENY_BUTTON] = !isFgUserSet - buttonVisibilities[DENY_AND_DONT_ASK_AGAIN_BUTTON] = isFgUserSet - if (needBgPermissions) { - // Case: sdk < R, BG/FG permission requesting both - message = RequestMessage.BG_MESSAGE - detailMessage = RequestMessage.BG_MESSAGE - } - } else if (needBgPermissions) { - // Case: sdk < R, BG/FG permission requesting BG only - if (!groupState.group.foreground.isGranted) { - Log.e(LOG_TAG, "Background permissions can't be requested " + - "solely before foreground permissions are granted.") + // If no permissions needed, do nothing + if (!needFgPermissions && !needBgPermissions) { value = null return } - message = RequestMessage.UPGRADE_MESSAGE - detailMessage = RequestMessage.UPGRADE_MESSAGE - buttonVisibilities[ALLOW_BUTTON] = false - buttonVisibilities[DENY_BUTTON] = false - buttonVisibilities[ALLOW_ONE_TIME_BUTTON] = false - if (groupState.group.isOneTime) { - buttonVisibilities[NO_UPGRADE_OT_BUTTON] = !isBgUserSet - buttonVisibilities[NO_UPGRADE_OT_AND_DONT_ASK_AGAIN_BUTTON] = - isBgUserSet - } else { - buttonVisibilities[NO_UPGRADE_BUTTON] = !isBgUserSet - buttonVisibilities[NO_UPGRADE_AND_DONT_ASK_AGAIN_BUTTON] = - isBgUserSet - } - } else { - // Not reached as the permissions should be auto-granted - value = null - return - } - } else { - // If no permissions needed, do nothing - if (!needFgPermissions && !needBgPermissions) { - value = null - return + // Case: sdk < R, Requesting normal permission + buttonVisibilities[DENY_BUTTON] = !isFgUserSet + buttonVisibilities[DENY_AND_DONT_ASK_AGAIN_BUTTON] = isFgUserSet } - // Case: sdk < R, Requesting normal permission - buttonVisibilities[DENY_BUTTON] = !isFgUserSet - buttonVisibilities[DENY_AND_DONT_ASK_AGAIN_BUTTON] = isFgUserSet } - } - buttonVisibilities[LINK_TO_SETTINGS] = - detailMessage != RequestMessage.NO_MESSAGE - - // Show location permission dialogs based on location permissions - val locationVisibilities = MutableList(NEXT_LOCATION_DIALOG) { false } - if (groupState.group.permGroupName == LOCATION && - isLocationAccuracyEnabledForApp(groupState.group)) { - if (needFgPermissions) { - locationVisibilities[LOCATION_ACCURACY_LAYOUT] = true - if (fgState != null && - fgState.affectedPermissions.contains(ACCESS_FINE_LOCATION)) { - val coarseLocationPerm = - groupState.group.allPermissions[ACCESS_COARSE_LOCATION] - if (coarseLocationPerm?.isGrantedIncludingAppOp == true) { - // Upgrade flow - locationVisibilities[DIALOG_WITH_FINE_LOCATION_ONLY] = true - message = RequestMessage.FG_FINE_LOCATION_MESSAGE - // If COARSE was granted one time, hide 'While in use' button - if (coarseLocationPerm.isOneTime) { - buttonVisibilities[ALLOW_FOREGROUND_BUTTON] = false - } - } else { - if (!fgState.affectedPermissions.contains(ACCESS_COARSE_LOCATION)) { - Log.e(LOG_TAG, "ACCESS_FINE_LOCATION must be requested " + - "with ACCESS_COARSE_LOCATION.") - value = null - return - } - // Normal flow with both Coarse and Fine locations - locationVisibilities[DIALOG_WITH_BOTH_LOCATIONS] = true - // Steps to decide location accuracy default state - // 1. If none of the FINE and COARSE isSelectedLocationAccuracy - // flags is set, then use default precision from device config. - // 2. Otherwise set to whichever isSelectedLocationAccuracy is true. - val fineLocationPerm = + buttonVisibilities[LINK_TO_SETTINGS] = + detailMessage != RequestMessage.NO_MESSAGE + + // Show location permission dialogs based on location permissions + val locationVisibilities = MutableList(NEXT_LOCATION_DIALOG) { false } + if ( + groupState.group.permGroupName == LOCATION && + isLocationAccuracyEnabledForApp(groupState.group) + ) { + if (needFgPermissions) { + locationVisibilities[LOCATION_ACCURACY_LAYOUT] = true + if ( + fgState != null && + fgState.affectedPermissions.contains(ACCESS_FINE_LOCATION) + ) { + val coarseLocationPerm = + groupState.group.allPermissions[ACCESS_COARSE_LOCATION] + if (coarseLocationPerm?.isGrantedIncludingAppOp == true) { + // Upgrade flow + locationVisibilities[DIALOG_WITH_FINE_LOCATION_ONLY] = true + message = RequestMessage.FG_FINE_LOCATION_MESSAGE + // If COARSE was granted one time, hide 'While in use' button + if (coarseLocationPerm.isOneTime) { + buttonVisibilities[ALLOW_FOREGROUND_BUTTON] = false + } + } else { + if ( + !fgState.affectedPermissions.contains( + ACCESS_COARSE_LOCATION + ) + ) { + Log.e( + LOG_TAG, + "ACCESS_FINE_LOCATION must be requested " + + "with ACCESS_COARSE_LOCATION." + ) + value = null + return + } + // Normal flow with both Coarse and Fine locations + locationVisibilities[DIALOG_WITH_BOTH_LOCATIONS] = true + // Steps to decide location accuracy default state + // 1. If none of the FINE and COARSE isSelectedLocationAccuracy + // flags is set, then use default precision from device + // config. + // 2. Otherwise set to whichever isSelectedLocationAccuracy is + // true. + val fineLocationPerm = groupState.group.allPermissions[ACCESS_FINE_LOCATION] - if (coarseLocationPerm?.isSelectedLocationAccuracy == false && - fineLocationPerm?.isSelectedLocationAccuracy == false) { - if (getDefaultPrecision()) { - locationVisibilities[FINE_RADIO_BUTTON] = true - } else { + if ( + coarseLocationPerm?.isSelectedLocationAccuracy == false && + fineLocationPerm?.isSelectedLocationAccuracy == false + ) { + if (getDefaultPrecision()) { + locationVisibilities[FINE_RADIO_BUTTON] = true + } else { + locationVisibilities[COARSE_RADIO_BUTTON] = true + } + } else if ( + coarseLocationPerm?.isSelectedLocationAccuracy == true + ) { locationVisibilities[COARSE_RADIO_BUTTON] = true + } else { + locationVisibilities[FINE_RADIO_BUTTON] = true } - } else if (coarseLocationPerm?.isSelectedLocationAccuracy == true) { - locationVisibilities[COARSE_RADIO_BUTTON] = true - } else { - locationVisibilities[FINE_RADIO_BUTTON] = true } + } else if ( + fgState != null && + fgState.affectedPermissions.contains(ACCESS_COARSE_LOCATION) + ) { + // Request Coarse only + locationVisibilities[DIALOG_WITH_COARSE_LOCATION_ONLY] = true + message = RequestMessage.FG_COARSE_LOCATION_MESSAGE } - } else if (fgState != null && fgState.affectedPermissions - .contains(ACCESS_COARSE_LOCATION)) { - // Request Coarse only - locationVisibilities[DIALOG_WITH_COARSE_LOCATION_ONLY] = true - message = RequestMessage.FG_COARSE_LOCATION_MESSAGE } } - } - if (SdkLevel.isAtLeastT()) { - // If app is T+, requests for the STORAGE group are ignored - if (packageInfo.targetSdkVersion > Build.VERSION_CODES.S_V2 && - groupState.group.permGroupName == Manifest.permission_group.STORAGE) { - continue - } - // If app is <T and requests STORAGE, grant dialogs has special text - if (groupState.group.permGroupName in - PermissionMapping.STORAGE_SUPERGROUP_PERMISSIONS) { - if (packageInfo.targetSdkVersion < Build.VERSION_CODES.Q) { - message = RequestMessage.STORAGE_SUPERGROUP_MESSAGE_PRE_Q - } else if (packageInfo.targetSdkVersion <= Build.VERSION_CODES.S_V2) { - message = RequestMessage.STORAGE_SUPERGROUP_MESSAGE_Q_TO_S + if (SdkLevel.isAtLeastT()) { + // If app is T+, requests for the STORAGE group are ignored + if ( + packageInfo.targetSdkVersion > Build.VERSION_CODES.S_V2 && + groupState.group.permGroupName == Manifest.permission_group.STORAGE + ) { + continue + } + // If app is <T and requests STORAGE, grant dialogs has special text + if ( + groupState.group.permGroupName in + PermissionMapping.STORAGE_SUPERGROUP_PERMISSIONS + ) { + if (packageInfo.targetSdkVersion < Build.VERSION_CODES.Q) { + message = RequestMessage.STORAGE_SUPERGROUP_MESSAGE_PRE_Q + } else if (packageInfo.targetSdkVersion <= Build.VERSION_CODES.S_V2) { + message = RequestMessage.STORAGE_SUPERGROUP_MESSAGE_Q_TO_S + } } } - } - - val safetyLabel = safetyLabelInfoLiveData?.value?.safetyLabel - val showPermissionRationale = - shouldShowPermissionRationale(safetyLabel, groupState.group.permGroupName) - buttonVisibilities[LINK_TO_PERMISSION_RATIONALE] = showPermissionRationale - - requestInfos.add(RequestInfo( - groupInfo, - buttonVisibilities, - locationVisibilities, - message, - detailMessage)) - } - sortPermissionGroups(requestInfos) + val safetyLabel = safetyLabelInfoLiveData?.value?.safetyLabel + val showPermissionRationale = + shouldShowPermissionRationale(safetyLabel, groupState.group.permGroupName) + buttonVisibilities[LINK_TO_PERMISSION_RATIONALE] = showPermissionRationale + + requestInfos.add( + RequestInfo( + groupInfo, + buttonVisibilities, + locationVisibilities, + message, + detailMessage + ) + ) + } - value = if (requestInfos.any { it.sendToSettingsImmediately } && - requestInfos.size > 1) { - Log.e(LOG_TAG, "For R+ apps, background permissions must be requested " + - "individually") - null - } else { - requestInfos + sortPermissionGroups(requestInfos) + + value = + if ( + requestInfos.any { it.sendToSettingsImmediately } && requestInfos.size > 1 + ) { + Log.e( + LOG_TAG, + "For R+ apps, background permissions must be requested " + + "individually" + ) + null + } else { + requestInfos + } } } - } fun sortPermissionGroups(requestInfos: MutableList<RequestInfo>) { requestInfos.sortWith { rhs, lhs -> @@ -618,8 +697,8 @@ class GrantPermissionsViewModel( val lhsHasOneTime = lhs.buttonVisibilities[ALLOW_ONE_TIME_BUTTON] if (rhsHasOneTime && !lhsHasOneTime) { -1 - } else if ((!rhsHasOneTime && lhsHasOneTime) || - Utils.isHealthPermissionGroup(rhs.groupName) + } else if ( + (!rhsHasOneTime && lhsHasOneTime) || Utils.isHealthPermissionGroup(rhs.groupName) ) { 1 } else { @@ -636,34 +715,37 @@ class GrantPermissionsViewModel( return false } - val purposes = SafetyLabelUtils.getSafetyLabelSharingPurposesForGroup(safetyLabel, - permissionGroupName) + val purposes = + SafetyLabelUtils.getSafetyLabelSharingPurposesForGroup(safetyLabel, permissionGroupName) return purposes.isNotEmpty() } - /** - * Converts a list of LightAppPermGroups into a list of GroupStates - */ + /** Converts a list of LightAppPermGroups into a list of GroupStates */ private fun getRequiredGroupStates( groups: List<LightAppPermGroup> ): MutableMap<Pair<String, Boolean>, GroupState> { val groupStates = mutableMapOf<Pair<String, Boolean>, GroupState>() - val filteredPermissions = unfilteredAffectedPermissions.filter { perm -> - val group = getGroupWithPerm(perm, groups) - group != null && isPermissionGrantableAndNotFixed(perm, group) - } + val filteredPermissions = + unfilteredAffectedPermissions.filter { perm -> + val group = getGroupWithPerm(perm, groups) + group != null && isPermissionGrantableAndNotFixed(perm, group) + } for (perm in filteredPermissions) { val group = getGroupWithPerm(perm, groups)!! val isBackground = perm in group.backgroundPermNames - val groupStateInfo = groupStates.getOrPut(group.permGroupName to isBackground) { - GroupState(group, isBackground) - } + val groupStateInfo = + groupStates.getOrPut(group.permGroupName to isBackground) { + GroupState(group, isBackground) + } var currGroupState = groupStateInfo.state if (storedState != null && currGroupState != STATE_UNKNOWN) { - currGroupState = storedState.getInt(getInstanceStateKey(group.permGroupName, - isBackground), STATE_UNKNOWN) + currGroupState = + storedState.getInt( + getInstanceStateKey(group.permGroupName, isBackground), + STATE_UNKNOWN + ) } val otherGroupPermissions = filteredPermissions.filter { it in group.permissions } @@ -694,7 +776,6 @@ class GrantPermissionsViewModel( * target an SDK before the split, this method automatically adds the split off permission. * * @param perm The requested permission - * * @return The actually requested permissions */ private fun computeAffectedPermissions( @@ -742,8 +823,10 @@ class GrantPermissionsViewModel( // If the permission is restricted it does not show in the UI and // is not added to the group at all, so check that first. if (perm in group.packageInfo.requestedPermissions && perm !in group.permissions) { - reportRequestResult(perm, - PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__IGNORED_RESTRICTED_PERMISSION) + reportRequestResult( + perm, + PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__IGNORED_RESTRICTED_PERMISSION + ) return false } @@ -751,11 +834,12 @@ class GrantPermissionsViewModel( return !(group.permissions[perm]?.isUserFixed ?: true) } - val subGroup = if (perm in group.backgroundPermNames) { - group.background - } else { - group.foreground - } + val subGroup = + if (perm in group.backgroundPermNames) { + group.background + } else { + group.foreground + } val lightPermission = group.permissions[perm] ?: return false @@ -772,18 +856,24 @@ class GrantPermissionsViewModel( // is still grantable. return true } - } else if (perm in getPartialStorageGrantPermissionsForGroup(group) && - lightPermission.isGrantedIncludingAppOp) { + } else if ( + perm in getPartialStorageGrantPermissionsForGroup(group) && + lightPermission.isGrantedIncludingAppOp + ) { // If a partial storage permission is granted as fixed, we should immediately show // the photo picker return true } - reportRequestResult(perm, - PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__IGNORED_USER_FIXED) + reportRequestResult( + perm, + PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__IGNORED_USER_FIXED + ) return false } else if (subGroup.isPolicyFixed && !subGroup.isGranted || lightPermission.isPolicyFixed) { - reportRequestResult(perm, - PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__IGNORED_POLICY_FIXED) + reportRequestResult( + perm, + PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__IGNORED_POLICY_FIXED + ) return false } @@ -800,15 +890,18 @@ class GrantPermissionsViewModel( return policyState } - if (perm == POST_NOTIFICATIONS && - packageInfo.targetSdkVersion <= Build.VERSION_CODES.S_V2 && - group.foreground.isUserSet) { + if ( + perm == POST_NOTIFICATIONS && + packageInfo.targetSdkVersion <= Build.VERSION_CODES.S_V2 && + group.foreground.isUserSet + ) { return STATE_SKIPPED } else if (perm == READ_MEDIA_VISUAL_USER_SELECTED) { val partialPerms = getPartialStorageGrantPermissionsForGroup(group) - val otherRequestedPerms = unfilteredAffectedPermissions.filter { otherPerm -> - otherPerm in group.permissions && otherPerm !in partialPerms - } + val otherRequestedPerms = + unfilteredAffectedPermissions.filter { otherPerm -> + otherPerm in group.permissions && otherPerm !in partialPerms + } if (otherRequestedPerms.isEmpty()) { // If the app requested USER_SELECTED while not supporting the photo picker, or if // the app explicitly requested only USER_SELECTED and/or ACCESS_MEDIA_LOCATION, @@ -819,37 +912,50 @@ class GrantPermissionsViewModel( val isBackground = perm in group.backgroundPermNames - val hasForegroundRequest = groupRequestedPermissions.any { - it !in group.backgroundPermNames - } + val hasForegroundRequest = + groupRequestedPermissions.any { it !in group.backgroundPermNames } // Do not attempt to grant background access if foreground access is not either already // granted or requested - if (isBackground && !group.foreground.isGrantedExcludingRWROrAllRWR && - !hasForegroundRequest) { - Log.w(LOG_TAG, "Cannot grant $perm as the matching foreground permission is not " + - "already granted.") - val affectedPermissions = groupRequestedPermissions.filter { - it in group.backgroundPermNames - } - reportRequestResult(affectedPermissions, - PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__IGNORED) + if ( + isBackground && !group.foreground.isGrantedExcludingRWROrAllRWR && !hasForegroundRequest + ) { + Log.w( + LOG_TAG, + "Cannot grant $perm as the matching foreground permission is not " + + "already granted." + ) + val affectedPermissions = + groupRequestedPermissions.filter { it in group.backgroundPermNames } + reportRequestResult( + affectedPermissions, + PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__IGNORED + ) return STATE_SKIPPED } - if ((isBackground && group.background.isGrantedExcludingRWROrAllRWR || - !isBackground && group.foreground.isGrantedExcludingRWROrAllRWR) && - canAutoGrantWholeGroup(group)) { + if ( + (isBackground && group.background.isGrantedExcludingRWROrAllRWR || + !isBackground && group.foreground.isGrantedExcludingRWROrAllRWR) && + canAutoGrantWholeGroup(group) + ) { if (group.permissions[perm]?.isGrantedIncludingAppOp == false) { if (isBackground) { grantBackgroundRuntimePermissions(app, group, listOf(perm)) } else { grantForegroundRuntimePermissions(app, group, listOf(perm), group.isOneTime) } - KotlinUtils.setGroupFlags(app, group, FLAG_PERMISSION_USER_SET to false, - FLAG_PERMISSION_USER_FIXED to false, filterPermissions = listOf(perm)) - reportRequestResult(perm, - PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__AUTO_GRANTED) + KotlinUtils.setGroupFlags( + app, + group, + FLAG_PERMISSION_USER_SET to false, + FLAG_PERMISSION_USER_FIXED to false, + filterPermissions = listOf(perm) + ) + reportRequestResult( + perm, + PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__AUTO_GRANTED + ) } return if (storedState == null) { @@ -862,14 +968,17 @@ class GrantPermissionsViewModel( } /** - * Determines if remaining permissions in the group can be auto granted based on - * granted permissions in the group. + * Determines if remaining permissions in the group can be auto granted based on granted + * permissions in the group. */ private fun canAutoGrantWholeGroup(group: LightAppPermGroup): Boolean { // If FINE location is not granted, do not grant it automatically when COARSE // location is already granted. - if (group.permGroupName == LOCATION && isLocationAccuracyEnabledForApp(group) && - group.allPermissions[ACCESS_FINE_LOCATION]?.isGrantedIncludingAppOp == false) { + if ( + group.permGroupName == LOCATION && + isLocationAccuracyEnabledForApp(group) && + group.allPermissions[ACCESS_FINE_LOCATION]?.isGrantedIncludingAppOp == false + ) { return false } // If READ_MEDIA_VISUAL_USER_SELECTED is the only permission in the group that is granted, @@ -881,10 +990,9 @@ class GrantPermissionsViewModel( } /** - * A partial storage grant happens when: - * An app which doesn't support the photo picker has READ_MEDIA_VISUAL_USER_SELECTED granted, or - * An app which does support the photo picker has READ_MEDIA_VISUAL_USER_SELECTED and/or - * ACCESS_MEDIA_LOCATION granted + * A partial storage grant happens when: An app which doesn't support the photo picker has + * READ_MEDIA_VISUAL_USER_SELECTED granted, or An app which does support the photo picker has + * READ_MEDIA_VISUAL_USER_SELECTED and/or ACCESS_MEDIA_LOCATION granted */ private fun isPartialStorageGrant(group: LightAppPermGroup): Boolean { if (!isPhotoPickerPromptSupported() || group.permGroupName != READ_MEDIA_VISUAL) { @@ -892,9 +1000,24 @@ class GrantPermissionsViewModel( } val partialPerms = getPartialStorageGrantPermissionsForGroup(group) - return group.isGranted && group.permissions.values.all { - it.name in partialPerms || (it.name !in partialPerms && !it.isGrantedIncludingAppOp) + return group.isGranted && + group.permissions.values.all { + it.name in partialPerms || (it.name !in partialPerms && !it.isGrantedIncludingAppOp) + } + } + + private fun shouldShowPhotoPickerPromptForApp(group: LightAppPermGroup): Boolean { + if ( + !isPhotoPickerPromptEnabled() || + group.packageInfo.targetSdkVersion < Build.VERSION_CODES.TIRAMISU + ) { + return false + } + if (group.packageInfo.targetSdkVersion >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { + return true } + val userSelectedPerm = group.permissions[READ_MEDIA_VISUAL_USER_SELECTED] ?: return false + return !userSelectedPerm.isImplicit } private fun getStateFromPolicy(perm: String, group: LightAppPermGroup): Int { @@ -903,36 +1026,54 @@ class GrantPermissionsViewModel( var state = STATE_UNKNOWN when (permissionPolicy) { DevicePolicyManager.PERMISSION_POLICY_AUTO_GRANT -> { - if (AdminRestrictedPermissionsUtils.mayAdminGrantPermission( - app, perm, user.identifier)) { + if ( + AdminRestrictedPermissionsUtils.mayAdminGrantPermission( + app, + perm, + user.identifier + ) + ) { if (isBackground) { grantBackgroundRuntimePermissions(app, group, listOf(perm)) } else { grantForegroundRuntimePermissions(app, group, listOf(perm)) } - KotlinUtils.setGroupFlags(app, group, FLAG_PERMISSION_POLICY_FIXED to true, - FLAG_PERMISSION_USER_SET to false, FLAG_PERMISSION_USER_FIXED to false, - filterPermissions = listOf(perm)) + KotlinUtils.setGroupFlags( + app, + group, + FLAG_PERMISSION_POLICY_FIXED to true, + FLAG_PERMISSION_USER_SET to false, + FLAG_PERMISSION_USER_FIXED to false, + filterPermissions = listOf(perm) + ) state = STATE_ALLOWED skipGroup = true getAutoGrantNotifier().onPermissionAutoGranted(perm) - reportRequestResult(perm, - PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__AUTO_GRANTED) + reportRequestResult( + perm, + PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__AUTO_GRANTED + ) } } - DevicePolicyManager.PERMISSION_POLICY_AUTO_DENY -> { if (group.permissions[perm]?.isPolicyFixed == false) { - KotlinUtils.setGroupFlags(app, group, FLAG_PERMISSION_POLICY_FIXED to true, - FLAG_PERMISSION_USER_SET to false, FLAG_PERMISSION_USER_FIXED to false, - filterPermissions = listOf(perm)) + KotlinUtils.setGroupFlags( + app, + group, + FLAG_PERMISSION_POLICY_FIXED to true, + FLAG_PERMISSION_USER_SET to false, + FLAG_PERMISSION_USER_FIXED to false, + filterPermissions = listOf(perm) + ) } state = STATE_DENIED skipGroup = true - reportRequestResult(perm, - PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__AUTO_DENIED) + reportRequestResult( + perm, + PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__AUTO_DENIED + ) } } if (skipGroup && storedState == null) { @@ -967,12 +1108,14 @@ class GrantPermissionsViewModel( } // If this is a legacy app, and a storage group is requested: request all storage groups - if (!alreadyRequestedStorageGroupsIfNeeded && - groupName in PermissionMapping.STORAGE_SUPERGROUP_PERMISSIONS && - packageInfo.targetSdkVersion <= Build.VERSION_CODES.S_V2) { + if ( + !alreadyRequestedStorageGroupsIfNeeded && + groupName in PermissionMapping.STORAGE_SUPERGROUP_PERMISSIONS && + packageInfo.targetSdkVersion <= Build.VERSION_CODES.S_V2 + ) { for (storageGroupName in PermissionMapping.STORAGE_SUPERGROUP_PERMISSIONS) { - val groupPerms = appPermGroupLiveDatas[storageGroupName] - ?.value?.allPermissions?.keys?.toList() + val groupPerms = + appPermGroupLiveDatas[storageGroupName]?.value?.allPermissions?.keys?.toList() onPermissionGrantResult(storageGroupName, groupPerms, result, true) } return @@ -983,13 +1126,17 @@ class GrantPermissionsViewModel( when (result) { CANCELED -> { if (foregroundGroupState != null) { - reportRequestResult(foregroundGroupState.affectedPermissions, - PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_IGNORED) + reportRequestResult( + foregroundGroupState.affectedPermissions, + PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_IGNORED + ) foregroundGroupState.state = STATE_SKIPPED } if (backgroundGroupState != null) { - reportRequestResult(backgroundGroupState.affectedPermissions, - PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_IGNORED) + reportRequestResult( + backgroundGroupState.affectedPermissions, + PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_IGNORED + ) backgroundGroupState.state = STATE_SKIPPED } requestInfosLiveData.update() @@ -997,67 +1144,108 @@ class GrantPermissionsViewModel( } GRANTED_ALWAYS -> { if (foregroundGroupState != null) { - onPermissionGrantResultSingleState(foregroundGroupState, - affectedForegroundPermissions, granted = true, isOneTime = false, - doNotAskAgain = false) + onPermissionGrantResultSingleState( + foregroundGroupState, + affectedForegroundPermissions, + granted = true, + isOneTime = false, + doNotAskAgain = false + ) } if (backgroundGroupState != null) { - onPermissionGrantResultSingleState(backgroundGroupState, - affectedForegroundPermissions, granted = true, isOneTime = false, - doNotAskAgain = false) + onPermissionGrantResultSingleState( + backgroundGroupState, + affectedForegroundPermissions, + granted = true, + isOneTime = false, + doNotAskAgain = false + ) } } GRANTED_FOREGROUND_ONLY -> { if (foregroundGroupState != null) { - onPermissionGrantResultSingleState(foregroundGroupState, - affectedForegroundPermissions, granted = true, isOneTime = false, - doNotAskAgain = false) + onPermissionGrantResultSingleState( + foregroundGroupState, + affectedForegroundPermissions, + granted = true, + isOneTime = false, + doNotAskAgain = false + ) } if (backgroundGroupState != null) { - onPermissionGrantResultSingleState(backgroundGroupState, - affectedForegroundPermissions, granted = false, isOneTime = false, - doNotAskAgain = false) + onPermissionGrantResultSingleState( + backgroundGroupState, + affectedForegroundPermissions, + granted = false, + isOneTime = false, + doNotAskAgain = false + ) } } GRANTED_ONE_TIME -> { if (foregroundGroupState != null) { - onPermissionGrantResultSingleState(foregroundGroupState, - affectedForegroundPermissions, granted = true, isOneTime = true, - doNotAskAgain = false) + onPermissionGrantResultSingleState( + foregroundGroupState, + affectedForegroundPermissions, + granted = true, + isOneTime = true, + doNotAskAgain = false + ) } if (backgroundGroupState != null) { - onPermissionGrantResultSingleState(backgroundGroupState, - affectedForegroundPermissions, granted = false, isOneTime = true, - doNotAskAgain = false) + onPermissionGrantResultSingleState( + backgroundGroupState, + affectedForegroundPermissions, + granted = false, + isOneTime = true, + doNotAskAgain = false + ) } } - GRANTED_USER_SELECTED, DENIED_MORE -> { + GRANTED_USER_SELECTED, + DENIED_MORE -> { if (foregroundGroupState != null) { grantUserSelectedVisualGroupPermissions(foregroundGroupState) } } DENIED -> { if (foregroundGroupState != null) { - onPermissionGrantResultSingleState(foregroundGroupState, - affectedForegroundPermissions, granted = false, isOneTime = false, - doNotAskAgain = false) + onPermissionGrantResultSingleState( + foregroundGroupState, + affectedForegroundPermissions, + granted = false, + isOneTime = false, + doNotAskAgain = false + ) } if (backgroundGroupState != null) { - onPermissionGrantResultSingleState(backgroundGroupState, - affectedForegroundPermissions, granted = false, isOneTime = false, - doNotAskAgain = false) + onPermissionGrantResultSingleState( + backgroundGroupState, + affectedForegroundPermissions, + granted = false, + isOneTime = false, + doNotAskAgain = false + ) } } DENIED_DO_NOT_ASK_AGAIN -> { if (foregroundGroupState != null) { - onPermissionGrantResultSingleState(foregroundGroupState, - affectedForegroundPermissions, granted = false, isOneTime = false, - doNotAskAgain = true) + onPermissionGrantResultSingleState( + foregroundGroupState, + affectedForegroundPermissions, + granted = false, + isOneTime = false, + doNotAskAgain = true + ) } if (backgroundGroupState != null) { - onPermissionGrantResultSingleState(backgroundGroupState, - affectedForegroundPermissions, granted = false, isOneTime = false, - doNotAskAgain = true) + onPermissionGrantResultSingleState( + backgroundGroupState, + affectedForegroundPermissions, + granted = false, + isOneTime = false, + doNotAskAgain = true + ) } } } @@ -1067,32 +1255,60 @@ class GrantPermissionsViewModel( val userSelectedPerm = groupState.group.permissions[READ_MEDIA_VISUAL_USER_SELECTED] ?: return if (userSelectedPerm.isImplicit) { - val nonSelectedPerms = groupState.group.permissions.keys - .filter { it != READ_MEDIA_VISUAL_USER_SELECTED } + val nonSelectedPerms = + groupState.group.permissions.keys.filter { it != READ_MEDIA_VISUAL_USER_SELECTED } // If the permission is implicit, grant USER_SELECTED as user set, and all other // permissions as one time, and without app ops. - grantForegroundRuntimePermissions(app, groupState.group, - listOf(READ_MEDIA_VISUAL_USER_SELECTED)) - grantForegroundRuntimePermissions(app, groupState.group, - nonSelectedPerms, isOneTime = true, userFixed = false, withoutAppOps = true) - val appPermGroup = AppPermissionGroup.create(app, packageName, - groupState.group.permGroupName, groupState.group.userHandle, false) + grantForegroundRuntimePermissions( + app, + groupState.group, + listOf(READ_MEDIA_VISUAL_USER_SELECTED) + ) + grantForegroundRuntimePermissions( + app, + groupState.group, + nonSelectedPerms, + isOneTime = true, + userFixed = false, + withoutAppOps = true + ) + val appPermGroup = + AppPermissionGroup.create( + app, + packageName, + groupState.group.permGroupName, + groupState.group.userHandle, + false + ) appPermGroup.setSelfRevoked() appPermGroup.persistChanges(false, null, nonSelectedPerms.toSet()) } else { - val partialPerms = getPartialStorageGrantPermissionsForGroup(groupState.group).filter { - it in groupState.affectedPermissions - } + val partialPerms = + getPartialStorageGrantPermissionsForGroup(groupState.group).filter { + it in groupState.affectedPermissions + } val nonSelectedPerms = groupState.affectedPermissions.filter { it !in partialPerms } val setUserFixed = userSelectedPerm.isUserFixed || userSelectedPerm.isUserSet - grantForegroundRuntimePermissions(app, groupState.group, - partialPerms.toList(), userFixed = setUserFixed) - revokeForegroundRuntimePermissions(app, groupState.group, - userFixed = setUserFixed, oneTime = false, filterPermissions = nonSelectedPerms) + grantForegroundRuntimePermissions( + app, + groupState.group, + partialPerms.toList(), + userFixed = setUserFixed + ) + revokeForegroundRuntimePermissions( + app, + groupState.group, + userFixed = setUserFixed, + oneTime = false, + filterPermissions = nonSelectedPerms + ) } groupState.state = STATE_ALLOWED - reportButtonClickResult(groupState, true, - PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__PHOTOS_SELECTED) + reportButtonClickResult( + groupState, + true, + PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__PHOTOS_SELECTED + ) } @SuppressLint("NewApi") @@ -1109,54 +1325,84 @@ class GrantPermissionsViewModel( } val result: Int if (granted) { - result = if (isOneTime) { - PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_GRANTED_ONE_TIME - } else { - PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_GRANTED - } + result = + if (isOneTime) { + PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_GRANTED_ONE_TIME + } else { + PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_GRANTED + } if (groupState.isBackground) { - grantBackgroundRuntimePermissions(app, groupState.group, - groupState.affectedPermissions) + grantBackgroundRuntimePermissions( + app, + groupState.group, + groupState.affectedPermissions + ) } else { if (affectedForegroundPermissions == null) { - grantForegroundRuntimePermissions(app, groupState.group, - groupState.affectedPermissions, isOneTime) + grantForegroundRuntimePermissions( + app, + groupState.group, + groupState.affectedPermissions, + isOneTime + ) // This prevents weird flag state when app targetSDK switches from S+ to R- if (groupState.affectedPermissions.contains(ACCESS_FINE_LOCATION)) { - KotlinUtils.setFlagsWhenLocationAccuracyChanged( - app, groupState.group, true) + KotlinUtils.setFlagsWhenLocationAccuracyChanged(app, groupState.group, true) } } else { - val newGroup = grantForegroundRuntimePermissions(app, - groupState.group, affectedForegroundPermissions, isOneTime) + val newGroup = + grantForegroundRuntimePermissions( + app, + groupState.group, + affectedForegroundPermissions, + isOneTime + ) if (!isOneTime || newGroup.isOneTime) { - KotlinUtils.setFlagsWhenLocationAccuracyChanged(app, newGroup, - affectedForegroundPermissions.contains(ACCESS_FINE_LOCATION)) + KotlinUtils.setFlagsWhenLocationAccuracyChanged( + app, + newGroup, + affectedForegroundPermissions.contains(ACCESS_FINE_LOCATION) + ) } } } groupState.state = STATE_ALLOWED } else { if (groupState.isBackground) { - revokeBackgroundRuntimePermissions(app, groupState.group, - userFixed = doNotAskAgain, filterPermissions = groupState.affectedPermissions) + revokeBackgroundRuntimePermissions( + app, + groupState.group, + userFixed = doNotAskAgain, + filterPermissions = groupState.affectedPermissions + ) } else { - if (affectedForegroundPermissions == null || - affectedForegroundPermissions.contains(ACCESS_COARSE_LOCATION)) { - revokeForegroundRuntimePermissions(app, groupState.group, + if ( + affectedForegroundPermissions == null || + affectedForegroundPermissions.contains(ACCESS_COARSE_LOCATION) + ) { + revokeForegroundRuntimePermissions( + app, + groupState.group, userFixed = doNotAskAgain, - filterPermissions = groupState.affectedPermissions, oneTime = isOneTime) + filterPermissions = groupState.affectedPermissions, + oneTime = isOneTime + ) } else { - revokeForegroundRuntimePermissions(app, groupState.group, + revokeForegroundRuntimePermissions( + app, + groupState.group, userFixed = doNotAskAgain, - filterPermissions = affectedForegroundPermissions, oneTime = isOneTime) + filterPermissions = affectedForegroundPermissions, + oneTime = isOneTime + ) } } - result = if (doNotAskAgain) { - PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_DENIED_WITH_PREJUDICE - } else { - PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_DENIED - } + result = + if (doNotAskAgain) { + PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_DENIED_WITH_PREJUDICE + } else { + PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_DENIED + } groupState.state = STATE_DENIED } reportButtonClickResult(groupState, granted, result) @@ -1166,8 +1412,12 @@ class GrantPermissionsViewModel( reportRequestResult(groupState.affectedPermissions, result) // group state has changed, reload liveData requestInfosLiveData.update() - PermissionDecisionStorageImpl.recordPermissionDecision(app.applicationContext, - packageName, groupState.group.permGroupName, granted) + PermissionDecisionStorageImpl.recordPermissionDecision( + app.applicationContext, + packageName, + groupState.group.permGroupName, + granted + ) PermissionChangeStorageImpl.recordPermissionChange(packageName) if (granted) { startDrivingDecisionReminderServiceIfNecessary(groupState.group.permGroupName) @@ -1183,7 +1433,10 @@ class GrantPermissionsViewModel( return } DrivingDecisionReminderService.startServiceIfCurrentlyRestricted( - Utils.getUserContext(app, user), packageName, permGroupName) + Utils.getUserContext(app, user), + packageName, + permGroupName + ) } private fun getGroupWithPerm( @@ -1207,12 +1460,13 @@ class GrantPermissionsViewModel( internal var state: Int = STATE_UNKNOWN ) { override fun toString(): String { - val stateStr: String = when (state) { - STATE_UNKNOWN -> "unknown" - STATE_ALLOWED -> "granted" - STATE_DENIED -> "denied" - else -> "skipped" - } + val stateStr: String = + when (state) { + STATE_UNKNOWN -> "unknown" + STATE_ALLOWED -> "granted" + STATE_DENIED -> "denied" + else -> "skipped" + } return "${group.permGroupName} $isBackground $stateStr $affectedPermissions" } } @@ -1231,19 +1485,30 @@ class GrantPermissionsViewModel( */ private fun reportRequestResult(permission: String, result: Int) { val isImplicit = permission !in requestedPermissions - val isPermissionRationaleShown = shouldShowPermissionRationale( - safetyLabelInfoLiveData?.value?.safetyLabel, - PermissionMapping.getGroupOfPlatformPermission(permission)) - - Log.v(LOG_TAG, "Permission grant result requestId=$sessionId " + - "callingUid=${packageInfo.uid} callingPackage=$packageName permission=$permission " + - "isImplicit=$isImplicit result=$result " + - "isPermissionRationaleShown=$isPermissionRationaleShown") + val isPermissionRationaleShown = + shouldShowPermissionRationale( + safetyLabelInfoLiveData?.value?.safetyLabel, + PermissionMapping.getGroupOfPlatformPermission(permission) + ) + + Log.v( + LOG_TAG, + "Permission grant result requestId=$sessionId " + + "callingUid=${packageInfo.uid} callingPackage=$packageName permission=$permission " + + "isImplicit=$isImplicit result=$result " + + "isPermissionRationaleShown=$isPermissionRationaleShown" + ) PermissionControllerStatsLog.write( - PERMISSION_GRANT_REQUEST_RESULT_REPORTED, sessionId, - packageInfo.uid, packageName, permission, isImplicit, result, - isPermissionRationaleShown) + PERMISSION_GRANT_REQUEST_RESULT_REPORTED, + sessionId, + packageInfo.uid, + packageName, + permission, + isImplicit, + result, + isPermissionRationaleShown + ) } /** @@ -1263,7 +1528,7 @@ class GrantPermissionsViewModel( * Determine if the activity should return permission state to the caller * * @return Whether or not state should be returned. False only if the package is pre-M, true - * otherwise. + * otherwise. */ fun shouldReturnPermissionState(): Boolean { return if (packageInfoLiveData.value != null) { @@ -1272,8 +1537,10 @@ class GrantPermissionsViewModel( // Should not be reached, as this method shouldn't be called before data is passed to // the activity for the first time try { - Utils.getUserContext(app, user).packageManager - .getApplicationInfo(packageName, 0).targetSdkVersion >= Build.VERSION_CODES.M + Utils.getUserContext(app, user) + .packageManager + .getApplicationInfo(packageName, 0) + .targetSdkVersion >= Build.VERSION_CODES.M } catch (e: PackageManager.NameNotFoundException) { true } @@ -1286,14 +1553,16 @@ class GrantPermissionsViewModel( permGroupsToSkip.add(HEALTH_PERMISSION_GROUP) requestInfosLiveData.update() } - val healthPermissions = unfilteredAffectedPermissions.filter { permission -> - isHealthPermission(activity, permission) - }.toTypedArray() - val intent: Intent = Intent(ACTION_REQUEST_HEALTH_PERMISSIONS) - .putExtra(Intent.EXTRA_PACKAGE_NAME, packageName) - .putExtra(PackageManager.EXTRA_REQUEST_PERMISSIONS_NAMES, healthPermissions) - .putExtra(Intent.EXTRA_USER, Process.myUserHandle()) - .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK) + val healthPermissions = + unfilteredAffectedPermissions + .filter { permission -> isHealthPermission(activity, permission) } + .toTypedArray() + val intent: Intent = + Intent(ACTION_REQUEST_HEALTH_PERMISSIONS) + .putExtra(Intent.EXTRA_PACKAGE_NAME, packageName) + .putExtra(PackageManager.EXTRA_REQUEST_PERMISSIONS_NAMES, healthPermissions) + .putExtra(Intent.EXTRA_USER, Process.myUserHandle()) + .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK) activity.startActivityForResult(intent, APP_PERMISSION_REQUEST_CODE) } } @@ -1309,14 +1578,23 @@ class GrantPermissionsViewModel( activityResultCallback = Consumer { data -> if (data?.getStringExtra(EXTRA_RESULT_PERMISSION_INTERACTED) == null) { // User didn't interact, count against rate limit - val group = groupStates[groupName to false]?.group - ?: groupStates[groupName to true]?.group ?: return@Consumer + val group = + groupStates[groupName to false]?.group + ?: groupStates[groupName to true]?.group ?: return@Consumer if (group.background.isUserSet) { - KotlinUtils.setGroupFlags(app, group, FLAG_PERMISSION_USER_FIXED to true, - filterPermissions = group.backgroundPermNames) + KotlinUtils.setGroupFlags( + app, + group, + FLAG_PERMISSION_USER_FIXED to true, + filterPermissions = group.backgroundPermNames + ) } else { - KotlinUtils.setGroupFlags(app, group, FLAG_PERMISSION_USER_SET to true, - filterPermissions = group.backgroundPermNames) + KotlinUtils.setGroupFlags( + app, + group, + FLAG_PERMISSION_USER_SET to true, + filterPermissions = group.backgroundPermNames + ) } } @@ -1344,18 +1622,8 @@ class GrantPermissionsViewModel( } requestInfosLiveData.update() } - // A clone profile doesn't have a MediaProvider. If this user is a clone profile, open - // the photo picker in the parent profile - val userManager = activity.getSystemService(UserManager::class.java)!! - val user = if (userManager.isCloneProfile) { - userManager.getProfileParent(Process.myUserHandle()) ?: Process.myUserHandle() - } else { - Process.myUserHandle() - } - activity.startActivityForResultAsUser(Intent(MediaStore.ACTION_USER_SELECT_IMAGES_FOR_APP) - .putExtra(Intent.EXTRA_UID, packageInfo.uid) - .setType(KotlinUtils.getMimeTypeForPermissions(unfilteredAffectedPermissions)), - PHOTO_PICKER_REQUEST_CODE, user) + openPhotoPickerForApp(activity, packageInfo.uid, unfilteredAffectedPermissions, + PHOTO_PICKER_REQUEST_CODE) } /** @@ -1378,21 +1646,22 @@ class GrantPermissionsViewModel( } /** - * Shows the Permission Rationale Dialog. For use with U+ only, otherwise no-op. - * - * @param activity The current activity - * @param groupName The name of the permission group whose fragment should be opened - */ + * Shows the Permission Rationale Dialog. For use with U+ only, otherwise no-op. + * + * @param activity The current activity + * @param groupName The name of the permission group whose fragment should be opened + */ fun showPermissionRationaleActivity(activity: Activity, groupName: String) { if (!SdkLevel.isAtLeastU()) { return } - val intent = Intent(activity, PermissionRationaleActivity::class.java).apply { - putExtra(Intent.EXTRA_PACKAGE_NAME, packageName) - putExtra(Intent.EXTRA_PERMISSION_GROUP_NAME, groupName) - putExtra(Constants.EXTRA_SESSION_ID, sessionId) - } + val intent = + Intent(activity, PermissionRationaleActivity::class.java).apply { + putExtra(Intent.EXTRA_PACKAGE_NAME, packageName) + putExtra(Intent.EXTRA_PERMISSION_GROUP_NAME, groupName) + putExtra(Constants.EXTRA_SESSION_ID, sessionId) + } activityResultCallback = Consumer { data -> val returnGroupName = data?.getStringExtra(EXTRA_RESULT_PERMISSION_INTERACTED) if (returnGroupName != null) { @@ -1406,14 +1675,17 @@ class GrantPermissionsViewModel( } private fun startAppPermissionFragment(activity: Activity, groupName: String) { - val intent = Intent(Intent.ACTION_MANAGE_APP_PERMISSION) - .putExtra(Intent.EXTRA_PACKAGE_NAME, packageName) - .putExtra(Intent.EXTRA_PERMISSION_GROUP_NAME, groupName) - .putExtra(Intent.EXTRA_USER, user) - .putExtra(ManagePermissionsActivity.EXTRA_CALLER_NAME, - GrantPermissionsActivity::class.java.name) - .putExtra(Constants.EXTRA_SESSION_ID, sessionId) - .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK) + val intent = + Intent(Intent.ACTION_MANAGE_APP_PERMISSION) + .putExtra(Intent.EXTRA_PACKAGE_NAME, packageName) + .putExtra(Intent.EXTRA_PERMISSION_GROUP_NAME, groupName) + .putExtra(Intent.EXTRA_USER, user) + .putExtra( + ManagePermissionsActivity.EXTRA_CALLER_NAME, + GrantPermissionsActivity::class.java.name + ) + .putExtra(Constants.EXTRA_SESSION_ID, sessionId) + .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK) activity.startActivityForResult(intent, APP_PERMISSION_REQUEST_CODE) } @@ -1429,42 +1701,58 @@ class GrantPermissionsViewModel( when (result) { GRANTED_ALWAYS -> { if (foregroundGroupState != null) { - reportRequestResult(foregroundGroupState.affectedPermissions, - PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_GRANTED_IN_SETTINGS) + reportRequestResult( + foregroundGroupState.affectedPermissions, + PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_GRANTED_IN_SETTINGS + ) } if (backgroundGroupState != null) { - reportRequestResult(backgroundGroupState.affectedPermissions, - PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_GRANTED_IN_SETTINGS) + reportRequestResult( + backgroundGroupState.affectedPermissions, + PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_GRANTED_IN_SETTINGS + ) } } GRANTED_FOREGROUND_ONLY -> { if (foregroundGroupState != null) { - reportRequestResult(foregroundGroupState.affectedPermissions, - PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_GRANTED_IN_SETTINGS) + reportRequestResult( + foregroundGroupState.affectedPermissions, + PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_GRANTED_IN_SETTINGS + ) } if (backgroundGroupState != null) { - reportRequestResult(backgroundGroupState.affectedPermissions, - PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_DENIED_IN_SETTINGS) + reportRequestResult( + backgroundGroupState.affectedPermissions, + PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_DENIED_IN_SETTINGS + ) } } DENIED -> { if (foregroundGroupState != null) { - reportRequestResult(foregroundGroupState.affectedPermissions, - PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_DENIED_IN_SETTINGS) + reportRequestResult( + foregroundGroupState.affectedPermissions, + PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_DENIED_IN_SETTINGS + ) } if (backgroundGroupState != null) { - reportRequestResult(backgroundGroupState.affectedPermissions, - PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_DENIED_IN_SETTINGS) + reportRequestResult( + backgroundGroupState.affectedPermissions, + PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_DENIED_IN_SETTINGS + ) } } DENIED_DO_NOT_ASK_AGAIN -> { if (foregroundGroupState != null) { - reportRequestResult(foregroundGroupState.affectedPermissions, - deniedPrejudiceInSettings) + reportRequestResult( + foregroundGroupState.affectedPermissions, + deniedPrejudiceInSettings + ) } if (backgroundGroupState != null) { - reportRequestResult(backgroundGroupState.affectedPermissions, - deniedPrejudiceInSettings) + reportRequestResult( + backgroundGroupState.affectedPermissions, + deniedPrejudiceInSettings + ) } } } @@ -1472,12 +1760,10 @@ class GrantPermissionsViewModel( private fun isLocationAccuracyEnabledForApp(group: LightAppPermGroup): Boolean { return isLocationAccuracyEnabled() && - group.packageInfo.targetSdkVersion >= Build.VERSION_CODES.S + group.packageInfo.targetSdkVersion >= Build.VERSION_CODES.S } - /** - * Log all permission groups which were requested - */ + /** Log all permission groups which were requested */ fun logRequestedPermissionGroups() { if (groupStates.isEmpty()) { return @@ -1491,7 +1777,7 @@ class GrantPermissionsViewModel( * * @param groupName The name of the permission group which was interacted with * @param selectedPrecision Selected precision of the location permission - bit flags indicate - * which locations were chosen + * which locations were chosen * @param clickedButton The button that was clicked by the user * @param presentedButtons All buttons which were shown to the user */ @@ -1507,29 +1793,42 @@ class GrantPermissionsViewModel( } if (!requestInfosLiveData.isInitialized || !packageInfoLiveData.isInitialized) { - Log.wtf(LOG_TAG, "Logged buttons presented and clicked permissionGroupName=" + + Log.wtf( + LOG_TAG, + "Logged buttons presented and clicked permissionGroupName=" + "$groupName package=$packageName presentedButtons=$presentedButtons " + "clickedButton=$clickedButton isPermissionRationaleShown=" + "$isPermissionRationaleShown sessionId=$sessionId, but requests were not yet" + - "initialized", IllegalStateException()) + "initialized", + IllegalStateException() + ) return } - PermissionControllerStatsLog.write(GRANT_PERMISSIONS_ACTIVITY_BUTTON_ACTIONS, - groupName, packageInfo.uid, packageName, presentedButtons, clickedButton, sessionId, - packageInfo.targetSdkVersion, selectedPrecision, - isPermissionRationaleShown) - Log.v(LOG_TAG, "Logged buttons presented and clicked permissionGroupName=" + + PermissionControllerStatsLog.write( + GRANT_PERMISSIONS_ACTIVITY_BUTTON_ACTIONS, + groupName, + packageInfo.uid, + packageName, + presentedButtons, + clickedButton, + sessionId, + packageInfo.targetSdkVersion, + selectedPrecision, + isPermissionRationaleShown + ) + Log.v( + LOG_TAG, + "Logged buttons presented and clicked permissionGroupName=" + "$groupName uid=${packageInfo.uid} selectedPrecision=$selectedPrecision " + "package=$packageName presentedButtons=$presentedButtons " + "clickedButton=$clickedButton isPermissionRationaleShown=" + "$isPermissionRationaleShown sessionId=$sessionId " + - "targetSdk=${packageInfo.targetSdkVersion}") + "targetSdk=${packageInfo.targetSdkVersion}" + ) } - /** - * Use the autoGrantNotifier to notify of auto-granted permissions. - */ + /** Use the autoGrantNotifier to notify of auto-granted permissions. */ fun autoGrantNotify() { autoGrantNotifier?.notifyOfAutoGrantPermissions(true) } @@ -1571,8 +1870,9 @@ class GrantPermissionsViewModel( .filter { !it.isNullOrEmpty() } // POST_NOTIFICATIONS is actively disallowed to be declared by apps below T. // Others we don't care as much if they were declared but not used. - .filter { targetSdkVersion >= Build.VERSION_CODES.TIRAMISU || - it != POST_NOTIFICATIONS } + .filter { + targetSdkVersion >= Build.VERSION_CODES.TIRAMISU || it != POST_NOTIFICATIONS + } .filterIsInstance<String>() } } @@ -1593,7 +1893,13 @@ class GrantPermissionsViewModelFactory( ) : ViewModelProvider.Factory { override fun <T : ViewModel> create(modelClass: Class<T>): T { @Suppress("UNCHECKED_CAST") - return GrantPermissionsViewModel(app, packageName, requestedPermissions, - sessionId, savedState) as T + return GrantPermissionsViewModel( + app, + packageName, + requestedPermissions, + sessionId, + savedState + ) + as T } } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/ManageCustomPermissionsViewModel.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/ManageCustomPermissionsViewModel.kt index 09866870a..bd80a88cd 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/ManageCustomPermissionsViewModel.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/ManageCustomPermissionsViewModel.kt @@ -35,12 +35,9 @@ import com.android.permissioncontroller.permission.utils.navigateSafe * * @param app The current application of the fragment */ -class ManageCustomPermissionsViewModel( - private val app: Application -) : AndroidViewModel(app) { +class ManageCustomPermissionsViewModel(private val app: Application) : AndroidViewModel(app) { - val uiDataLiveData = PermGroupsPackagesUiInfoLiveData(app, - UsedCustomPermGroupNamesLiveData()) + val uiDataLiveData = PermGroupsPackagesUiInfoLiveData(app, UsedCustomPermGroupNamesLiveData()) /** * Navigate to a Permission Apps fragment @@ -58,12 +55,10 @@ class ManageCustomPermissionsViewModel( * * @param app The current application of the fragment */ -class ManageCustomPermissionsViewModelFactory( - private val app: Application -) : ViewModelProvider.Factory { +class ManageCustomPermissionsViewModelFactory(private val app: Application) : + ViewModelProvider.Factory { override fun <T : ViewModel> create(modelClass: Class<T>): T { - @Suppress("UNCHECKED_CAST") - return ManageCustomPermissionsViewModel(app) as T + @Suppress("UNCHECKED_CAST") return ManageCustomPermissionsViewModel(app) as T } } @@ -72,14 +67,13 @@ class ManageCustomPermissionsViewModelFactory( * package. This includes single-permission permission groups, as well as the Undefined permission * group, and any other permission groups not defined by the system. */ -class UsedCustomPermGroupNamesLiveData : - SmartUpdateMediatorLiveData<List<String>>() { +class UsedCustomPermGroupNamesLiveData : SmartUpdateMediatorLiveData<List<String>>() { init { - addSource(PermGroupsPackagesLiveData.get(customGroups = true)) { - value = it.keys.toList() - } + addSource(PermGroupsPackagesLiveData.get(customGroups = true)) { value = it.keys.toList() } } - override fun onUpdate() { /* No op override */ } + override fun onUpdate() { + /* No op override */ + } } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/ManagePermissionsViewModel.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/ManagePermissionsViewModel.kt index 871a89aeb..f964fb9d2 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/ManagePermissionsViewModel.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/ManagePermissionsViewModel.kt @@ -26,71 +26,71 @@ import com.android.permissioncontroller.permission.model.livedatatypes.PermGroup /** * A [androidx.lifecycle.ViewModel] for [ManagePermissionsFragment] and - * [ManagePermissionsOtherFragment]. - * However, [ManagePermissionsViewModel] is designed in a way so that its owner should be an - * [Activity][androidx.fragment.app.FragmentActivity] rather than individual - * [Fragments][androidx.fragment.app.Fragment], and the aforementioned Fragments that manage - * different sets of the permission groups should to share a single instance of + * [ManagePermissionsOtherFragment]. However, [ManagePermissionsViewModel] is designed in a way so + * that its owner should be an [Activity][androidx.fragment.app.FragmentActivity] rather than + * individual [Fragments][androidx.fragment.app.Fragment], and the aforementioned Fragments that + * manage different sets of the permission groups should to share a single instance of * [ManagePermissionsViewModel]. */ class ManagePermissionsViewModel(app: Application) : AndroidViewModel(app) { - /** - * [LiveData] that contains a list of all platform-defined permission groups. - */ + /** [LiveData] that contains a list of all platform-defined permission groups. */ val standardPermGroupsLiveData: LiveData<List<PermGroupPackagesUiInfo>> = MediatorLiveData<List<PermGroupPackagesUiInfo>>().apply { addSource(PermGroupsPackagesUiInfoLiveData(app, StandardPermGroupNamesLiveData)) { - permGroups -> value = permGroups.values.filterNotNull() + permGroups -> + value = permGroups.values.filterNotNull() } } /** - * [LiveData] that contains a list of platform-defined permission groups, such - * that at least one the permissions in the group has been requested at runtime by at least one - * non-system application or has been pregranted to a non-system application. + * [LiveData] that contains a list of platform-defined permission groups, such that at least one + * the permissions in the group has been requested at runtime by at least one non-system + * application or has been pregranted to a non-system application. + * * @see com.android.permissioncontroller.permission.ui.television.ManagePermissionsFragment */ val usedPermissionGroups: LiveData<List<PermGroupPackagesUiInfo>> = MediatorLiveData<List<PermGroupPackagesUiInfo>>().apply { - addSource(standardPermGroupsLiveData) { - permGroups -> value = permGroups.filter { it.nonSystemUserSetOrPreGranted > 0 } + addSource(standardPermGroupsLiveData) { permGroups -> + value = permGroups.filter { it.nonSystemUserSetOrPreGranted > 0 } } } /** - * [LiveData] that contains a list of platform-defined permission groups, such that all - * of the permissions in the group neither has been requested at runtime by any of the - * non-system applications nor has been pregranted to any such application. But at least one of - * the permissions in the group is requested by or pregranted to at least one system - * application, other than the Shell (we do not show permission groups that are granted only to - * the Shell, because it has all the permissions granted). + * [LiveData] that contains a list of platform-defined permission groups, such that all of the + * permissions in the group neither has been requested at runtime by any of the non-system + * applications nor has been pregranted to any such application. But at least one of the + * permissions in the group is requested by or pregranted to at least one system application, + * other than the Shell (we do not show permission groups that are granted only to the Shell, + * because it has all the permissions granted). + * * @see com.android.permissioncontroller.permission.ui.television.ManagePermissionsOtherFragment */ val unusedPermissionGroups: LiveData<List<PermGroupPackagesUiInfo>> = MediatorLiveData<List<PermGroupPackagesUiInfo>>().apply { - addSource(standardPermGroupsLiveData) { - permGroups -> value = permGroups - .filter { it.nonSystemUserSetOrPreGranted == 0 } - .filter { it.systemUserSetOrPreGranted > 0 } - .filterNot { it.onlyShellPackageGranted } + addSource(standardPermGroupsLiveData) { permGroups -> + value = + permGroups + .filter { it.nonSystemUserSetOrPreGranted == 0 } + .filter { it.systemUserSetOrPreGranted > 0 } + .filterNot { it.onlyShellPackageGranted } } } /** - * [LiveData] that contains a list of the application-defined permission groups - * (a.k.a. "custom" permissions), such that at least one of the permissions in the group has - * been requested at runtime by or has been pregranted to at least one application (system or - * non-system). + * [LiveData] that contains a list of the application-defined permission groups (a.k.a. "custom" + * permissions), such that at least one of the permissions in the group has been requested at + * runtime by or has been pregranted to at least one application (system or non-system). + * * @see com.android.permissioncontroller.permission.ui.television.ManagePermissionsOtherFragment */ val additionalPermissionGroups: LiveData<List<PermGroupPackagesUiInfo>> = MediatorLiveData<List<PermGroupPackagesUiInfo>>().apply { - addSource(PermGroupsPackagesUiInfoLiveData( - app, UsedCustomPermGroupNamesLiveData())) { - permGroups -> value = permGroups.values - .filterNotNull() - .filter { + addSource(PermGroupsPackagesUiInfoLiveData(app, UsedCustomPermGroupNamesLiveData())) { + permGroups -> + value = + permGroups.values.filterNotNull().filter { (it.nonSystemUserSetOrPreGranted > 0) or (it.systemUserSetOrPreGranted > 0) } } @@ -98,16 +98,18 @@ class ManagePermissionsViewModel(app: Application) : AndroidViewModel(app) { /** * [LiveData] that indicates whether there any unused or additional permission groups. + * * @see com.android.permissioncontroller.permission.ui.television.ManagePermissionsFragment */ @get:JvmName("hasUnusedOrAdditionalPermissionGroups") val hasUnusedOrAdditionalPermissionGroups: LiveData<Boolean> = MediatorLiveData<Boolean>().apply { val updateValue: (Any?) -> Unit = { - value = !unusedPermissionGroups.value.isNullOrEmpty() || - !additionalPermissionGroups.value.isNullOrEmpty() + value = + !unusedPermissionGroups.value.isNullOrEmpty() || + !additionalPermissionGroups.value.isNullOrEmpty() } addSource(unusedPermissionGroups, updateValue) addSource(additionalPermissionGroups, updateValue) } -}
\ No newline at end of file +} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/ManageStandardPermissionsViewModel.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/ManageStandardPermissionsViewModel.kt index e529f1cd5..b7754e66e 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/ManageStandardPermissionsViewModel.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/ManageStandardPermissionsViewModel.kt @@ -37,20 +37,16 @@ import com.android.permissioncontroller.permission.utils.navigateSafe /** * A ViewModel for the ManageStandardPermissionsFragment. Provides a LiveData which watches over all * platform permission groups, and sends async updates when these groups have changes. It also - * provides a liveData which watches the custom permission groups of the system, and provides - * a list of group names. + * provides a liveData which watches the custom permission groups of the system, and provides a list + * of group names. + * * @param app The current application of the fragment */ -class ManageStandardPermissionsViewModel( - private val app: Application -) : AndroidViewModel(app) { +class ManageStandardPermissionsViewModel(private val app: Application) : AndroidViewModel(app) { - val uiDataLiveData = PermGroupsPackagesUiInfoLiveData(app, - StandardPermGroupNamesLiveData) + val uiDataLiveData = PermGroupsPackagesUiInfoLiveData(app, StandardPermGroupNamesLiveData) val numCustomPermGroups = NumCustomPermGroupsWithPackagesLiveData() - val numAutoRevoked = Transformations.map(unusedAutoRevokePackagesLiveData) { - it?.size ?: 0 - } + val numAutoRevoked = Transformations.map(unusedAutoRevokePackagesLiveData) { it?.size ?: 0 } /** * Navigate to the Custom Permissions screen @@ -74,8 +70,7 @@ class ManageStandardPermissionsViewModel( Utils.navigateToNotificationSettings(fragment.context!!) return } - if (Utils.isHealthPermissionUiEnabled() && - groupName == HEALTH_PERMISSION_GROUP) { + if (Utils.isHealthPermissionUiEnabled() && groupName == HEALTH_PERMISSION_GROUP) { Utils.navigateToHealthConnectSettings(fragment.context!!) return } @@ -91,15 +86,12 @@ class ManageStandardPermissionsViewModel( * A LiveData which tracks the number of custom permission groups that are used by at least one * package */ -class NumCustomPermGroupsWithPackagesLiveData() : - SmartUpdateMediatorLiveData<Int>() { +class NumCustomPermGroupsWithPackagesLiveData() : SmartUpdateMediatorLiveData<Int>() { private val customPermGroupPackages = PermGroupsPackagesLiveData.get(customGroups = true) init { - addSource(customPermGroupPackages) { - update() - } + addSource(customPermGroupPackages) { update() } } override fun onUpdate() { diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/PermissionAppsViewModel.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/PermissionAppsViewModel.kt index 1b17041b6..c64fd2e6f 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/PermissionAppsViewModel.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/PermissionAppsViewModel.kt @@ -67,9 +67,9 @@ import java.util.concurrent.TimeUnit import kotlin.math.max /** - * ViewModel for the PermissionAppsFragment. Has a liveData with all of the UI info for each - * package which requests permissions in this permission group, a liveData which tracks whether or - * not to show system apps, and a liveData tracking whether there are any system apps which request + * ViewModel for the PermissionAppsFragment. Has a liveData with all of the UI info for each package + * which requests permissions in this permission group, a liveData which tracks whether or not to + * show system apps, and a liveData tracking whether there are any system apps which request * permissions in this group. * * @param app The current application @@ -96,10 +96,10 @@ class PermissionAppsViewModel( val categorizedAppsLiveData = CategorizedAppsLiveData(groupName) @get:RequiresApi(Build.VERSION_CODES.S) - val sensorStatusLiveData: SensorStatusLiveData by lazy(LazyThreadSafetyMode.NONE) - @RequiresApi(Build.VERSION_CODES.S) { - SensorStatusLiveData() - } + val sensorStatusLiveData: SensorStatusLiveData by + lazy(LazyThreadSafetyMode.NONE) @RequiresApi(Build.VERSION_CODES.S) { + SensorStatusLiveData() + } fun updateShowSystem(showSystem: Boolean) { if (showSystem != state.get(SHOULD_SHOW_SYSTEM_KEY)) { @@ -111,9 +111,7 @@ class PermissionAppsViewModel( get() = state.get(CREATION_LOGGED_KEY) ?: false set(value) = state.set(CREATION_LOGGED_KEY, value) - /** - * A LiveData that tracks the status (blocked or available) of a sensor - */ + /** A LiveData that tracks the status (blocked or available) of a sensor */ @RequiresApi(Build.VERSION_CODES.S) inner class SensorStatusLiveData() : SmartUpdateMediatorLiveData<Boolean>() { val sensorPrivacyManager = app.getSystemService(SensorPrivacyManager::class.java)!! @@ -157,13 +155,9 @@ class PermissionAppsViewModel( } } - private val listener = { _: Int, status: Boolean -> - value = status - } + private val listener = { _: Int, status: Boolean -> value = status } - private val locListener = { status: Boolean -> - value = !status - } + private val locListener = { status: Boolean -> value = !status } override fun onUpdate() { // Do nothing @@ -171,8 +165,9 @@ class PermissionAppsViewModel( } inner class CategorizedAppsLiveData(groupName: String) : - MediatorLiveData<@kotlin.jvm.JvmSuppressWildcards - Map<Category, List<Pair<String, UserHandle>>>>() { + MediatorLiveData< + @kotlin.jvm.JvmSuppressWildcards Map<Category, List<Pair<String, UserHandle>>> + >() { private val packagesUiInfoLiveData = SinglePermGroupPackagesUiInfoLiveData[groupName] init { @@ -193,18 +188,18 @@ class PermissionAppsViewModel( } addSource(packagesUiInfoLiveData) { - if (fullStorageLiveData == null || fullStorageLiveData.isInitialized) - update() + if (fullStorageLiveData == null || fullStorageLiveData.isInitialized) update() } addSource(shouldShowSystemLiveData) { - if (fullStorageLiveData == null || fullStorageLiveData.isInitialized) - update() + if (fullStorageLiveData == null || fullStorageLiveData.isInitialized) update() } - if ((fullStorageLiveData == null || fullStorageLiveData.isInitialized) && - packagesUiInfoLiveData.isInitialized) { - packagesWithFullFileAccess = fullStorageLiveData?.value?.filter { it.isGranted } - ?: emptyList() + if ( + (fullStorageLiveData == null || fullStorageLiveData.isInitialized) && + packagesUiInfoLiveData.isInitialized + ) { + packagesWithFullFileAccess = + fullStorageLiveData?.value?.filter { it.isGranted } ?: emptyList() update() } } @@ -218,12 +213,14 @@ class PermissionAppsViewModel( categoryMap[Category.ASK] = mutableListOf() categoryMap[Category.DENIED] = mutableListOf() - val packageMap = packagesUiInfoLiveData.value ?: run { - if (packagesUiInfoLiveData.isInitialized) { - value = categoryMap - } - return - } + val packageMap = + packagesUiInfoLiveData.value + ?: run { + if (packagesUiInfoLiveData.isInitialized) { + value = categoryMap + } + return + } val hasSystem = packageMap.any { it.value.isSystem && it.value.shouldShow } if (hasSystem != state.get(HAS_SYSTEM_APPS_KEY)) { @@ -241,22 +238,31 @@ class PermissionAppsViewModel( continue } - if (uiInfo.permGrantState == PermGrantState.PERMS_ALLOWED_ALWAYS || - uiInfo.permGrantState == PermGrantState.PERMS_ALLOWED_FOREGROUND_ONLY) { + if ( + uiInfo.permGrantState == PermGrantState.PERMS_ALLOWED_ALWAYS || + uiInfo.permGrantState == PermGrantState.PERMS_ALLOWED_FOREGROUND_ONLY + ) { showAlwaysAllowedString = true } - var category = when (uiInfo.permGrantState) { - PermGrantState.PERMS_ALLOWED -> Category.ALLOWED - PermGrantState.PERMS_ALLOWED_FOREGROUND_ONLY -> Category.ALLOWED_FOREGROUND - PermGrantState.PERMS_ALLOWED_ALWAYS -> Category.ALLOWED - PermGrantState.PERMS_DENIED -> Category.DENIED - PermGrantState.PERMS_ASK -> Category.ASK - } + var category = + when (uiInfo.permGrantState) { + PermGrantState.PERMS_ALLOWED -> Category.ALLOWED + PermGrantState.PERMS_ALLOWED_FOREGROUND_ONLY -> Category.ALLOWED_FOREGROUND + PermGrantState.PERMS_ALLOWED_ALWAYS -> Category.ALLOWED + PermGrantState.PERMS_DENIED -> Category.DENIED + PermGrantState.PERMS_ASK -> Category.ASK + } - if (!SdkLevel.isAtLeastT() && groupName == Manifest.permission_group.STORAGE && - packagesWithFullFileAccess.any { !it.isLegacy && it.isGranted && - it.packageName to it.user == packageUserPair }) { + if ( + !SdkLevel.isAtLeastT() && + groupName == Manifest.permission_group.STORAGE && + packagesWithFullFileAccess.any { + !it.isLegacy && + it.isGranted && + it.packageName to it.user == packageUserPair + } + ) { category = Category.ALLOWED } categoryMap[category]!!.add(packageUserPair) @@ -267,9 +273,9 @@ class PermissionAppsViewModel( } /** - * If this is the storage permission group, some apps have full access to storage, while - * others just have access to media files. This list contains the packages with full access. - * To listen for changes, create and observe a FullStoragePermissionAppsLiveData + * If this is the storage permission group, some apps have full access to storage, while others + * just have access to media files. This list contains the packages with full access. To listen + * for changes, create and observe a FullStoragePermissionAppsLiveData */ private var packagesWithFullFileAccess = listOf<FullStoragePackageState>() @@ -280,17 +286,15 @@ class PermissionAppsViewModel( * * @param packageName The name of the package we want to check * @param user The name of the user whose package we want to check - * * @return true if the package and user has full file access */ fun packageHasFullStorage(packageName: String, user: UserHandle): Boolean { - return packagesWithFullFileAccess.any { - it.packageName == packageName && it.user == user } + return packagesWithFullFileAccess.any { it.packageName == packageName && it.user == user } } /** - * Whether or not packages have been loaded from the system. - * To update, need to observe the allPackageInfosLiveData. + * Whether or not packages have been loaded from the system. To update, need to observe the + * allPackageInfosLiveData. * * @return Whether or not all packages have been loaded */ @@ -313,16 +317,16 @@ class PermissionAppsViewModel( args: Bundle ) { val activity = fragment.activity!! - if (LocationUtils.isLocationGroupAndProvider( - activity, groupName, packageName)) { + if (LocationUtils.isLocationGroupAndProvider(activity, groupName, packageName)) { val intent = Intent(activity, LocationProviderInterceptDialog::class.java) intent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName) activity.startActivityAsUser(intent, user) return } - if (LocationUtils.isLocationGroupAndControllerExtraPackage( - activity, groupName, packageName)) { + if ( + LocationUtils.isLocationGroupAndControllerExtraPackage(activity, groupName, packageName) + ) { // Redirect to location controller extra package settings. LocationUtils.startLocationControllerExtraPackageSettings(activity, user) return @@ -332,31 +336,38 @@ class PermissionAppsViewModel( } fun getFilterTimeBeginMillis(): Long { - val aggregateDataFilterBeginDays = if (is7DayToggleEnabled()) - AGGREGATE_DATA_FILTER_BEGIN_DAYS_7 else AGGREGATE_DATA_FILTER_BEGIN_DAYS_1 + val aggregateDataFilterBeginDays = + if (is7DayToggleEnabled()) AGGREGATE_DATA_FILTER_BEGIN_DAYS_7 + else AGGREGATE_DATA_FILTER_BEGIN_DAYS_1 - return max(System.currentTimeMillis() - + return max( + System.currentTimeMillis() - TimeUnit.DAYS.toMillis(aggregateDataFilterBeginDays.toLong()), - Instant.EPOCH.toEpochMilli()) + Instant.EPOCH.toEpochMilli() + ) } /** * Return a mapping of user + packageName to their last access timestamps for the permission * group. */ - fun extractGroupUsageLastAccessTime(appPermissionUsages: List<AppPermissionUsage>): - MutableMap<String, Long> { + fun extractGroupUsageLastAccessTime( + appPermissionUsages: List<AppPermissionUsage> + ): MutableMap<String, Long> { val accessTime: MutableMap<String, Long> = HashMap() if (!SdkLevel.isAtLeastS()) { return accessTime } - val aggregateDataFilterBeginDays = if (is7DayToggleEnabled()) - AGGREGATE_DATA_FILTER_BEGIN_DAYS_7 else AGGREGATE_DATA_FILTER_BEGIN_DAYS_1 + val aggregateDataFilterBeginDays = + if (is7DayToggleEnabled()) AGGREGATE_DATA_FILTER_BEGIN_DAYS_7 + else AGGREGATE_DATA_FILTER_BEGIN_DAYS_1 val now = System.currentTimeMillis() - val filterTimeBeginMillis = max( + val filterTimeBeginMillis = + max( now - TimeUnit.DAYS.toMillis(aggregateDataFilterBeginDays.toLong()), - Instant.EPOCH.toEpochMilli()) + Instant.EPOCH.toEpochMilli() + ) val numApps: Int = appPermissionUsages.size for (appIndex in 0 until numApps) { val appUsage: AppPermissionUsage = appPermissionUsages.get(appIndex) @@ -380,41 +391,39 @@ class PermissionAppsViewModel( return accessTime } - /** - * Return the String preference summary based on the last access time. - */ - fun getPreferenceSummary(res: Resources, summaryTimestamp: Triple<String, Int, String>): - String { + /** Return the String preference summary based on the last access time. */ + fun getPreferenceSummary( + res: Resources, + summaryTimestamp: Triple<String, Int, String> + ): String { return when (summaryTimestamp.second) { - Utils.LAST_24H_CONTENT_PROVIDER -> res.getString( - R.string.app_perms_content_provider_24h) - Utils.LAST_7D_CONTENT_PROVIDER -> res.getString( - R.string.app_perms_content_provider_7d) - Utils.LAST_24H_SENSOR_TODAY -> res.getString(R.string.app_perms_24h_access, - summaryTimestamp.first) - Utils.LAST_24H_SENSOR_YESTERDAY -> res.getString(R.string.app_perms_24h_access_yest, - summaryTimestamp.first) - Utils.LAST_7D_SENSOR -> res.getString(R.string.app_perms_7d_access, - summaryTimestamp.third, summaryTimestamp.first) + Utils.LAST_24H_CONTENT_PROVIDER -> + res.getString(R.string.app_perms_content_provider_24h) + Utils.LAST_7D_CONTENT_PROVIDER -> res.getString(R.string.app_perms_content_provider_7d) + Utils.LAST_24H_SENSOR_TODAY -> + res.getString(R.string.app_perms_24h_access, summaryTimestamp.first) + Utils.LAST_24H_SENSOR_YESTERDAY -> + res.getString(R.string.app_perms_24h_access_yest, summaryTimestamp.first) + Utils.LAST_7D_SENSOR -> + res.getString( + R.string.app_perms_7d_access, + summaryTimestamp.third, + summaryTimestamp.first + ) else -> "" } } - /** - * Return two preferences to determine their ordering. - */ + /** Return two preferences to determine their ordering. */ fun comparePreference(collator: Collator, lhs: Preference, rhs: Preference): Int { - var result: Int = collator.compare(lhs.title.toString(), - rhs.title.toString()) + var result: Int = collator.compare(lhs.title.toString(), rhs.title.toString()) if (result == 0) { result = lhs.key.compareTo(rhs.key) } return result } - /** - * Log that the fragment was created. - */ + /** Log that the fragment was created. */ fun logPermissionAppsFragmentCreated( packageName: String, user: UserHandle, @@ -439,14 +448,30 @@ class PermissionAppsViewModel( category = PERMISSION_APPS_FRAGMENT_VIEWED__CATEGORY__DENIED } } - val uid = getPackageUid(application, - packageName, user) ?: return + val uid = getPackageUid(application, packageName, user) ?: return PermissionControllerStatsLog.write( - PermissionControllerStatsLog.PERMISSION_APPS_FRAGMENT_VIEWED, sessionId, viewId, - permGroupName, uid, packageName, category) - Log.v(tag, tag + " created with sessionId=" + sessionId + - " permissionGroupName=" + permGroupName + " appUid=" + uid + - " packageName=" + packageName + " category=" + category) + PermissionControllerStatsLog.PERMISSION_APPS_FRAGMENT_VIEWED, + sessionId, + viewId, + permGroupName, + uid, + packageName, + category + ) + Log.v( + tag, + tag + + " created with sessionId=" + + sessionId + + " permissionGroupName=" + + permGroupName + + " appUid=" + + uid + + " packageName=" + + packageName + + " category=" + + category + ) } } @@ -470,7 +495,6 @@ class PermissionAppsViewModelFactory( state.set(HAS_SYSTEM_APPS_KEY, state.get<Boolean>(HAS_SYSTEM_APPS_KEY) ?: true) state.set(SHOW_ALWAYS_ALLOWED, state.get<Boolean>(SHOW_ALWAYS_ALLOWED) ?: false) state.set(CREATION_LOGGED_KEY, state.get<Boolean>(CREATION_LOGGED_KEY) ?: false) - @Suppress("UNCHECKED_CAST") - return PermissionAppsViewModel(state, app, groupName) as T + @Suppress("UNCHECKED_CAST") return PermissionAppsViewModel(state, app, groupName) as T } } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/ReviewPermissionsViewModel.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/ReviewPermissionsViewModel.kt index 4e1fc1861..8613d1cae 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/ReviewPermissionsViewModel.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/ReviewPermissionsViewModel.kt @@ -39,53 +39,51 @@ import com.android.settingslib.RestrictedLockUtils import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin import java.util.stream.Collectors -/** - * View model for legacy {@link ReviewPermissionsFragment}. - */ -class ReviewPermissionsViewModel( - val app: Application, - val packageInfo: PackageInfo -) : ViewModel() { +/** View model for legacy {@link ReviewPermissionsFragment}. */ +class ReviewPermissionsViewModel(val app: Application, val packageInfo: PackageInfo) : ViewModel() { private val mUser = android.os.Process.myUserHandle() - /** - * Holds permission groups for a package or an empty map in case no user review is required. - */ + /** Holds permission groups for a package or an empty map in case no user review is required. */ val permissionGroupsLiveData = object : SmartUpdateMediatorLiveData<Map<String, LightAppPermGroup>>() { val packagePermsLiveData = PackagePermissionsLiveData[packageInfo.packageName, mUser] init { - addSource(packagePermsLiveData) { - update() - } + addSource(packagePermsLiveData) { update() } } val permissionGroups = mutableMapOf<String, LightAppPermGroupLiveData>() override fun onUpdate() { val permissionGroupsMap = packagePermsLiveData.value ?: return - val filteredGroups = permissionGroupsMap.keys.stream() - .filter { it -> !it.equals(NON_RUNTIME_NORMAL_PERMS) } - .collect(Collectors.toList()) + val filteredGroups = + permissionGroupsMap.keys + .stream() + .filter { it -> !it.equals(NON_RUNTIME_NORMAL_PERMS) } + .collect(Collectors.toList()) val getPermGroupLiveData = { permGroupName: String -> LightAppPermGroupLiveData[packageInfo.packageName, permGroupName, mUser] } setSourcesToDifference(filteredGroups, permissionGroups, getPermGroupLiveData) - if (permissionGroups.values.all { it.isInitialized } && - permissionGroups.values.all { !it.isStale }) { - val permGroups: List<LightAppPermGroup?> = permissionGroups.values.map { - it.value } - val reviewGroups = permGroups.filterNotNull().filter { - shouldShowPermission(it) && - Utils.OS_PKG == it.permGroupInfo.packageName - }.associateBy { - it.permGroupName - } - value = if (reviewGroups.any { it.value.isReviewRequired }) reviewGroups - else emptyMap() + if ( + permissionGroups.values.all { it.isInitialized } && + permissionGroups.values.all { !it.isStale } + ) { + val permGroups: List<LightAppPermGroup?> = + permissionGroups.values.map { it.value } + val reviewGroups = + permGroups + .filterNotNull() + .filter { + shouldShowPermission(it) && + Utils.OS_PKG == it.permGroupInfo.packageName + } + .associateBy { it.permGroupName } + value = + if (reviewGroups.any { it.value.isReviewRequired }) reviewGroups + else emptyMap() } } } @@ -110,15 +108,15 @@ class ReviewPermissionsViewModel( } /** - * Update the summary of a permission group that has background permission. - * This does not apply to permission groups that are fixed by policy + * Update the summary of a permission group that has background permission. This does not apply + * to permission groups that are fixed by policy */ - fun getSummaryForPermGroupWithBackgroundPermission( - state: PermissionTarget - ): PermissionSummary { + fun getSummaryForPermGroupWithBackgroundPermission(state: PermissionTarget): PermissionSummary { if (state != PermissionTarget.PERMISSION_NONE) { - if (state.and(PermissionTarget.PERMISSION_BACKGROUND) - != PermissionTarget.PERMISSION_NONE.value) { + if ( + state.and(PermissionTarget.PERMISSION_BACKGROUND) != + PermissionTarget.PERMISSION_NONE.value + ) { return SummaryMessage.ACCESS_ALWAYS.toPermSummary() } else { return SummaryMessage.ACCESS_ONLY_FOREGROUND.toPermSummary() @@ -152,9 +150,7 @@ class ReviewPermissionsViewModel( } } - /** - * Show all individual permissions in this group in a new fragment. - */ + /** Show all individual permissions in this group in a new fragment. */ fun showAllPermissions(fragment: Fragment, args: Bundle) { val navController: NavController = NavHostFragment.findNavController(fragment) navController.navigateSafe(R.id.app_to_all_perms, args) @@ -222,8 +218,10 @@ class ReviewPermissionsViewModel( SummaryMessage.ENFORCED_BY_POLICY.toPermSummary() } } else { - if (mState.and(PermissionTarget.PERMISSION_BACKGROUND) != - PermissionTarget.PERMISSION_NONE.value) { + if ( + mState.and(PermissionTarget.PERMISSION_BACKGROUND) != + PermissionTarget.PERMISSION_NONE.value + ) { return if (hasAdmin) { SummaryMessage.ENABLED_BY_ADMIN.toPermSummary() } else { @@ -242,8 +240,10 @@ class ReviewPermissionsViewModel( } else { // Part of the permission group can still be switched if (permGroup.background.isPolicyFixed) { - return if (mState.and(PermissionTarget.PERMISSION_BACKGROUND) != - PermissionTarget.PERMISSION_NONE.value) { + return if ( + mState.and(PermissionTarget.PERMISSION_BACKGROUND) != + PermissionTarget.PERMISSION_NONE.value + ) { if (hasAdmin) { SummaryMessage.ENABLED_BY_ADMIN_BACKGROUND_ONLY.toPermSummary(true) } else { @@ -277,11 +277,10 @@ class ReviewPermissionsViewModel( return mGroup.foreground.isPolicyFixed && !mGroup.isGranted } - /** - * Whether policy is system fixed or fully fixed or foreground disabled - */ + /** Whether policy is system fixed or fully fixed or foreground disabled */ fun isFixedOrForegroundDisabled(mGroup: LightAppPermGroup): Boolean { - return mGroup.isSystemFixed || mGroup.isPolicyFullyFixed || + return mGroup.isSystemFixed || + mGroup.isPolicyFullyFixed || isForegroundDisabledByPolicy(mGroup) } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/UnusedAppsViewModel.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/UnusedAppsViewModel.kt index 3c3f347a4..868f9229f 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/UnusedAppsViewModel.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/UnusedAppsViewModel.kt @@ -96,85 +96,89 @@ class UnusedAppsViewModel(private val app: Application, private val sessionId: L private data class PackageLastUsageTime(val packageName: String, val usageTime: Long) val unusedPackageCategoriesLiveData = - object : SmartAsyncMediatorLiveData<Map<UnusedPeriod, List<UnusedPackageInfo>>>( - alwaysUpdateOnActive = false - ) { - // Get apps usage stats from the longest interesting period (MAX_UNUSED_PERIOD_MILLIS) - private val usageStatsLiveData = UsageStatsLiveData[MAX_UNUSED_PERIOD_MILLIS] - - init { - addSource(getUnusedPackages()) { - onUpdate() - } - - addSource(AllPackageInfosLiveData) { - onUpdate() - } + object : + SmartAsyncMediatorLiveData<Map<UnusedPeriod, List<UnusedPackageInfo>>>( + alwaysUpdateOnActive = false + ) { + // Get apps usage stats from the longest interesting period (MAX_UNUSED_PERIOD_MILLIS) + private val usageStatsLiveData = UsageStatsLiveData[MAX_UNUSED_PERIOD_MILLIS] - addSource(usageStatsLiveData) { - onUpdate() - } - } + init { + addSource(getUnusedPackages()) { onUpdate() } - override suspend fun loadDataAndPostValue(job: Job) { - if (!getUnusedPackages().isInitialized || - !usageStatsLiveData.isInitialized || !AllPackageInfosLiveData.isInitialized - ) { - return - } + addSource(AllPackageInfosLiveData) { onUpdate() } - val unusedApps = getUnusedPackages().value!! - Log.i(LOG_TAG, "Unused apps: $unusedApps") - val categorizedApps = mutableMapOf<UnusedPeriod, MutableList<UnusedPackageInfo>>() - for (period in UnusedPeriod.allPeriods) { - categorizedApps[period] = mutableListOf() + addSource(usageStatsLiveData) { onUpdate() } } - // Get all packages which cannot be uninstalled. - val systemApps = getUnusedSystemApps(AllPackageInfosLiveData.value!!, unusedApps) - val lastUsedDataUnusedApps = - extractUnusedAppsUsageData(usageStatsLiveData.value!!, unusedApps) - { it: UsageStats -> - PackageLastUsageTime(it.packageName, it.lastTimePackageUsed()) + override suspend fun loadDataAndPostValue(job: Job) { + if ( + !getUnusedPackages().isInitialized || + !usageStatsLiveData.isInitialized || + !AllPackageInfosLiveData.isInitialized + ) { + return } - val firstInstallDataUnusedApps = - extractUnusedAppsUsageData(AllPackageInfosLiveData.value!!, unusedApps) - { it: LightPackageInfo -> - PackageLastUsageTime(it.packageName, it.firstInstallTime) + + val unusedApps = getUnusedPackages().value!! + Log.i(LOG_TAG, "Unused apps: $unusedApps") + val categorizedApps = mutableMapOf<UnusedPeriod, MutableList<UnusedPackageInfo>>() + for (period in UnusedPeriod.allPeriods) { + categorizedApps[period] = mutableListOf() } - val now = System.currentTimeMillis() - unusedApps.keys.forEach { (packageName, user) -> - val userPackage = packageName to user - - // If we didn't find the stat for a package in our usageStats search, it is more than - // 6 months old, or the app has never been opened. Then use first install date instead. - var lastUsageTime = - lastUsedDataUnusedApps[userPackage] ?: firstInstallDataUnusedApps[ - userPackage] ?: 0L - - val period = UnusedPeriod.findLongestValidPeriod(now - lastUsageTime) - categorizedApps[period]!!.add( - UnusedPackageInfo( - packageName, user, systemApps.contains(userPackage), - unusedApps[userPackage]!! + // Get all packages which cannot be uninstalled. + val systemApps = getUnusedSystemApps(AllPackageInfosLiveData.value!!, unusedApps) + val lastUsedDataUnusedApps = + extractUnusedAppsUsageData(usageStatsLiveData.value!!, unusedApps) { + it: UsageStats -> + PackageLastUsageTime(it.packageName, it.lastTimePackageUsed()) + } + val firstInstallDataUnusedApps = + extractUnusedAppsUsageData(AllPackageInfosLiveData.value!!, unusedApps) { + it: LightPackageInfo -> + PackageLastUsageTime(it.packageName, it.firstInstallTime) + } + + val now = System.currentTimeMillis() + unusedApps.keys.forEach { (packageName, user) -> + val userPackage = packageName to user + + // If we didn't find the stat for a package in our usageStats search, it is more + // than + // 6 months old, or the app has never been opened. Then use first install date + // instead. + var lastUsageTime = + lastUsedDataUnusedApps[userPackage] + ?: firstInstallDataUnusedApps[userPackage] ?: 0L + + val period = UnusedPeriod.findLongestValidPeriod(now - lastUsageTime) + categorizedApps[period]!!.add( + UnusedPackageInfo( + packageName, + user, + systemApps.contains(userPackage), + unusedApps[userPackage]!! + ) ) - ) - } + } - postValue(categorizedApps) + postValue(categorizedApps) + } } - } // Extract UserPackage information for unused system apps from source map. private fun getUnusedSystemApps( userPackages: Map<UserHandle, List<LightPackageInfo>>, - unusedApps: Map<UserPackage, Set<String>>, + unusedApps: Map<UserPackage, Set<String>>, ): List<UserPackage> { - return userPackages.flatMap { (userHandle, packageList) -> - packageList.filter { (it.appFlags and ApplicationInfo.FLAG_SYSTEM) != 0 } - .map { it.packageName to userHandle } - }.filter { unusedApps.contains(it) } + return userPackages + .flatMap { (userHandle, packageList) -> + packageList + .filter { (it.appFlags and ApplicationInfo.FLAG_SYSTEM) != 0 } + .map { it.packageName to userHandle } + } + .filter { unusedApps.contains(it) } } /** @@ -187,9 +191,11 @@ class UnusedAppsViewModel(private val app: Application, private val sessionId: L unusedApps: Map<UserPackage, Set<String>>, extractUsageData: (fullData: PackageData) -> PackageLastUsageTime, ): Map<UserPackage, Long> { - return userPackages.flatMap { (userHandle, fullData) -> - fullData.map { userHandle to extractUsageData(it) } - }.associate { (handle, appData) -> (appData.packageName to handle) to appData.usageTime } + return userPackages + .flatMap { (userHandle, fullData) -> + fullData.map { userHandle to extractUsageData(it) } + } + .associate { (handle, appData) -> (appData.packageName to handle) to appData.usageTime } .filterKeys { unusedApps.contains(it) } } @@ -215,37 +221,57 @@ class UnusedAppsViewModel(private val app: Application, private val sessionId: L Log.i(LOG_TAG, "sessionId: $sessionId, Disabling $packageName, $user") logAppInteraction(packageName, user, AUTO_REVOKED_APP_INTERACTION__ACTION__REMOVE) val userContext = Utils.getUserContext(app, user) - userContext.packageManager.setApplicationEnabledSetting(packageName, - PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER, 0) + userContext.packageManager.setApplicationEnabledSetting( + packageName, + PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER, + 0 + ) } private fun logAppInteraction(packageName: String, user: UserHandle, action: Int) { GlobalScope.launch(IPC) { // If we are logging an app interaction, then the AllPackageInfosLiveData is not stale. - val uid = AllPackageInfosLiveData.value?.get(user) - ?.find { info -> info.packageName == packageName }?.uid + val uid = + AllPackageInfosLiveData.value + ?.get(user) + ?.find { info -> info.packageName == packageName } + ?.uid if (uid != null) { - PermissionControllerStatsLog.write(AUTO_REVOKED_APP_INTERACTION, sessionId, - uid, packageName, action) + PermissionControllerStatsLog.write( + AUTO_REVOKED_APP_INTERACTION, + sessionId, + uid, + packageName, + action + ) } } } fun logAppView(packageName: String, user: UserHandle, groupName: String, isNew: Boolean) { GlobalScope.launch(IPC) { - val uid = AllPackageInfosLiveData.value!![user]!!.find { info -> - info.packageName == packageName - }?.uid + val uid = + AllPackageInfosLiveData.value!![user]!!.find { info -> + info.packageName == packageName + } + ?.uid if (uid != null) { - val bucket = if (isNew) { - AUTO_REVOKE_FRAGMENT_APP_VIEWED__AGE__NEWER_BUCKET - } else { - AUTO_REVOKE_FRAGMENT_APP_VIEWED__AGE__OLDER_BUCKET - } - PermissionControllerStatsLog.write(AUTO_REVOKE_FRAGMENT_APP_VIEWED, sessionId, - uid, packageName, groupName, bucket) + val bucket = + if (isNew) { + AUTO_REVOKE_FRAGMENT_APP_VIEWED__AGE__NEWER_BUCKET + } else { + AUTO_REVOKE_FRAGMENT_APP_VIEWED__AGE__OLDER_BUCKET + } + PermissionControllerStatsLog.write( + AUTO_REVOKE_FRAGMENT_APP_VIEWED, + sessionId, + uid, + packageName, + groupName, + bucket + ) } } } @@ -259,7 +285,6 @@ class UnusedAppsViewModelFactory( ) : ViewModelProvider.Factory { override fun <T : ViewModel> create(modelClass: Class<T>): T { - @Suppress("UNCHECKED_CAST") - return UnusedAppsViewModel(app, sessionId) as T + @Suppress("UNCHECKED_CAST") return UnusedAppsViewModel(app, sessionId) as T } } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v31/PermissionUsageControlPreferenceUtils.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v31/PermissionUsageControlPreferenceUtils.kt index db79165c3..cd046ec73 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v31/PermissionUsageControlPreferenceUtils.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v31/PermissionUsageControlPreferenceUtils.kt @@ -36,11 +36,12 @@ import com.android.permissioncontroller.permission.utils.StringUtils @RequiresApi(Build.VERSION_CODES.S) object PermissionUsageControlPreferenceUtils { - private val SENSOR_DATA_PERMISSIONS: List<String> = listOf( - Manifest.permission_group.LOCATION, - Manifest.permission_group.CAMERA, - Manifest.permission_group.MICROPHONE - ) + private val SENSOR_DATA_PERMISSIONS: List<String> = + listOf( + Manifest.permission_group.LOCATION, + Manifest.permission_group.CAMERA, + Manifest.permission_group.MICROPHONE + ) @JvmStatic fun initPreference( @@ -56,19 +57,28 @@ object PermissionUsageControlPreferenceUtils { return preference.apply { title = permGroupLabel icon = KotlinUtils.getPermGroupIcon(context, groupName) - summary = StringUtils.getIcuPluralsString(context, - R.string.permission_usage_preference_label, count) + summary = + StringUtils.getIcuPluralsString( + context, + R.string.permission_usage_preference_label, + count + ) if (count == 0) { isEnabled = false - val permissionUsageSummaryNotUsed = if (show7Days) { - StringUtils.getIcuPluralsString(context, + val permissionUsageSummaryNotUsed = + if (show7Days) { + StringUtils.getIcuPluralsString( + context, R.string.permission_usage_preference_summary_not_used_in_past_n_days, - 7) - } else { - StringUtils.getIcuPluralsString(context, + 7 + ) + } else { + StringUtils.getIcuPluralsString( + context, R.string.permission_usage_preference_summary_not_used_in_past_n_hours, - 24) - } + 24 + ) + } setSummary(permissionUsageSummaryNotUsed) } else if (SENSOR_DATA_PERMISSIONS.contains(groupName)) { onPreferenceClickListener = OnPreferenceClickListener { @@ -92,18 +102,19 @@ object PermissionUsageControlPreferenceUtils { } private fun logSensorDataTimelineViewed(groupName: String, sessionId: Long) { - val act = when (groupName) { - Manifest.permission_group.LOCATION -> { - PERMISSION_USAGE_FRAGMENT_INTERACTION__ACTION__LOCATION_ACCESS_TIMELINE_VIEWED - } - Manifest.permission_group.CAMERA -> { - PERMISSION_USAGE_FRAGMENT_INTERACTION__ACTION__CAMERA_ACCESS_TIMELINE_VIEWED - } - Manifest.permission_group.MICROPHONE -> { - PERMISSION_USAGE_FRAGMENT_INTERACTION__ACTION__MICROPHONE_ACCESS_TIMELINE_VIEWED + val act = + when (groupName) { + Manifest.permission_group.LOCATION -> { + PERMISSION_USAGE_FRAGMENT_INTERACTION__ACTION__LOCATION_ACCESS_TIMELINE_VIEWED + } + Manifest.permission_group.CAMERA -> { + PERMISSION_USAGE_FRAGMENT_INTERACTION__ACTION__CAMERA_ACCESS_TIMELINE_VIEWED + } + Manifest.permission_group.MICROPHONE -> { + PERMISSION_USAGE_FRAGMENT_INTERACTION__ACTION__MICROPHONE_ACCESS_TIMELINE_VIEWED + } + else -> 0 } - else -> 0 - } PermissionControllerStatsLog.write(PERMISSION_USAGE_FRAGMENT_INTERACTION, sessionId, act) } } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v31/PermissionUsageDetailsViewModel.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v31/PermissionUsageDetailsViewModel.kt index f77bfff3b..6dba04b55 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v31/PermissionUsageDetailsViewModel.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v31/PermissionUsageDetailsViewModel.kt @@ -28,6 +28,7 @@ import android.content.Context import android.content.Intent import android.content.pm.PackageManager import android.content.res.Resources +import android.graphics.drawable.Drawable import android.os.Build import android.os.Bundle import android.os.UserHandle @@ -37,7 +38,6 @@ import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import androidx.savedstate.SavedStateRegistryOwner import com.android.modules.utils.build.SdkLevel -import com.android.permissioncontroller.PermissionControllerApplication import com.android.permissioncontroller.R import com.android.permissioncontroller.permission.compat.IntentCompat import com.android.permissioncontroller.permission.data.AppPermGroupUiInfoLiveData @@ -54,7 +54,6 @@ import com.android.permissioncontroller.permission.model.livedatatypes.v31.Light import com.android.permissioncontroller.permission.ui.handheld.v31.getDurationUsedStr import com.android.permissioncontroller.permission.ui.handheld.v31.shouldShowSubattributionInPermissionsDashboard import com.android.permissioncontroller.permission.utils.KotlinUtils -import com.android.permissioncontroller.permission.utils.KotlinUtils.getPackageLabel import com.android.permissioncontroller.permission.utils.PermissionMapping import com.android.permissioncontroller.permission.utils.Utils import com.android.permissioncontroller.permission.utils.v31.SubattributionUtils @@ -80,6 +79,9 @@ class PermissionUsageDetailsViewModel( val showSystemLiveData = state.getLiveData(SHOULD_SHOW_SYSTEM_KEY, false) val show7DaysLiveData = state.getLiveData(SHOULD_SHOW_7_DAYS_KEY, false) + private val packageIconCache: MutableMap<Pair<String, UserHandle>, Drawable> = mutableMapOf() + private val packageLabelCache: MutableMap<String, String> = mutableMapOf() + private val roleManager = Utils.getSystemServiceSafe(application.applicationContext, RoleManager::class.java) @@ -109,14 +111,19 @@ class PermissionUsageDetailsViewModel( } val startTime = (System.currentTimeMillis() - showPermissionUsagesDuration).coerceAtLeast( - Instant.EPOCH.toEpochMilli()) + Instant.EPOCH.toEpochMilli() + ) return PermissionUsageDetailsUiInfo( show7Days, showSystem, buildAppPermissionAccessUiInfoList( - allLightHistoricalPackageOpsLiveData, startTime, showSystem), - containsSystemAppUsages(allLightHistoricalPackageOpsLiveData, startTime)) + allLightHistoricalPackageOpsLiveData, + startTime, + showSystem + ), + containsSystemAppUsages(allLightHistoricalPackageOpsLiveData, startTime) + ) } /** @@ -162,9 +169,11 @@ class PermissionUsageDetailsViewModel( // The Telecom doesn't request microphone or camera permissions. However, telecom app may // use these permissions and they are considered system app permissions, so we return true // even if the AppPermGroupUiInfo is unavailable. - if (appPermissionId.packageName == TELECOM_PACKAGE && - (appPermissionId.permissionGroup == Manifest.permission_group.CAMERA || - appPermissionId.permissionGroup == Manifest.permission_group.MICROPHONE)) { + if ( + appPermissionId.packageName == TELECOM_PACKAGE && + (appPermissionId.permissionGroup == Manifest.permission_group.CAMERA || + appPermissionId.permissionGroup == Manifest.permission_group.MICROPHONE) + ) { return true } return false @@ -244,7 +253,8 @@ class PermissionUsageDetailsViewModel( it.appPermissionId, it.attributionLabel, it.attributionTags, - updatedDiscreteAccesses) + updatedDiscreteAccesses + ) } /** Filters out data for apps and permissions that don't need to be displayed in the UI. */ @@ -268,7 +278,8 @@ class PermissionUsageDetailsViewModel( this.appPermissionId, Resources.ID_NULL, attributionTags = emptyList(), - this.discreteAccesses) + this.discreteAccesses + ) /** Groups tag-attributed accesses for the provided app and permission by attribution label. */ private fun AttributedAppPermissionDiscreteAccesses.groupAccessesByLabel( @@ -307,7 +318,9 @@ class PermissionUsageDetailsViewModel( appPermissionId, label, tags, - discreteAccesses.sortedBy { -1 * it.accessTimeMs })) + discreteAccesses.sortedBy { -1 * it.accessTimeMs } + ) + ) } return appPermissionDiscreteAccessWithLabels @@ -334,7 +347,9 @@ class PermissionUsageDetailsViewModel( appPermAccesses.appPermissionId, appPermAccesses.attributionLabel, appPermAccesses.attributionTags, - currentDiscreteAccesses.toMutableList())) + currentDiscreteAccesses.toMutableList() + ) + ) currentDiscreteAccesses.clear() currentDiscreteAccesses.add(discreteAccess) } else { @@ -348,7 +363,9 @@ class PermissionUsageDetailsViewModel( appPermAccesses.appPermissionId, appPermAccesses.attributionLabel, appPermAccesses.attributionTags, - currentDiscreteAccesses.toMutableList())) + currentDiscreteAccesses.toMutableList() + ) + ) } return clusters } @@ -384,12 +401,15 @@ class PermissionUsageDetailsViewModel( return AppPermissionAccessUiInfo( this.appPermissionId.userHandle, this.appPermissionId.packageName, + getPackageLabel(this.appPermissionId.packageName, this.appPermissionId.userHandle), permissionGroup, this.discreteAccesses.last().accessTimeMs, this.discreteAccesses.first().accessTimeMs, summary, showingSubAttribution, - ArrayList(this.attributionTags)) + ArrayList(this.attributionTags), + getBadgedPackageIcon(this.appPermissionId.packageName, this.appPermissionId.userHandle) + ) } /** Builds a summary of the permission access. */ @@ -410,10 +430,14 @@ class PermissionUsageDetailsViewModel( R.string.history_preference_subtext_3, subTextStrings[0], subTextStrings[1], - subTextStrings[2]) + subTextStrings[2] + ) 2 -> context.getString( - R.string.history_preference_subtext_2, subTextStrings[0], subTextStrings[1]) + R.string.history_preference_subtext_2, + subTextStrings[0], + subTextStrings[1] + ) 1 -> subTextStrings[0] else -> null } @@ -462,7 +486,6 @@ class PermissionUsageDetailsViewModel( .firstOrNull { it.proxy?.packageName != null } ?.let { getPackageLabel( - PermissionControllerApplication.get(), it.proxy!!.packageName!!, UserHandle.getUserHandleForUid(it.proxy.uid)) } @@ -492,13 +515,15 @@ class PermissionUsageDetailsViewModel( /** Data used to create a preference for an app's permission usage. */ data class AppPermissionAccessUiInfo( val userHandle: UserHandle, - val pkgName: String, + val packageName: String, + val packageLabel: String, val permissionGroup: String, val accessStartTime: Long, val accessEndTime: Long, val summaryText: CharSequence?, val showingAttribution: Boolean, val attributionTags: ArrayList<String>, + val badgedPackageIcon: Drawable?, ) /** @@ -593,13 +618,17 @@ class PermissionUsageDetailsViewModel( setSourcesToDifference( appPermissionIds, appPermGroupUiInfoLiveDataList, - getAppPermGroupUiInfoLiveData) { - update() - } + getAppPermGroupUiInfoLiveData + ) { + update() + } setSourcesToDifference( - allPackages, lightPackageInfoLiveDataMap, getLightPackageInfoLiveData) { - update() - } + allPackages, + lightPackageInfoLiveDataMap, + getLightPackageInfoLiveData + ) { + update() + } if (appPermGroupUiInfoLiveDataList.any { it.value.isStale }) { return @@ -613,6 +642,36 @@ class PermissionUsageDetailsViewModel( } } + /** + * Returns the icon for the provided package name and user, by first searching the cache + * otherwise retrieving it from the app's [android.content.pm.ApplicationInfo]. + */ + private fun getBadgedPackageIcon(packageName: String, userHandle: UserHandle): Drawable? { + val packageNameWithUser: Pair<String, UserHandle> = Pair(packageName, userHandle) + if (packageIconCache.containsKey(packageNameWithUser)) { + return requireNotNull(packageIconCache[packageNameWithUser]) + } + val packageIcon = KotlinUtils.getBadgedPackageIcon(application, packageName, userHandle) + if (packageIcon != null) packageIconCache[packageNameWithUser] = packageIcon + + return packageIcon + } + + /** + * Returns the label for the provided package name, by first searching the cache otherwise + * retrieving it from the app's [android.content.pm.ApplicationInfo]. + */ + private fun getPackageLabel(packageName: String, user: UserHandle): String { + if (packageLabelCache.containsKey(packageName)) { + return requireNotNull(packageLabelCache[packageName]) + } + + val packageLabel = KotlinUtils.getPackageLabel(application, packageName, user) + packageLabelCache[packageName] = packageLabel + + return packageLabel + } + /** Companion object for [PermissionUsageDetailsViewModel]. */ companion object { private const val ONE_HOUR_MS = 3_600_000 @@ -629,7 +688,8 @@ class PermissionUsageDetailsViewModel( listOf( Manifest.permission_group.CAMERA, Manifest.permission_group.LOCATION, - Manifest.permission_group.MICROPHONE) + Manifest.permission_group.MICROPHONE + ) .flatMap { group -> PermissionMapping.getPlatformPermissionNamesOfGroup(group) } .mapNotNull { permName -> AppOpsManager.permissionToOp(permName) } .toMutableSet() @@ -659,7 +719,8 @@ class PermissionUsageDetailsViewModel( accessStartTime, accessEndTime, showingAttribution, - attributionTags) + attributionTags + ) ?: getDefaultManageAppPermissionsIntent(packageName, userHandle) } @@ -692,11 +753,16 @@ class PermissionUsageDetailsViewModel( } val resolveInfo = context.packageManager.resolveActivity( - intent, PackageManager.ResolveInfoFlags.of(0)) - if (resolveInfo?.activityInfo == null || - !Objects.equals( - resolveInfo.activityInfo.permission, - Manifest.permission.START_VIEW_PERMISSION_USAGE)) { + intent, + PackageManager.ResolveInfoFlags.of(0) + ) + if ( + resolveInfo?.activityInfo == null || + !Objects.equals( + resolveInfo.activityInfo.permission, + Manifest.permission.START_VIEW_PERMISSION_USAGE + ) + ) { return null } intent.component = ComponentName(packageName, resolveInfo.activityInfo.name) diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v31/PermissionUsageViewModel.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v31/PermissionUsageViewModel.kt index eeac22124..efb16ea2f 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v31/PermissionUsageViewModel.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v31/PermissionUsageViewModel.kt @@ -98,7 +98,8 @@ class PermissionUsageViewModel( showSystem, show7Days, mAllLightPackageOpsLiveData.containsSystemAppUsages(startTime), - mAllLightPackageOpsLiveData.buildPermissionGroupsWithUsageCounts(startTime, showSystem)) + mAllLightPackageOpsLiveData.buildPermissionGroupsWithUsageCounts(startTime, showSystem) + ) } /** Builds a map of permission groups to the number of apps that recently accessed them. */ @@ -119,9 +120,14 @@ class PermissionUsageViewModel( lightPackageOps.lastPermissionGroupAccessTimesMs.entries .filterOutExemptedPermissionGroupsFromKeys() .filterOutPermissionsNotRequestedByApp( - lightPackageOps.packageName, lightPackageOps.userHandle) + lightPackageOps.packageName, + lightPackageOps.userHandle + ) .filterOutSystemAppPermissionsIfNecessary( - showSystem, lightPackageOps.packageName, lightPackageOps.userHandle) + showSystem, + lightPackageOps.packageName, + lightPackageOps.userHandle + ) .filterAccessTimeLaterThan(startTime) val recentlyUsedPermissions: List<String> = permGroupsToLastAccess.map { it.key } @@ -147,10 +153,14 @@ class PermissionUsageViewModel( .filterAccessTimeLaterThan(startTime) .map { it.key } .toSet() - if (recentlyUsedPermissions - .filterOutExemptedPermissionGroups() - .containsSystemAppPermission( - lightPackageOps.packageName, lightPackageOps.userHandle)) { + if ( + recentlyUsedPermissions + .filterOutExemptedPermissionGroups() + .containsSystemAppPermission( + lightPackageOps.packageName, + lightPackageOps.userHandle + ) + ) { return true } } @@ -186,9 +196,11 @@ class PermissionUsageViewModel( // The Telecom doesn't request microphone or camera permissions. However, telecom app may // use these permissions and they are considered system app permissions, so we return true // even if the AppPermGroupUiInfo is unavailable. - if (appPermissionId.packageName == TELECOM_PACKAGE && - (appPermissionId.permissionGroup == Manifest.permission_group.CAMERA || - appPermissionId.permissionGroup == Manifest.permission_group.MICROPHONE)) { + if ( + appPermissionId.packageName == TELECOM_PACKAGE && + (appPermissionId.permissionGroup == Manifest.permission_group.CAMERA || + appPermissionId.permissionGroup == Manifest.permission_group.MICROPHONE) + ) { return true } return false @@ -312,27 +324,31 @@ class PermissionUsageViewModel( packageWithUserHandle.first, packageWithUserHandle.second, permissionGroup, - )) + ) + ) } } setSourcesToDifference( appPermissionIds, appPermGroupUiInfoLiveDataList, - getAppPermGroupUiInfoLiveData) { - update() - } + getAppPermGroupUiInfoLiveData + ) { + update() + } setSourcesToDifference( - allPackages, lightPackageInfoLiveDataMap, getLightPackageInfoLiveData) { - update() - } + allPackages, + lightPackageInfoLiveDataMap, + getLightPackageInfoLiveData + ) { + update() + } if (lightPackageInfoLiveDataMap.any { it.value.isStale }) { return } - if (appPermGroupUiInfoLiveDataList.any { it.value.isStale }) { return } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v31/ReviewOngoingUsageViewModel.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v31/ReviewOngoingUsageViewModel.kt index 9c9a55523..2fccd7e06 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v31/ReviewOngoingUsageViewModel.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v31/ReviewOngoingUsageViewModel.kt @@ -14,6 +14,7 @@ * limitations under the License. */ @file:Suppress("DEPRECATION") + package com.android.permissioncontroller.permission.ui.model.v31 import android.Manifest @@ -62,16 +63,11 @@ private const val CALL_OP_USAGE_KEY = "CALL_OP_USAGE" private const val USAGES_KEY = "USAGES_KEY" private const val MIC_MUTED_KEY = "MIC_MUTED_KEY" -/** - * ViewModel for {@link ReviewOngoingUsageFragment} - */ -class ReviewOngoingUsageViewModel( - state: SavedStateHandle, - extraDurationMills: Long -) : ViewModel() { +/** ViewModel for {@link ReviewOngoingUsageFragment} */ +class ReviewOngoingUsageViewModel(state: SavedStateHandle, extraDurationMills: Long) : ViewModel() { /** Time of oldest usages considered */ - private val startTime = max(state.get<Long>(FIRST_OPENED_KEY)!! - extraDurationMills, - Instant.EPOCH.toEpochMilli()) + private val startTime = + max(state.get<Long>(FIRST_OPENED_KEY)!! - extraDurationMills, Instant.EPOCH.toEpochMilli()) private val SYSTEM_PKG = "android" @@ -80,8 +76,10 @@ class ReviewOngoingUsageViewModel( val appUsages: Map<PackageAttribution, Set<String>>, /** Op-names of phone call accesses */ val callUsages: Collection<String>, - /** A map of attribution, packageName and user -> list of attribution labels to show with - * microphone*/ + /** + * A map of attribution, packageName and user -> list of attribution labels to show with + * microphone + */ val shownAttributions: Map<PackageAttribution, List<CharSequence>> = emptyMap() ) @@ -101,292 +99,355 @@ class ReviewOngoingUsageViewModel( * * <p>Note: This does not use a cached live-data to avoid getting stale data */ - private val permGroupUsages = LoadAndFreezeLifeData(state, USAGES_KEY, - PermGroupUsageLiveData(PermissionControllerApplication.get(), - if (shouldShowPermissionsDashboard() || shouldShowLocationIndicators()) { - listOf(CAMERA, LOCATION, MICROPHONE) - } else { - listOf(CAMERA, MICROPHONE) - }, System.currentTimeMillis() - startTime)) + private val permGroupUsages = + LoadAndFreezeLifeData( + state, + USAGES_KEY, + PermGroupUsageLiveData( + PermissionControllerApplication.get(), + if (shouldShowPermissionsDashboard() || shouldShowLocationIndicators()) { + listOf(CAMERA, LOCATION, MICROPHONE) + } else { + listOf(CAMERA, MICROPHONE) + }, + System.currentTimeMillis() - startTime + ) + ) - /** - * Whether the mic is muted - */ + /** Whether the mic is muted */ private val isMicMuted = LoadAndFreezeLifeData(state, MIC_MUTED_KEY, micMutedLiveData) /** App runtime permission usages */ - private val appUsagesLiveData = object : SmartUpdateMediatorLiveData<Map<PackageAttribution, - Set<String>>>() { - private val app = PermissionControllerApplication.get() - - init { - addSource(permGroupUsages) { - update() - } + private val appUsagesLiveData = + object : SmartUpdateMediatorLiveData<Map<PackageAttribution, Set<String>>>() { + private val app = PermissionControllerApplication.get() - addSource(isMicMuted) { - update() - } - } + init { + addSource(permGroupUsages) { update() } - override fun onUpdate() { - if (!permGroupUsages.isInitialized || !isMicMuted.isInitialized) { - return + addSource(isMicMuted) { update() } } - if (permGroupUsages.value == null) { - value = null - return - } + override fun onUpdate() { + if (!permGroupUsages.isInitialized || !isMicMuted.isInitialized) { + return + } - // Filter out system package - val filteredUsages = mutableMapOf<PackageAttribution, MutableSet<String>>() - for ((permGroupName, usages) in permGroupUsages.value!!) { - if (permGroupName == MICROPHONE && isMicMuted.value == true) { - continue + if (permGroupUsages.value == null) { + value = null + return } - for (usage in usages) { - if (usage.packageName != SYSTEM_PKG) { - filteredUsages.getOrPut(getPackageAttr(usage), - { mutableSetOf() }).add(permGroupName) + // Filter out system package + val filteredUsages = mutableMapOf<PackageAttribution, MutableSet<String>>() + for ((permGroupName, usages) in permGroupUsages.value!!) { + if (permGroupName == MICROPHONE && isMicMuted.value == true) { + continue + } + + for (usage in usages) { + if (usage.packageName != SYSTEM_PKG) { + filteredUsages + .getOrPut(getPackageAttr(usage), { mutableSetOf() }) + .add(permGroupName) + } } } - } - value = filteredUsages - } + value = filteredUsages + } - // TODO ntmyren: Replace this with better check if this moves beyond teamfood - private fun isAppPredictor(usage: OpAccess): Boolean { - return Utils.getUserContext(app, usage.user).packageManager.checkPermission( - Manifest.permission.MANAGE_APP_PREDICTIONS, usage.packageName) == - PackageManager.PERMISSION_GRANTED + // TODO ntmyren: Replace this with better check if this moves beyond teamfood + private fun isAppPredictor(usage: OpAccess): Boolean { + return Utils.getUserContext(app, usage.user) + .packageManager + .checkPermission( + Manifest.permission.MANAGE_APP_PREDICTIONS, + usage.packageName + ) == PackageManager.PERMISSION_GRANTED + } } - } /** - * Gets all trusted proxied voice IME and voice recognition microphone uses, and get the - * label needed to display with it, as well as information about the proxy whose label is being - * shown, if applicable. + * Gets all trusted proxied voice IME and voice recognition microphone uses, and get the label + * needed to display with it, as well as information about the proxy whose label is being shown, + * if applicable. */ - private val trustedAttrsLiveData = object : SmartAsyncMediatorLiveData< - Map<PackageAttribution, CharSequence>>() { - private val VOICE_IME_SUBTYPE = "voice" + private val trustedAttrsLiveData = + object : SmartAsyncMediatorLiveData<Map<PackageAttribution, CharSequence>>() { + private val VOICE_IME_SUBTYPE = "voice" - private val attributionLabelLiveDatas = - mutableMapOf<Triple<String?, String, UserHandle>, AttributionLabelLiveData>() + private val attributionLabelLiveDatas = + mutableMapOf<Triple<String?, String, UserHandle>, AttributionLabelLiveData>() - init { - addSource(permGroupUsages) { - updateAsync() + init { + addSource(permGroupUsages) { updateAsync() } } - } - override suspend fun loadDataAndPostValue(job: Job) { - if (!permGroupUsages.isInitialized) { - return - } - val usages = permGroupUsages.value?.get(MICROPHONE) ?: run { - postValue(emptyMap()) - return - } - val proxies = usages.mapNotNull { it.proxyAccess } - - val proxyLabelLiveDatas = proxies.map { - Triple(it.attributionTag, it.packageName, it.user) } - val toAddLabelLiveDatas = (usages.map { Triple(it.attributionTag, it.packageName, - it.user) } + proxyLabelLiveDatas).distinct() - val getLiveDataFun = { key: Triple<String?, String, UserHandle> -> - AttributionLabelLiveData[key] } - setSourcesToDifference(toAddLabelLiveDatas, attributionLabelLiveDatas, getLiveDataFun) - - if (attributionLabelLiveDatas.any { !it.value.isInitialized }) { - return - } + override suspend fun loadDataAndPostValue(job: Job) { + if (!permGroupUsages.isInitialized) { + return + } + val usages = + permGroupUsages.value?.get(MICROPHONE) + ?: run { + postValue(emptyMap()) + return + } + val proxies = usages.mapNotNull { it.proxyAccess } + + val proxyLabelLiveDatas = + proxies.map { Triple(it.attributionTag, it.packageName, it.user) } + val toAddLabelLiveDatas = + (usages.map { Triple(it.attributionTag, it.packageName, it.user) } + + proxyLabelLiveDatas) + .distinct() + val getLiveDataFun = { key: Triple<String?, String, UserHandle> -> + AttributionLabelLiveData[key] + } + setSourcesToDifference( + toAddLabelLiveDatas, + attributionLabelLiveDatas, + getLiveDataFun + ) + + if (attributionLabelLiveDatas.any { !it.value.isInitialized }) { + return + } - val approvedAttrs = mutableMapOf<PackageAttribution, String>() - for (user in usages.map { it.user }.distinct()) { - val userContext = Utils.getUserContext(PermissionControllerApplication.get(), user) - - // TODO ntmyren: Observe changes, possibly split into separate LiveDatas - val voiceInputs = mutableMapOf<String, CharSequence>() - userContext.getSystemService(InputMethodManager::class.java)!! - .enabledInputMethodList.forEach { - for (i in 0 until it.subtypeCount) { - if (it.getSubtypeAt(i).mode == VOICE_IME_SUBTYPE) { - voiceInputs[it.packageName] = - it.serviceInfo.loadSafeLabel(userContext.packageManager, - Float.MAX_VALUE, 0) - break + val approvedAttrs = mutableMapOf<PackageAttribution, String>() + for (user in usages.map { it.user }.distinct()) { + val userContext = + Utils.getUserContext(PermissionControllerApplication.get(), user) + + // TODO ntmyren: Observe changes, possibly split into separate LiveDatas + val voiceInputs = mutableMapOf<String, CharSequence>() + userContext + .getSystemService(InputMethodManager::class.java)!! + .enabledInputMethodList + .forEach { + for (i in 0 until it.subtypeCount) { + if (it.getSubtypeAt(i).mode == VOICE_IME_SUBTYPE) { + voiceInputs[it.packageName] = + it.serviceInfo.loadSafeLabel( + userContext.packageManager, + Float.MAX_VALUE, + 0 + ) + break + } } } + + // Get the currently selected recognizer from the secure setting. + val recognitionPackageName = + Settings.Secure.getString( + userContext.contentResolver, + // Settings.Secure.VOICE_RECOGNITION_SERVICE + "voice_recognition_service" + ) + ?.let(ComponentName::unflattenFromString) + ?.packageName + + val recognizers = mutableMapOf<String, CharSequence>() + val availableRecognizers = + userContext.packageManager.queryIntentServices( + Intent(RecognitionService.SERVICE_INTERFACE), + PackageManager.GET_META_DATA + ) + availableRecognizers.forEach { + val sI = it.serviceInfo + if (sI.packageName == recognitionPackageName) { + recognizers[sI.packageName] = + sI.loadSafeLabel(userContext.packageManager, Float.MAX_VALUE, 0) + } } - // Get the currently selected recognizer from the secure setting. - val recognitionPackageName = Settings.Secure.getString(userContext.contentResolver, - // Settings.Secure.VOICE_RECOGNITION_SERVICE - "voice_recognition_service") - ?.let(ComponentName::unflattenFromString)?.packageName - - val recognizers = mutableMapOf<String, CharSequence>() - val availableRecognizers = userContext.packageManager.queryIntentServices( - Intent(RecognitionService.SERVICE_INTERFACE), PackageManager.GET_META_DATA) - availableRecognizers.forEach { - val sI = it.serviceInfo - if (sI.packageName == recognitionPackageName) { - recognizers[sI.packageName] = sI.loadSafeLabel(userContext.packageManager, - Float.MAX_VALUE, 0) + val recognizerIntents = mutableMapOf<String, CharSequence>() + val availableRecognizerIntents = + userContext.packageManager.queryIntentActivities( + Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH), + PackageManager.GET_META_DATA + ) + availableRecognizers.forEach { rI -> + val servicePkg = rI.serviceInfo.packageName + if ( + servicePkg == recognitionPackageName && + availableRecognizerIntents.any { + it.activityInfo.packageName == servicePkg + } + ) { + // If this recognizer intent is also a recognizer service, and is + // trusted, + // Then attribute to voice recognition + recognizerIntents[servicePkg] = + rI.serviceInfo.loadSafeLabel( + userContext.packageManager, + Float.MAX_VALUE, + 0 + ) + } } - } - val recognizerIntents = mutableMapOf<String, CharSequence>() - val availableRecognizerIntents = userContext.packageManager.queryIntentActivities( - Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH), PackageManager.GET_META_DATA) - availableRecognizers.forEach { rI -> - val servicePkg = rI.serviceInfo.packageName - if (servicePkg == recognitionPackageName && availableRecognizerIntents.any { - it.activityInfo.packageName == servicePkg }) { - // If this recognizer intent is also a recognizer service, and is trusted, - // Then attribute to voice recognition - recognizerIntents[servicePkg] = - rI.serviceInfo.loadSafeLabel(userContext.packageManager, - Float.MAX_VALUE, 0) + // get attribution labels for voice IME, recognition intents, and recognition + // services + for (opAccess in usages) { + setTrustedAttrsForAccess( + userContext, + opAccess, + user, + false, + voiceInputs, + approvedAttrs + ) + setTrustedAttrsForAccess( + userContext, + opAccess, + user, + false, + recognizerIntents, + approvedAttrs + ) + setTrustedAttrsForAccess( + userContext, + opAccess, + user, + true, + recognizers, + approvedAttrs + ) } } + postValue(approvedAttrs) + } + + private fun setTrustedAttrsForAccess( + context: Context, + opAccess: OpAccess, + currUser: UserHandle, + getProxyLabel: Boolean, + trustedMap: Map<String, CharSequence>, + toSetMap: MutableMap<PackageAttribution, String> + ) { + val access = + if (getProxyLabel) { + opAccess.proxyAccess + } else { + opAccess + } - // get attribution labels for voice IME, recognition intents, and recognition - // services - for (opAccess in usages) { - setTrustedAttrsForAccess(userContext, opAccess, user, false, voiceInputs, - approvedAttrs) - setTrustedAttrsForAccess(userContext, opAccess, user, false, recognizerIntents, - approvedAttrs) - setTrustedAttrsForAccess(userContext, opAccess, user, true, recognizers, - approvedAttrs) + if ( + access == null || access.user != currUser || access.packageName !in trustedMap + ) { + return } - } - postValue(approvedAttrs) - } - - private fun setTrustedAttrsForAccess( - context: Context, - opAccess: OpAccess, - currUser: UserHandle, - getProxyLabel: Boolean, - trustedMap: Map<String, CharSequence>, - toSetMap: MutableMap<PackageAttribution, String> - ) { - val access = if (getProxyLabel) { - opAccess.proxyAccess - } else { - opAccess - } - if (access == null || access.user != currUser || access.packageName !in trustedMap) { - return - } - - val appAttr = getPackageAttr(access) - val packageName = access.packageName - - val labelResId = attributionLabelLiveDatas[Triple(access.attributionTag, - access.packageName, access.user)]?.value ?: 0 - val label = try { - context.createPackageContext(packageName, 0) - .getString(labelResId) - } catch (e: Exception) { - return - } - if (trustedMap[packageName] == label) { - toSetMap[appAttr] = label + val appAttr = getPackageAttr(access) + val packageName = access.packageName + + val labelResId = + attributionLabelLiveDatas[ + Triple(access.attributionTag, access.packageName, access.user)] + ?.value + ?: 0 + val label = + try { + context.createPackageContext(packageName, 0).getString(labelResId) + } catch (e: Exception) { + return + } + if (trustedMap[packageName] == label) { + toSetMap[appAttr] = label + } } } - } /** * Get all chains of proxy usages. A proxy chain is defined as one usage at the root, then * further proxy usages, where the app and attribution tag of the proxy matches the previous * usage in the chain. */ - private val proxyChainsLiveData = object : SmartUpdateMediatorLiveData<Set<List<OpAccess>>>() { - init { - addSource(permGroupUsages) { - update() - } - } - override fun onUpdate() { - if (!permGroupUsages.isInitialized) { - return - } - val usages = permGroupUsages.value?.get(MICROPHONE) ?: emptyList() - // a map of chain start -> in progress chain - val proxyChains = mutableMapOf<PackageAttribution, MutableList<OpAccess>>() - - val remainingProxyChainUsages = mutableMapOf<PackageAttribution, OpAccess>() - for (usage in usages) { - remainingProxyChainUsages[getPackageAttr(usage)] = usage + private val proxyChainsLiveData = + object : SmartUpdateMediatorLiveData<Set<List<OpAccess>>>() { + init { + addSource(permGroupUsages) { update() } } - // find all one-link chains (that is, all proxied apps whose proxy is not included in - // the usage list) - for (usage in usages) { - val usageAttr = getPackageAttr(usage) - val proxyAttr = getPackageAttr(usage.proxyAccess ?: continue) - if (!usages.any { getPackageAttr(it) == proxyAttr }) { - proxyChains[usageAttr] = mutableListOf(usage) - remainingProxyChainUsages.remove(usageAttr) + override fun onUpdate() { + if (!permGroupUsages.isInitialized) { + return } - } + val usages = permGroupUsages.value?.get(MICROPHONE) ?: emptyList() + // a map of chain start -> in progress chain + val proxyChains = mutableMapOf<PackageAttribution, MutableList<OpAccess>>() - // find all possible starting points for chains - for ((usageAttr, usage) in remainingProxyChainUsages.toMap()) { - // If this usage has a proxy, but is not a proxy, it is the start of a chain. - // If it has no proxy, and isn't a proxy, remove it. - if (!remainingProxyChainUsages.values.any { it.proxyAccess != null && - getPackageAttr(it.proxyAccess) == usageAttr }) { - if (usage.proxyAccess != null) { + val remainingProxyChainUsages = mutableMapOf<PackageAttribution, OpAccess>() + for (usage in usages) { + remainingProxyChainUsages[getPackageAttr(usage)] = usage + } + // find all one-link chains (that is, all proxied apps whose proxy is not included + // in + // the usage list) + for (usage in usages) { + val usageAttr = getPackageAttr(usage) + val proxyAttr = getPackageAttr(usage.proxyAccess ?: continue) + if (!usages.any { getPackageAttr(it) == proxyAttr }) { proxyChains[usageAttr] = mutableListOf(usage) - } else { remainingProxyChainUsages.remove(usageAttr) } } - } - // assemble the chains - for ((startUsageAttr, proxyChain) in proxyChains) { - var currentUsage = remainingProxyChainUsages[startUsageAttr] ?: continue - while (currentUsage.proxyAccess != null) { - val currPackageAttr = getPackageAttr(currentUsage.proxyAccess!!) - currentUsage = remainingProxyChainUsages[currPackageAttr] ?: break - if (proxyChain.any { it == currentUsage }) { - // we have a cycle, and should break - break + // find all possible starting points for chains + for ((usageAttr, usage) in remainingProxyChainUsages.toMap()) { + // If this usage has a proxy, but is not a proxy, it is the start of a chain. + // If it has no proxy, and isn't a proxy, remove it. + if ( + !remainingProxyChainUsages.values.any { + it.proxyAccess != null && getPackageAttr(it.proxyAccess) == usageAttr + } + ) { + if (usage.proxyAccess != null) { + proxyChains[usageAttr] = mutableListOf(usage) + } else { + remainingProxyChainUsages.remove(usageAttr) + } } - proxyChain.add(currentUsage) } - // invert the lists, so the element without a proxy is first on the list - proxyChain.reverse() - } - value = proxyChains.values.toSet() + // assemble the chains + for ((startUsageAttr, proxyChain) in proxyChains) { + var currentUsage = remainingProxyChainUsages[startUsageAttr] ?: continue + while (currentUsage.proxyAccess != null) { + val currPackageAttr = getPackageAttr(currentUsage.proxyAccess!!) + currentUsage = remainingProxyChainUsages[currPackageAttr] ?: break + if (proxyChain.any { it == currentUsage }) { + // we have a cycle, and should break + break + } + proxyChain.add(currentUsage) + } + // invert the lists, so the element without a proxy is first on the list + proxyChain.reverse() + } + + value = proxyChains.values.toSet() + } } - } /** Phone call usages */ private val callOpUsageLiveData = object : SmartUpdateMediatorLiveData<Collection<String>>() { - private val rawOps = LoadAndFreezeLifeData(state, CALL_OP_USAGE_KEY, - OpUsageLiveData[listOf(PHONE_CALL, VIDEO_CALL), - System.currentTimeMillis() - startTime]) + private val rawOps = + LoadAndFreezeLifeData( + state, + CALL_OP_USAGE_KEY, + OpUsageLiveData[ + listOf(PHONE_CALL, VIDEO_CALL), System.currentTimeMillis() - startTime] + ) init { - addSource(rawOps) { - update() - } + addSource(rawOps) { update() } - addSource(isMicMuted) { - update() - } + addSource(isMicMuted) { update() } } override fun onUpdate() { @@ -394,145 +455,152 @@ class ReviewOngoingUsageViewModel( return } - value = if (isMicMuted.value == true) { - rawOps.value!!.keys.filter { it != PHONE_CALL } - } else { - rawOps.value!!.keys - } + value = + if (isMicMuted.value == true) { + rawOps.value!!.keys.filter { it != PHONE_CALL } + } else { + rawOps.value!!.keys + } } } /** App, system, and call usages in a single, nice, handy package */ - val usages = object : SmartAsyncMediatorLiveData<Usages>() { - private val app = PermissionControllerApplication.get() + val usages = + object : SmartAsyncMediatorLiveData<Usages>() { + private val app = PermissionControllerApplication.get() - init { - addSource(appUsagesLiveData) { - update() - } - - addSource(callOpUsageLiveData) { - update() - } - - addSource(trustedAttrsLiveData) { - update() - } + init { + addSource(appUsagesLiveData) { update() } - addSource(proxyChainsLiveData) { - update() - } - } + addSource(callOpUsageLiveData) { update() } - override suspend fun loadDataAndPostValue(job: Job) { - if (job.isCancelled) { - return - } + addSource(trustedAttrsLiveData) { update() } - if (!callOpUsageLiveData.isInitialized || !appUsagesLiveData.isInitialized || - !trustedAttrsLiveData.isInitialized || !proxyChainsLiveData.isInitialized) { - return + addSource(proxyChainsLiveData) { update() } } - val callOpUsages = callOpUsageLiveData.value?.toMutableSet() - val appUsages = appUsagesLiveData.value?.toMutableMap() - val approvedAttrs = trustedAttrsLiveData.value?.toMutableMap() ?: mutableMapOf() - val proxyChains = proxyChainsLiveData.value ?: emptySet() + override suspend fun loadDataAndPostValue(job: Job) { + if (job.isCancelled) { + return + } - if (callOpUsages == null || appUsages == null) { - postValue(null) - return - } + if ( + !callOpUsageLiveData.isInitialized || + !appUsagesLiveData.isInitialized || + !trustedAttrsLiveData.isInitialized || + !proxyChainsLiveData.isInitialized + ) { + return + } - // If there is nothing to show the dialog should be closed, hence return a "invalid" - // value - if (appUsages.isEmpty() && callOpUsages.isEmpty()) { - postValue(null) - return - } + val callOpUsages = callOpUsageLiveData.value?.toMutableSet() + val appUsages = appUsagesLiveData.value?.toMutableMap() + val approvedAttrs = trustedAttrsLiveData.value?.toMutableMap() ?: mutableMapOf() + val proxyChains = proxyChainsLiveData.value ?: emptySet() - // If we are in a VOIP call (aka MODE_IN_COMMUNICATION), and have a carrier privileged - // app using the mic, hide phone usage. - val audioManager = app.getSystemService(AudioManager::class.java)!! - if (callOpUsages.isNotEmpty() && audioManager.mode == MODE_IN_COMMUNICATION) { - val telephonyManager = app.getSystemService(TelephonyManager::class.java)!! - for ((pkg, usages) in appUsages) { - if (telephonyManager.checkCarrierPrivilegesForPackage(pkg.packageName) == - CARRIER_PRIVILEGE_STATUS_HAS_ACCESS && usages.contains(MICROPHONE)) { - callOpUsages.clear() - continue - } + if (callOpUsages == null || appUsages == null) { + postValue(null) + return } - } - // Find labels for proxies, and assign them to the proper app, removing other usages - val approvedLabels = mutableMapOf<PackageAttribution, List<CharSequence>>() - for (chain in proxyChains) { - // if the final link in the chain is not user sensitive, do not show the chain - if (getPackageAttr(chain[chain.size - 1]) !in appUsages) { - continue + // If there is nothing to show the dialog should be closed, hence return a "invalid" + // value + if (appUsages.isEmpty() && callOpUsages.isEmpty()) { + postValue(null) + return } - // if the proxy access is missing, for some reason, do not show the proxy - if (chain.size == 1) { - continue + // If we are in a VOIP call (aka MODE_IN_COMMUNICATION), and have a carrier + // privileged + // app using the mic, hide phone usage. + val audioManager = app.getSystemService(AudioManager::class.java)!! + if (callOpUsages.isNotEmpty() && audioManager.mode == MODE_IN_COMMUNICATION) { + val telephonyManager = app.getSystemService(TelephonyManager::class.java)!! + for ((pkg, usages) in appUsages) { + if ( + telephonyManager.checkCarrierPrivilegesForPackage(pkg.packageName) == + CARRIER_PRIVILEGE_STATUS_HAS_ACCESS && usages.contains(MICROPHONE) + ) { + callOpUsages.clear() + continue + } + } } - val labels = mutableListOf<CharSequence>() - for ((idx, opAccess) in chain.withIndex()) { - val appAttr = getPackageAttr(opAccess) - // If this is the last link in the proxy chain, assign it the series of labels - // Else, if it has a special label, add that label - // Else, if there are no other apps in the remaining part of the chain which - // have the same package name, add the app label - // If it is not the last link in the chain, remove its attribution - if (idx == chain.size - 1) { - approvedLabels[appAttr] = labels + // Find labels for proxies, and assign them to the proper app, removing other usages + val approvedLabels = mutableMapOf<PackageAttribution, List<CharSequence>>() + for (chain in proxyChains) { + // if the final link in the chain is not user sensitive, do not show the chain + if (getPackageAttr(chain[chain.size - 1]) !in appUsages) { continue - } else if (appAttr in approvedAttrs) { - labels.add(approvedAttrs[appAttr]!!) - approvedAttrs.remove(appAttr) - } else if (chain.subList(idx + 1, chain.size).all { - it.packageName != opAccess.packageName } && - opAccess.packageName != SYSTEM_PKG) { - labels.add(KotlinUtils.getPackageLabel(app, opAccess.packageName, - opAccess.user)) } - appUsages.remove(appAttr) + + // if the proxy access is missing, for some reason, do not show the proxy + if (chain.size == 1) { + continue + } + + val labels = mutableListOf<CharSequence>() + for ((idx, opAccess) in chain.withIndex()) { + val appAttr = getPackageAttr(opAccess) + // If this is the last link in the proxy chain, assign it the series of + // labels + // Else, if it has a special label, add that label + // Else, if there are no other apps in the remaining part of the chain which + // have the same package name, add the app label + // If it is not the last link in the chain, remove its attribution + if (idx == chain.size - 1) { + approvedLabels[appAttr] = labels + continue + } else if (appAttr in approvedAttrs) { + labels.add(approvedAttrs[appAttr]!!) + approvedAttrs.remove(appAttr) + } else if ( + chain.subList(idx + 1, chain.size).all { + it.packageName != opAccess.packageName + } && opAccess.packageName != SYSTEM_PKG + ) { + labels.add( + KotlinUtils.getPackageLabel( + app, + opAccess.packageName, + opAccess.user + ) + ) + } + appUsages.remove(appAttr) + } } - } - // Any remaining truested attributions must be for non-proxy usages, so add them - for ((packageAttr, label) in approvedAttrs) { - approvedLabels[packageAttr] = listOf(label) - } + // Any remaining truested attributions must be for non-proxy usages, so add them + for ((packageAttr, label) in approvedAttrs) { + approvedLabels[packageAttr] = listOf(label) + } - removeDuplicates(appUsages, approvedLabels.keys) + removeDuplicates(appUsages, approvedLabels.keys) - postValue(Usages(appUsages, callOpUsages, approvedLabels)) - } + postValue(Usages(appUsages, callOpUsages, approvedLabels)) + } - /** - * Merge any usages for the same app which don't have a special attribution - */ - private fun removeDuplicates( - appUsages: MutableMap<PackageAttribution, Set<String>>, - approvedUsages: Collection<PackageAttribution> - ) { - // Iterate over all non-special attribution keys - for (packageAttr in appUsages.keys.minus(approvedUsages)) { - var groupSet = appUsages[packageAttr] ?: continue - - for (otherAttr in appUsages.keys.minus(approvedUsages)) { - if (otherAttr.pkgEq(packageAttr)) { - groupSet = groupSet.plus(appUsages[otherAttr] ?: emptySet()) - appUsages.remove(otherAttr) + /** Merge any usages for the same app which don't have a special attribution */ + private fun removeDuplicates( + appUsages: MutableMap<PackageAttribution, Set<String>>, + approvedUsages: Collection<PackageAttribution> + ) { + // Iterate over all non-special attribution keys + for (packageAttr in appUsages.keys.minus(approvedUsages)) { + var groupSet = appUsages[packageAttr] ?: continue + + for (otherAttr in appUsages.keys.minus(approvedUsages)) { + if (otherAttr.pkgEq(packageAttr)) { + groupSet = groupSet.plus(appUsages[otherAttr] ?: emptySet()) + appUsages.remove(otherAttr) + } } + appUsages[packageAttr] = groupSet } - appUsages[packageAttr] = groupSet } } - } private fun getPackageAttr(usage: OpAccess): PackageAttribution { return PackageAttribution(usage.attributionTag, usage.packageName, usage.user) @@ -552,8 +620,7 @@ class ReviewOngoingUsageViewModelFactory( defaultArgs: Bundle ) : AbstractSavedStateViewModelFactory(owner, defaultArgs) { override fun <T : ViewModel> create(p0: String, p1: Class<T>, state: SavedStateHandle): T { - state.set(FIRST_OPENED_KEY, state.get<Long>(FIRST_OPENED_KEY) - ?: System.currentTimeMillis()) + state.set(FIRST_OPENED_KEY, state.get<Long>(FIRST_OPENED_KEY) ?: System.currentTimeMillis()) @Suppress("UNCHECKED_CAST") return ReviewOngoingUsageViewModel(state, extraDurationMillis) as T } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v33/ReviewPermissionDecisionsViewModel.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v33/ReviewPermissionDecisionsViewModel.kt index 69709264b..bb3c44675 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v33/ReviewPermissionDecisionsViewModel.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v33/ReviewPermissionDecisionsViewModel.kt @@ -28,7 +28,6 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import com.android.permissioncontroller.DumpableLog import com.android.permissioncontroller.R -import com.android.permissioncontroller.permission.utils.PermissionMapping import com.android.permissioncontroller.permission.data.SmartAsyncMediatorLiveData import com.android.permissioncontroller.permission.data.UserPackageInfosLiveData import com.android.permissioncontroller.permission.data.v33.PermissionDecision @@ -37,9 +36,10 @@ import com.android.permissioncontroller.permission.model.livedatatypes.LightPack import com.android.permissioncontroller.permission.ui.ManagePermissionsActivity import com.android.permissioncontroller.permission.ui.auto.AutoReviewPermissionDecisionsFragment import com.android.permissioncontroller.permission.utils.KotlinUtils +import com.android.permissioncontroller.permission.utils.PermissionMapping import com.android.permissioncontroller.permission.utils.StringUtils -import kotlinx.coroutines.Job import java.util.concurrent.TimeUnit +import kotlinx.coroutines.Job /** Viewmodel for [ReviewPermissionDecisionsFragment] */ @RequiresApi(Build.VERSION_CODES.TIRAMISU) @@ -50,82 +50,94 @@ class ReviewPermissionDecisionsViewModel(val app: Application, val user: UserHan private val recentPermissionsLiveData = RecentPermissionDecisionsLiveData() private val userPackageInfosLiveData = UserPackageInfosLiveData[user] - val recentPermissionDecisionsLiveData = object - : SmartAsyncMediatorLiveData<List<PermissionDecision>>( - alwaysUpdateOnActive = false - ) { + val recentPermissionDecisionsLiveData = + object : + SmartAsyncMediatorLiveData<List<PermissionDecision>>(alwaysUpdateOnActive = false) { - init { - addSource(recentPermissionsLiveData) { - onUpdate() - } + init { + addSource(recentPermissionsLiveData) { onUpdate() } - addSource(userPackageInfosLiveData) { - onUpdate() - } - } - - override suspend fun loadDataAndPostValue(job: Job) { - if (!recentPermissionsLiveData.isInitialized || - !userPackageInfosLiveData.isInitialized) { - return + addSource(userPackageInfosLiveData) { onUpdate() } } - // create package info lookup map for performance - val packageToLightPackageInfo: MutableMap<String, LightPackageInfo> = mutableMapOf() - for (lightPackageInfo in userPackageInfosLiveData.value!!) { - packageToLightPackageInfo[lightPackageInfo.packageName] = lightPackageInfo - } - - // verify that permission state is still correct. Will also filter out any apps that - // were uninstalled - val decisionsToReview: MutableList<PermissionDecision> = mutableListOf() - for (recentDecision in recentPermissionsLiveData.value!!) { - val lightPackageInfo = packageToLightPackageInfo[recentDecision.packageName] - if (lightPackageInfo == null) { - DumpableLog.e(LOG_TAG, "Package $recentDecision.packageName " + - "is no longer installed") - continue + override suspend fun loadDataAndPostValue(job: Job) { + if ( + !recentPermissionsLiveData.isInitialized || + !userPackageInfosLiveData.isInitialized + ) { + return } - val grantedGroups: List<String?> = lightPackageInfo.grantedPermissions.map { - PermissionMapping.getGroupOfPermission( - app.packageManager.getPermissionInfo(it, /* flags= */ 0)) + + // create package info lookup map for performance + val packageToLightPackageInfo: MutableMap<String, LightPackageInfo> = mutableMapOf() + for (lightPackageInfo in userPackageInfosLiveData.value!!) { + packageToLightPackageInfo[lightPackageInfo.packageName] = lightPackageInfo } - val currentlyGranted = grantedGroups.contains(recentDecision.permissionGroupName) - if (currentlyGranted && recentDecision.isGranted) { - decisionsToReview.add(recentDecision) - } else if (!currentlyGranted && !recentDecision.isGranted) { - decisionsToReview.add(recentDecision) - } else { - // It's okay for this to happen - the state could change due to role changes, - // app hibernation, or other non-user-driven actions. - DumpableLog.d(LOG_TAG, - "Permission decision grant state (${recentDecision.isGranted}) " + - "for ${recentDecision.packageName} access to " + - "${recentDecision.permissionGroupName} does not match current " + - "grant state $currentlyGranted") + + // verify that permission state is still correct. Will also filter out any apps that + // were uninstalled + val decisionsToReview: MutableList<PermissionDecision> = mutableListOf() + for (recentDecision in recentPermissionsLiveData.value!!) { + val lightPackageInfo = packageToLightPackageInfo[recentDecision.packageName] + if (lightPackageInfo == null) { + DumpableLog.e( + LOG_TAG, + "Package $recentDecision.packageName " + "is no longer installed" + ) + continue + } + val grantedGroups: List<String?> = + lightPackageInfo.grantedPermissions.map { + PermissionMapping.getGroupOfPermission( + app.packageManager.getPermissionInfo(it, /* flags= */ 0) + ) + } + val currentlyGranted = + grantedGroups.contains(recentDecision.permissionGroupName) + if (currentlyGranted && recentDecision.isGranted) { + decisionsToReview.add(recentDecision) + } else if (!currentlyGranted && !recentDecision.isGranted) { + decisionsToReview.add(recentDecision) + } else { + // It's okay for this to happen - the state could change due to role + // changes, + // app hibernation, or other non-user-driven actions. + DumpableLog.d( + LOG_TAG, + "Permission decision grant state (${recentDecision.isGranted}) " + + "for ${recentDecision.packageName} access to " + + "${recentDecision.permissionGroupName} does not match current " + + "grant state $currentlyGranted" + ) + } } - } - postValue(decisionsToReview) + postValue(decisionsToReview) + } } - } fun getAppIcon(packageName: String): Drawable? { return KotlinUtils.getBadgedPackageIcon(app, packageName, user) } fun createPreferenceTitle(permissionDecision: PermissionDecision): String { - val packageLabel = BidiFormatter.getInstance().unicodeWrap( - KotlinUtils.getPackageLabel(app, permissionDecision.packageName, user)) - val permissionGroupLabel = KotlinUtils.getPermGroupLabel(app, - permissionDecision.permissionGroupName).toString() + val packageLabel = + BidiFormatter.getInstance() + .unicodeWrap(KotlinUtils.getPackageLabel(app, permissionDecision.packageName, user)) + val permissionGroupLabel = + KotlinUtils.getPermGroupLabel(app, permissionDecision.permissionGroupName).toString() return if (permissionDecision.isGranted) { - app.getString(R.string.granted_permission_decision, packageLabel, - UCharacter.toLowerCase(permissionGroupLabel)) + app.getString( + R.string.granted_permission_decision, + packageLabel, + UCharacter.toLowerCase(permissionGroupLabel) + ) } else { - app.getString(R.string.denied_permission_decision, packageLabel, - UCharacter.toLowerCase(permissionGroupLabel)) + app.getString( + R.string.denied_permission_decision, + packageLabel, + UCharacter.toLowerCase(permissionGroupLabel) + ) } } @@ -134,8 +146,10 @@ class ReviewPermissionDecisionsViewModel(val app: Application, val user: UserHan putExtra(Intent.EXTRA_PACKAGE_NAME, permissionDecision.packageName) putExtra(Intent.EXTRA_PERMISSION_NAME, permissionDecision.permissionGroupName) putExtra(Intent.EXTRA_USER, user) - putExtra(ManagePermissionsActivity.EXTRA_CALLER_NAME, - AutoReviewPermissionDecisionsFragment::class.java.name) + putExtra( + ManagePermissionsActivity.EXTRA_CALLER_NAME, + AutoReviewPermissionDecisionsFragment::class.java.name + ) } } @@ -146,15 +160,12 @@ class ReviewPermissionDecisionsViewModel(val app: Application, val user: UserHan } } -/** - * Factory for a [ReviewPermissionDecisionsViewModel] - */ +/** Factory for a [ReviewPermissionDecisionsViewModel] */ @RequiresApi(Build.VERSION_CODES.TIRAMISU) class ReviewPermissionDecisionsViewModelFactory(val app: Application, val user: UserHandle) : ViewModelProvider.Factory { override fun <T : ViewModel> create(modelClass: Class<T>): T { - @Suppress("UNCHECKED_CAST") - return ReviewPermissionDecisionsViewModel(app, user) as T + @Suppress("UNCHECKED_CAST") return ReviewPermissionDecisionsViewModel(app, user) as T } -}
\ No newline at end of file +} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v34/PermissionRationaleViewModel.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v34/PermissionRationaleViewModel.kt index 304dc3db4..7bc0e1e1f 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v34/PermissionRationaleViewModel.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v34/PermissionRationaleViewModel.kt @@ -32,14 +32,14 @@ import androidx.lifecycle.ViewModelProvider import com.android.permissioncontroller.Constants import com.android.permissioncontroller.PermissionControllerStatsLog import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_RATIONALE_DIALOG_ACTION_REPORTED -import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_RATIONALE_DIALOG_ACTION_REPORTED__BUTTON_PRESSED__INSTALL_SOURCE import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_RATIONALE_DIALOG_ACTION_REPORTED__BUTTON_PRESSED__HELP_CENTER +import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_RATIONALE_DIALOG_ACTION_REPORTED__BUTTON_PRESSED__INSTALL_SOURCE import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_RATIONALE_DIALOG_ACTION_REPORTED__BUTTON_PRESSED__PERMISSION_SETTINGS import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_RATIONALE_DIALOG_VIEWED import com.android.permissioncontroller.R -import com.android.permissioncontroller.permission.data.v34.SafetyLabelInfoLiveData import com.android.permissioncontroller.permission.data.SmartUpdateMediatorLiveData import com.android.permissioncontroller.permission.data.get +import com.android.permissioncontroller.permission.data.v34.SafetyLabelInfoLiveData import com.android.permissioncontroller.permission.ui.ManagePermissionsActivity import com.android.permissioncontroller.permission.ui.ManagePermissionsActivity.EXTRA_RESULT_PERMISSION_INTERACTED import com.android.permissioncontroller.permission.ui.ManagePermissionsActivity.EXTRA_RESULT_PERMISSION_RESULT @@ -75,8 +75,8 @@ class PermissionRationaleViewModel( /** * Should be invoked by base activity when a valid onActivityResult is received * - * @param data [Intent] which may contain result data from a started Activity - * (various data can be attached to Intent "extras") + * @param data [Intent] which may contain result data from a started Activity (various data + * can be attached to Intent "extras") * @return {@code true} if Activity should finish after processing this result */ fun shouldFinishActivityForResult(data: Intent?): Boolean @@ -88,12 +88,12 @@ class PermissionRationaleViewModel( * should be shown with it. */ data class PermissionRationaleInfo( - val groupName: String, - val isPreloadedApp: Boolean, - val installSourcePackageName: String?, - val installSourceLabel: String?, - val purposeSet: Set<Int> - ) + val groupName: String, + val isPreloadedApp: Boolean, + val installSourcePackageName: String?, + val installSourceLabel: String?, + val purposeSet: Set<Int> + ) /** A [LiveData] which holds the currently pending PermissionRationaleInfo */ val permissionRationaleInfoLiveData = @@ -126,8 +126,11 @@ class PermissionRationaleViewModel( KotlinUtils.getPackageLabel(app, it, Process.myUserHandle()) } - val purposes = SafetyLabelUtils.getSafetyLabelSharingPurposesForGroup( - safetyLabelInfo.safetyLabel, permissionGroupName) + val purposes = + SafetyLabelUtils.getSafetyLabelSharingPurposesForGroup( + safetyLabelInfo.safetyLabel, + permissionGroupName + ) if (value == null) { logPermissionRationaleDialogViewed(purposes) } @@ -137,7 +140,8 @@ class PermissionRationaleViewModel( safetyLabelInfo.installSourceInfo.isPreloadedApp, installSourcePackageName, installSourceLabel, - purposes) + purposes + ) } } @@ -147,7 +151,8 @@ class PermissionRationaleViewModel( fun sendToAppStore(context: Context, installSourcePackageName: String) { logPermissionRationaleDialogActionReported( - PERMISSION_RATIONALE_DIALOG_ACTION_REPORTED__BUTTON_PRESSED__INSTALL_SOURCE) + PERMISSION_RATIONALE_DIALOG_ACTION_REPORTED__BUTTON_PRESSED__INSTALL_SOURCE + ) val storeIntent = getAppStoreIntent(context, installSourcePackageName, packageName) context.startActivity(storeIntent) } @@ -162,14 +167,17 @@ class PermissionRationaleViewModel( if (activityResultCallback != null) { return } - activityResultCallback = object : ActivityResultCallback { - override fun shouldFinishActivityForResult(data: Intent?): Boolean { - val returnGroupName = data?.getStringExtra(EXTRA_RESULT_PERMISSION_INTERACTED) - return (returnGroupName != null) && data.hasExtra(EXTRA_RESULT_PERMISSION_RESULT) + activityResultCallback = + object : ActivityResultCallback { + override fun shouldFinishActivityForResult(data: Intent?): Boolean { + val returnGroupName = data?.getStringExtra(EXTRA_RESULT_PERMISSION_INTERACTED) + return (returnGroupName != null) && + data.hasExtra(EXTRA_RESULT_PERMISSION_RESULT) + } } - } logPermissionRationaleDialogActionReported( - PERMISSION_RATIONALE_DIALOG_ACTION_REPORTED__BUTTON_PRESSED__PERMISSION_SETTINGS) + PERMISSION_RATIONALE_DIALOG_ACTION_REPORTED__BUTTON_PRESSED__PERMISSION_SETTINGS + ) startAppPermissionFragment(activity, groupName) } @@ -192,11 +200,13 @@ class PermissionRationaleViewModel( // Add in some extra locale query parameters val fullUri = HelpUtils.uriWithAddedParameters(activity, Uri.parse(getHelpCenterUrlString(activity))) - val intent = Intent(Intent.ACTION_VIEW, fullUri).apply { - setFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) - } + val intent = + Intent(Intent.ACTION_VIEW, fullUri).apply { + setFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) + } logPermissionRationaleDialogActionReported( - PERMISSION_RATIONALE_DIALOG_ACTION_REPORTED__BUTTON_PRESSED__HELP_CENTER) + PERMISSION_RATIONALE_DIALOG_ACTION_REPORTED__BUTTON_PRESSED__HELP_CENTER + ) try { activity.startActivity(intent) } catch (e: ActivityNotFoundException) { @@ -206,14 +216,17 @@ class PermissionRationaleViewModel( } private fun startAppPermissionFragment(activity: Activity, groupName: String) { - val intent = Intent(Intent.ACTION_MANAGE_APP_PERMISSION) - .putExtra(Intent.EXTRA_PACKAGE_NAME, packageName) - .putExtra(Intent.EXTRA_PERMISSION_GROUP_NAME, groupName) - .putExtra(Intent.EXTRA_USER, user) - .putExtra(ManagePermissionsActivity.EXTRA_CALLER_NAME, - PermissionRationaleActivity::class.java.name) - .putExtra(Constants.EXTRA_SESSION_ID, sessionId) - .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK) + val intent = + Intent(Intent.ACTION_MANAGE_APP_PERMISSION) + .putExtra(Intent.EXTRA_PACKAGE_NAME, packageName) + .putExtra(Intent.EXTRA_PERMISSION_GROUP_NAME, groupName) + .putExtra(Intent.EXTRA_USER, user) + .putExtra( + ManagePermissionsActivity.EXTRA_CALLER_NAME, + PermissionRationaleActivity::class.java.name + ) + .putExtra(Constants.EXTRA_SESSION_ID, sessionId) + .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK) activity.startActivityForResult(intent, APP_PERMISSION_REQUEST_CODE) } @@ -225,14 +238,24 @@ class PermissionRationaleViewModel( purposes.forEach { purposeInt -> purposesPresented = purposesPresented or 1.shl(purposeInt) } - PermissionControllerStatsLog.write(PERMISSION_RATIONALE_DIALOG_VIEWED, sessionId, uid, - permissionGroupName, purposesPresented) + PermissionControllerStatsLog.write( + PERMISSION_RATIONALE_DIALOG_VIEWED, + sessionId, + uid, + permissionGroupName, + purposesPresented + ) } fun logPermissionRationaleDialogActionReported(buttonPressed: Int) { val uid = KotlinUtils.getPackageUid(app, packageName, user) ?: return - PermissionControllerStatsLog.write(PERMISSION_RATIONALE_DIALOG_ACTION_REPORTED, sessionId, - uid, permissionGroupName, buttonPressed) + PermissionControllerStatsLog.write( + PERMISSION_RATIONALE_DIALOG_ACTION_REPORTED, + sessionId, + uid, + permissionGroupName, + buttonPressed + ) } private fun getHelpCenterUrlString(context: Context): String? { @@ -257,7 +280,12 @@ class PermissionRationaleViewModelFactory( override fun <T : ViewModel> create(modelClass: Class<T>): T { @Suppress("UNCHECKED_CAST") return PermissionRationaleViewModel( - app, packageName, permissionGroupName, sessionId, savedState) + app, + packageName, + permissionGroupName, + sessionId, + savedState + ) as T } } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/television/AppPermissionFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/television/AppPermissionFragment.java index e2df47009..399a694d0 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/television/AppPermissionFragment.java +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/television/AppPermissionFragment.java @@ -289,7 +289,7 @@ public class AppPermissionFragment extends SettingsWithHeader }); mAllowAlwaysButton.setOnPreferenceClickListener((v) -> { if (mIsStorageGroup) { - showConfirmDialog(ChangeRequest.GRANT_All_FILE_ACCESS, + showConfirmDialog(ChangeRequest.GRANT_ALL_FILE_ACCESS, R.string.special_file_access_dialog, -1, false); } else { mViewModel.requestChange(false, this, this, ChangeRequest.GRANT_BOTH, @@ -464,7 +464,7 @@ public class AppPermissionFragment extends SettingsWithHeader public Dialog onCreateDialog(Bundle savedInstanceState) { AppPermissionFragment fragment = (AppPermissionFragment) getTargetFragment(); boolean isGrantFileAccess = getArguments().getSerializable(CHANGE_REQUEST) - == ChangeRequest.GRANT_All_FILE_ACCESS; + == ChangeRequest.GRANT_ALL_FILE_ACCESS; int positiveButtonStringResId = R.string.grant_dialog_button_deny_anyway; if (isGrantFileAccess) { positiveButtonStringResId = R.string.grant_dialog_button_allow; diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/television/TvUnusedAppsFragment.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/television/TvUnusedAppsFragment.kt index 07e2ab08f..6797f5dff 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/television/TvUnusedAppsFragment.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/television/TvUnusedAppsFragment.kt @@ -27,16 +27,14 @@ import com.android.permissioncontroller.hibernation.isHibernationEnabled import com.android.permissioncontroller.permission.ui.UnusedAppsFragment import com.android.permissioncontroller.permission.ui.UnusedAppsFragment.Companion.INFO_MSG_CATEGORY -/** - * TV wrapper, with customizations, around [UnusedAppsFragment]. - */ -class TvUnusedAppsFragment : SettingsWithHeader(), - UnusedAppsFragment.Parent<TvUnusedAppsPreference> { +/** TV wrapper, with customizations, around [UnusedAppsFragment]. */ +class TvUnusedAppsFragment : + SettingsWithHeader(), UnusedAppsFragment.Parent<TvUnusedAppsPreference> { companion object { private const val UNUSED_PREFERENCE_KEY = "unused_pref_row_key" - /** Create a new instance of this fragment. */ + /** Create a new instance of this fragment. */ @JvmStatic fun newInstance(): TvUnusedAppsFragment { return TvUnusedAppsFragment() @@ -50,15 +48,12 @@ class TvUnusedAppsFragment : SettingsWithHeader(), override fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) if (savedInstanceState == null) { - val fragment: - UnusedAppsFragment<TvUnusedAppsFragment, TvUnusedAppsPreference> = + val fragment: UnusedAppsFragment<TvUnusedAppsFragment, TvUnusedAppsPreference> = UnusedAppsFragment.newInstance() fragment.arguments = arguments // child fragment does not have its own UI - it will add to the preferences of this // parent fragment - childFragmentManager.beginTransaction() - .add(fragment, null) - .commit() + childFragmentManager.beginTransaction().add(fragment, null).commit() } } @@ -67,8 +62,7 @@ class TvUnusedAppsFragment : SettingsWithHeader(), if (isHibernationEnabled()) { preference.summary = getString(R.string.unused_apps_page_tv_summary) } else { - preference.summary = - getString(R.string.auto_revoked_apps_page_summary) + preference.summary = getString(R.string.auto_revoked_apps_page_summary) } preference.setIcon(R.drawable.ic_info_outline) preference.isSelectable = false @@ -93,9 +87,9 @@ class TvUnusedAppsFragment : SettingsWithHeader(), override fun setEmptyState(empty: Boolean) { val infoMsgCategory = - preferenceScreen.findPreference<PreferenceCategory>(INFO_MSG_CATEGORY)!! + preferenceScreen.findPreference<PreferenceCategory>(INFO_MSG_CATEGORY)!! val noUnusedAppsPreference: Preference? = - infoMsgCategory.findPreference<Preference>(UNUSED_PREFERENCE_KEY) + infoMsgCategory.findPreference<Preference>(UNUSED_PREFERENCE_KEY) if (empty && noUnusedAppsPreference == null) { infoMsgCategory.addPreference(createNoUnusedAppsPreference()) } else if (noUnusedAppsPreference != null) { diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/television/TvUnusedAppsPreference.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/television/TvUnusedAppsPreference.kt index 905aa30d1..bc29e928f 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/television/TvUnusedAppsPreference.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/television/TvUnusedAppsPreference.kt @@ -16,10 +16,10 @@ package com.android.permissioncontroller.permission.ui.television -import androidx.preference.Preference import android.app.Application import android.content.Context import android.os.UserHandle +import androidx.preference.Preference import com.android.permissioncontroller.permission.ui.RemovablePref import com.android.permissioncontroller.permission.utils.KotlinUtils diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/v33/AdvancedConfirmDialogArgs.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/v33/AdvancedConfirmDialogArgs.kt index b841f3aeb..a2505312d 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/v33/AdvancedConfirmDialogArgs.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/v33/AdvancedConfirmDialogArgs.kt @@ -28,8 +28,7 @@ data class AdvancedConfirmDialogArgs( @StringRes val messageId: Int, @StringRes val negativeButtonTextId: Int, @StringRes val positiveButtonTextId: Int, - val changeRequest: AppPermissionViewModel.ChangeRequest? = null, val setOneTime: Boolean? = null, val buttonClicked: Int? = null -)
\ No newline at end of file +) diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/v33/widget/SafetyProtectionSectionView.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/v33/widget/SafetyProtectionSectionView.kt index daea1c198..9f91f3c18 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/v33/widget/SafetyProtectionSectionView.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/v33/widget/SafetyProtectionSectionView.kt @@ -49,11 +49,12 @@ class SafetyProtectionSectionView : LinearLayout { init { gravity = Gravity.CENTER orientation = HORIZONTAL - visibility = if (KotlinUtils.shouldShowSafetyProtectionResources(context)) { - View.VISIBLE - } else { - View.GONE - } + visibility = + if (KotlinUtils.shouldShowSafetyProtectionResources(context)) { + View.VISIBLE + } else { + View.GONE + } } override fun onFinishInflate() { @@ -62,8 +63,9 @@ class SafetyProtectionSectionView : LinearLayout { LayoutInflater.from(context).inflate(R.layout.safety_protection_section, this) val safetyProtectionDisplayTextView = requireViewById<TextView>(R.id.safety_protection_display_text) - safetyProtectionDisplayTextView.setText(Html.fromHtml( - context.getString(android.R.string.safety_protection_display_text), 0)) + safetyProtectionDisplayTextView.setText( + Html.fromHtml(context.getString(android.R.string.safety_protection_display_text), 0) + ) } } -}
\ No newline at end of file +} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/v34/PermissionRationaleViewHandler.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/v34/PermissionRationaleViewHandler.kt index 0b7537136..02f5e9d41 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/v34/PermissionRationaleViewHandler.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/v34/PermissionRationaleViewHandler.kt @@ -38,8 +38,8 @@ interface PermissionRationaleViewHandler { } /** - * Listener interface for getting notified when the user responds to a permission rationale - * user action. + * Listener interface for getting notified when the user responds to a permission rationale user + * action. */ interface ResultListener { fun onPermissionRationaleResult(groupName: String?, @Result result: Int) @@ -60,7 +60,7 @@ interface PermissionRationaleViewHandler { * @param groupName the name of the permission group * @param title the title for the dialog * @param dataSharingSourceMessage the data sharing source data usage comes from message to - * display the user + * display the user * @param purposeTitle the data usage purposes title to display the user * @param purposeMessage the data usage purposes message to display the user * @param learnMoreMessage the more info about safety labels message to display the user @@ -91,9 +91,7 @@ interface PermissionRationaleViewHandler { /** Gives a chance for handling the back key. */ fun onBackPressed() - /** - * Handles cancel event for the permission rationale dialog. - */ + /** Handles cancel event for the permission rationale dialog. */ fun onCancelled() {} /** diff --git a/PermissionController/src/com/android/permissioncontroller/permission/utils/AndroidUtils.kt b/PermissionController/src/com/android/permissioncontroller/permission/utils/AndroidUtils.kt index 6a6623da7..a5f78aa53 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/utils/AndroidUtils.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/utils/AndroidUtils.kt @@ -26,19 +26,19 @@ import android.content.pm.PackageManager import android.content.pm.ResolveInfo import android.os.Looper import android.os.UserHandle -import kotlinx.coroutines.asCoroutineDispatcher import java.util.concurrent.Executors +import kotlinx.coroutines.asCoroutineDispatcher -/** - * Gets an [Application] instance from a regular [Context] - */ -val Context.application: Application get() = when (this) { - is Application -> this - is Activity -> application - is Service -> application - is ContextWrapper -> baseContext.application - else -> applicationContext as Application -} +/** Gets an [Application] instance from a regular [Context] */ +val Context.application: Application + get() = + when (this) { + is Application -> this + is Activity -> application + is Service -> application + is ContextWrapper -> baseContext.application + else -> applicationContext as Application + } /** * The number of threads in the IPC thread pool. Set to the maximum number of binder threads allowed @@ -46,21 +46,16 @@ val Context.application: Application get() = when (this) { */ const val IPC_THREAD_POOL_COUNT = 8 -/** - * A coroutine dispatcher with a fixed thread pool size, to be used for background tasks - */ +/** A coroutine dispatcher with a fixed thread pool size, to be used for background tasks */ val IPC = Executors.newFixedThreadPool(IPC_THREAD_POOL_COUNT).asCoroutineDispatcher() -/** - * Assert that an operation is running on main thread - */ -fun ensureMainThread() = check(Looper.myLooper() == Looper.getMainLooper()) { - "Only meant to be used on the main thread" -} +/** Assert that an operation is running on main thread */ +fun ensureMainThread() = + check(Looper.myLooper() == Looper.getMainLooper()) { + "Only meant to be used on the main thread" + } -/** - * A more readable version of [PackageManager.updatePermissionFlags] - */ +/** A more readable version of [PackageManager.updatePermissionFlags] */ fun PackageManager.updatePermissionFlags( permissionName: String, packageName: String, @@ -68,18 +63,14 @@ fun PackageManager.updatePermissionFlags( vararg flags: Pair<Int, Boolean> ) { val mask = flags.fold(0, { mask, (flag, _) -> mask or flag }) - val value = flags.fold(0, - { mask2, (flag, flagValue) -> if (flagValue) mask2 or flag else mask2 }) + val value = + flags.fold(0, { mask2, (flag, flagValue) -> if (flagValue) mask2 or flag else mask2 }) updatePermissionFlags(permissionName, packageName, mask, value, user) } -/** - * Gets a [ComponentInfo] from a [ResolveInfo] - */ +/** Gets a [ComponentInfo] from a [ResolveInfo] */ val ResolveInfo.componentInfo: ComponentInfo get() { return (activityInfo as ComponentInfo?) - ?: serviceInfo - ?: providerInfo - ?: throw IllegalStateException("Missing ComponentInfo!") - }
\ No newline at end of file + ?: serviceInfo ?: providerInfo ?: throw IllegalStateException("Missing ComponentInfo!") + } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/utils/DebugUtils.kt b/PermissionController/src/com/android/permissioncontroller/permission/utils/DebugUtils.kt index cf8a69b40..96d634ded 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/utils/DebugUtils.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/utils/DebugUtils.kt @@ -25,24 +25,21 @@ import java.util.Collections.reverse */ fun shortStackTrace() = permissionsStackTrace().toShortString() -/** - * [StackTraceElement]s of only the permission-related frames - */ -fun permissionsStackTrace() = stackTraceWithin("com.android.permissioncontroller") - .dropLastWhile { it.className.contains(".DebugUtils") } +/** [StackTraceElement]s of only the permission-related frames */ +fun permissionsStackTrace() = + stackTraceWithin("com.android.permissioncontroller").dropLastWhile { + it.className.contains(".DebugUtils") + } /** * [StackTraceElement]s of only frames who's [full class name][StackTraceElement.getClassName] * starts with [pkgPrefix] */ -fun stackTraceWithin(pkgPrefix: String) = Thread - .currentThread() - .stackTrace - .dropWhile { - !it.className.startsWith(pkgPrefix) - }.takeWhile { - it.className.startsWith(pkgPrefix) - } +fun stackTraceWithin(pkgPrefix: String) = + Thread.currentThread() + .stackTrace + .dropWhile { !it.className.startsWith(pkgPrefix) } + .takeWhile { it.className.startsWith(pkgPrefix) } /** * Renders a stack trace slice to a short-ish single-line string. diff --git a/PermissionController/src/com/android/permissioncontroller/permission/utils/KotlinUtils.kt b/PermissionController/src/com/android/permissioncontroller/permission/utils/KotlinUtils.kt index 358f86f4b..f2aa0ae66 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/utils/KotlinUtils.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/utils/KotlinUtils.kt @@ -25,6 +25,7 @@ import android.Manifest.permission.POST_NOTIFICATIONS import android.Manifest.permission.READ_MEDIA_IMAGES import android.Manifest.permission.READ_MEDIA_VIDEO import android.Manifest.permission_group.NOTIFICATIONS +import android.app.Activity import android.app.ActivityManager import android.app.AppOpsManager import android.app.AppOpsManager.MODE_ALLOWED @@ -59,8 +60,10 @@ import android.health.connect.HealthConnectManager import android.os.Build import android.os.Bundle import android.os.UserHandle +import android.os.UserManager import android.permission.PermissionManager import android.provider.DeviceConfig +import android.provider.MediaStore import android.provider.Settings import android.safetylabel.SafetyLabelConstants.PERMISSION_RATIONALE_ENABLED import android.safetylabel.SafetyLabelConstants.SAFETY_LABEL_CHANGE_NOTIFICATIONS_ENABLED @@ -297,17 +300,14 @@ object KotlinUtils { ) } - /** - * Whether the Photo Picker Prompt is supported by the device - * - */ + /** Whether the Photo Picker Prompt is supported by the device */ @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codename = "UpsideDownCake") fun isPhotoPickerPromptSupported(): Boolean { val app = PermissionControllerApplication.get() return SdkLevel.isAtLeastU() && - !DeviceUtils.isAuto(app) && - !DeviceUtils.isTelevision(app) && - !DeviceUtils.isWear(app) + !DeviceUtils.isAuto(app) && + !DeviceUtils.isTelevision(app) && + !DeviceUtils.isWear(app) } /* @@ -650,6 +650,28 @@ object KotlinUtils { } } + fun openPhotoPickerForApp( + activity: Activity, + uid: Int, + requestedPermissions: List<String>, + requestCode: Int + ) { + // A clone profile doesn't have a MediaProvider. If the app's user is a clone profile, open + // the photo picker in the parent profile + val appUser = UserHandle.getUserHandleForUid(uid) + val userManager = + activity.createContextAsUser(appUser, 0).getSystemService(UserManager::class.java)!! + val user = if (userManager.isCloneProfile) { + userManager.getProfileParent(appUser) ?: appUser + } else { + appUser + } + val pickerIntent = Intent(MediaStore.ACTION_USER_SELECT_IMAGES_FOR_APP) + .putExtra(Intent.EXTRA_UID, uid) + .setType(getMimeTypeForPermissions(requestedPermissions)) + activity.startActivityForResultAsUser(pickerIntent, requestCode, user) + } + /** Return a specific MIME type, if a set of permissions is associated with one */ fun getMimeTypeForPermissions(permissions: List<String>): String? { if (permissions.contains(READ_MEDIA_IMAGES) && !permissions.contains(READ_MEDIA_VIDEO)) { diff --git a/PermissionController/src/com/android/permissioncontroller/permission/utils/PermissionMapping.kt b/PermissionController/src/com/android/permissioncontroller/permission/utils/PermissionMapping.kt index 32d3a5325..840a033c3 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/utils/PermissionMapping.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/utils/PermissionMapping.kt @@ -23,10 +23,9 @@ import android.content.pm.PackageManager import android.content.pm.PermissionInfo import android.health.connect.HealthPermissions.HEALTH_PERMISSION_GROUP import android.util.Log - import com.android.modules.utils.build.SdkLevel -import com.android.permissioncontroller.permission.model.livedatatypes.LightAppPermGroup import com.android.permission.safetylabel.DataCategoryConstants +import com.android.permissioncontroller.permission.model.livedatatypes.LightAppPermGroup /** * This file contains the canonical mapping of permission to permission group, used in the @@ -36,15 +35,16 @@ object PermissionMapping { private val LOG_TAG = "PermissionMapping" - private val PERMISSION_GROUPS_TO_DATA_CATEGORIES: Map<String, List<String>> = mapOf( - Manifest.permission_group.LOCATION to listOf(DataCategoryConstants.CATEGORY_LOCATION)) + private val PERMISSION_GROUPS_TO_DATA_CATEGORIES: Map<String, List<String>> = + mapOf(Manifest.permission_group.LOCATION to listOf(DataCategoryConstants.CATEGORY_LOCATION)) @JvmField val SENSOR_DATA_PERMISSIONS: List<String> = listOf( Manifest.permission_group.LOCATION, Manifest.permission_group.CAMERA, - Manifest.permission_group.MICROPHONE) + Manifest.permission_group.MICROPHONE + ) @JvmField val STORAGE_SUPERGROUP_PERMISSIONS: List<String> = @@ -53,7 +53,8 @@ object PermissionMapping { listOf( Manifest.permission_group.STORAGE, Manifest.permission_group.READ_MEDIA_AURAL, - Manifest.permission_group.READ_MEDIA_VISUAL) + Manifest.permission_group.READ_MEDIA_VISUAL + ) val PARTIAL_MEDIA_PERMISSIONS: MutableSet<String> = mutableSetOf() @@ -68,7 +69,6 @@ object PermissionMapping { private val HEALTH_PERMISSIONS_SET: MutableSet<String> = mutableSetOf() - init { PLATFORM_PERMISSIONS[Manifest.permission.READ_CONTACTS] = Manifest.permission_group.CONTACTS PLATFORM_PERMISSIONS[Manifest.permission.WRITE_CONTACTS] = @@ -202,7 +202,6 @@ object PermissionMapping { * platform permission. * * @param permission the permission to resolve - * * @return The group the permission belongs to */ @JvmStatic @@ -214,7 +213,6 @@ object PermissionMapping { * Get name of the permission group a permission belongs to. * * @param permission the [info][PermissionInfo] of the permission to resolve - * * @return The group the permission belongs to */ @JvmStatic @@ -230,9 +228,8 @@ object PermissionMapping { * Get the names for all platform permissions belonging to a group. * * @param group the group - * * @return The permission names or an empty list if the group does not have platform runtime - * permissions + * permissions */ @JvmStatic fun getPlatformPermissionNamesOfGroup(group: String): List<String> { @@ -245,19 +242,19 @@ object PermissionMapping { * * @param pm Package manager to use to resolve permission infos * @param group the group - * * @return The infos for platform permissions belonging to the group or an empty list if the - * group does not have platform runtime permissions + * group does not have platform runtime permissions */ @JvmStatic fun getPlatformPermissionsOfGroup(pm: PackageManager, group: String): List<PermissionInfo> { val permInfos = mutableListOf<PermissionInfo>() for (permName in PLATFORM_PERMISSION_GROUPS[group] ?: emptyList()) { - val permInfo: PermissionInfo = try { + val permInfo: PermissionInfo = + try { pm.getPermissionInfo(permName, 0) - } catch (e: PackageManager.NameNotFoundException) { - throw IllegalStateException("$permName not defined by platform", e) - } + } catch (e: PackageManager.NameNotFoundException) { + throw IllegalStateException("$permName not defined by platform", e) + } permInfos.add(permInfo) } return permInfos @@ -300,6 +297,7 @@ object PermissionMapping { /** * Whether the permission group supports one-time + * * @param permissionGroup The permission group to check * @return `true` iff the group supports one-time */ @@ -308,9 +306,7 @@ object PermissionMapping { return ONE_TIME_PERMISSION_GROUPS.contains(permissionGroup) } - /** - * Adds health permissions as platform permissions. - */ + /** Adds health permissions as platform permissions. */ @JvmStatic fun addHealthPermissionsToPlatform(permissions: Set<String>) { if (permissions.isEmpty()) { @@ -338,8 +334,9 @@ object PermissionMapping { return emptySet() } - val appSupportsPickerPrompt = group - .permissions[Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED]?.isImplicit == false + val appSupportsPickerPrompt = + group.permissions[Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED]?.isImplicit == + false return if (appSupportsPickerPrompt) { PARTIAL_MEDIA_PERMISSIONS @@ -348,9 +345,7 @@ object PermissionMapping { } } - /** - * Returns true if the given permission is a health platform permission. - */ + /** Returns true if the given permission is a health platform permission. */ @JvmStatic fun isHealthPermission(permissionName: String): Boolean { return HEALTH_PERMISSIONS_SET.contains(permissionName) @@ -386,8 +381,8 @@ object PermissionMapping { /** * Get the SafetyLabel categories pertaining to a specified permission group. * - * @return The categories, or an empty list if the group does not have a supported mapping - * to safety label category + * @return The categories, or an empty list if the group does not have a supported mapping to + * safety label category */ fun getDataCategoriesForPermissionGroup(permissionGroupName: String): List<String> { return if (isSafetyLabelAwarePermissionGroup(permissionGroupName)) { diff --git a/PermissionController/src/com/android/permissioncontroller/permission/utils/SystemTimeSource.kt b/PermissionController/src/com/android/permissioncontroller/permission/utils/SystemTimeSource.kt index dc05ededc..116c498be 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/utils/SystemTimeSource.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/utils/SystemTimeSource.kt @@ -18,9 +18,7 @@ package com.android.permissioncontroller.permission.utils import android.os.SystemClock -/** - * Time source that uses the system time. - */ +/** Time source that uses the system time. */ class SystemTimeSource : TimeSource { override fun currentTimeMillis(): Long { @@ -30,4 +28,4 @@ class SystemTimeSource : TimeSource { override fun elapsedRealtime(): Long { return SystemClock.elapsedRealtime() } -}
\ No newline at end of file +} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/utils/TimeSource.kt b/PermissionController/src/com/android/permissioncontroller/permission/utils/TimeSource.kt index 00b93f405..29883e1f3 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/utils/TimeSource.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/utils/TimeSource.kt @@ -16,18 +16,12 @@ package com.android.permissioncontroller.permission.utils -/** - * Interface for system time components. - */ +/** Interface for system time components. */ interface TimeSource { - /** - * Returns the current time in milliseconds. - */ + /** Returns the current time in milliseconds. */ fun currentTimeMillis(): Long - /** - * Returns milliseconds since boot, including time spent in sleep. - */ + /** Returns milliseconds since boot, including time spent in sleep. */ fun elapsedRealtime(): Long -}
\ No newline at end of file +} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/utils/UserSensitiveFlagsUtils.kt b/PermissionController/src/com/android/permissioncontroller/permission/utils/UserSensitiveFlagsUtils.kt index 3cd9891ee..50cacf876 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/utils/UserSensitiveFlagsUtils.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/utils/UserSensitiveFlagsUtils.kt @@ -25,9 +25,9 @@ import com.android.permissioncontroller.PermissionControllerApplication import com.android.permissioncontroller.permission.data.UserSensitivityLiveData import com.android.permissioncontroller.permission.model.livedatatypes.UidSensitivityState import com.android.permissioncontroller.permission.utils.Utils.FLAGS_ALWAYS_USER_SENSITIVE +import java.lang.IllegalStateException import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch -import java.lang.IllegalStateException private const val LOG_TAG = "UserSensitiveFlagsUtils" @@ -35,10 +35,9 @@ private const val LOG_TAG = "UserSensitiveFlagsUtils" * Update the [PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED] and * [PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED] for all apps of this user. * - * @see UserSensitivityLiveData.loadDataAndPostValue - * * @param user The user for whom packages will be updated * @param callback A callback which will be executed when finished + * @see UserSensitivityLiveData.loadDataAndPostValue */ fun updateUserSensitiveForUser(user: UserHandle, callback: Runnable) { GlobalScope.launch(IPC) { @@ -47,7 +46,8 @@ fun updateUserSensitiveForUser(user: UserHandle, callback: Runnable) { if (uidUserSensitivity == null) { callback.run() throw IllegalStateException( - "All uids sensitivity liveData should not be null if initialized") + "All uids sensitivity liveData should not be null if initialized" + ) } updateUserSensitiveForUidsInternal(uidUserSensitivity, user, callback) } @@ -62,28 +62,38 @@ private fun updateUserSensitiveForUidsInternal( val pm = userContext.packageManager for ((uid, uidState) in uidsUserSensitivity) { - for (pkg in uidState.packages) { - for (perm in pkg.requestedPermissions) { - var flags = uidState.permStates[perm] ?: continue + for (pkg in uidState.packages) { + for (perm in pkg.requestedPermissions) { + var flags = uidState.permStates[perm] ?: continue - try { - val oldFlags = pm.getPermissionFlags(perm, pkg.packageName, user) and + try { + val oldFlags = + pm.getPermissionFlags(perm, pkg.packageName, user) and FLAGS_ALWAYS_USER_SENSITIVE - if (flags != oldFlags) { - pm.updatePermissionFlags(perm, pkg.packageName, - FLAGS_ALWAYS_USER_SENSITIVE, flags, user) - } - } catch (e: IllegalArgumentException) { - if (e.message?.startsWith("Unknown permission: ") == false) { - Log.e(LOG_TAG, "Unexpected exception while updating flags for " + - "${pkg.packageName} (uid $uid) permission $perm", e) - } else { - // Unknown permission - ignore - } + if (flags != oldFlags) { + pm.updatePermissionFlags( + perm, + pkg.packageName, + FLAGS_ALWAYS_USER_SENSITIVE, + flags, + user + ) + } + } catch (e: IllegalArgumentException) { + if (e.message?.startsWith("Unknown permission: ") == false) { + Log.e( + LOG_TAG, + "Unexpected exception while updating flags for " + + "${pkg.packageName} (uid $uid) permission $perm", + e + ) + } else { + // Unknown permission - ignore } } } } + } callback?.run() } @@ -98,8 +108,11 @@ fun updateUserSensitiveForUid(uid: Int, callback: Runnable? = null) { GlobalScope.launch(IPC) { val uidSensitivityState = UserSensitivityLiveData[uid].getInitializedValue() if (uidSensitivityState != null) { - updateUserSensitiveForUidsInternal(uidSensitivityState, - UserHandle.getUserHandleForUid(uid), callback) + updateUserSensitiveForUidsInternal( + uidSensitivityState, + UserHandle.getUserHandleForUid(uid), + callback + ) } else { Log.e(LOG_TAG, "No packages associated with uid $uid, not updating flags") callback?.run() diff --git a/PermissionController/src/com/android/permissioncontroller/permission/utils/v34/SafetyLabelUtils.kt b/PermissionController/src/com/android/permissioncontroller/permission/utils/v34/SafetyLabelUtils.kt index 5dbe203f9..9fd8ab916 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/utils/v34/SafetyLabelUtils.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/utils/v34/SafetyLabelUtils.kt @@ -32,8 +32,8 @@ object SafetyLabelUtils { groupName: String ): Set<Int> { val purposeSet = mutableSetOf<Int>() - val categoriesForPermission = PermissionMapping - .getDataCategoriesForPermissionGroup(groupName) + val categoriesForPermission = + PermissionMapping.getDataCategoriesForPermissionGroup(groupName) categoriesForPermission.forEach categoryLoop@{ category -> val dataCategory: DataCategory? = safetyLabel.dataLabel.dataShared[category] if (dataCategory == null) { diff --git a/PermissionController/src/com/android/permissioncontroller/privacysources/AccessibilitySettingsUtil.kt b/PermissionController/src/com/android/permissioncontroller/privacysources/AccessibilitySettingsUtil.kt index 59dbf635d..f9079c41b 100644 --- a/PermissionController/src/com/android/permissioncontroller/privacysources/AccessibilitySettingsUtil.kt +++ b/PermissionController/src/com/android/permissioncontroller/privacysources/AccessibilitySettingsUtil.kt @@ -32,24 +32,28 @@ object AccessibilitySettingsUtil { private val LOG_TAG = AccessibilitySettingsUtil::class.java.simpleName private val lock = Mutex() - /** - * Changes an accessibility component's state. - */ + /** Changes an accessibility component's state. */ suspend fun disableAccessibilityService(context: Context, serviceToBeDisabled: ComponentName) { lock.withLock { val settingsEnabledA11yServices = getEnabledServicesFromSettings(context) - if (settingsEnabledA11yServices.isEmpty() || - !settingsEnabledA11yServices.contains(serviceToBeDisabled) + if ( + settingsEnabledA11yServices.isEmpty() || + !settingsEnabledA11yServices.contains(serviceToBeDisabled) ) { - Log.w(LOG_TAG, "${serviceToBeDisabled.toShortString()} is already disabled " + - "or not installed.") + Log.w( + LOG_TAG, + "${serviceToBeDisabled.toShortString()} is already disabled " + + "or not installed." + ) return } settingsEnabledA11yServices.remove(serviceToBeDisabled) - val updatedEnabledServices = settingsEnabledA11yServices.map { it.flattenToString() } - .joinToString(separator = ENABLED_ACCESSIBILITY_SERVICES_SEPARATOR.toString()) + val updatedEnabledServices = + settingsEnabledA11yServices + .map { it.flattenToString() } + .joinToString(separator = ENABLED_ACCESSIBILITY_SERVICES_SEPARATOR.toString()) Settings.Secure.putString( context.contentResolver, @@ -59,27 +63,24 @@ object AccessibilitySettingsUtil { } } - /** - * @return the mutable set of enabled accessibility services. - */ + /** @return the mutable set of enabled accessibility services. */ fun getEnabledServicesFromSettings(context: Context): MutableSet<ComponentName> { - val enabledServicesSetting = Settings.Secure.getString( - context.contentResolver, Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES - ) + val enabledServicesSetting = + Settings.Secure.getString( + context.contentResolver, + Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES + ) val enabledServices = mutableSetOf<ComponentName>() if (TextUtils.isEmpty(enabledServicesSetting)) { return enabledServices } - val colonSplitter: TextUtils.StringSplitter = TextUtils.SimpleStringSplitter( - ENABLED_ACCESSIBILITY_SERVICES_SEPARATOR - ) + val colonSplitter: TextUtils.StringSplitter = + TextUtils.SimpleStringSplitter(ENABLED_ACCESSIBILITY_SERVICES_SEPARATOR) colonSplitter.setString(enabledServicesSetting) for (componentNameString in colonSplitter) { - val enabledService = ComponentName.unflattenFromString( - componentNameString - ) + val enabledService = ComponentName.unflattenFromString(componentNameString) if (enabledService != null) { enabledServices.add(enabledService) } else { diff --git a/PermissionController/src/com/android/permissioncontroller/privacysources/AccessibilitySourceService.kt b/PermissionController/src/com/android/permissioncontroller/privacysources/AccessibilitySourceService.kt index 33acd8285..5f29e87fa 100644 --- a/PermissionController/src/com/android/permissioncontroller/privacysources/AccessibilitySourceService.kt +++ b/PermissionController/src/com/android/permissioncontroller/privacysources/AccessibilitySourceService.kt @@ -80,8 +80,7 @@ import kotlinx.coroutines.sync.withLock const val PROPERTY_SC_ACCESSIBILITY_SOURCE_ENABLED = "sc_accessibility_source_enabled" const val PROPERTY_SC_ACCESSIBILITY_LISTENER_ENABLED = "sc_accessibility_listener_enabled" const val SC_ACCESSIBILITY_SOURCE_ID = "AndroidAccessibility" -const val SC_ACCESSIBILITY_REMOVE_ACCESS_ACTION_ID = - "revoke_accessibility_app_access" +const val SC_ACCESSIBILITY_REMOVE_ACCESS_ACTION_ID = "revoke_accessibility_app_access" private const val DEBUG = false @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.TIRAMISU) @@ -97,9 +96,7 @@ fun isAccessibilitySourceEnabled(): Boolean { ) } -/** - * cts test needs to disable the listener. - */ +/** cts test needs to disable the listener. */ fun isAccessibilityListenerEnabled(): Boolean { return DeviceConfig.getBoolean( DeviceConfig.NAMESPACE_PRIVACY, @@ -110,24 +107,21 @@ fun isAccessibilityListenerEnabled(): Boolean { @RequiresApi(Build.VERSION_CODES.TIRAMISU) private fun isSafetyCenterEnabled(context: Context): Boolean { - return getSystemServiceSafe(context, SafetyCenterManager::class.java) - .isSafetyCenterEnabled + return getSystemServiceSafe(context, SafetyCenterManager::class.java).isSafetyCenterEnabled } @RequiresApi(Build.VERSION_CODES.TIRAMISU) -class AccessibilitySourceService( - val context: Context, - val random: Random = Random() -) : PrivacySource { +class AccessibilitySourceService(val context: Context, val random: Random = Random()) : + PrivacySource { private val parentUserContext = Utils.getParentUserContext(context) private val packageManager = parentUserContext.packageManager - private val sharedPrefs: SharedPreferences = parentUserContext.getSharedPreferences( - ACCESSIBILITY_PREFERENCES_FILE, Context.MODE_PRIVATE) - private val notificationsManager = getSystemServiceSafe(parentUserContext, - NotificationManager::class.java) - private val safetyCenterManager = getSystemServiceSafe(parentUserContext, - SafetyCenterManager::class.java) + private val sharedPrefs: SharedPreferences = + parentUserContext.getSharedPreferences(ACCESSIBILITY_PREFERENCES_FILE, Context.MODE_PRIVATE) + private val notificationsManager = + getSystemServiceSafe(parentUserContext, NotificationManager::class.java) + private val safetyCenterManager = + getSystemServiceSafe(parentUserContext, SafetyCenterManager::class.java) @WorkerThread internal suspend fun processAccessibilityJob( @@ -157,15 +151,15 @@ class AccessibilitySourceService( val lastShownNotification = sharedPrefs.getLong(KEY_LAST_ACCESSIBILITY_NOTIFICATION_SHOWN, 0) - val showNotification = ((System.currentTimeMillis() - lastShownNotification) > - getNotificationsIntervalMillis()) && getCurrentNotification() == null + val showNotification = + ((System.currentTimeMillis() - lastShownNotification) > + getNotificationsIntervalMillis()) && getCurrentNotification() == null if (showNotification) { val alreadyNotifiedServices = getNotifiedServices() - val toBeNotifiedServices = a11yServiceList.filter { - !alreadyNotifiedServices.contains(it.id) - } + val toBeNotifiedServices = + a11yServiceList.filter { !alreadyNotifiedServices.contains(it.id) } if (toBeNotifiedServices.isNotEmpty()) { if (DEBUG) { @@ -194,9 +188,7 @@ class AccessibilitySourceService( } } - /** - * sends a notification for a given accessibility package - */ + /** sends a notification for a given accessibility package */ private suspend fun sendNotification( serviceToBeNotified: AccessibilityServiceInfo, sessionId: Long @@ -214,13 +206,13 @@ class AccessibilitySourceService( identifier = componentName.flattenToString() } - val title = parentUserContext.getString( - R.string.accessibility_access_reminder_notification_title - ) - val summary = parentUserContext.getString( - R.string.accessibility_access_reminder_notification_content, - pkgLabel - ) + val title = + parentUserContext.getString(R.string.accessibility_access_reminder_notification_title) + val summary = + parentUserContext.getString( + R.string.accessibility_access_reminder_notification_content, + pkgLabel + ) val (appLabel, smallIcon, color) = KotlinUtils.getSafetyCenterNotificationResources(parentUserContext) @@ -236,8 +228,11 @@ class AccessibilitySourceService( .setAutoCancel(true) .setDeleteIntent( PendingIntent.getBroadcast( - parentUserContext, 0, notificationDeleteIntent, - PendingIntent.FLAG_ONE_SHOT or PendingIntent.FLAG_UPDATE_CURRENT or + parentUserContext, + 0, + notificationDeleteIntent, + PendingIntent.FLAG_ONE_SHOT or + PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE ) ) @@ -256,10 +251,10 @@ class AccessibilitySourceService( ) sharedPrefsLock.withLock { - sharedPrefs.edit().putLong( - KEY_LAST_ACCESSIBILITY_NOTIFICATION_SHOWN, - System.currentTimeMillis() - ).apply() + sharedPrefs + .edit() + .putLong(KEY_LAST_ACCESSIBILITY_NOTIFICATION_SHOWN, System.currentTimeMillis()) + .apply() } markServiceAsNotified(ComponentName.unflattenFromString(serviceToBeNotified.id)!!) @@ -277,11 +272,12 @@ class AccessibilitySourceService( /** Create the channel for a11y notifications */ private fun createPermissionReminderChannel() { - val permissionReminderChannel = NotificationChannel( - Constants.PERMISSION_REMINDER_CHANNEL_ID, - context.getString(R.string.permission_reminders), - NotificationManager.IMPORTANCE_LOW - ) + val permissionReminderChannel = + NotificationChannel( + Constants.PERMISSION_REMINDER_CHANNEL_ID, + context.getString(R.string.permission_reminders), + NotificationManager.IMPORTANCE_LOW + ) notificationsManager.createNotificationChannel(permissionReminderChannel) } @@ -298,32 +294,37 @@ class AccessibilitySourceService( val pkgLabel = a11yService.resolveInfo.loadLabel(packageManager).toString() val uid = a11yService.resolveInfo.serviceInfo.applicationInfo.uid - val removeAccessPendingIntent = getRemoveAccessPendingIntent( - context, - componentName, - safetySourceIssueId, - uid, - sessionId - ) + val removeAccessPendingIntent = + getRemoveAccessPendingIntent( + context, + componentName, + safetySourceIssueId, + uid, + sessionId + ) - val removeAccessAction = SafetySourceIssue.Action.Builder( - SC_ACCESSIBILITY_REMOVE_ACCESS_ACTION_ID, - parentUserContext.getString(R.string.accessibility_remove_access_button_label), - removeAccessPendingIntent - ) - .setWillResolve(true) - .setSuccessMessage(parentUserContext.getString( - R.string.accessibility_remove_access_success_label)) - .build() + val removeAccessAction = + SafetySourceIssue.Action.Builder( + SC_ACCESSIBILITY_REMOVE_ACCESS_ACTION_ID, + parentUserContext.getString(R.string.accessibility_remove_access_button_label), + removeAccessPendingIntent + ) + .setWillResolve(true) + .setSuccessMessage( + parentUserContext.getString(R.string.accessibility_remove_access_success_label) + ) + .build() val accessibilityActivityPendingIntent = getAccessibilityActivityPendingIntent(context, uid, sessionId) - val accessibilityActivityAction = SafetySourceIssue.Action.Builder( - SC_ACCESSIBILITY_SHOW_ACCESSIBILITY_ACTIVITY_ACTION_ID, - parentUserContext.getString(R.string.accessibility_show_all_apps_button_label), - accessibilityActivityPendingIntent - ).build() + val accessibilityActivityAction = + SafetySourceIssue.Action.Builder( + SC_ACCESSIBILITY_SHOW_ACCESSIBILITY_ACTIVITY_ACTION_ID, + parentUserContext.getString(R.string.accessibility_show_all_apps_button_label), + accessibilityActivityPendingIntent + ) + .build() val warningCardDismissIntent = Intent(parentUserContext, AccessibilityWarningCardDismissalReceiver::class.java).apply { @@ -334,15 +335,19 @@ class AccessibilitySourceService( putExtra(Intent.EXTRA_UID, uid) } - val warningCardDismissPendingIntent = PendingIntent.getBroadcast( - parentUserContext, 0, warningCardDismissIntent, - PendingIntent.FLAG_ONE_SHOT or PendingIntent.FLAG_UPDATE_CURRENT or - PendingIntent.FLAG_IMMUTABLE - ) - val title = parentUserContext.getString( - R.string.accessibility_access_reminder_notification_title) - val summary = parentUserContext.getString( - R.string.accessibility_access_warning_card_content) + val warningCardDismissPendingIntent = + PendingIntent.getBroadcast( + parentUserContext, + 0, + warningCardDismissIntent, + PendingIntent.FLAG_ONE_SHOT or + PendingIntent.FLAG_UPDATE_CURRENT or + PendingIntent.FLAG_IMMUTABLE + ) + val title = + parentUserContext.getString(R.string.accessibility_access_reminder_notification_title) + val summary = + parentUserContext.getString(R.string.accessibility_access_warning_card_content) return SafetySourceIssue.Builder( safetySourceIssueId, @@ -359,9 +364,7 @@ class AccessibilitySourceService( .build() } - /** - * @return pending intent for remove access button on the warning card. - */ + /** @return pending intent for remove access button on the warning card. */ private fun getRemoveAccessPendingIntent( context: Context, serviceComponentName: ComponentName, @@ -387,9 +390,7 @@ class AccessibilitySourceService( ) } - /** - * @return pending intent for redirecting user to the accessibility page - */ + /** @return pending intent for redirecting user to the accessibility page */ private fun getAccessibilityActivityPendingIntent( context: Context, uid: Int, @@ -412,9 +413,7 @@ class AccessibilitySourceService( ) } - /** - * @return pending intent to redirect the user to safety center on notification click - */ + /** @return pending intent to redirect the user to safety center on notification click */ private fun getSafetyCenterActivityIntent( context: Context, uid: Int, @@ -478,9 +477,7 @@ class AccessibilitySourceService( sendIssuesToSafetyCenter(enabledServices, safetyEvent) } - /** - * If [.cancel] throw an [InterruptedException]. - */ + /** If [.cancel] throw an [InterruptedException]. */ @Throws(InterruptedException::class) private fun interruptJobIfCanceled(cancel: BooleanSupplier?) { if (cancel != null && cancel.asBoolean) { @@ -488,26 +485,28 @@ class AccessibilitySourceService( } } - private val accessibilityManager = getSystemServiceSafe(parentUserContext, - AccessibilityManager::class.java) + private val accessibilityManager = + getSystemServiceSafe(parentUserContext, AccessibilityManager::class.java) - /** - * @return enabled 3rd party accessibility services. - */ + /** @return enabled 3rd party accessibility services. */ fun getEnabledAccessibilityServices(): List<AccessibilityServiceInfo> { - val installedServices = accessibilityManager.getInstalledAccessibilityServiceList() - .associateBy { ComponentName.unflattenFromString(it.id) } - val enabledServices = AccessibilitySettingsUtil.getEnabledServicesFromSettings(context) - .map { + val installedServices = + accessibilityManager.getInstalledAccessibilityServiceList().associateBy { + ComponentName.unflattenFromString(it.id) + } + val enabledServices = + AccessibilitySettingsUtil.getEnabledServicesFromSettings(context).map { if (installedServices[it] == null) { - Log.e(LOG_TAG, "enabled accessibility service ($it) not found in installed" + - "services: ${installedServices.keys}") + Log.e( + LOG_TAG, + "enabled accessibility service ($it) not found in installed" + + "services: ${installedServices.keys}" + ) } installedServices[it] } - return enabledServices.filterNotNull() - .filter { !it.isAccessibilityTool } + return enabledServices.filterNotNull().filter { !it.isAccessibilityTool } } /** @@ -523,13 +522,14 @@ class AccessibilitySourceService( internal suspend fun removeFromNotifiedServices(a11Service: ComponentName) { sharedPrefsLock.withLock { val notifiedServices = getNotifiedServices() - val filteredServices = notifiedServices.filter { - it != a11Service.flattenToShortString() - }.toSet() + val filteredServices = + notifiedServices.filter { it != a11Service.flattenToShortString() }.toSet() if (filteredServices.size < notifiedServices.size) { - sharedPrefs.edit().putStringSet(KEY_ALREADY_NOTIFIED_SERVICES, filteredServices) - .apply() + sharedPrefs + .edit() + .putStringSet(KEY_ALREADY_NOTIFIED_SERVICES, filteredServices) + .apply() } } } @@ -538,7 +538,9 @@ class AccessibilitySourceService( sharedPrefsLock.withLock { val alreadyNotifiedServices = getNotifiedServices() alreadyNotifiedServices.add(a11Service.flattenToShortString()) - sharedPrefs.edit().putStringSet(KEY_ALREADY_NOTIFIED_SERVICES, alreadyNotifiedServices) + sharedPrefs + .edit() + .putStringSet(KEY_ALREADY_NOTIFIED_SERVICES, alreadyNotifiedServices) .apply() } } @@ -548,7 +550,9 @@ class AccessibilitySourceService( val alreadyNotifiedServices = getNotifiedServices() val services = alreadyNotifiedServices.filter { enabledA11yServices.contains(it) } if (services.size < alreadyNotifiedServices.size) { - sharedPrefs.edit().putStringSet(KEY_ALREADY_NOTIFIED_SERVICES, services.toSet()) + sharedPrefs + .edit() + .putStringSet(KEY_ALREADY_NOTIFIED_SERVICES, services.toSet()) .apply() } } @@ -563,17 +567,13 @@ class AccessibilitySourceService( return sharedPrefs } - /** - * Remove notification when safety center feature is turned off - */ + /** Remove notification when safety center feature is turned off */ private fun removeAccessibilityNotification() { val notification: StatusBarNotification = getCurrentNotification() ?: return cancelNotification(notification.tag) } - /** - * Remove notification (if needed) when an accessibility event occur. - */ + /** Remove notification (if needed) when an accessibility event occur. */ fun removeAccessibilityNotification(a11yEnabledComponents: Set<String>) { val notification = getCurrentNotification() ?: return if (a11yEnabledComponents.contains(notification.tag)) { @@ -582,9 +582,7 @@ class AccessibilitySourceService( cancelNotification(notification.tag) } - /** - * Remove notification when a package is uninstalled. - */ + /** Remove notification when a package is uninstalled. */ private fun removeAccessibilityNotification(pkg: String) { val notification = getCurrentNotification() ?: return val component = ComponentName.unflattenFromString(notification.tag) @@ -594,9 +592,7 @@ class AccessibilitySourceService( cancelNotification(notification.tag) } - /** - * Remove notification for a component, when warning card is dismissed. - */ + /** Remove notification for a component, when warning card is dismissed. */ fun removeAccessibilityNotification(component: ComponentName) { val notification = getCurrentNotification() ?: return if (component.flattenToShortString() == notification.tag) { @@ -612,17 +608,19 @@ class AccessibilitySourceService( internal suspend fun removePackageState(pkg: String) { sharedPrefsLock.withLock { removeAccessibilityNotification(pkg) - val notifiedServices = getNotifiedServices().mapNotNull { - ComponentName.unflattenFromString(it) - } - - val filteredServices = notifiedServices.filterNot { it.packageName == pkg } - .map { it.flattenToShortString() }.toSet() + val notifiedServices = + getNotifiedServices().mapNotNull { ComponentName.unflattenFromString(it) } + + val filteredServices = + notifiedServices + .filterNot { it.packageName == pkg } + .map { it.flattenToShortString() } + .toSet() if (filteredServices.size < notifiedServices.size) { - sharedPrefs.edit().putStringSet( - KEY_ALREADY_NOTIFIED_SERVICES, - filteredServices - ).apply() + sharedPrefs + .edit() + .putStringSet(KEY_ALREADY_NOTIFIED_SERVICES, filteredServices) + .apply() } } } @@ -640,8 +638,8 @@ class AccessibilitySourceService( "sc_accessibility_job_interval_millis" private val DEFAULT_SC_ACCESSIBILITY_JOB_INTERVAL_MILLIS = TimeUnit.DAYS.toMillis(1) - private val sourceStateChanged = SafetyEvent.Builder( - SafetyEvent.SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED).build() + private val sourceStateChanged = + SafetyEvent.Builder(SafetyEvent.SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED).build() /** lock for processing a job */ internal val lock = Mutex() @@ -667,7 +665,6 @@ class AccessibilitySourceService( /** * Flexibility of the periodic check. * - * * 10% of [.getPeriodicCheckIntervalMillis] * * @return The flexibility of the periodic check in milliseconds @@ -679,7 +676,6 @@ class AccessibilitySourceService( /** * Minimum time in between showing two notifications. * - * * This is just small enough so that the periodic check can always show a notification. * * @return The minimum time in milliseconds @@ -716,8 +712,9 @@ class AccessibilityPackageResetHandler : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { val action = intent.action - if (action != Intent.ACTION_PACKAGE_DATA_CLEARED && - action != Intent.ACTION_PACKAGE_FULLY_REMOVED + if ( + action != Intent.ACTION_PACKAGE_DATA_CLEARED && + action != Intent.ACTION_PACKAGE_FULLY_REMOVED ) { return } @@ -732,9 +729,7 @@ class AccessibilityPackageResetHandler : BroadcastReceiver() { if (DEBUG) { Log.v(LOG_TAG, "package reset event occurred for ${data.schemeSpecificPart}") } - AccessibilitySourceService(context).run { - removePackageState(data.schemeSpecificPart) - } + AccessibilitySourceService(context).run { removePackageState(data.schemeSpecificPart) } } } } @@ -762,9 +757,7 @@ class AccessibilityNotificationDeleteHandler : BroadcastReceiver() { } } -/** - * Handler for Remove access action (warning cards) in safety center dashboard - */ +/** Handler for Remove access action (warning cards) in safety center dashboard */ @RequiresApi(Build.VERSION_CODES.TIRAMISU) class AccessibilityRemoveAccessHandler : BroadcastReceiver() { private val LOG_TAG = AccessibilityRemoveAccessHandler::class.java.simpleName @@ -783,27 +776,27 @@ class AccessibilityRemoveAccessHandler : BroadcastReceiver() { AccessibilitySourceService.lock.withLock { val accessibilityService = AccessibilitySourceService(context) var a11yEnabledServices = accessibilityService.getEnabledAccessibilityServices() - val builder = try { - AccessibilitySettingsUtil.disableAccessibilityService(context, a11yService) - accessibilityService.removeFromNotifiedServices(a11yService) - a11yEnabledServices = a11yEnabledServices.filter { - it.id != a11yService.flattenToShortString() + val builder = + try { + AccessibilitySettingsUtil.disableAccessibilityService(context, a11yService) + accessibilityService.removeFromNotifiedServices(a11yService) + a11yEnabledServices = + a11yEnabledServices.filter { + it.id != a11yService.flattenToShortString() + } + SafetyEvent.Builder( + SafetyEvent.SAFETY_EVENT_TYPE_RESOLVING_ACTION_SUCCEEDED + ) + } catch (ex: Exception) { + Log.w(LOG_TAG, "error occurred in disabling a11y service.", ex) + SafetyEvent.Builder(SafetyEvent.SAFETY_EVENT_TYPE_RESOLVING_ACTION_FAILED) } - SafetyEvent.Builder( - SafetyEvent.SAFETY_EVENT_TYPE_RESOLVING_ACTION_SUCCEEDED - ) - } catch (ex: Exception) { - Log.w(LOG_TAG, "error occurred in disabling a11y service.", ex) - SafetyEvent.Builder( - SafetyEvent.SAFETY_EVENT_TYPE_RESOLVING_ACTION_FAILED - ) - } - val safetySourceIssueId = intent.getStringExtra( - EXTRA_SAFETY_SOURCE_ISSUE_ID - ) - val safetyEvent = builder.setSafetySourceIssueId(safetySourceIssueId) - .setSafetySourceIssueActionId(SC_ACCESSIBILITY_REMOVE_ACCESS_ACTION_ID) - .build() + val safetySourceIssueId = intent.getStringExtra(EXTRA_SAFETY_SOURCE_ISSUE_ID) + val safetyEvent = + builder + .setSafetySourceIssueId(safetySourceIssueId) + .setSafetySourceIssueActionId(SC_ACCESSIBILITY_REMOVE_ACCESS_ACTION_ID) + .build() accessibilityService.sendIssuesToSafetyCenter(a11yEnabledServices, safetyEvent) } if (DEBUG) { @@ -820,9 +813,7 @@ class AccessibilityRemoveAccessHandler : BroadcastReceiver() { } } -/** - * Handler for accessibility warning cards dismissal in safety center dashboard - */ +/** Handler for accessibility warning cards dismissal in safety center dashboard */ @RequiresApi(Build.VERSION_CODES.TIRAMISU) class AccessibilityWarningCardDismissalReceiver : BroadcastReceiver() { private val LOG_TAG = AccessibilityWarningCardDismissalReceiver::class.java.simpleName @@ -857,8 +848,8 @@ class AccessibilityWarningCardDismissalReceiver : BroadcastReceiver() { } /** - * Schedules periodic job to send notifications for third part accessibility services, - * the job also sends this data to Safety Center. + * Schedules periodic job to send notifications for third part accessibility services, the job also + * sends this data to Safety Center. */ @RequiresApi(Build.VERSION_CODES.TIRAMISU) class AccessibilityOnBootReceiver : BroadcastReceiver() { @@ -876,15 +867,16 @@ class AccessibilityOnBootReceiver : BroadcastReceiver() { val jobScheduler = getSystemServiceSafe(context, JobScheduler::class.java) if (jobScheduler.getPendingJob(Constants.PERIODIC_ACCESSIBILITY_CHECK_JOB_ID) == null) { - val jobInfo = JobInfo.Builder( - Constants.PERIODIC_ACCESSIBILITY_CHECK_JOB_ID, - ComponentName(context, AccessibilityJobService::class.java) - ) - .setPeriodic( - AccessibilitySourceService.getJobsIntervalMillis(), - AccessibilitySourceService.getFlexJobsIntervalMillis() - ) - .build() + val jobInfo = + JobInfo.Builder( + Constants.PERIODIC_ACCESSIBILITY_CHECK_JOB_ID, + ComponentName(context, AccessibilityJobService::class.java) + ) + .setPeriodic( + AccessibilitySourceService.getJobsIntervalMillis(), + AccessibilitySourceService.getFlexJobsIntervalMillis() + ) + .build() val status = jobScheduler.schedule(jobInfo) if (status != JobScheduler.RESULT_SUCCESS) { @@ -901,8 +893,7 @@ class AccessibilityJobService : JobService() { private var mSourceService: AccessibilitySourceService? = null private val mLock = Object() - @GuardedBy("mLock") - private var mCurrentJob: Job? = null + @GuardedBy("mLock") private var mCurrentJob: Job? = null override fun onCreate() { super.onCreate() @@ -917,24 +908,28 @@ class AccessibilityJobService : JobService() { Log.v(LOG_TAG, "Accessibility privacy source job already running") return false } - if (!isAccessibilitySourceEnabled() || - !isSafetyCenterEnabled(this@AccessibilityJobService)) { + if ( + !isAccessibilitySourceEnabled() || + !isSafetyCenterEnabled(this@AccessibilityJobService) + ) { Log.v(LOG_TAG, "either privacy source or safety center is not enabled") jobFinished(params, false) mCurrentJob = null return false } val coroutineScope = CoroutineScope(Dispatchers.Default + SupervisorJob()) - mCurrentJob = coroutineScope.launch(Dispatchers.Default) { - mSourceService?.processAccessibilityJob( - params, - this@AccessibilityJobService, - BooleanSupplier { - val job = mCurrentJob - return@BooleanSupplier job?.isCancelled ?: false - } - ) ?: jobFinished(params, false) - } + mCurrentJob = + coroutineScope.launch(Dispatchers.Default) { + mSourceService?.processAccessibilityJob( + params, + this@AccessibilityJobService, + BooleanSupplier { + val job = mCurrentJob + return@BooleanSupplier job?.isCancelled ?: false + } + ) + ?: jobFinished(params, false) + } } return true } @@ -942,20 +937,19 @@ class AccessibilityJobService : JobService() { override fun onStopJob(params: JobParameters?): Boolean { var job: Job? synchronized(mLock) { - job = if (mCurrentJob == null) { - return false - } else { - mCurrentJob - } + job = + if (mCurrentJob == null) { + return false + } else { + mCurrentJob + } } job?.cancel() return false } fun clearJob() { - synchronized(mLock) { - mCurrentJob = null - } + synchronized(mLock) { mCurrentJob = null } } } @@ -971,8 +965,9 @@ class SafetyCenterAccessibilityListener(val context: Context) : return } - if (!isAccessibilitySourceEnabled() || !isSafetyCenterEnabled(context) || - isProfile(context)) { + if ( + !isAccessibilitySourceEnabled() || !isSafetyCenterEnabled(context) || isProfile(context) + ) { Log.v(LOG_TAG, "accessibility event occurred, safety center feature not enabled.") return } @@ -986,9 +981,13 @@ class SafetyCenterAccessibilityListener(val context: Context) : val a11ySourceService = AccessibilitySourceService(context) val a11yEnabledServices = a11ySourceService.getEnabledAccessibilityServices() a11ySourceService.sendIssuesToSafetyCenter(a11yEnabledServices) - val enabledComponents = a11yEnabledServices.map { a11yService -> - ComponentName.unflattenFromString(a11yService.id)!!.flattenToShortString() - }.toSet() + val enabledComponents = + a11yEnabledServices + .map { a11yService -> + ComponentName.unflattenFromString(a11yService.id)!! + .flattenToShortString() + } + .toSet() a11ySourceService.removeAccessibilityNotification(enabledComponents) a11ySourceService.updateServiceAsNotified(enabledComponents) } diff --git a/PermissionController/src/com/android/permissioncontroller/privacysources/AutoRevokePrivacySource.kt b/PermissionController/src/com/android/permissioncontroller/privacysources/AutoRevokePrivacySource.kt index 0660955ff..93bb376ba 100644 --- a/PermissionController/src/com/android/permissioncontroller/privacysources/AutoRevokePrivacySource.kt +++ b/PermissionController/src/com/android/permissioncontroller/privacysources/AutoRevokePrivacySource.kt @@ -25,9 +25,7 @@ import com.android.permissioncontroller.hibernation.cancelUnusedAppsNotification import com.android.permissioncontroller.hibernation.rescanAndPushDataToSafetyCenter import java.util.Random -/** - * Privacy source for auto-revoked permissions. - */ +/** Privacy source for auto-revoked permissions. */ @RequiresApi(Build.VERSION_CODES.TIRAMISU) class AutoRevokePrivacySource : PrivacySource { override val shouldProcessProfileRequest: Boolean = false diff --git a/PermissionController/src/com/android/permissioncontroller/privacysources/LocationAccessPrivacySource.kt b/PermissionController/src/com/android/permissioncontroller/privacysources/LocationAccessPrivacySource.kt index df35048e5..97d1f3ebf 100644 --- a/PermissionController/src/com/android/permissioncontroller/privacysources/LocationAccessPrivacySource.kt +++ b/PermissionController/src/com/android/permissioncontroller/privacysources/LocationAccessPrivacySource.kt @@ -20,7 +20,6 @@ import android.content.Context import android.content.Intent import android.os.Build import androidx.annotation.RequiresApi - import com.android.permissioncontroller.permission.service.LocationAccessCheck import com.android.permissioncontroller.privacysources.SafetyCenterReceiver.RefreshEvent @@ -40,4 +39,4 @@ class LocationAccessPrivacySource : PrivacySource { val safetyRefreshEvent = getSafetyCenterEvent(refreshEvent, intent) LocationAccessCheck(context, null).rescanAndPushSafetyCenterData(safetyRefreshEvent, null) } -}
\ No newline at end of file +} diff --git a/PermissionController/src/com/android/permissioncontroller/privacysources/NotificationListenerCheck.kt b/PermissionController/src/com/android/permissioncontroller/privacysources/NotificationListenerCheck.kt index 91a043a6a..4d5adfcc0 100644 --- a/PermissionController/src/com/android/permissioncontroller/privacysources/NotificationListenerCheck.kt +++ b/PermissionController/src/com/android/permissioncontroller/privacysources/NotificationListenerCheck.kt @@ -109,7 +109,10 @@ private val DEFAULT_NOTIFICATION_LISTENER_CHECK_INTERVAL_MILLIS = DAYS.toMillis( private fun isNotificationListenerCheckFlagEnabled(): Boolean { // TODO: b/249789657 Set default to true after policy exemption + impact analysis return DeviceConfig.getBoolean( - DeviceConfig.NAMESPACE_PRIVACY, PROPERTY_NOTIFICATION_LISTENER_CHECK_ENABLED, false) + DeviceConfig.NAMESPACE_PRIVACY, + PROPERTY_NOTIFICATION_LISTENER_CHECK_ENABLED, + false + ) } /** @@ -123,7 +126,8 @@ private fun getPeriodicCheckIntervalMillis(): Long { return DeviceConfig.getLong( DeviceConfig.NAMESPACE_PRIVACY, PROPERTY_NOTIFICATION_LISTENER_CHECK_INTERVAL_MILLIS, - DEFAULT_NOTIFICATION_LISTENER_CHECK_INTERVAL_MILLIS) + DEFAULT_NOTIFICATION_LISTENER_CHECK_INTERVAL_MILLIS + ) } /** @@ -178,8 +182,8 @@ private fun getSafetySourceIssueIdFromComponentName(componentName: ComponentName * <p>We rate limit the number of notification we show and only ever show one notification at a * time. * - * <p>As there are many cases why a notification should not been shown, we always schedule a {@link - * #addNotificationListenerNotificationIfNeeded check} which then might add a notification. + * <p>As there are many cases why a notification should not been shown, we always schedule a + * {@link #addNotificationListenerNotificationIfNeeded check} which then might add a notification. * * @param context Used to resolve managers * @param shouldCancel If supplied, can be used to interrupt long-running operations @@ -199,10 +203,11 @@ internal class NotificationListenerCheckInternal( @VisibleForTesting val exemptPackagesDelegate = lazy { getExemptedPackages( - getSystemServiceSafe(parentUserContext, RoleManager::class.java), parentUserContext) + getSystemServiceSafe(parentUserContext, RoleManager::class.java), + parentUserContext + ) } - @VisibleForTesting - val exemptPackages: Set<String> by exemptPackagesDelegate + @VisibleForTesting val exemptPackages: Set<String> by exemptPackagesDelegate companion object { @VisibleForTesting const val NLS_PREFERENCE_FILE = "nls_preference" @@ -231,7 +236,8 @@ internal class NotificationListenerCheckInternal( SYSTEM_AUDIO_INTELLIGENCE, SYSTEM_NOTIFICATION_INTELLIGENCE, SYSTEM_TEXT_INTELLIGENCE, - SYSTEM_VISUAL_INTELLIGENCE) + SYSTEM_VISUAL_INTELLIGENCE + ) /** Lock required for all public methods */ private val nlsLock = Mutex() @@ -286,7 +292,8 @@ internal class NotificationListenerCheckInternal( TAG, "Found ${enabledComponents.size} enabled notification listeners. " + "${notifiedComponents.size} already notified. ${unNotifiedComponents.size} " + - "unnotified, sessionId = $sessionId") + "unnotified, sessionId = $sessionId" + ) } throwInterruptedExceptionIfTaskIsCanceled() @@ -318,7 +325,8 @@ internal class NotificationListenerCheckInternal( TAG, "enabledNotificationListeners=$enabledNotificationListeners\n" + "enabledNotificationListenersExcludingExemptPackages=" + - "$enabledNotificationListenersExcludingExemptPackages") + "$enabledNotificationListenersExcludingExemptPackages" + ) } throwInterruptedExceptionIfTaskIsCanceled() @@ -421,13 +429,16 @@ internal class NotificationListenerCheckInternal( val componentsInternal = components.toMutableList() // Don't show too many notification within certain timespan - if (currentTimeMillis() - getLastNotificationShownTimeMillis() < - getInBetweenNotificationsMillis()) { + if ( + currentTimeMillis() - getLastNotificationShownTimeMillis() < + getInBetweenNotificationsMillis() + ) { if (DEBUG) { Log.v( TAG, "Notification not posted, within " + - "$DEFAULT_NOTIFICATION_LISTENER_CHECK_INTERVAL_MILLIS ms") + "$DEFAULT_NOTIFICATION_LISTENER_CHECK_INTERVAL_MILLIS ms" + ) } return } @@ -458,7 +469,8 @@ internal class NotificationListenerCheckInternal( if (DEBUG) { Log.v( TAG, - "Attempting to get PackageInfo for " + componentToNotifyFor.packageName) + "Attempting to get PackageInfo for " + componentToNotifyFor.packageName + ) } pkgInfo = Utils.getPackageInfoForComponentName(parentUserContext, componentToNotifyFor) @@ -483,7 +495,8 @@ internal class NotificationListenerCheckInternal( NotificationChannel( Constants.PERMISSION_REMINDER_CHANNEL_ID, parentUserContext.getString(R.string.permission_reminders), - NotificationManager.IMPORTANCE_LOW) + NotificationManager.IMPORTANCE_LOW + ) val notificationManager = getSystemServiceSafe(parentUserContext, NotificationManager::class.java) @@ -503,8 +516,7 @@ internal class NotificationListenerCheckInternal( pkg: PackageInfo, sessionId: Long ) { - val pkgLabel = - Utils.getApplicationLabel(parentUserContext, pkg.applicationInfo) + val pkgLabel = Utils.getApplicationLabel(parentUserContext, pkg.applicationInfo) val uid = pkg.applicationInfo.uid val deletePendingIntent = @@ -516,7 +528,9 @@ internal class NotificationListenerCheckInternal( parentUserContext.getString(R.string.notification_listener_reminder_notification_title) val text = parentUserContext.getString( - R.string.notification_listener_reminder_notification_content, pkgLabel) + R.string.notification_listener_reminder_notification_content, + pkgLabel + ) val (appLabel, smallIcon, color) = KotlinUtils.getSafetyCenterNotificationResources(parentUserContext) @@ -543,13 +557,17 @@ internal class NotificationListenerCheckInternal( val notificationManager = getSystemServiceSafe(parentUserContext, NotificationManager::class.java) notificationManager.notify( - componentName.flattenToString(), NOTIFICATION_LISTENER_CHECK_NOTIFICATION_ID, b.build()) + componentName.flattenToString(), + NOTIFICATION_LISTENER_CHECK_NOTIFICATION_ID, + b.build() + ) if (DEBUG) { Log.v( TAG, "Notification listener check notification shown with component=" + - "${componentName.flattenToString()}, uid=$uid, sessionId=$sessionId") + "${componentName.flattenToString()}, uid=$uid, sessionId=$sessionId" + ) } PermissionControllerStatsLog.write( @@ -557,7 +575,8 @@ internal class NotificationListenerCheckInternal( PRIVACY_SIGNAL_NOTIFICATION_INTERACTION__PRIVACY_SOURCE__NOTIFICATION_LISTENER, uid, PRIVACY_SIGNAL_NOTIFICATION_INTERACTION__ACTION__NOTIFICATION_SHOWN, - sessionId) + sessionId + ) updateLastShownNotificationTime() } @@ -571,7 +590,8 @@ internal class NotificationListenerCheckInternal( val intent = Intent( parentUserContext, - NotificationListenerCheckNotificationDeleteHandler::class.java) + NotificationListenerCheckNotificationDeleteHandler::class.java + ) .apply { putExtra(EXTRA_COMPONENT_NAME, componentName) putExtra(Constants.EXTRA_SESSION_ID, sessionId) @@ -580,7 +600,11 @@ internal class NotificationListenerCheckInternal( identifier = componentName.flattenToString() } return PendingIntent.getBroadcast( - context, 0, intent, FLAG_ONE_SHOT or FLAG_UPDATE_CURRENT or FLAG_IMMUTABLE) + context, + 0, + intent, + FLAG_ONE_SHOT or FLAG_UPDATE_CURRENT or FLAG_IMMUTABLE + ) } /** @return [PendingIntent] to safety center */ @@ -595,7 +619,8 @@ internal class NotificationListenerCheckInternal( putExtra(EXTRA_SAFETY_SOURCE_ID, SC_NLS_SOURCE_ID) putExtra( EXTRA_SAFETY_SOURCE_ISSUE_ID, - getSafetySourceIssueIdFromComponentName(componentName)) + getSafetySourceIssueIdFromComponentName(componentName) + ) putExtra(EXTRA_COMPONENT_NAME, componentName) putExtra(Constants.EXTRA_SESSION_ID, sessionId) putExtra(Intent.EXTRA_UID, uid) @@ -603,7 +628,11 @@ internal class NotificationListenerCheckInternal( identifier = componentName.flattenToString() } return PendingIntent.getActivity( - context, 0, intent, FLAG_ONE_SHOT or FLAG_UPDATE_CURRENT or FLAG_IMMUTABLE) + context, + 0, + intent, + FLAG_ONE_SHOT or FLAG_UPDATE_CURRENT or FLAG_IMMUTABLE + ) } /** @@ -684,7 +713,7 @@ internal class NotificationListenerCheckInternal( /** * @param componentName enabled [NotificationListenerService] * @return safety source issue, shown as the warning card in safety center. Null if unable to - * create safety source issue + * create safety source issue */ @VisibleForTesting fun createSafetySourceIssue(componentName: ComponentName, sessionId: Long): SafetySourceIssue? { @@ -703,30 +732,45 @@ internal class NotificationListenerCheckInternal( val disableNlsPendingIntent = getDisableNlsPendingIntent( - parentUserContext, safetySourceIssueId, componentName, uid, sessionId) + parentUserContext, + safetySourceIssueId, + componentName, + uid, + sessionId + ) val disableNlsAction = SafetySourceIssue.Action.Builder( SC_NLS_DISABLE_ACTION_ID, parentUserContext.getString( - R.string.notification_listener_remove_access_button_label), - disableNlsPendingIntent) + R.string.notification_listener_remove_access_button_label + ), + disableNlsPendingIntent + ) .setWillResolve(true) .setSuccessMessage( parentUserContext.getString( - R.string.notification_listener_remove_access_success_label)) + R.string.notification_listener_remove_access_success_label + ) + ) .build() val notificationListenerDetailSettingsPendingIntent = getNotificationListenerDetailSettingsPendingIntent( - parentUserContext, componentName, uid, sessionId) + parentUserContext, + componentName, + uid, + sessionId + ) val showNotificationListenerSettingsAction = SafetySourceIssue.Action.Builder( SC_SHOW_NLS_SETTINGS_ACTION_ID, parentUserContext.getString( - R.string.notification_listener_review_app_button_label), - notificationListenerDetailSettingsPendingIntent) + R.string.notification_listener_review_app_button_label + ), + notificationListenerDetailSettingsPendingIntent + ) .build() val actionCardDismissPendingIntent = @@ -742,7 +786,8 @@ internal class NotificationListenerCheckInternal( title, summary, SafetySourceData.SEVERITY_LEVEL_INFORMATION, - SC_NLS_ISSUE_TYPE_ID) + SC_NLS_ISSUE_TYPE_ID + ) .setSubtitle(pkgLabel) .addAction(disableNlsAction) .addAction(showNotificationListenerSettingsAction) @@ -784,7 +829,9 @@ internal class NotificationListenerCheckInternal( flags = FLAG_ACTIVITY_NEW_TASK identifier = componentName.flattenToString() putExtra( - EXTRA_NOTIFICATION_LISTENER_COMPONENT_NAME, componentName.flattenToString()) + EXTRA_NOTIFICATION_LISTENER_COMPONENT_NAME, + componentName.flattenToString() + ) putExtra(Constants.EXTRA_SESSION_ID, sessionId) putExtra(Intent.EXTRA_UID, uid) putExtra(Constants.EXTRA_IS_FROM_SLICE, true) @@ -843,14 +890,14 @@ class NotificationListenerCheckJobService : JobService() { val job = addNotificationListenerNotificationIfNeededJob return@BooleanSupplier job?.isCancelled ?: false } - }) + } + ) } /** * Starts an asynchronous check if a notification listener notification should be shown. * * @param params Not used other than for interacting with job scheduling - * * @return `false` if another check is already running, or if SDK Check fails (below T) */ override fun onStartJob(params: JobParameters): Boolean { @@ -869,7 +916,9 @@ class NotificationListenerCheckJobService : JobService() { GlobalScope.launch(Default) { notificationListenerCheckInternal ?.getEnabledNotificationListenersAndNotifyIfNeeded( - params, this@NotificationListenerCheckJobService) + params, + this@NotificationListenerCheckJobService + ) ?: jobFinished(params, true) } } @@ -880,7 +929,6 @@ class NotificationListenerCheckJobService : JobService() { * Abort the check if still running. * * @param params ignored - * * @return false */ override fun onStopJob(params: JobParameters): Boolean { @@ -922,13 +970,16 @@ class SetupPeriodicNotificationListenerCheck : BroadcastReceiver() { val job = JobInfo.Builder( PERIODIC_NOTIFICATION_LISTENER_CHECK_JOB_ID, - ComponentName(context, NotificationListenerCheckJobService::class.java)) + ComponentName(context, NotificationListenerCheckJobService::class.java) + ) .setPeriodic(getPeriodicCheckIntervalMillis(), getFlexForPeriodicCheckMillis()) .build() val scheduleResult = jobScheduler.schedule(job) if (scheduleResult != JobScheduler.RESULT_SUCCESS) { Log.e( - TAG, "Could not schedule periodic notification listener check $scheduleResult") + TAG, + "Could not schedule periodic notification listener check $scheduleResult" + ) } else if (DEBUG) { Log.i(TAG, "Scheduled periodic notification listener check") } @@ -957,14 +1008,16 @@ class NotificationListenerCheckNotificationDeleteHandler : BroadcastReceiver() { Log.v( TAG, "Notification listener check notification declined with component=" + - "${componentName.flattenToString()} , uid=$uid, sessionId=$sessionId") + "${componentName.flattenToString()} , uid=$uid, sessionId=$sessionId" + ) } PermissionControllerStatsLog.write( PRIVACY_SIGNAL_NOTIFICATION_INTERACTION, PRIVACY_SIGNAL_NOTIFICATION_INTERACTION__PRIVACY_SOURCE__NOTIFICATION_LISTENER, uid, PRIVACY_SIGNAL_NOTIFICATION_INTERACTION__ACTION__DISMISSED, - sessionId) + sessionId + ) } } @@ -984,7 +1037,8 @@ class DisableNotificationListenerComponentHandler : BroadcastReceiver() { Log.v( TAG, "DisableComponentHandler: disabling $componentName," + - "uid=$uid, sessionId=$sessionId") + "uid=$uid, sessionId=$sessionId" + ) } val safetyEventBuilder = @@ -993,7 +1047,10 @@ class DisableNotificationListenerComponentHandler : BroadcastReceiver() { getSystemServiceSafe(context, NotificationManager::class.java) disallowNlsLock.withLock { notificationManager.setNotificationListenerAccessGranted( - componentName, /* granted= */ false, /* userSet= */ true) + componentName, + /* granted= */ false, + /* userSet= */ true + ) } SafetyEvent.Builder(SafetyEvent.SAFETY_EVENT_TYPE_RESOLVING_ACTION_SUCCEEDED) @@ -1019,7 +1076,8 @@ class DisableNotificationListenerComponentHandler : BroadcastReceiver() { PRIVACY_SIGNAL_ISSUE_CARD_INTERACTION__PRIVACY_SOURCE__NOTIFICATION_LISTENER, uid, PRIVACY_SIGNAL_ISSUE_CARD_INTERACTION__ACTION__CLICKED_CTA1, - sessionId) + sessionId + ) } } @@ -1044,7 +1102,8 @@ class NotificationListenerActionCardDismissalReceiver : BroadcastReceiver() { Log.v( TAG, "ActionCardDismissalReceiver: $componentName dismissed," + - "uid=$uid, sessionId=$sessionId") + "uid=$uid, sessionId=$sessionId" + ) } NotificationListenerCheckInternal(context, null).run { removeNotificationsForComponent(componentName) @@ -1056,7 +1115,8 @@ class NotificationListenerActionCardDismissalReceiver : BroadcastReceiver() { PRIVACY_SIGNAL_ISSUE_CARD_INTERACTION__PRIVACY_SOURCE__NOTIFICATION_LISTENER, uid, PRIVACY_SIGNAL_ISSUE_CARD_INTERACTION__ACTION__CARD_DISMISSED, - sessionId) + sessionId + ) } } @@ -1068,8 +1128,10 @@ class NotificationListenerActionCardDismissalReceiver : BroadcastReceiver() { class NotificationListenerPackageResetHandler : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { val action = intent.action - if (action != Intent.ACTION_PACKAGE_DATA_CLEARED && - action != Intent.ACTION_PACKAGE_FULLY_REMOVED) { + if ( + action != Intent.ACTION_PACKAGE_DATA_CLEARED && + action != Intent.ACTION_PACKAGE_FULLY_REMOVED + ) { return } diff --git a/PermissionController/src/com/android/permissioncontroller/privacysources/PrivacySourceData.kt b/PermissionController/src/com/android/permissioncontroller/privacysources/PrivacySourceData.kt index f5903dfc2..4abd7129c 100644 --- a/PermissionController/src/com/android/permissioncontroller/privacysources/PrivacySourceData.kt +++ b/PermissionController/src/com/android/permissioncontroller/privacysources/PrivacySourceData.kt @@ -23,4 +23,4 @@ interface PrivacySourceData { interface Creator<T> { fun fromStorageData(data: String): T } -}
\ No newline at end of file +} diff --git a/PermissionController/src/com/android/permissioncontroller/privacysources/PrivacySourceStorageRepository.kt b/PermissionController/src/com/android/permissioncontroller/privacysources/PrivacySourceStorageRepository.kt index 1eee90e31..621a7219d 100644 --- a/PermissionController/src/com/android/permissioncontroller/privacysources/PrivacySourceStorageRepository.kt +++ b/PermissionController/src/com/android/permissioncontroller/privacysources/PrivacySourceStorageRepository.kt @@ -21,4 +21,4 @@ interface PrivacySourceStorageRepository { fun persistData(dataList: List<PrivacySourceData>) fun <T> readData(creator: PrivacySourceData.Creator<T>): List<T> -}
\ No newline at end of file +} diff --git a/PermissionController/src/com/android/permissioncontroller/privacysources/TextStorageRepository.kt b/PermissionController/src/com/android/permissioncontroller/privacysources/TextStorageRepository.kt index 8a8711bb6..a2e5b1376 100644 --- a/PermissionController/src/com/android/permissioncontroller/privacysources/TextStorageRepository.kt +++ b/PermissionController/src/com/android/permissioncontroller/privacysources/TextStorageRepository.kt @@ -40,14 +40,16 @@ class TextStorageRepository(private val file: File) : PrivacySourceStorageReposi override fun <T> readData(creator: PrivacySourceData.Creator<T>): List<T> { try { BufferedReader(FileReader(file)).useLines { lines -> - return lines.mapNotNull { - try { - creator.fromStorageData(it) - } catch (ex: Exception) { - Log.e(LOG_TAG, "corrupted data : $it in file ${file.absolutePath}", ex) - null + return lines + .mapNotNull { + try { + creator.fromStorageData(it) + } catch (ex: Exception) { + Log.e(LOG_TAG, "corrupted data : $it in file ${file.absolutePath}", ex) + null + } } - }.toList() + .toList() } } catch (ignored: FileNotFoundException) { Log.e(LOG_TAG, "Could not find file ${file.absolutePath}") @@ -66,4 +68,4 @@ class TextStorageRepository(private val file: File) : PrivacySourceStorageReposi } } } -}
\ No newline at end of file +} diff --git a/PermissionController/src/com/android/permissioncontroller/privacysources/WorkPolicyInfo.kt b/PermissionController/src/com/android/permissioncontroller/privacysources/WorkPolicyInfo.kt index 2ab6d37f0..bf9f92398 100644 --- a/PermissionController/src/com/android/permissioncontroller/privacysources/WorkPolicyInfo.kt +++ b/PermissionController/src/com/android/permissioncontroller/privacysources/WorkPolicyInfo.kt @@ -74,7 +74,10 @@ class WorkPolicyInfo(private val workPolicyUtils: WorkPolicyUtils) : PrivacySour val safetySourceData: SafetySourceData? = createSafetySourceDataForWorkPolicy(context) safetyCenterManager.setSafetySourceData( - WORK_POLICY_INFO_SOURCE_ID, safetySourceData, safetyEvent) + WORK_POLICY_INFO_SOURCE_ID, + safetySourceData, + safetyEvent + ) } private fun createSafetySourceDataForWorkPolicy(context: Context): SafetySourceData? { @@ -84,16 +87,25 @@ class WorkPolicyInfo(private val workPolicyUtils: WorkPolicyUtils) : PrivacySour when { deviceOwnerIntent != null -> { PendingIntent.getActivity( - context, 0, deviceOwnerIntent, PendingIntent.FLAG_IMMUTABLE) + context, + 0, + deviceOwnerIntent, + PendingIntent.FLAG_IMMUTABLE + ) } profileOwnerIntent != null -> { val managedProfileContext = context.createPackageContextAsUser( context.packageName, 0, - UserHandle.of(workPolicyUtils.managedProfileUserId)) + UserHandle.of(workPolicyUtils.managedProfileUserId) + ) PendingIntent.getActivity( - managedProfileContext, 0, profileOwnerIntent, PendingIntent.FLAG_IMMUTABLE) + managedProfileContext, + 0, + profileOwnerIntent, + PendingIntent.FLAG_IMMUTABLE + ) } else -> null } @@ -102,10 +114,17 @@ class WorkPolicyInfo(private val workPolicyUtils: WorkPolicyUtils) : PrivacySour val safetySourceStatus: SafetySourceStatus = SafetySourceStatus.Builder( Utils.getEnterpriseString( - context, WORK_POLICY_TITLE, R.string.work_policy_title), + context, + WORK_POLICY_TITLE, + R.string.work_policy_title + ), Utils.getEnterpriseString( - context, WORK_POLICY_SUMMARY, R.string.work_policy_summary), - SafetySourceData.SEVERITY_LEVEL_UNSPECIFIED) + context, + WORK_POLICY_SUMMARY, + R.string.work_policy_summary + ), + SafetySourceData.SEVERITY_LEVEL_UNSPECIFIED + ) .setPendingIntent(pendingIntent) .build() @@ -120,7 +139,8 @@ class WorkPolicyInfo(private val workPolicyUtils: WorkPolicyUtils) : PrivacySour RefreshEvent.EVENT_REFRESH_REQUESTED -> { val refreshBroadcastId = intent.getStringExtra( - SafetyCenterManager.EXTRA_REFRESH_SAFETY_SOURCES_BROADCAST_ID) + SafetyCenterManager.EXTRA_REFRESH_SAFETY_SOURCES_BROADCAST_ID + ) SafetyEvent.Builder(SafetyEvent.SAFETY_EVENT_TYPE_REFRESH_REQUESTED) .setRefreshBroadcastId(refreshBroadcastId) .build() diff --git a/PermissionController/src/com/android/permissioncontroller/privacysources/v34/AppDataSharingUpdatesPrivacySource.kt b/PermissionController/src/com/android/permissioncontroller/privacysources/v34/AppDataSharingUpdatesPrivacySource.kt index 8bedc3979..ec86a49a4 100644 --- a/PermissionController/src/com/android/permissioncontroller/privacysources/v34/AppDataSharingUpdatesPrivacySource.kt +++ b/PermissionController/src/com/android/permissioncontroller/privacysources/v34/AppDataSharingUpdatesPrivacySource.kt @@ -71,13 +71,16 @@ class AppDataSharingUpdatesPrivacySource : PrivacySource { SafetySourceStatus.Builder( context.getString(R.string.data_sharing_updates_title), context.getString(R.string.data_sharing_updates_summary), - SEVERITY_LEVEL_INFORMATION) + SEVERITY_LEVEL_INFORMATION + ) .setPendingIntent( PendingIntent.getActivity( context, /* requestCode= */ 0, Intent(ACTION_REVIEW_APP_DATA_SHARING_UPDATES), - FLAG_UPDATE_CURRENT or FLAG_IMMUTABLE)) + FLAG_UPDATE_CURRENT or FLAG_IMMUTABLE + ) + ) .build(), ) .build() @@ -88,7 +91,8 @@ class AppDataSharingUpdatesPrivacySource : PrivacySource { safetyCenterManager.setSafetySourceData( APP_DATA_SHARING_UPDATES_SOURCE_ID, safetySourceData, - createSafetyEventForDataSharingUpdates(refreshEvent, intent)) + createSafetyEventForDataSharingUpdates(refreshEvent, intent) + ) } private fun createSafetyEventForDataSharingUpdates( @@ -99,7 +103,8 @@ class AppDataSharingUpdatesPrivacySource : PrivacySource { EVENT_REFRESH_REQUESTED -> { val refreshBroadcastId = intent.getStringExtra( - SafetyCenterManager.EXTRA_REFRESH_SAFETY_SOURCES_BROADCAST_ID) + SafetyCenterManager.EXTRA_REFRESH_SAFETY_SOURCES_BROADCAST_ID + ) SafetyEvent.Builder(SAFETY_EVENT_TYPE_REFRESH_REQUESTED) .setRefreshBroadcastId(refreshBroadcastId) .build() diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/CollapsableGroupCardHelper.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/CollapsableGroupCardHelper.kt index cdc5cc1f3..20ac4c7a5 100644 --- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/CollapsableGroupCardHelper.kt +++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/CollapsableGroupCardHelper.kt @@ -27,7 +27,7 @@ internal class CollapsableGroupCardHelper { private companion object { private const val EXPANDED_ENTRY_GROUPS_SAVED_INSTANCE_STATE_KEY = - "expanded_entry_groups_saved_instance_state_key" + "expanded_entry_groups_saved_instance_state_key" } fun restoreState(state: Bundle?) { @@ -39,8 +39,8 @@ internal class CollapsableGroupCardHelper { fun saveState(outState: Bundle) { outState.putCharSequenceArray( - EXPANDED_ENTRY_GROUPS_SAVED_INSTANCE_STATE_KEY, - expandedGroups.toTypedArray() + EXPANDED_ENTRY_GROUPS_SAVED_INSTANCE_STATE_KEY, + expandedGroups.toTypedArray() ) } @@ -52,6 +52,5 @@ internal class CollapsableGroupCardHelper { expandedGroups.add(groupId) } - fun isGroupExpanded(groupId: CharSequence): Boolean = - expandedGroups.contains(groupId) + fun isGroupExpanded(groupId: CharSequence): Boolean = expandedGroups.contains(groupId) } diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/ComparablePreference.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/ComparablePreference.kt index 1950a0976..d5c7c3738 100644 --- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/ComparablePreference.kt +++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/ComparablePreference.kt @@ -17,14 +17,15 @@ package com.android.permissioncontroller.safetycenter.ui import androidx.preference.Preference -/** Allows comparison with a [Preference] to determine if it has been changed. +/** + * Allows comparison with a [Preference] to determine if it has been changed. * * @see SafetyPreferenceComparisonCallback */ internal interface ComparablePreference { - /** Returns true if given Preference represents an item of the same kind. */ + /** Returns true if given Preference represents an item of the same kind. */ fun isSameItem(preference: Preference): Boolean - /** Returns true if given Preference contains the same data. */ + /** Returns true if given Preference contains the same data. */ fun hasSameContents(preference: Preference): Boolean } diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/ComparablePreferenceCategory.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/ComparablePreferenceCategory.kt index 55d27dbb3..7a9d164cf 100644 --- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/ComparablePreferenceCategory.kt +++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/ComparablePreferenceCategory.kt @@ -25,13 +25,13 @@ import androidx.preference.PreferenceCategory import com.android.permissioncontroller.R /** A {@link PreferenceCategory} that implements {@link ComparablePreference} interface. */ -internal class ComparablePreferenceCategory @JvmOverloads constructor( +internal class ComparablePreferenceCategory +@JvmOverloads +constructor( context: Context, attrs: AttributeSet? = null, - defStyleAttr: Int = getAttr( - context, - R.attr.preferenceCategoryStyle, - android.R.attr.preferenceCategoryStyle), + defStyleAttr: Int = + getAttr(context, R.attr.preferenceCategoryStyle, android.R.attr.preferenceCategoryStyle), defStyleRes: Int = 0 ) : PreferenceCategory(context, attrs, defStyleAttr, defStyleRes), ComparablePreference { @@ -47,8 +47,8 @@ internal class ComparablePreferenceCategory @JvmOverloads constructor( } override fun isSameItem(preference: Preference): Boolean = - preference is ComparablePreferenceCategory && TextUtils.equals(key, preference.key) + preference is ComparablePreferenceCategory && TextUtils.equals(key, preference.key) override fun hasSameContents(preference: Preference): Boolean = - preference is ComparablePreferenceCategory && TextUtils.equals(title, preference.title) + preference is ComparablePreferenceCategory && TextUtils.equals(title, preference.title) } diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/EntriesTopPaddingPreference.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/EntriesTopPaddingPreference.kt new file mode 100644 index 000000000..077ccfa2d --- /dev/null +++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/EntriesTopPaddingPreference.kt @@ -0,0 +1,13 @@ +package com.android.permissioncontroller.safetycenter.ui + +import android.content.Context +import android.util.AttributeSet +import androidx.preference.Preference +import com.android.permissioncontroller.R + +class EntriesTopPaddingPreference(context: Context, attrs: AttributeSet) : + Preference(context, attrs) { + init { + layoutResource = R.layout.preference_entries_top_padding + } +} diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/EqualWidthContainer.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/EqualWidthContainer.kt index 6f56874eb..1bdc0dc39 100644 --- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/EqualWidthContainer.kt +++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/EqualWidthContainer.kt @@ -43,7 +43,8 @@ class EqualWidthContainer @JvmOverloads constructor(context: Context, attrs: Att nonSpaceItems.forEach { it.measure( MeasureSpec.makeMeasureSpec(neededWidthPerNonSpaceItem, EXACTLY), - MeasureSpec.makeMeasureSpec(it.measuredHeight, EXACTLY)) + MeasureSpec.makeMeasureSpec(it.measuredHeight, EXACTLY) + ) } } } diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/InteractionLogger.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/InteractionLogger.kt index 58bec87b8..51f550944 100644 --- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/InteractionLogger.kt +++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/InteractionLogger.kt @@ -43,10 +43,7 @@ import java.math.BigInteger import java.security.MessageDigest @RequiresApi(Build.VERSION_CODES.TIRAMISU) -class InteractionLogger -private constructor( - private val noLogSourceIds: Set<String?> -) { +class InteractionLogger private constructor(private val noLogSourceIds: Set<String?>) { var sessionId: Long = Constants.INVALID_SESSION_ID var viewType: ViewType = ViewType.UNKNOWN var navigationSource: NavigationSource = NavigationSource.UNKNOWN diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/MoreIssuesCardAnimator.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/MoreIssuesCardAnimator.kt index 55293f775..9121c76c2 100644 --- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/MoreIssuesCardAnimator.kt +++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/MoreIssuesCardAnimator.kt @@ -46,16 +46,19 @@ class MoreIssuesCardAnimator { super.onAnimationEnd(drawable) statusIcon.setImageResource(endSeverityLevelResId) } - }) + } + ) setStatusIconDrawable.start() } } fun cancelStatusAnimation(statusIcon: ImageView) { val statusDrawable: Drawable? = statusIcon.drawable - if (statusDrawable != null && - statusDrawable is AnimatedVectorDrawable && - statusDrawable.isRunning) { + if ( + statusDrawable != null && + statusDrawable is AnimatedVectorDrawable && + statusDrawable.isRunning + ) { statusDrawable.clearAnimationCallbacks() statusDrawable.stop() } @@ -106,7 +109,10 @@ class MoreIssuesCardAnimator { Log.e( MoreIssuesCardPreference.TAG, String.format( - "Unexpected SafetyCenterIssue.IssueSeverityLevel: %d", endSeverityLevel)) + "Unexpected SafetyCenterIssue.IssueSeverityLevel: %d", + endSeverityLevel + ) + ) R.drawable.ic_safety_null_state } } diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/ParsedSafetyCenterIntent.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/ParsedSafetyCenterIntent.kt index 6bf4b52e8..377c8d5f5 100644 --- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/ParsedSafetyCenterIntent.kt +++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/ParsedSafetyCenterIntent.kt @@ -41,7 +41,10 @@ data class ParsedSafetyCenterIntent( getParcelableExtra(EXTRA_SAFETY_SOURCE_USER_HANDLE, UserHandle::class.java) val safetyCenterIssueKey: SafetyCenterIssueKey? = createSafetyCenterIssueKey( - safetySourceId, safetySourceIssueId, safetySourceUserHandle) + safetySourceId, + safetySourceIssueId, + safetySourceUserHandle + ) // Check if we've navigated from QS or if focusing on single issue and issues should be // expanded @@ -56,8 +59,8 @@ data class ParsedSafetyCenterIntent( * * @param safetySourceId source ID for a {@link SafetySourceIssue} * @param safetySourceIssueId an issue ID for a {@link SafetySourceIssue} - * @param safetySourceUserHandle the specific a {@link android.os.UserHandle} associated with - * issue + * @param safetySourceUserHandle the specific a {@link android.os.UserHandle} associated + * with issue */ private fun createSafetyCenterIssueKey( safetySourceId: String?, diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/PositionInCardList.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/PositionInCardList.kt index 01e8f8d15..b1c48befd 100644 --- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/PositionInCardList.kt +++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/PositionInCardList.kt @@ -51,10 +51,12 @@ internal enum class PositionInCardList(val backgroundDrawableResId: Int) { fun getTopMargin(context: Context): Int = when (this) { - CARD_START, CARD_START_END, CARD_START_LIST_END -> - context.resources.getDimensionPixelSize(R.dimen.sc_card_margin) - LIST_START, LIST_START_CARD_END, LIST_START_END -> - context.resources.getDimensionPixelSize(R.dimen.sc_list_margin_top) + CARD_START, + CARD_START_END, + CARD_START_LIST_END -> context.resources.getDimensionPixelSize(R.dimen.sc_card_margin) + LIST_START, + LIST_START_CARD_END, + LIST_START_END -> context.resources.getDimensionPixelSize(R.dimen.sc_list_margin_top) else -> 0 } diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/PrivacySubpageFragment.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/PrivacySubpageFragment.kt index 54ba9560f..9fb17ce03 100644 --- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/PrivacySubpageFragment.kt +++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/PrivacySubpageFragment.kt @@ -114,7 +114,8 @@ class PrivacySubpageFragment : SafetyCenterFragment() { subpageIssues, subpageDismissedIssues, uiData.resolvedIssues, - requireActivity().getTaskId()) + requireActivity().getTaskId() + ) } private fun updateSafetyCenterEntries(entryGroup: SafetyCenterEntryGroup) { @@ -130,9 +131,13 @@ class PrivacySubpageFragment : SafetyCenterFragment() { SafetySubpageEntryPreference( requireContext(), PendingIntentSender.getTaskIdForEntry( - entryId, sameTaskSourceIds, requireActivity()), + entryId, + sameTaskSourceIds, + requireActivity() + ), entry, - safetyCenterViewModel) + safetyCenterViewModel + ) if (sourceId == "AndroidPrivacyControls") { // No action required here because the privacy controls are rendered separately @@ -149,7 +154,11 @@ class PrivacySubpageFragment : SafetyCenterFragment() { fun setSwitchPreference(prefType: Pref) { val switchPreference: ClickableDisabledSwitchPreference? = findPreference(prefType.key) switchPreference?.setupState( - prefStates[prefType], prefType, privacyControlsViewModel, this) + prefStates[prefType], + prefType, + privacyControlsViewModel, + this + ) } setSwitchPreference(Pref.MIC) diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterDashboardFragment.java b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterDashboardFragment.java index 57462c20e..f6d653c99 100644 --- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterDashboardFragment.java +++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterDashboardFragment.java @@ -117,6 +117,7 @@ public final class SafetyCenterDashboardFragment extends SafetyCenterFragment { mIssuesGroup = getPreferenceScreen().findPreference(ISSUES_GROUP_KEY); mEntriesGroup = getPreferenceScreen().findPreference(ENTRIES_GROUP_KEY); mStaticEntriesGroup = getPreferenceScreen().findPreference(STATIC_ENTRIES_GROUP_KEY); + if (mIsQuickSettingsFragment) { getPreferenceScreen().removePreference(mEntriesGroup); mEntriesGroup = null; @@ -142,6 +143,7 @@ public final class SafetyCenterDashboardFragment extends SafetyCenterFragment { } return recyclerView; } + // Set the default divider line between preferences to be transparent @Override public void setDivider(Drawable divider) { diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterTouchTarget.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterTouchTarget.kt index 0e368291e..01d23241f 100644 --- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterTouchTarget.kt +++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterTouchTarget.kt @@ -28,6 +28,7 @@ import androidx.annotation.RequiresApi object SafetyCenterTouchTarget { /** * Resizes the touch target of views by delegating to the parent component. + * * @param view component that will be expanded * @param minTouchTargetSizeResource required minimum touch target size */ diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyStatusPreference.java b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyStatusPreference.java index 0b8706a38..ad19d9a22 100644 --- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyStatusPreference.java +++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyStatusPreference.java @@ -27,6 +27,7 @@ import android.os.Looper; import android.safetycenter.SafetyCenterStatus; import android.text.TextUtils; import android.util.AttributeSet; +import android.util.Log; import android.widget.ImageView; import android.widget.TextView; @@ -49,6 +50,8 @@ import java.util.Objects; @RequiresApi(TIRAMISU) public class SafetyStatusPreference extends Preference implements ComparablePreference { + private static final String TAG = "SafetyStatusPreference"; + @Nullable private StatusUiData mStatus; @Nullable private SafetyCenterViewModel mViewModel; @@ -72,6 +75,7 @@ public class SafetyStatusPreference extends Preference implements ComparablePref @Override public void onBindViewHolder(PreferenceViewHolder holder) { super.onBindViewHolder(holder); + Log.d(TAG, String.format("onBindViewHolder called for status %s", mStatus)); if (mStatus == null) { return; @@ -334,7 +338,12 @@ public class SafetyStatusPreference extends Preference implements ComparablePref } void setData(StatusUiData statusUiData) { + if (Objects.equals(mStatus, statusUiData)) { + return; + } + mStatus = statusUiData; + Log.d(TAG, String.format("setData called for status %s", mStatus)); safeNotifyChanged(); } @@ -349,7 +358,14 @@ public class SafetyStatusPreference extends Preference implements ComparablePref // Calling notifyChanged while recyclerview is scrolling or computing layout will result in an // IllegalStateException. Post to handler to wait for UI to settle. private void safeNotifyChanged() { - new Handler(Looper.getMainLooper()).post(this::notifyChanged); + new Handler(Looper.getMainLooper()) + .post( + () -> { + Log.d( + TAG, + String.format("Calling notifyChanged for status %s", mStatus)); + notifyChanged(); + }); } @Override diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SpacerPreference.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SpacerPreference.kt index bb09783be..a5356962a 100644 --- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SpacerPreference.kt +++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SpacerPreference.kt @@ -62,21 +62,24 @@ internal class SpacerPreference(context: Context, attrs: AttributeSet) : // we should ensure we won't add multiple listeners to the same view, // and Preferences API does not allow to do cleanups when onViewRecycled, // so we are keeping a track of the added listener attaching it as a tag to the View - val listener: View.OnLayoutChangeListener = spacer.tag as? View.OnLayoutChangeListener - ?: object : View.OnLayoutChangeListener { - override fun onLayoutChange( - v: View?, - left: Int, - top: Int, - right: Int, - bottom: Int, - oldLeft: Int, - oldTop: Int, - oldRight: Int, - oldBottom: Int - ) { - adjustHeight(spacer) - }}.also { spacer.tag = it } + val listener: View.OnLayoutChangeListener = + spacer.tag as? View.OnLayoutChangeListener + ?: object : View.OnLayoutChangeListener { + override fun onLayoutChange( + v: View?, + left: Int, + top: Int, + right: Int, + bottom: Int, + oldLeft: Int, + oldTop: Int, + oldRight: Int, + oldBottom: Int + ) { + adjustHeight(spacer) + } + } + .also { spacer.tag = it } spacer.removeOnLayoutChangeListener(listener) spacer.addOnLayoutChangeListener(listener) @@ -96,12 +99,13 @@ internal class SpacerPreference(context: Context, attrs: AttributeSet) : // differently due to the auto-scroll to highlight a specific item, // and in this case we need to wait the content parent to be measured if (contentParent.height == 0) { - val globalLayoutObserver = object : ViewTreeObserver.OnGlobalLayoutListener { - override fun onGlobalLayout() { - contentParent.viewTreeObserver.removeOnGlobalLayoutListener(this) - adjustHeight(spacer) + val globalLayoutObserver = + object : ViewTreeObserver.OnGlobalLayoutListener { + override fun onGlobalLayout() { + contentParent.viewTreeObserver.removeOnGlobalLayoutListener(this) + adjustHeight(spacer) + } } - } contentParent.viewTreeObserver.addOnGlobalLayoutListener(globalLayoutObserver) return } @@ -110,13 +114,14 @@ internal class SpacerPreference(context: Context, attrs: AttributeSet) : maxKnownToolbarHeight = max(maxKnownToolbarHeight, collapsingToolbar.height) val contentHeight = spacer.top + maxKnownToolbarHeight - val desiredSpacerHeight = if (contentHeight > contentParent.height) { - // making it 0 height will remove if from recyclerview - 1 - } else { - // to unlock the scrolling we need spacer to go slightly beyond the screen - contentParent.height - contentHeight + 1 - } + val desiredSpacerHeight = + if (contentHeight > contentParent.height) { + // making it 0 height will remove if from recyclerview + 1 + } else { + // to unlock the scrolling we need spacer to go slightly beyond the screen + contentParent.height - contentHeight + 1 + } val layoutParams = spacer.layoutParams if (layoutParams.height != desiredSpacerHeight) { diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/StatusAnimationResolver.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/StatusAnimationResolver.kt index 55288564c..8409ca75d 100644 --- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/StatusAnimationResolver.kt +++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/StatusAnimationResolver.kt @@ -129,21 +129,24 @@ object StatusAnimationResolver { @JvmStatic fun getStatusChangeAnimation(fromSeverity: Int, toSeverity: Int): Int = - if (fromSeverity == toSeverity && - fromSeverity != SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_OK) { + if ( + fromSeverity == toSeverity && + fromSeverity != SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_OK + ) { 0 - } else when (fromSeverity) { - SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_OK -> - R.drawable.safety_status_info_to_info_anim - SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_RECOMMENDATION -> - R.drawable.safety_status_recommend_to_info_anim - SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_CRITICAL_WARNING -> { - if (toSeverity == SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_OK) { - R.drawable.safety_status_warn_to_info_anim - } else { - R.drawable.safety_status_warn_to_recommend_anim + } else + when (fromSeverity) { + SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_OK -> + R.drawable.safety_status_info_to_info_anim + SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_RECOMMENDATION -> + R.drawable.safety_status_recommend_to_info_anim + SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_CRITICAL_WARNING -> { + if (toSeverity == SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_OK) { + R.drawable.safety_status_warn_to_info_anim + } else { + R.drawable.safety_status_warn_to_recommend_anim + } } + else -> 0 } - else -> 0 - } } diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/TextFadeAnimator.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/TextFadeAnimator.kt index a98fcf2ad..656c3800a 100644 --- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/TextFadeAnimator.kt +++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/TextFadeAnimator.kt @@ -78,7 +78,8 @@ constructor(targetIds: List<Int>, changeDuration: Duration = DEFAULT_TEXT_CHANGE super.onTransitionEnd(transition) fadeTextIn(textChanges, parentViewGroup, onFinish) } - }) + } + ) parentViewGroup.post { TransitionManager.beginDelayedTransition(parentViewGroup, fadeOutTransition) for ((textView, _) in textChanges) { @@ -101,7 +102,8 @@ constructor(targetIds: List<Int>, changeDuration: Duration = DEFAULT_TEXT_CHANGE super.onTransitionEnd(transition) onFinish?.run() } - }) + } + ) parent.post { TransitionManager.beginDelayedTransition(parent, fadeInTransition) diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/SafetyCenterQsViewModel.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/SafetyCenterQsViewModel.kt index 73c7da99f..b1ac62874 100644 --- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/SafetyCenterQsViewModel.kt +++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/SafetyCenterQsViewModel.kt @@ -83,7 +83,10 @@ class SafetyCenterQsViewModel( val userHandle = UserHandle.getUserHandleForUid(permGroupUsage.uid) val lightAppPermissionGroupUsageKey = LightAppPermissionGroupUsageKey( - packageName, permissionGroupName, userHandle) + packageName, + permissionGroupName, + userHandle + ) val appPermGroupLiveData: LightAppPermGroupLiveData = LightAppPermGroupLiveData[ Triple(packageName, permissionGroupName, userHandle)] @@ -110,7 +113,8 @@ class SafetyCenterQsViewModel( LightAppPermissionGroupUsageKey( usage.packageName, usage.permissionGroupName, - UserHandle.getUserHandleForUid(usage.uid))] + UserHandle.getUserHandleForUid(usage.uid) + )] ?: return false return group.supportsRuntimePerms && !group.hasInstallToRuntimeSplit && @@ -125,7 +129,8 @@ class SafetyCenterQsViewModel( LightAppPermissionGroupUsageKey( usage.packageName, usage.permissionGroupName, - UserHandle.getUserHandleForUid(usage.uid))] + UserHandle.getUserHandleForUid(usage.uid) + )] ?: return KotlinUtils.revokeForegroundRuntimePermissions(app, group) @@ -177,13 +182,16 @@ class SafetyCenterQsViewModel( getSensorState( Sensors.CAMERA, UserManager.DISALLOW_CAMERA_TOGGLE, - configCameraToggleEnabled), + configCameraToggleEnabled + ), MICROPHONE to getSensorState( Sensors.MICROPHONE, UserManager.DISALLOW_MICROPHONE_TOGGLE, - configMicToggleEnabled), - LOCATION to SensorState(true, locationEnabled, locationEnforcedAdmin)) + configMicToggleEnabled + ), + LOCATION to SensorState(true, locationEnabled, locationEnforcedAdmin) + ) } @Suppress("OVERRIDE_DEPRECATION") @@ -221,13 +229,14 @@ class SafetyCenterQsViewModel( return SensorState( sensorConfigEnabled && sensorPrivacyManager.supportsSensorToggle(sensor), !sensorPrivacyManager.isSensorPrivacyEnabled(TOGGLE_TYPE_SOFTWARE, sensor), - getEnforcedAdmin(restriction)) + getEnforcedAdmin(restriction) + ) } private fun getEnforcedAdmin(restriction: String) = - if (userManager - .getUserRestrictionSources(restriction, Process.myUserHandle()) - .isNotEmpty()) { + if ( + userManager.getUserRestrictionSources(restriction, Process.myUserHandle()).isNotEmpty() + ) { RestrictedLockUtils.getProfileOrDeviceOwner(app, Process.myUserHandle()) } else { null @@ -250,10 +259,12 @@ class SafetyCenterQsViewModel( intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) val resolveInfo: ResolveInfo? = context.packageManager.resolveActivity(intent, PackageManager.ResolveInfoFlags.of(0)) - if (resolveInfo != null && - resolveInfo.activityInfo != null && - resolveInfo.activityInfo.permission == - android.Manifest.permission.START_VIEW_PERMISSION_USAGE) { + if ( + resolveInfo != null && + resolveInfo.activityInfo != null && + resolveInfo.activityInfo.permission == + android.Manifest.permission.START_VIEW_PERMISSION_USAGE + ) { intent.component = ComponentName(usage.packageName, resolveInfo.activityInfo.name) return intent } diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/view/SafetyEntryGroupView.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/view/SafetyEntryGroupView.kt index 7e4223298..b00b7f765 100644 --- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/view/SafetyEntryGroupView.kt +++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/view/SafetyEntryGroupView.kt @@ -51,7 +51,6 @@ constructor( ) : LinearLayout(context, attrs, defStyleAttr, defStyleRes) { private companion object { - val TAG = SafetyEntryGroupView::class.java.simpleName const val EXPAND_COLLAPSE_ANIMATION_DURATION_MS = 183L } @@ -107,8 +106,15 @@ constructor( val params = layoutParams as MarginLayoutParams if (params.topMargin != topMargin) { params.topMargin = topMargin - layoutParams = params } + + if (isLastCard) { + params.bottomMargin = context.resources.getDimensionPixelSize(R.dimen.sc_spacing_large) + } else { + params.bottomMargin = 0 + } + + layoutParams = params } private fun showGroupDetails(group: SafetyCenterEntryGroup) { diff --git a/PermissionController/src/com/android/permissioncontroller/safetylabel/AppsSafetyLabelHistory.kt b/PermissionController/src/com/android/permissioncontroller/safetylabel/AppsSafetyLabelHistory.kt index 26f327e96..69e696695 100644 --- a/PermissionController/src/com/android/permissioncontroller/safetylabel/AppsSafetyLabelHistory.kt +++ b/PermissionController/src/com/android/permissioncontroller/safetylabel/AppsSafetyLabelHistory.kt @@ -59,7 +59,8 @@ data class AppsSafetyLabelHistory(val appSafetyLabelHistories: List<AppSafetyLab .toMutableList() .apply { add(safetyLabel) } .sortedBy { it.receivedAt } - .takeLast(maxToPersist)) + .takeLast(maxToPersist) + ) } /** Data class representing the information about an app. */ @@ -90,7 +91,8 @@ data class AppsSafetyLabelHistory(val appSafetyLabelHistories: List<AppSafetyLab SafetyLabel( AppInfo(packageName), receivedAt, - DataLabel.extractLocationSharingDataLabel(appMetadataSafetyLabel.dataLabel)) + DataLabel.extractLocationSharingDataLabel(appMetadataSafetyLabel.dataLabel) + ) } } @@ -113,7 +115,8 @@ data class AppsSafetyLabelHistory(val appSafetyLabelHistories: List<AppSafetyLab .filter { it.key == DataCategoryConstants.CATEGORY_LOCATION } .mapValues { categoryEntry -> DataCategory.fromAppMetadataDataCategory(categoryEntry.value) - }) + } + ) } } @@ -134,7 +137,8 @@ data class AppsSafetyLabelHistory(val appSafetyLabelHistories: List<AppSafetyLab DataCategory( appMetadataDataCategory.dataTypes.values.any { it.purposeSet.contains(PURPOSE_ADVERTISING) - }) + } + ) } } diff --git a/PermissionController/src/com/android/permissioncontroller/safetylabel/AppsSafetyLabelHistoryPersistence.kt b/PermissionController/src/com/android/permissioncontroller/safetylabel/AppsSafetyLabelHistoryPersistence.kt index 9bb7e819f..ce6233fdf 100644 --- a/PermissionController/src/com/android/permissioncontroller/safetylabel/AppsSafetyLabelHistoryPersistence.kt +++ b/PermissionController/src/com/android/permissioncontroller/safetylabel/AppsSafetyLabelHistoryPersistence.kt @@ -90,10 +90,14 @@ object AppsSafetyLabelHistoryPersistence { Log.e(LOG_TAG, "File not found: $file") } catch (e: IOException) { Log.e( - LOG_TAG, "Failed to read file: $file, encountered exception ${e.localizedMessage}") + LOG_TAG, + "Failed to read file: $file, encountered exception ${e.localizedMessage}" + ) } catch (e: XmlPullParserException) { Log.e( - LOG_TAG, "Failed to parse file: $file, encountered exception ${e.localizedMessage}") + LOG_TAG, + "Failed to parse file: $file, encountered exception ${e.localizedMessage}" + ) } return AppsSafetyLabelHistoryFileContent(appsSafetyLabelHistory = null, INITIAL_VERSION) @@ -133,13 +137,15 @@ object AppsSafetyLabelHistoryPersistence { AppsSafetyLabelHistory( currentHistories.toMutableList().apply { add(AppSafetyLabelHistory(appInfo, listOf(safetyLabel))) - }) + } + ) } else { AppsSafetyLabelHistory( currentHistories.map { if (it.appInfo != appInfo) it else it.addSafetyLabelIfChanged(safetyLabel) - }) + } + ) } write(file, updatedAppsSafetyLabelHistory) @@ -231,7 +237,9 @@ object AppsSafetyLabelHistoryPersistence { // to startTime. The aim is retain one safety label prior to start time to // be used as the "before" safety label when determining updates. AppSafetyLabelHistory( - appHistory.appInfo, history.subList(last, history.size)) + appHistory.appInfo, + history.subList(last, history.size) + ) } } @@ -265,7 +273,10 @@ object AppsSafetyLabelHistoryPersistence { listeners.forEach { it.onSafetyLabelHistoryChanged() } } catch (e: Exception) { Log.i( - LOG_TAG, "Failed to write to $file. Previous version of file will be restored.", e) + LOG_TAG, + "Failed to write to $file. Previous version of file will be restored.", + e + ) atomicFile.failWrite(outputStream) } finally { try { @@ -284,10 +295,12 @@ object AppsSafetyLabelHistoryPersistence { return currentAppsSafetyLabelHistory.appSafetyLabelHistories.mapNotNull { val before = it.getSafetyLabelAt(startTime) val after = it.getLatestSafetyLabel() - if (before == null || - after == null || - before == after || - before.receivedAt.isAfter(after.receivedAt)) + if ( + before == null || + after == null || + before == after || + before.receivedAt.isAfter(after.receivedAt) + ) null else AppSafetyLabelDiff(before, after) } @@ -332,7 +345,8 @@ object AppsSafetyLabelHistoryPersistence { else -> throw IllegalArgumentException( "Unexpected attribute ${getAttributeName(i)} in tag" + - " $TAG_APPS_SAFETY_LABEL_HISTORY") + " $TAG_APPS_SAFETY_LABEL_HISTORY" + ) } } if (version == null) { @@ -350,7 +364,9 @@ object AppsSafetyLabelHistoryPersistence { next() return AppsSafetyLabelHistoryFileContent( - AppsSafetyLabelHistory(appSafetyLabelHistories), version) + AppsSafetyLabelHistory(appSafetyLabelHistories), + version + ) } private fun XmlPullParser.parseAppSafetyLabelHistory(): AppSafetyLabelHistory { @@ -379,7 +395,8 @@ object AppsSafetyLabelHistoryPersistence { ATTRIBUTE_RECEIVED_AT -> receivedAt = parseInstant(getAttributeValue(i)) else -> throw IllegalArgumentException( - "Unexpected attribute ${getAttributeName(i)} in tag $TAG_SAFETY_LABEL") + "Unexpected attribute ${getAttributeName(i)} in tag $TAG_SAFETY_LABEL" + ) } } if (receivedAt == null) { @@ -432,7 +449,8 @@ object AppsSafetyLabelHistoryPersistence { ATTRIBUTE_CONTAINS_ADS -> hasAds = getAttributeValue(i).toBoolean() else -> throw IllegalArgumentException( - "Unexpected attribute ${getAttributeName(i)} in tag $TAG_DATA_SHARED_ENTRY") + "Unexpected attribute ${getAttributeName(i)} in tag $TAG_DATA_SHARED_ENTRY" + ) } } if (category == null) { @@ -440,7 +458,8 @@ object AppsSafetyLabelHistoryPersistence { } if (hasAds == null) { throw IllegalArgumentException( - "Missing $ATTRIBUTE_CONTAINS_ADS in $TAG_DATA_SHARED_ENTRY") + "Missing $ATTRIBUTE_CONTAINS_ADS in $TAG_DATA_SHARED_ENTRY" + ) } nextTag() @@ -458,7 +477,8 @@ object AppsSafetyLabelHistoryPersistence { ATTRIBUTE_PACKAGE_NAME -> packageName = getAttributeValue(i) else -> throw IllegalArgumentException( - "Unexpected attribute ${getAttributeName(i)} in tag $TAG_APP_INFO") + "Unexpected attribute ${getAttributeName(i)} in tag $TAG_APP_INFO" + ) } } if (packageName == null) { @@ -540,7 +560,8 @@ object AppsSafetyLabelHistoryPersistence { attribute( null, ATTRIBUTE_CONTAINS_ADS, - dataSharedEntry.value.containsAdvertisingPurpose.toString()) + dataSharedEntry.value.containsAdvertisingPurpose.toString() + ) endTag(null, TAG_DATA_SHARED_ENTRY) } @@ -592,7 +613,10 @@ object AppsSafetyLabelHistoryPersistence { */ private fun getMaxSafetyLabelsToPersist() = DeviceConfig.getInt( - DeviceConfig.NAMESPACE_PRIVACY, PROPERTY_MAX_SAFETY_LABELS_PERSISTED_PER_APP, 20) + DeviceConfig.NAMESPACE_PRIVACY, + PROPERTY_MAX_SAFETY_LABELS_PERSISTED_PER_APP, + 20 + ) /** An interface to listen to changes to persisted safety labels. */ interface ChangeListener { diff --git a/PermissionController/src/com/android/permissioncontroller/safetylabel/SafetyLabelChangedBroadcastReceiver.kt b/PermissionController/src/com/android/permissioncontroller/safetylabel/SafetyLabelChangedBroadcastReceiver.kt index 99f4adb02..0a7375642 100644 --- a/PermissionController/src/com/android/permissioncontroller/safetylabel/SafetyLabelChangedBroadcastReceiver.kt +++ b/PermissionController/src/com/android/permissioncontroller/safetylabel/SafetyLabelChangedBroadcastReceiver.kt @@ -56,8 +56,10 @@ class SafetyLabelChangedBroadcastReceiver : BroadcastReceiver() { } val packageChangeEvent = getPackageChangeEvent(intent) - if (!(packageChangeEvent == PackageChangeEvent.NEW_INSTALL || - packageChangeEvent == PackageChangeEvent.UPDATE)) { + if ( + !(packageChangeEvent == PackageChangeEvent.NEW_INSTALL || + packageChangeEvent == PackageChangeEvent.UPDATE) + ) { return } @@ -74,7 +76,8 @@ class SafetyLabelChangedBroadcastReceiver : BroadcastReceiver() { "received broadcast packageName: $packageName, current user: $currentUser," + " packageChangeEvent: $packageChangeEvent, intent user:" + " ${intent.getParcelableExtra(Intent.EXTRA_USER, UserHandle::class.java) - ?: currentUser}") + ?: currentUser}" + ) } val userManager = Utils.getSystemServiceSafe(context, UserManager::class.java) if (userManager.isProfile) { @@ -127,7 +130,8 @@ class SafetyLabelChangedBroadcastReceiver : BroadcastReceiver() { Log.i( TAG, "writeSafetyLabel called for packageName: $packageName, currentUser:" + - " ${Process.myUserHandle()}") + " ${Process.myUserHandle()}" + ) } // Get the context for the user in which the app is installed. @@ -155,7 +159,10 @@ class SafetyLabelChangedBroadcastReceiver : BroadcastReceiver() { val safetyLabelForPersistence: SafetyLabelForPersistence = AppsSafetyLabelHistory.SafetyLabel.extractLocationSharingSafetyLabel( - packageName, Instant.ofEpochMilli(receivedAtMs), safetyLabel) + packageName, + Instant.ofEpochMilli(receivedAtMs), + safetyLabel + ) val historyFile = AppsSafetyLabelHistoryPersistence.getSafetyLabelHistoryFile(context) AppsSafetyLabelHistoryPersistence.recordSafetyLabel(safetyLabelForPersistence, historyFile) @@ -212,12 +219,14 @@ class SafetyLabelChangedBroadcastReceiver : BroadcastReceiver() { Log.i( TAG, "Forwarding intent from current user: $currentUser to profile parent" + - " $profileParent") + " $profileParent" + ) context.sendBroadcastAsUser( Intent(intent) .setAction(ACTION_PACKAGE_ADDED_PERMISSIONCONTROLLER_FORWARDED) .putExtra(Intent.EXTRA_USER, currentUser), - profileParent) + profileParent + ) } /** Types of package change events. */ |