| /* |
| * Copyright (C) 2020 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 |
| |
| import android.app.AppOpsManager |
| import android.app.AppOpsManager.MODE_ALLOWED |
| import android.app.AppOpsManager.MODE_IGNORED |
| import android.app.AppOpsManager.OPSTR_AUTO_REVOKE_PERMISSIONS_IF_UNUSED |
| import android.os.Bundle |
| import android.os.UserHandle |
| import androidx.fragment.app.Fragment |
| import androidx.lifecycle.ViewModel |
| import androidx.lifecycle.ViewModelProvider |
| import androidx.navigation.fragment.findNavController |
| import com.android.permissioncontroller.PermissionControllerApplication |
| import com.android.permissioncontroller.R |
| import com.android.permissioncontroller.permission.data.AppPermGroupUiInfoLiveData |
| import com.android.permissioncontroller.permission.data.AutoRevokeStateLiveData |
| import com.android.permissioncontroller.permission.data.LightPackageInfoLiveData |
| 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.AppPermGroupUiInfo.PermGrantState |
| import com.android.permissioncontroller.permission.ui.Category |
| import com.android.permissioncontroller.permission.utils.KotlinUtils |
| import com.android.permissioncontroller.permission.utils.Utils |
| import kotlinx.coroutines.Dispatchers.IO |
| import kotlinx.coroutines.GlobalScope |
| import kotlinx.coroutines.launch |
| |
| /** |
| * ViewModel for the AppPermissionGroupsFragment. Has a liveData with the UI information for all |
| * permission groups that this package requests runtime permissions from |
| * |
| * @param packageName The name of the package this viewModel is representing |
| * @param user The user of the package this viewModel is representing |
| */ |
| class AppPermissionGroupsViewModel( |
| private val packageName: String, |
| private val user: UserHandle |
| ) : ViewModel() { |
| |
| /** |
| * 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>>>() { |
| |
| private val packagePermsLiveData = |
| PackagePermissionsLiveData[packageName, user] |
| private val appPermGroupUiInfoLiveDatas = mutableMapOf<String, AppPermGroupUiInfoLiveData>() |
| |
| init { |
| addSource(packagePermsLiveData) { |
| updateIfActive() |
| } |
| updateIfActive() |
| } |
| |
| 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) { |
| return |
| } |
| |
| addAndRemoveAppPermGroupLiveDatas(groups) |
| |
| if (!appPermGroupUiInfoLiveDatas.all { it.value.isInitialized }) { |
| return |
| } |
| |
| val groupGrantStates = mutableMapOf<Category, |
| MutableList<GroupUiInfo>>() |
| groupGrantStates[Category.ALLOWED] = mutableListOf() |
| groupGrantStates[Category.ASK] = mutableListOf() |
| groupGrantStates[Category.DENIED] = mutableListOf() |
| |
| for (groupName in groups) { |
| val isSystem = Utils.getPlatformPermissionGroups().contains(groupName) |
| appPermGroupUiInfoLiveDatas[groupName]?.value?.let { uiInfo -> |
| when (uiInfo.permGrantState) { |
| PermGrantState.PERMS_ALLOWED -> groupGrantStates[Category.ALLOWED]!!.add( |
| GroupUiInfo(groupName, isSystem)) |
| PermGrantState.PERMS_ALLOWED_ALWAYS -> groupGrantStates[ |
| Category.ALLOWED]!!.add(GroupUiInfo(groupName, isSystem)) |
| PermGrantState.PERMS_ALLOWED_FOREGROUND_ONLY -> groupGrantStates[ |
| Category.ALLOWED]!!.add(GroupUiInfo(groupName, isSystem, |
| isForeground = true)) |
| PermGrantState.PERMS_DENIED -> groupGrantStates[Category.DENIED]!!.add( |
| GroupUiInfo(groupName, isSystem)) |
| PermGrantState.PERMS_ASK -> groupGrantStates[Category.ASK]!!.add( |
| GroupUiInfo(groupName, isSystem)) |
| } |
| } |
| } |
| |
| value = groupGrantStates |
| } |
| |
| private fun addAndRemoveAppPermGroupLiveDatas(groupNames: List<String>) { |
| val (toAdd, toRemove) = KotlinUtils.getMapAndListDifferences(groupNames, |
| appPermGroupUiInfoLiveDatas) |
| |
| for (groupToAdd in toAdd) { |
| val appPermGroupUiInfoLiveData = |
| AppPermGroupUiInfoLiveData[packageName, groupToAdd, user] |
| appPermGroupUiInfoLiveDatas[groupToAdd] = appPermGroupUiInfoLiveData |
| } |
| |
| for (groupToAdd in toAdd) { |
| addSource(appPermGroupUiInfoLiveDatas[groupToAdd]!!) { |
| updateIfActive() |
| } |
| } |
| |
| for (groupToRemove in toRemove) { |
| removeSource(appPermGroupUiInfoLiveDatas[groupToRemove]!!) |
| appPermGroupUiInfoLiveDatas.remove(groupToRemove) |
| } |
| } |
| } |
| |
| data class GroupUiInfo( |
| val groupName: String, |
| val isSystem: Boolean = false, |
| val isForeground: Boolean = false |
| ) |
| |
| val autoRevokeLiveData = AutoRevokeStateLiveData[packageName, user] |
| |
| fun setAutoRevoke(enabled: Boolean) { |
| GlobalScope.launch(IO) { |
| val aom = PermissionControllerApplication.get() |
| .getSystemService(AppOpsManager::class.java)!! |
| val packageInfo = LightPackageInfoLiveData[packageName, user].getInitializedValue() |
| val mode = if (enabled) { |
| MODE_ALLOWED |
| } else { |
| MODE_IGNORED |
| } |
| aom.setUidMode(OPSTR_AUTO_REVOKE_PERMISSIONS_IF_UNUSED, packageInfo.uid, mode) |
| } |
| } |
| |
| fun showExtraPerms(fragment: Fragment, args: Bundle) { |
| fragment.findNavController().navigate(R.id.perm_groups_to_extra, args) |
| } |
| |
| fun showAllPermissions(fragment: Fragment, args: Bundle) { |
| fragment.findNavController().navigate(R.id.perm_groups_to_all_perms, args) |
| } |
| } |
| |
| /** |
| * Factory for an AppPermissionGroupsViewModel |
| * |
| * @param packageName The name of the package this viewModel is representing |
| * @param user The user of the package this viewModel is representing |
| */ |
| class AppPermissionGroupsViewModelFactory( |
| private val packageName: String, |
| private val user: UserHandle |
| ) : ViewModelProvider.Factory { |
| |
| override fun <T : ViewModel> create(modelClass: Class<T>): T { |
| @Suppress("UNCHECKED_CAST") |
| return AppPermissionGroupsViewModel(packageName, user) as T |
| } |
| } |