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.theme.WearPermissionTheme 50 import com.android.permissioncontroller.permission.utils.KotlinUtils.is7DayToggleEnabled 51 import java.time.Instant 52 import java.util.concurrent.TimeUnit 53 54 class WearAppPermissionGroupsFragment : Fragment(), PermissionsUsagesChangeCallback { 55 private lateinit var permissionUsages: PermissionUsages 56 private lateinit var wearViewModel: WearAppPermissionUsagesViewModel 57 private lateinit var helper: WearAppPermissionGroupsHelper 58 59 // Suppress warning of the deprecated class [android.app.LoaderManager] since other form factors 60 // are using the class to load PermissionUsages. 61 @Suppress("DEPRECATION") onCreateViewnull62 override fun onCreateView( 63 inflater: LayoutInflater, 64 container: ViewGroup?, 65 savedInstanceState: Bundle? 66 ): View? { 67 val packageName = arguments?.getString(Intent.EXTRA_PACKAGE_NAME) ?: "" 68 val user = 69 arguments?.let { 70 BundleCompat.getParcelable(it, Intent.EXTRA_USER, UserHandle::class.java)!! 71 } 72 ?: UserHandle.SYSTEM 73 74 val activity: Activity = requireActivity() 75 val packageManager = activity.packageManager 76 77 val packageInfo: PackageInfo? = 78 try { 79 packageManager.getPackageInfo(packageName, PackageManager.GET_PERMISSIONS) 80 } catch (e: PackageManager.NameNotFoundException) { 81 Log.i(LOG_TAG, "No package:" + activity.getCallingPackage(), e) 82 null 83 } 84 85 if (packageInfo == null) { 86 Toast.makeText(activity, R.string.app_not_found_dlg_title, Toast.LENGTH_LONG).show() 87 activity.finish() 88 return null 89 } 90 val sessionId = arguments?.getLong(EXTRA_SESSION_ID, 0) ?: 0 91 val appPermissions = AppPermissions(activity, packageInfo, true, { activity.finish() }) 92 val factory = AppPermissionGroupsViewModelFactory(packageName, user, sessionId) 93 val viewModel = 94 ViewModelProvider(this, factory).get(AppPermissionGroupsViewModel::class.java) 95 wearViewModel = 96 ViewModelProvider(this, WearAppPermissionUsagesViewModelFactory()) 97 .get(WearAppPermissionUsagesViewModel::class.java) 98 val revokeDialogViewModel = 99 ViewModelProvider(this, AppPermissionGroupsRevokeDialogViewModelFactory()) 100 .get(AppPermissionGroupsRevokeDialogViewModel::class.java) 101 102 val context = requireContext() 103 104 // If the build type is below S, the app ops for permission usage can't be found. Thus, we 105 // shouldn't load permission usages, for them. 106 if (SdkLevel.isAtLeastS()) { 107 permissionUsages = PermissionUsages(context) 108 val aggregateDataFilterBeginDays = 109 (if (is7DayToggleEnabled()) 110 AppPermissionGroupsViewModel.AGGREGATE_DATA_FILTER_BEGIN_DAYS_7 111 else AppPermissionGroupsViewModel.AGGREGATE_DATA_FILTER_BEGIN_DAYS_1) 112 .toLong() 113 114 val filterTimeBeginMillis = 115 Math.max( 116 System.currentTimeMillis() - 117 TimeUnit.DAYS.toMillis(aggregateDataFilterBeginDays), 118 Instant.EPOCH.toEpochMilli() 119 ) 120 permissionUsages.load( 121 null, 122 null, 123 filterTimeBeginMillis, 124 Long.MAX_VALUE, 125 PermissionUsages.USAGE_FLAG_LAST, 126 requireActivity().getLoaderManager(), 127 false, 128 false, 129 this, 130 false 131 ) 132 } 133 helper = 134 WearAppPermissionGroupsHelper( 135 context = context, 136 fragment = this, 137 user = user, 138 packageName = packageName, 139 sessionId = sessionId, 140 appPermissions = appPermissions, 141 viewModel = viewModel, 142 wearViewModel = wearViewModel, 143 revokeDialogViewModel = revokeDialogViewModel 144 ) 145 146 return ComposeView(activity).apply { 147 setContent { WearPermissionTheme { WearAppPermissionGroupsScreen(helper) } } 148 } 149 } 150 onPausenull151 override fun onPause() { 152 super.onPause() 153 helper.logAndClearToggledGroups() 154 } 155 156 @RequiresApi(Build.VERSION_CODES.S) onPermissionUsagesChangednull157 override fun onPermissionUsagesChanged() { 158 if (permissionUsages.usages.isEmpty()) { 159 return 160 } 161 if (getContext() == null) { 162 // Async result has come in after our context is gone. 163 return 164 } 165 wearViewModel.appPermissionUsages.value = 166 ArrayList<AppPermissionUsage>(permissionUsages.usages) 167 } 168 169 companion object { 170 const val LOG_TAG = "WearAppPermissionGroups" 171 } 172 } 173