diff options
author | Nate Myren <ntmyren@google.com> | 2022-03-18 10:34:35 -0700 |
---|---|---|
committer | Nate Myren <ntmyren@google.com> | 2022-04-05 21:35:48 +0000 |
commit | 6c86be3232085956f168955fd4425c0bc68e7c68 (patch) | |
tree | f2a1f194475d5e3ddfa3d7e59c34ad1028d836bc /PermissionController/src/com/android/permissioncontroller/permission/ui/model/v33 | |
parent | f9ea621b6a4c42c41ab00965b8579ba14e6e3fdc (diff) | |
download | Permission-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')
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 + } +} |