summaryrefslogtreecommitdiff
path: root/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v33
diff options
context:
space:
mode:
authorNate Myren <ntmyren@google.com>2022-03-18 10:34:35 -0700
committerNate Myren <ntmyren@google.com>2022-04-05 21:35:48 +0000
commit6c86be3232085956f168955fd4425c0bc68e7c68 (patch)
treef2a1f194475d5e3ddfa3d7e59c34ad1028d836bc /PermissionController/src/com/android/permissioncontroller/permission/ui/model/v33
parentf9ea621b6a4c42c41ab00965b8579ba14e6e3fdc (diff)
downloadPermission-6c86be3232085956f168955fd4425c0bc68e7c68.tar.gz
Add v31/v33 packages in PC
Fixes: 223422689 Fixes: 223418221 Test: build Change-Id: I03bd5a3fc95ccc958b1ac52b23bb1f2abd23e4a8 Merged-In: I9298d8436ce314ac77ae7048fcc00348f09ee90d Merged-In: I03bd5a3fc95ccc958b1ac52b23bb1f2abd23e4a8
Diffstat (limited to 'PermissionController/src/com/android/permissioncontroller/permission/ui/model/v33')
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/model/v33/ReviewPermissionsViewModel.kt327
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/model/v33/SafetyCenterQsViewModel.kt231
2 files changed, 558 insertions, 0 deletions
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v33/ReviewPermissionsViewModel.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v33/ReviewPermissionsViewModel.kt
new file mode 100644
index 000000000..94d0df0d5
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v33/ReviewPermissionsViewModel.kt
@@ -0,0 +1,327 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.permissioncontroller.permission.ui.model.v33
+
+import android.app.Application
+import android.content.Context
+import android.content.pm.PackageInfo
+import android.os.Bundle
+import androidx.fragment.app.Fragment
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.ViewModelProvider
+import androidx.navigation.NavController
+import androidx.navigation.fragment.NavHostFragment
+import com.android.permissioncontroller.R
+import com.android.permissioncontroller.permission.data.LightAppPermGroupLiveData
+import com.android.permissioncontroller.permission.data.PackagePermissionsLiveData
+import com.android.permissioncontroller.permission.data.PackagePermissionsLiveData.Companion.NON_RUNTIME_NORMAL_PERMS
+import com.android.permissioncontroller.permission.data.SmartUpdateMediatorLiveData
+import com.android.permissioncontroller.permission.data.get
+import com.android.permissioncontroller.permission.model.livedatatypes.LightAppPermGroup
+import com.android.permissioncontroller.permission.utils.Utils
+import com.android.permissioncontroller.permission.utils.navigateSafe
+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() {
+
+ private val mUser = android.os.Process.myUserHandle()
+
+ /**
+ * 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()
+ }
+ }
+
+ 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 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()
+ }
+ }
+ }
+
+ fun isInitialized(): Boolean {
+ return permissionGroupsLiveData.isInitialized
+ }
+
+ private fun shouldShowPermission(group: LightAppPermGroup): Boolean {
+ if (!(group.foreground.isGrantable || group.background.isGrantable)) {
+ return false
+ }
+ val isPlatformPermission = group.packageName == Utils.OS_PKG
+ // Show legacy permissions only if the user chose that.
+ return !(isPlatformPermission && !Utils.isModernPermissionGroup(group.permGroupName))
+ }
+
+ fun isPackageUpdated(): Boolean {
+ val permGroupsMap: Map<String, LightAppPermGroup> = permissionGroupsLiveData.value!!
+ return permGroupsMap.any { !it.value.isReviewRequired }
+ }
+
+ /**
+ * 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 {
+ if (state != PermissionTarget.PERMISSION_NONE) {
+ if (state.and(PermissionTarget.PERMISSION_BACKGROUND)
+ != PermissionTarget.PERMISSION_NONE.value) {
+ return SummaryMessage.ACCESS_ALWAYS.toPermSummary()
+ } else {
+ return SummaryMessage.ACCESS_ONLY_FOREGROUND.toPermSummary()
+ }
+ } else {
+ return SummaryMessage.ACCESS_NEVER.toPermSummary()
+ }
+ }
+
+ fun getSummaryForIndividuallyControlledPermGroup(
+ permGroup: LightAppPermGroup
+ ): PermissionSummary {
+ var revokedCount = 0
+ val lightPerms = permGroup.allPermissions.values.toList()
+ val permissionCount = lightPerms.size
+ for (i in 0 until permissionCount) {
+ if (!lightPerms[i].isGrantedIncludingAppOp) {
+ revokedCount++
+ }
+ }
+ return when (revokedCount) {
+ 0 -> {
+ SummaryMessage.REVOKED_NONE.toPermSummary()
+ }
+ permissionCount -> {
+ SummaryMessage.REVOKED_ALL.toPermSummary()
+ }
+ else -> {
+ PermissionSummary(SummaryMessage.REVOKED_COUNT, false, revokedCount)
+ }
+ }
+ }
+
+ /**
+ * 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)
+ }
+
+ enum class SummaryMessage {
+ NO_SUMMARY,
+ DISABLED_BY_ADMIN,
+ ENABLED_BY_ADMIN,
+ ENABLED_SYSTEM_FIXED,
+ ENFORCED_BY_POLICY,
+ ENABLED_BY_ADMIN_FOREGROUND_ONLY,
+ ENABLED_BY_POLICY_FOREGROUND_ONLY,
+ ENABLED_BY_ADMIN_BACKGROUND_ONLY,
+ ENABLED_BY_POLICY_BACKGROUND_ONLY,
+ DISABLED_BY_ADMIN_BACKGROUND_ONLY,
+ DISABLED_BY_POLICY_BACKGROUND_ONLY,
+ REVOKED_NONE,
+ REVOKED_ALL,
+ REVOKED_COUNT,
+ ACCESS_ALWAYS,
+ ACCESS_ONLY_FOREGROUND,
+ ACCESS_NEVER;
+
+ fun toPermSummary(): PermissionSummary {
+ return PermissionSummary(this, false)
+ }
+
+ fun toPermSummary(isEnterprise: Boolean): PermissionSummary {
+ return PermissionSummary(this, isEnterprise)
+ }
+ }
+
+ data class PermissionSummary(
+ val msg: SummaryMessage,
+ val isEnterprise: Boolean = false,
+ val revokeCount: Int = 0
+ )
+
+ fun getSummaryForFixedByPolicyPermissionGroup(
+ mState: PermissionTarget,
+ permGroup: LightAppPermGroup,
+ context: Context
+ ): PermissionSummary {
+ val admin = getAdmin(context, permGroup)
+ val hasAdmin = admin != null
+ if (permGroup.isSystemFixed) {
+ // Permission is fully controlled by the system and cannot be switched
+ return SummaryMessage.ENABLED_SYSTEM_FIXED.toPermSummary()
+ } else if (isForegroundDisabledByPolicy(permGroup)) {
+ // Permission is fully controlled by policy and cannot be switched
+ return if (hasAdmin) {
+ SummaryMessage.DISABLED_BY_ADMIN.toPermSummary()
+ } else {
+ // Disabled state will be displayed by switch, so no need to add text for that
+ SummaryMessage.ENFORCED_BY_POLICY.toPermSummary()
+ }
+ } else if (permGroup.isPolicyFullyFixed) {
+ // Permission is fully controlled by policy and cannot be switched
+ if (!permGroup.hasBackgroundGroup) {
+ return if (hasAdmin) {
+ SummaryMessage.ENABLED_BY_ADMIN.toPermSummary()
+ } else {
+ // Enabled state will be displayed by switch, so no need to add text for that
+ SummaryMessage.ENFORCED_BY_POLICY.toPermSummary()
+ }
+ } else {
+ if (mState.and(PermissionTarget.PERMISSION_BACKGROUND) !=
+ PermissionTarget.PERMISSION_NONE.value) {
+ return if (hasAdmin) {
+ SummaryMessage.ENABLED_BY_ADMIN.toPermSummary()
+ } else {
+ // Enabled state will be displayed by switch, so no need to add text for
+ // that
+ SummaryMessage.ENFORCED_BY_POLICY.toPermSummary()
+ }
+ } else {
+ return if (hasAdmin) {
+ SummaryMessage.ENABLED_BY_ADMIN_FOREGROUND_ONLY.toPermSummary()
+ } else {
+ SummaryMessage.ENABLED_BY_POLICY_BACKGROUND_ONLY.toPermSummary()
+ }
+ }
+ }
+ } 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) {
+ if (hasAdmin) {
+ SummaryMessage.ENABLED_BY_ADMIN_BACKGROUND_ONLY.toPermSummary(true)
+ } else {
+ SummaryMessage.ENABLED_BY_POLICY_BACKGROUND_ONLY.toPermSummary()
+ }
+ } else {
+ if (hasAdmin) {
+ SummaryMessage.DISABLED_BY_ADMIN_BACKGROUND_ONLY.toPermSummary(true)
+ } else {
+ SummaryMessage.DISABLED_BY_POLICY_BACKGROUND_ONLY.toPermSummary()
+ }
+ }
+ } else if (permGroup.foreground.isPolicyFixed) {
+ return if (hasAdmin) {
+ SummaryMessage.ENABLED_BY_ADMIN_FOREGROUND_ONLY.toPermSummary(true)
+ } else {
+ SummaryMessage.ENABLED_BY_POLICY_FOREGROUND_ONLY.toPermSummary()
+ }
+ }
+ }
+ return SummaryMessage.NO_SUMMARY.toPermSummary()
+ }
+
+ /**
+ * Is the foreground part of this group disabled. If the foreground is disabled, there is no
+ * need to possible grant background access.
+ *
+ * @return `true` iff the permissions of this group are fixed
+ */
+ private fun isForegroundDisabledByPolicy(mGroup: LightAppPermGroup): Boolean {
+ return mGroup.foreground.isPolicyFixed && !mGroup.isGranted
+ }
+
+ /**
+ * Whether policy is system fixed or fully fixed or foreground disabled
+ */
+ fun isFixedOrForegroundDisabled(mGroup: LightAppPermGroup): Boolean {
+ return mGroup.isSystemFixed || mGroup.isPolicyFullyFixed ||
+ isForegroundDisabledByPolicy(mGroup)
+ }
+
+ /**
+ * Get the app that acts as admin for this profile.
+ *
+ * @return The admin or `null` if there is no admin.
+ */
+ fun getAdmin(context: Context, mGroup: LightAppPermGroup): EnforcedAdmin? {
+ return RestrictedLockUtils.getProfileOrDeviceOwner(context, mGroup.userHandle)
+ }
+
+ enum class PermissionTarget(val value: Int) {
+ PERMISSION_NONE(0),
+ PERMISSION_FOREGROUND(1),
+ PERMISSION_BACKGROUND(2),
+ PERMISSION_BOTH(3);
+
+ infix fun and(other: PermissionTarget): Int {
+ return value and other.value
+ }
+
+ infix fun and(other: Int): Int {
+ return value and other
+ }
+
+ infix fun or(other: PermissionTarget): Int {
+ return value or other.value
+ }
+
+ companion object {
+ fun fromInt(value: Int) = values().first { it.value == value }
+ }
+ }
+}
+
+class ReviewPermissionViewModelFactory(
+ private val app: Application,
+ private val packageInfo: PackageInfo
+) : ViewModelProvider.Factory {
+ override fun <T : ViewModel> create(modelClass: Class<T>): T {
+ @Suppress("UNCHECKED_CAST")
+ return ReviewPermissionsViewModel(app, packageInfo = packageInfo) as T
+ }
+} \ No newline at end of file
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v33/SafetyCenterQsViewModel.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v33/SafetyCenterQsViewModel.kt
new file mode 100644
index 000000000..cccfb7fdf
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v33/SafetyCenterQsViewModel.kt
@@ -0,0 +1,231 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.permissioncontroller.permission.ui.model.v33
+
+import android.Manifest.permission_group.CAMERA
+import android.Manifest.permission_group.LOCATION
+import android.Manifest.permission_group.MICROPHONE
+import android.app.Application
+import android.content.ComponentName
+import android.content.Context
+import android.content.Intent
+import android.content.pm.PackageManager
+import android.content.pm.ResolveInfo
+import android.hardware.SensorPrivacyManager
+import android.hardware.SensorPrivacyManager.Sensors
+import android.location.LocationManager
+import android.os.Build
+import android.os.Process
+import android.os.UserHandle
+import android.permission.PermissionGroupUsage
+import android.provider.Settings
+import androidx.annotation.RequiresApi
+import androidx.fragment.app.Fragment
+import androidx.lifecycle.AndroidViewModel
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.ViewModelProvider
+import com.android.permissioncontroller.permission.data.LightAppPermGroupLiveData
+import com.android.permissioncontroller.permission.data.SmartUpdateMediatorLiveData
+import com.android.permissioncontroller.permission.model.livedatatypes.LightAppPermGroup
+import com.android.permissioncontroller.permission.utils.KotlinUtils
+import com.android.permissioncontroller.permission.utils.LocationUtils
+import kotlin.collections.set
+
+@RequiresApi(Build.VERSION_CODES.TIRAMISU)
+class SafetyCenterQsViewModel(
+ private val app: Application,
+ private val sessionId: Long,
+ private val permGroupUsages: List<PermissionGroupUsage>
+) : AndroidViewModel(app) {
+
+ private val sensorPrivacyManager: SensorPrivacyManager =
+ app.getSystemService(SensorPrivacyManager::class.java)!!
+ private val locationManager: LocationManager =
+ app.getSystemService(LocationManager::class.java)!!
+
+ val lightAppPermMap = mutableMapOf<Triple<String, String, UserHandle>, LightAppPermGroup?>()
+
+ val permDataLoadedLiveData = object
+ : SmartUpdateMediatorLiveData<Boolean>() {
+
+ private val lightAppPermLiveDatas = mutableMapOf<Triple<String, String, UserHandle>,
+ LightAppPermGroupLiveData>()
+
+ init {
+ for (permGroupUsage in permGroupUsages) {
+ val pgTriple = Triple(permGroupUsage.packageName,
+ permGroupUsage.permissionGroupName,
+ UserHandle.getUserHandleForUid(permGroupUsage.uid))
+ val appPermGroupLiveData = LightAppPermGroupLiveData[pgTriple]
+ lightAppPermLiveDatas[pgTriple] = appPermGroupLiveData
+ addSource(appPermGroupLiveData) {
+ update()
+ }
+ }
+ }
+
+ override fun onUpdate() {
+ if (!lightAppPermLiveDatas.all { it.value.isInitialized }) {
+ return
+ }
+ for ((pgTriple, lightAppPermLiveData) in lightAppPermLiveDatas) {
+ lightAppPermMap[pgTriple] = lightAppPermLiveData.value
+ }
+ value = true
+ }
+ }
+
+ fun shouldAllowRevoke(usage: PermissionGroupUsage): Boolean {
+ val pgTriple = Triple(usage.packageName,
+ usage.permissionGroupName,
+ UserHandle.getUserHandleForUid(usage.uid))
+ val group = lightAppPermMap[pgTriple] ?: return false
+ return group.supportsRuntimePerms &&
+ !group.hasInstallToRuntimeSplit &&
+ !group.isBackgroundFixed &&
+ !group.isForegroundFixed &&
+ !group.isGrantedByDefault
+ }
+
+ fun revokePermission(usage: PermissionGroupUsage) {
+ val group = lightAppPermMap.get(Triple(usage.packageName, usage.permissionGroupName,
+ UserHandle.getUserHandleForUid(usage.uid))) ?: return
+
+ if (group != null) {
+ KotlinUtils.revokeForegroundRuntimePermissions(app, group)
+ KotlinUtils.revokeBackgroundRuntimePermissions(app, group)
+ }
+ }
+
+ fun toggleSensor(groupName: String) {
+ when (groupName) {
+ MICROPHONE -> {
+ val blocked = sensorPrivacyManager.isSensorPrivacyEnabled(Sensors.MICROPHONE)
+ sensorPrivacyManager.setSensorPrivacy(Sensors.MICROPHONE, !blocked)
+ sensorPrivacyLiveData.update()
+ }
+ CAMERA -> {
+ val blocked = sensorPrivacyManager.isSensorPrivacyEnabled(Sensors.CAMERA)
+ sensorPrivacyManager.setSensorPrivacy(Sensors.CAMERA, !blocked)
+ sensorPrivacyLiveData.update()
+ }
+ LOCATION -> {
+ val enabled = locationManager.isLocationEnabledForUser(Process.myUserHandle())
+ locationManager.setLocationEnabledForUser(!enabled, Process.myUserHandle())
+ sensorPrivacyLiveData.update()
+ }
+ }
+ }
+
+ fun navigateToSecuritySettings(fragment: Fragment) {
+ fragment.startActivity(Intent(Settings.ACTION_SECURITY_SETTINGS))
+ }
+
+ val sensorPrivacyLiveData: SmartUpdateMediatorLiveData<Map<String, Boolean>> =
+ object : SmartUpdateMediatorLiveData<Map<String, Boolean>>(),
+ SensorPrivacyManager.OnSensorPrivacyChangedListener, LocationUtils.LocationListener {
+ override fun onUpdate() {
+ val cameraEnabled = !sensorPrivacyManager.isSensorPrivacyEnabled(Sensors.CAMERA)
+ val micEnabled = !sensorPrivacyManager.isSensorPrivacyEnabled(Sensors.MICROPHONE)
+ val locationEnabled =
+ locationManager.isLocationEnabledForUser(Process.myUserHandle())
+ value = mapOf(CAMERA to cameraEnabled, MICROPHONE to micEnabled,
+ LOCATION to locationEnabled)
+ }
+
+ override fun onSensorPrivacyChanged(sensor: Int, enabled: Boolean) {
+ update()
+ }
+
+ override fun onLocationStateChange(enabled: Boolean) {
+ update()
+ }
+
+ override fun onActive() {
+ super.onActive()
+ sensorPrivacyManager.addSensorPrivacyListener(Sensors.CAMERA, this)
+ sensorPrivacyManager.addSensorPrivacyListener(Sensors.MICROPHONE, this)
+ LocationUtils.addLocationListener(this)
+ }
+
+ override fun onInactive() {
+ super.onInactive()
+ sensorPrivacyManager.removeSensorPrivacyListener(Sensors.CAMERA, this)
+ sensorPrivacyManager.removeSensorPrivacyListener(Sensors.MICROPHONE, this)
+ LocationUtils.removeLocationListener(this)
+ }
+ }
+
+ fun navigateToManageService(fragment: Fragment, navigationIntent: Intent) {
+ fragment.startActivity(navigationIntent)
+ }
+
+ fun navigateToManageAppPermissions(fragment: Fragment, usage: PermissionGroupUsage) {
+ fragment.startActivity(getDefaultManageAppPermissionsIntent(usage.packageName, usage.uid))
+ }
+
+ fun getStartViewPermissionUsageIntent(context: Context, usage: PermissionGroupUsage):
+ Intent? {
+ var intent: Intent = Intent(Intent.ACTION_MANAGE_PERMISSION_USAGE)
+ intent.setPackage(usage.packageName)
+ intent.putExtra(Intent.EXTRA_PERMISSION_GROUP_NAME, usage.permissionGroupName)
+ intent.putExtra(Intent.EXTRA_ATTRIBUTION_TAGS, arrayOf(usage.attributionTag.toString()))
+ intent.putExtra(Intent.EXTRA_SHOWING_ATTRIBUTION, true)
+ 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) {
+ intent.component = ComponentName(usage.packageName, resolveInfo.activityInfo.name)
+ return intent
+ }
+ return null
+ }
+
+ private fun getDefaultManageAppPermissionsIntent(packageName: String, uid: Int): Intent {
+ val intent = Intent(Intent.ACTION_MANAGE_APP_PERMISSIONS)
+ intent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName)
+ intent.putExtra(Intent.EXTRA_USER, UserHandle.getUserHandleForUid(uid))
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ return intent
+ }
+
+ fun navigateToSeeUsage(fragment: Fragment, permGroupName: String) {
+ val seeUsageIntent = Intent(Intent.ACTION_REVIEW_PERMISSION_HISTORY)
+ seeUsageIntent.putExtra(Intent.EXTRA_PERMISSION_GROUP_NAME, permGroupName)
+ fragment.startActivity(seeUsageIntent)
+ }
+}
+
+/**
+ * Factory for a SafetyCenterQsViewModel
+ *
+ * @param app The current application
+ * @param sessionId A session ID used in logs to identify this particular session
+ */
+@RequiresApi(Build.VERSION_CODES.S)
+class SafetyCenterQsViewModelFactory(
+ private val app: Application,
+ private val sessionId: Long,
+ private val permGroupUsages: List<PermissionGroupUsage>
+) : ViewModelProvider.Factory {
+ override fun <T : ViewModel> create(modelClass: Class<T>): T {
+ @Suppress("UNCHECKED_CAST")
+ return SafetyCenterQsViewModel(app, sessionId, permGroupUsages) as T
+ }
+}