1 /* 2 * Copyright (C) 2015 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; 18 19 import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS; 20 21 import static com.android.permissioncontroller.Constants.ACTION_MANAGE_AUTO_REVOKE; 22 import static com.android.permissioncontroller.Constants.EXTRA_SESSION_ID; 23 import static com.android.permissioncontroller.Constants.INVALID_SESSION_ID; 24 import static com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSION_GROUPS_FRAGMENT_AUTO_REVOKE_ACTION; 25 import static com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSION_GROUPS_FRAGMENT_AUTO_REVOKE_ACTION__ACTION__OPENED_FOR_AUTO_REVOKE; 26 import static com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSION_GROUPS_FRAGMENT_AUTO_REVOKE_ACTION__ACTION__OPENED_FROM_INTENT; 27 import static com.android.permissioncontroller.PermissionControllerStatsLog.AUTO_REVOKE_NOTIFICATION_CLICKED; 28 import static com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_USAGE_FRAGMENT_INTERACTION; 29 import static com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_USAGE_FRAGMENT_INTERACTION__ACTION__OPEN; 30 31 import android.app.ActionBar; 32 import android.content.Intent; 33 import android.content.pm.PackageManager; 34 import android.content.pm.PermissionInfo; 35 import android.os.Build; 36 import android.os.Bundle; 37 import android.os.UserHandle; 38 import android.util.Log; 39 import android.view.MenuItem; 40 41 import androidx.fragment.app.Fragment; 42 import androidx.navigation.NavGraph; 43 import androidx.navigation.NavInflater; 44 import androidx.navigation.Navigation; 45 import androidx.navigation.fragment.NavHostFragment; 46 47 import com.android.permissioncontroller.Constants; 48 import com.android.permissioncontroller.DeviceUtils; 49 import com.android.permissioncontroller.PermissionControllerStatsLog; 50 import com.android.permissioncontroller.R; 51 import com.android.permissioncontroller.permission.ui.auto.AutoAllAppPermissionsFragment; 52 import com.android.permissioncontroller.permission.ui.auto.AutoAppPermissionsFragment; 53 import com.android.permissioncontroller.permission.ui.auto.AutoManageStandardPermissionsFragment; 54 import com.android.permissioncontroller.permission.ui.auto.AutoPermissionAppsFragment; 55 import com.android.permissioncontroller.permission.ui.auto.AutoUnusedAppsFragment; 56 import com.android.permissioncontroller.permission.ui.handheld.AppPermissionFragment; 57 import com.android.permissioncontroller.permission.ui.handheld.AppPermissionGroupsFragment; 58 import com.android.permissioncontroller.permission.ui.handheld.HandheldUnusedAppsWrapperFragment; 59 import com.android.permissioncontroller.permission.ui.handheld.PermissionAppsFragment; 60 import com.android.permissioncontroller.permission.ui.handheld.dashboard.PermissionDetailsWrapperFragment; 61 import com.android.permissioncontroller.permission.ui.handheld.dashboard.PermissionUsageV2WrapperFragment; 62 import com.android.permissioncontroller.permission.ui.legacy.AppPermissionActivity; 63 import com.android.permissioncontroller.permission.ui.wear.AppPermissionsFragmentWear; 64 import com.android.permissioncontroller.permission.utils.KotlinUtils; 65 import com.android.permissioncontroller.permission.utils.Utils; 66 67 import java.util.Random; 68 69 /** 70 * Activity to review and manage permissions 71 */ 72 public final class ManagePermissionsActivity extends SettingsActivity { 73 private static final String LOG_TAG = ManagePermissionsActivity.class.getSimpleName(); 74 75 /** 76 * Name of the extra parameter that indicates whether or not to show all app permissions 77 */ 78 public static final String EXTRA_ALL_PERMISSIONS = 79 "com.android.permissioncontroller.extra.ALL_PERMISSIONS"; 80 81 /** 82 * Name of the extra parameter that is the fragment that called the current fragment. 83 */ 84 public static final String EXTRA_CALLER_NAME = 85 "com.android.permissioncontroller.extra.CALLER_NAME"; 86 87 // The permission group which was interacted with 88 public static final String EXTRA_RESULT_PERMISSION_INTERACTED = "com.android" 89 + ".permissioncontroller.extra.RESULT_PERMISSION_INTERACTED"; 90 /** 91 * The result of the permission in terms of {@link GrantPermissionsViewHandler.Result} 92 */ 93 public static final String EXTRA_RESULT_PERMISSION_RESULT = "com.android" 94 + ".permissioncontroller.extra.PERMISSION_RESULT"; 95 96 /** 97 * Whether to show system apps in UI receiving an intent containing this extra. 98 */ 99 public static final String EXTRA_SHOW_SYSTEM = "com.android" 100 + ".permissioncontroller.extra.SHOW_SYSTEM"; 101 102 /** 103 * The requestCode used when we decide not to use this activity, but instead launch 104 * another activity in our place. When that activity finishes, we set it's result 105 * as our result and then finish. 106 */ 107 private static final int PROXY_ACTIVITY_REQUEST_CODE = 5; 108 109 @Override onCreate(Bundle savedInstanceState)110 public void onCreate(Bundle savedInstanceState) { 111 if (DeviceUtils.isAuto(this)) { 112 // Automotive relies on a different theme. Apply before calling super so that 113 // fragments are restored properly on configuration changes. 114 setTheme(R.style.CarSettings); 115 } 116 super.onCreate(savedInstanceState); 117 118 // If this is not a phone (which uses the Navigation component), and there is a previous 119 // instance, re-use its Fragment instead of making a new one. 120 if ((DeviceUtils.isTelevision(this) || DeviceUtils.isAuto(this) 121 || DeviceUtils.isWear(this)) && savedInstanceState != null) { 122 return; 123 } 124 125 android.app.Fragment fragment = null; 126 Fragment androidXFragment = null; 127 String action = getIntent().getAction(); 128 129 getWindow().addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS); 130 131 long sessionId = getIntent().getLongExtra(Constants.EXTRA_SESSION_ID, INVALID_SESSION_ID); 132 while (sessionId == INVALID_SESSION_ID) { 133 sessionId = new Random().nextLong(); 134 } 135 136 int autoRevokeAction = 137 APP_PERMISSION_GROUPS_FRAGMENT_AUTO_REVOKE_ACTION__ACTION__OPENED_FOR_AUTO_REVOKE; 138 int openFromIntentAction = 139 APP_PERMISSION_GROUPS_FRAGMENT_AUTO_REVOKE_ACTION__ACTION__OPENED_FROM_INTENT; 140 141 String permissionName; 142 switch (action) { 143 case Intent.ACTION_MANAGE_PERMISSIONS: 144 Bundle arguments = new Bundle(); 145 arguments.putLong(EXTRA_SESSION_ID, sessionId); 146 if (DeviceUtils.isAuto(this)) { 147 androidXFragment = AutoManageStandardPermissionsFragment.newInstance(); 148 androidXFragment.setArguments(arguments); 149 } else if (DeviceUtils.isTelevision(this)) { 150 androidXFragment = 151 com.android.permissioncontroller.permission.ui.television 152 .ManagePermissionsFragment.newInstance(); 153 } else { 154 setContentView(R.layout.nav_host_fragment); 155 Navigation.findNavController(this, R.id.nav_host_fragment).setGraph( 156 R.navigation.nav_graph, arguments); 157 return; 158 159 } 160 break; 161 162 case Intent.ACTION_REVIEW_PERMISSION_USAGE: { 163 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) { 164 finishAfterTransition(); 165 return; 166 } 167 168 169 PermissionControllerStatsLog.write(PERMISSION_USAGE_FRAGMENT_INTERACTION, sessionId, 170 PERMISSION_USAGE_FRAGMENT_INTERACTION__ACTION__OPEN); 171 String groupName = getIntent().getStringExtra(Intent.EXTRA_PERMISSION_GROUP_NAME); 172 androidXFragment = PermissionUsageV2WrapperFragment.newInstance(groupName, 173 Long.MAX_VALUE, sessionId); 174 } break; 175 176 case Intent.ACTION_REVIEW_PERMISSION_HISTORY: { 177 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) { 178 finishAfterTransition(); 179 return; 180 } 181 182 String groupName = getIntent() 183 .getStringExtra(Intent.EXTRA_PERMISSION_GROUP_NAME); 184 boolean showSystem = getIntent() 185 .getBooleanExtra(EXTRA_SHOW_SYSTEM, false); 186 androidXFragment = PermissionDetailsWrapperFragment 187 .newInstance(groupName, Long.MAX_VALUE, showSystem, sessionId); 188 break; 189 } 190 191 case Intent.ACTION_MANAGE_APP_PERMISSION: { 192 if (DeviceUtils.isAuto(this) || DeviceUtils.isTelevision(this) 193 || DeviceUtils.isWear(this)) { 194 Intent compatIntent = new Intent(this, AppPermissionActivity.class); 195 compatIntent.putExtras(getIntent().getExtras()); 196 startActivityForResult(compatIntent, PROXY_ACTIVITY_REQUEST_CODE); 197 return; 198 } 199 String packageName = getIntent().getStringExtra(Intent.EXTRA_PACKAGE_NAME); 200 permissionName = getIntent().getStringExtra(Intent.EXTRA_PERMISSION_NAME); 201 String groupName = getIntent().getStringExtra(Intent.EXTRA_PERMISSION_GROUP_NAME); 202 UserHandle userHandle = getIntent().getParcelableExtra(Intent.EXTRA_USER); 203 String caller = getIntent().getStringExtra(EXTRA_CALLER_NAME); 204 205 Bundle args = AppPermissionFragment.createArgs(packageName, permissionName, 206 groupName, userHandle, caller, sessionId, null); 207 setNavGraph(args, R.id.app_permission); 208 return; 209 } 210 211 case Intent.ACTION_MANAGE_APP_PERMISSIONS: { 212 String packageName = getIntent().getStringExtra(Intent.EXTRA_PACKAGE_NAME); 213 if (packageName == null) { 214 Log.i(LOG_TAG, "Missing mandatory argument EXTRA_PACKAGE_NAME"); 215 finishAfterTransition(); 216 return; 217 } 218 219 UserHandle userHandle = getIntent().getParcelableExtra(Intent.EXTRA_USER); 220 if (userHandle == null) { 221 userHandle = UserHandle.of(UserHandle.myUserId()); 222 } 223 224 try { 225 int uid = getPackageManager().getApplicationInfoAsUser(packageName, 0, 226 userHandle).uid; 227 long settingsSessionId = getIntent().getLongExtra( 228 Intent.ACTION_AUTO_REVOKE_PERMISSIONS, INVALID_SESSION_ID); 229 if (settingsSessionId != INVALID_SESSION_ID) { 230 sessionId = settingsSessionId; 231 Log.i(LOG_TAG, "sessionId: " + sessionId 232 + " Reaching AppPermissionGroupsFragment for auto revoke. " 233 + "packageName: " + packageName + " uid " + uid); 234 PermissionControllerStatsLog.write( 235 APP_PERMISSION_GROUPS_FRAGMENT_AUTO_REVOKE_ACTION, sessionId, uid, 236 packageName, autoRevokeAction); 237 } else { 238 if (KotlinUtils.INSTANCE.isROrAutoRevokeEnabled(getApplication(), 239 packageName, userHandle)) { 240 Log.i(LOG_TAG, "sessionId: " + sessionId 241 + " Reaching AppPermissionGroupsFragment from intent. " 242 + "packageName " + packageName + " uid " + uid); 243 PermissionControllerStatsLog.write( 244 APP_PERMISSION_GROUPS_FRAGMENT_AUTO_REVOKE_ACTION, sessionId, 245 uid, packageName, openFromIntentAction); 246 } 247 } 248 } catch (PackageManager.NameNotFoundException e) { 249 // Do no logging 250 } 251 252 final boolean allPermissions = getIntent().getBooleanExtra( 253 EXTRA_ALL_PERMISSIONS, false); 254 255 256 if (DeviceUtils.isAuto(this)) { 257 if (allPermissions) { 258 androidXFragment = AutoAllAppPermissionsFragment.newInstance(packageName, 259 userHandle, sessionId); 260 } else { 261 androidXFragment = AutoAppPermissionsFragment.newInstance(packageName, 262 userHandle, sessionId, /* isSystemPermsScreen= */ true); 263 } 264 } else if (DeviceUtils.isWear(this)) { 265 androidXFragment = AppPermissionsFragmentWear.newInstance(packageName); 266 } else if (DeviceUtils.isTelevision(this)) { 267 androidXFragment = com.android.permissioncontroller.permission.ui.television 268 .AppPermissionsFragment.newInstance(packageName, userHandle); 269 } else { 270 Bundle args = AppPermissionGroupsFragment.createArgs(packageName, userHandle, 271 sessionId, /* isSystemPermsScreen= */ true); 272 setNavGraph(args, R.id.app_permission_groups); 273 return; 274 } 275 } break; 276 277 case Intent.ACTION_MANAGE_PERMISSION_APPS: { 278 permissionName = getIntent().getStringExtra(Intent.EXTRA_PERMISSION_NAME); 279 280 String permissionGroupName = getIntent().getStringExtra( 281 Intent.EXTRA_PERMISSION_GROUP_NAME); 282 if (permissionGroupName == null) { 283 try { 284 PermissionInfo permInfo = getPackageManager().getPermissionInfo( 285 permissionName, 0); 286 permissionGroupName = Utils.getGroupOfPermission(permInfo); 287 } catch (PackageManager.NameNotFoundException e) { 288 Log.i(LOG_TAG, "Permission " + permissionName + " does not exist"); 289 } 290 } 291 292 if (permissionName == null && permissionGroupName == null) { 293 Log.i(LOG_TAG, "Missing mandatory argument EXTRA_PERMISSION_NAME or" 294 + "EXTRA_PERMISSION_GROUP_NAME"); 295 finishAfterTransition(); 296 return; 297 } 298 if (DeviceUtils.isAuto(this)) { 299 androidXFragment = 300 AutoPermissionAppsFragment.newInstance(permissionName, sessionId); 301 } else if (DeviceUtils.isTelevision(this)) { 302 androidXFragment = com.android.permissioncontroller.permission.ui.television 303 .PermissionAppsFragment.newInstance(permissionName); 304 } else { 305 306 Bundle args = PermissionAppsFragment.createArgs(permissionGroupName, sessionId); 307 args.putString(Intent.EXTRA_PERMISSION_NAME, permissionName); 308 setNavGraph(args, R.id.permission_apps); 309 return; 310 } 311 } break; 312 313 case Intent.ACTION_MANAGE_UNUSED_APPS : 314 // fall through 315 case ACTION_MANAGE_AUTO_REVOKE: { 316 Log.i(LOG_TAG, "sessionId " + sessionId + " starting auto revoke fragment" 317 + " from notification"); 318 PermissionControllerStatsLog.write(AUTO_REVOKE_NOTIFICATION_CLICKED, sessionId); 319 320 if (DeviceUtils.isAuto(this)) { 321 androidXFragment = AutoUnusedAppsFragment.newInstance(); 322 androidXFragment.setArguments(UnusedAppsFragment.createArgs(sessionId)); 323 } else if (DeviceUtils.isWear(this) || DeviceUtils.isTelevision(this)) { 324 androidXFragment = HandheldUnusedAppsWrapperFragment.newInstance(); 325 androidXFragment.setArguments(UnusedAppsFragment.createArgs(sessionId)); 326 } else { 327 setNavGraph(UnusedAppsFragment.createArgs(sessionId), R.id.auto_revoke); 328 return; 329 } 330 } break; 331 332 default: { 333 Log.w(LOG_TAG, "Unrecognized action " + action); 334 finishAfterTransition(); 335 return; 336 } 337 } 338 339 if (fragment != null) { 340 getFragmentManager().beginTransaction().replace(android.R.id.content, fragment) 341 .commit(); 342 } else if (androidXFragment != null) { 343 getSupportFragmentManager().beginTransaction().replace(android.R.id.content, 344 androidXFragment).commit(); 345 } 346 } 347 setNavGraph(Bundle args, int startDestination)348 private void setNavGraph(Bundle args, int startDestination) { 349 setContentView(R.layout.nav_host_fragment); 350 NavHostFragment navHost = (NavHostFragment) getSupportFragmentManager() 351 .findFragmentById(R.id.nav_host_fragment); 352 NavInflater inflater = navHost.getNavController().getNavInflater(); 353 NavGraph graph = inflater.inflate(R.navigation.nav_graph); 354 graph.setStartDestination(startDestination); 355 navHost.getNavController().setGraph(graph, args); 356 } 357 358 @Override getActionBar()359 public ActionBar getActionBar() { 360 ActionBar ab = super.getActionBar(); 361 if (ab != null) { 362 ab.setHomeActionContentDescription(R.string.back); 363 } 364 return ab; 365 } 366 367 @Override onOptionsItemSelected(MenuItem item)368 public boolean onOptionsItemSelected(MenuItem item) { 369 // in automotive mode, there's no system wide back button, so need to add that 370 if (DeviceUtils.isAuto(this)) { 371 switch (item.getItemId()) { 372 case android.R.id.home: 373 onBackPressed(); 374 finishAfterTransition(); 375 return true; 376 default: 377 return super.onOptionsItemSelected(item); 378 } 379 } 380 return super.onOptionsItemSelected(item); 381 } 382 383 @Override onActivityResult(int requestCode, int resultCode, Intent data)384 protected void onActivityResult(int requestCode, int resultCode, Intent data) { 385 super.onActivityResult(requestCode, resultCode, data); 386 if (requestCode == PROXY_ACTIVITY_REQUEST_CODE) { 387 setResult(resultCode, data); 388 finishAfterTransition(); 389 } 390 } 391 392 } 393