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