1 /* 2 * Copyright (C) 2023 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.permissioncontroller.permission.ui.wear 18 19 import android.app.Activity 20 import android.content.Intent 21 import android.content.pm.PackageInfo 22 import android.content.pm.PackageManager 23 import android.os.Build 24 import android.os.Bundle 25 import android.os.UserHandle 26 import android.util.Log 27 import android.view.LayoutInflater 28 import android.view.View 29 import android.view.ViewGroup 30 import android.widget.Toast 31 import androidx.annotation.RequiresApi 32 import androidx.compose.ui.platform.ComposeView 33 import androidx.core.os.BundleCompat 34 import androidx.fragment.app.Fragment 35 import androidx.lifecycle.ViewModelProvider 36 import com.android.modules.utils.build.SdkLevel 37 import com.android.permissioncontroller.Constants.EXTRA_SESSION_ID 38 import com.android.permissioncontroller.R 39 import com.android.permissioncontroller.permission.model.AppPermissions 40 import com.android.permissioncontroller.permission.model.v31.AppPermissionUsage 41 import com.android.permissioncontroller.permission.model.v31.PermissionUsages 42 import com.android.permissioncontroller.permission.model.v31.PermissionUsages.PermissionsUsagesChangeCallback 43 import com.android.permissioncontroller.permission.ui.model.AppPermissionGroupsViewModel 44 import com.android.permissioncontroller.permission.ui.model.AppPermissionGroupsViewModelFactory 45 import com.android.permissioncontroller.permission.ui.wear.model.AppPermissionGroupsRevokeDialogViewModel 46 import com.android.permissioncontroller.permission.ui.wear.model.AppPermissionGroupsRevokeDialogViewModelFactory 47 import com.android.permissioncontroller.permission.ui.wear.model.WearAppPermissionUsagesViewModel 48 import com.android.permissioncontroller.permission.ui.wear.model.WearAppPermissionUsagesViewModelFactory 49 import com.android.permissioncontroller.permission.ui.wear.model.WearLocationProviderInterceptDialogViewModel 50 import com.android.permissioncontroller.permission.ui.wear.model.WearLocationProviderInterceptDialogViewModelFactory 51 import com.android.permissioncontroller.wear.permission.components.theme.WearPermissionTheme 52 import java.time.Instant 53 import java.util.concurrent.TimeUnit 54 55 class WearAppPermissionGroupsFragment : Fragment(), PermissionsUsagesChangeCallback { 56 private lateinit var permissionUsages: PermissionUsages 57 private lateinit var wearViewModel: WearAppPermissionUsagesViewModel 58 private lateinit var helper: WearAppPermissionGroupsHelper 59 60 // Suppress warning of the deprecated class [android.app.LoaderManager] since other form factors 61 // are using the class to load PermissionUsages. 62 @Suppress("DEPRECATION") onCreateViewnull63 override fun onCreateView( 64 inflater: LayoutInflater, 65 container: ViewGroup?, 66 savedInstanceState: Bundle?, 67 ): View? { 68 val packageName = arguments?.getString(Intent.EXTRA_PACKAGE_NAME) ?: "" 69 val user = 70 arguments?.let { 71 BundleCompat.getParcelable(it, Intent.EXTRA_USER, UserHandle::class.java)!! 72 } ?: UserHandle.SYSTEM 73 74 val activity: Activity = requireActivity() 75 val packageManager = activity.packageManager 76 val packageInfo: PackageInfo? = 77 try { 78 packageManager.getPackageInfo(packageName, PackageManager.GET_PERMISSIONS) 79 } catch (e: PackageManager.NameNotFoundException) { 80 Log.i(LOG_TAG, "No package:" + activity.callingPackage, e) 81 null 82 } 83 84 if (packageInfo == null) { 85 Toast.makeText(activity, R.string.app_not_found_dlg_title, Toast.LENGTH_LONG).show() 86 activity.finish() 87 return null 88 } 89 val sessionId = arguments?.getLong(EXTRA_SESSION_ID, 0) ?: 0 90 91 val appPermissions = AppPermissions(activity, packageInfo, true) { activity.finish() } 92 93 val viewModel = 94 ViewModelProvider( 95 owner = this, 96 factory = AppPermissionGroupsViewModelFactory(packageName, user, sessionId), 97 )[AppPermissionGroupsViewModel::class.java] 98 99 wearViewModel = 100 ViewModelProvider(owner = this, factory = WearAppPermissionUsagesViewModelFactory())[ 101 WearAppPermissionUsagesViewModel::class.java] 102 103 val revokeDialogViewModel = 104 ViewModelProvider( 105 owner = this, 106 factory = AppPermissionGroupsRevokeDialogViewModelFactory(), 107 )[AppPermissionGroupsRevokeDialogViewModel::class.java] 108 109 val locationProviderInterceptDialogViewModel = 110 ViewModelProvider( 111 owner = this, 112 factory = WearLocationProviderInterceptDialogViewModelFactory(), 113 )[WearLocationProviderInterceptDialogViewModel::class.java] 114 115 val context = requireContext() 116 117 // If the build type is below S, the app ops for permission usage can't be found. Thus, we 118 // shouldn't load permission usages, for them. 119 if (SdkLevel.isAtLeastS()) { 120 permissionUsages = PermissionUsages(context) 121 val aggregateDataFilterBeginDays = 122 AppPermissionGroupsViewModel.AGGREGATE_DATA_FILTER_BEGIN_DAYS_1.toLong() 123 124 val filterTimeBeginMillis = 125 maxOf( 126 System.currentTimeMillis() - 127 TimeUnit.DAYS.toMillis(aggregateDataFilterBeginDays), 128 Instant.EPOCH.toEpochMilli(), 129 ) 130 permissionUsages.load( 131 null, 132 null, 133 filterTimeBeginMillis, 134 Long.MAX_VALUE, 135 PermissionUsages.USAGE_FLAG_LAST, 136 requireActivity().loaderManager, 137 false, 138 false, 139 this, 140 false, 141 ) 142 } 143 helper = 144 WearAppPermissionGroupsHelper( 145 context = context, 146 fragment = this, 147 user = user, 148 packageName = packageName, 149 sessionId = sessionId, 150 appPermissions = appPermissions, 151 viewModel = viewModel, 152 wearViewModel = wearViewModel, 153 revokeDialogViewModel = revokeDialogViewModel, 154 locationProviderInterceptDialogViewModel = locationProviderInterceptDialogViewModel, 155 ) 156 157 return ComposeView(activity).apply { 158 setContent { WearPermissionTheme { WearAppPermissionGroupsScreen(helper) } } 159 } 160 } 161 onPausenull162 override fun onPause() { 163 super.onPause() 164 helper.logAndClearToggledGroups() 165 } 166 167 @RequiresApi(Build.VERSION_CODES.S) onPermissionUsagesChangednull168 override fun onPermissionUsagesChanged() { 169 if (permissionUsages.usages.isEmpty()) { 170 return 171 } 172 if (getContext() == null) { 173 // Async result has come in after our context is gone. 174 return 175 } 176 wearViewModel.appPermissionUsages.value = 177 ArrayList<AppPermissionUsage>(permissionUsages.usages) 178 } 179 180 companion object { 181 const val LOG_TAG = "WearAppPermissionGroups" 182 } 183 } 184