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.packageinstaller.permission.ui; 18 19 import static android.content.pm.PackageManager.PERMISSION_DENIED; 20 import static android.content.pm.PackageManager.PERMISSION_GRANTED; 21 22 import android.app.admin.DevicePolicyManager; 23 import android.app.KeyguardManager; 24 import android.content.Intent; 25 import android.content.pm.PackageInfo; 26 import android.content.pm.PackageManager; 27 import android.content.pm.PackageManager.NameNotFoundException; 28 import android.content.pm.PackageParser; 29 import android.content.pm.PermissionInfo; 30 import android.content.res.Configuration; 31 import android.content.res.Resources; 32 import android.graphics.drawable.Icon; 33 import android.os.Build; 34 import android.os.Bundle; 35 import android.text.Html; 36 import android.text.Spanned; 37 import android.util.Log; 38 import android.view.KeyEvent; 39 import android.view.MotionEvent; 40 import android.view.View; 41 import android.view.Window; 42 import android.view.WindowManager; 43 44 import com.android.packageinstaller.DeviceUtils; 45 import com.android.packageinstaller.R; 46 import com.android.packageinstaller.permission.model.AppPermissionGroup; 47 import com.android.packageinstaller.permission.model.AppPermissions; 48 import com.android.packageinstaller.permission.model.Permission; 49 import com.android.packageinstaller.permission.ui.auto.GrantPermissionsAutoViewHandler; 50 import com.android.packageinstaller.permission.ui.handheld.GrantPermissionsViewHandlerImpl; 51 import com.android.packageinstaller.permission.utils.ArrayUtils; 52 import com.android.packageinstaller.permission.utils.EventLogger; 53 import com.android.packageinstaller.permission.utils.SafetyNetLogger; 54 55 import java.util.ArrayList; 56 import java.util.Arrays; 57 import java.util.LinkedHashMap; 58 import java.util.List; 59 60 public class GrantPermissionsActivity extends OverlayTouchActivity 61 implements GrantPermissionsViewHandler.ResultListener { 62 63 private static final String LOG_TAG = "GrantPermissionsActivity"; 64 65 private String[] mRequestedPermissions; 66 private int[] mGrantResults; 67 68 private LinkedHashMap<String, GroupState> mRequestGrantPermissionGroups = new LinkedHashMap<>(); 69 70 private GrantPermissionsViewHandler mViewHandler; 71 private AppPermissions mAppPermissions; 72 73 boolean mResultSet; 74 75 @Override onCreate(Bundle icicle)76 public void onCreate(Bundle icicle) { 77 super.onCreate(icicle); 78 setFinishOnTouchOutside(false); 79 80 getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED); 81 getWindow().addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON); 82 83 setTitle(R.string.permission_request_title); 84 85 if (DeviceUtils.isTelevision(this)) { 86 mViewHandler = new com.android.packageinstaller.permission.ui.television 87 .GrantPermissionsViewHandlerImpl(this, 88 getCallingPackage()).setResultListener(this); 89 } else if (DeviceUtils.isWear(this)) { 90 mViewHandler = new GrantPermissionsWatchViewHandler(this).setResultListener(this); 91 } else if (DeviceUtils.isAuto(this)) { 92 mViewHandler = new GrantPermissionsAutoViewHandler(this, getCallingPackage()) 93 .setResultListener(this); 94 } else { 95 mViewHandler = new com.android.packageinstaller.permission.ui.handheld 96 .GrantPermissionsViewHandlerImpl(this, getCallingPackage()) 97 .setResultListener(this); 98 } 99 100 mRequestedPermissions = getIntent().getStringArrayExtra( 101 PackageManager.EXTRA_REQUEST_PERMISSIONS_NAMES); 102 if (mRequestedPermissions == null) { 103 mRequestedPermissions = new String[0]; 104 } 105 106 final int requestedPermCount = mRequestedPermissions.length; 107 mGrantResults = new int[requestedPermCount]; 108 Arrays.fill(mGrantResults, PackageManager.PERMISSION_DENIED); 109 110 if (requestedPermCount == 0) { 111 setResultAndFinish(); 112 return; 113 } 114 115 PackageInfo callingPackageInfo = getCallingPackageInfo(); 116 117 if (callingPackageInfo == null || callingPackageInfo.requestedPermissions == null 118 || callingPackageInfo.requestedPermissions.length <= 0) { 119 setResultAndFinish(); 120 return; 121 } 122 123 // Don't allow legacy apps to request runtime permissions. 124 if (callingPackageInfo.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M) { 125 // Returning empty arrays means a cancellation. 126 mRequestedPermissions = new String[0]; 127 mGrantResults = new int[0]; 128 setResultAndFinish(); 129 return; 130 } 131 132 DevicePolicyManager devicePolicyManager = getSystemService(DevicePolicyManager.class); 133 final int permissionPolicy = devicePolicyManager.getPermissionPolicy(null); 134 135 // If calling package is null we default to deny all. 136 updateDefaultResults(callingPackageInfo, permissionPolicy); 137 138 mAppPermissions = new AppPermissions(this, callingPackageInfo, null, false, 139 new Runnable() { 140 @Override 141 public void run() { 142 setResultAndFinish(); 143 } 144 }); 145 146 for (String requestedPermission : mRequestedPermissions) { 147 AppPermissionGroup group = null; 148 for (AppPermissionGroup nextGroup : mAppPermissions.getPermissionGroups()) { 149 if (nextGroup.hasPermission(requestedPermission)) { 150 group = nextGroup; 151 break; 152 } 153 } 154 if (group == null) { 155 continue; 156 } 157 if (!group.isGrantingAllowed()) { 158 // Skip showing groups that we know cannot be granted. 159 continue; 160 } 161 // We allow the user to choose only non-fixed permissions. A permission 162 // is fixed either by device policy or the user denying with prejudice. 163 if (!group.isUserFixed() && !group.isPolicyFixed()) { 164 switch (permissionPolicy) { 165 case DevicePolicyManager.PERMISSION_POLICY_AUTO_GRANT: { 166 if (!group.areRuntimePermissionsGranted()) { 167 group.grantRuntimePermissions(false, computeAffectedPermissions( 168 callingPackageInfo, requestedPermission)); 169 } 170 group.setPolicyFixed(); 171 } break; 172 173 case DevicePolicyManager.PERMISSION_POLICY_AUTO_DENY: { 174 if (group.areRuntimePermissionsGranted()) { 175 group.revokeRuntimePermissions(false, computeAffectedPermissions( 176 callingPackageInfo, requestedPermission)); 177 } 178 group.setPolicyFixed(); 179 } break; 180 181 default: { 182 if (!group.areRuntimePermissionsGranted()) { 183 GroupState state = mRequestGrantPermissionGroups.get(group.getName()); 184 if (state == null) { 185 state = new GroupState(group); 186 mRequestGrantPermissionGroups.put(group.getName(), state); 187 } 188 String[] affectedPermissions = computeAffectedPermissions( 189 callingPackageInfo, requestedPermission); 190 if (affectedPermissions != null) { 191 for (String affectedPermission : affectedPermissions) { 192 state.affectedPermissions = ArrayUtils.appendString( 193 state.affectedPermissions, affectedPermission); 194 } 195 } 196 } else { 197 group.grantRuntimePermissions(false, computeAffectedPermissions( 198 callingPackageInfo, requestedPermission)); 199 updateGrantResults(group); 200 } 201 } break; 202 } 203 } else { 204 // if the permission is fixed, ensure that we return the right request result 205 updateGrantResults(group); 206 } 207 } 208 209 setContentView(mViewHandler.createView()); 210 211 Window window = getWindow(); 212 WindowManager.LayoutParams layoutParams = window.getAttributes(); 213 mViewHandler.updateWindowAttributes(layoutParams); 214 window.setAttributes(layoutParams); 215 216 if (!showNextPermissionGroupGrantRequest()) { 217 setResultAndFinish(); 218 } else if (icicle == null) { 219 int numRequestedPermissions = mRequestedPermissions.length; 220 for (int permissionNum = 0; permissionNum < numRequestedPermissions; permissionNum++) { 221 String permission = mRequestedPermissions[permissionNum]; 222 223 EventLogger.logPermissionRequested(this, permission, 224 mAppPermissions.getPackageInfo().packageName); 225 } 226 } 227 } 228 229 @Override onConfigurationChanged(Configuration newConfig)230 public void onConfigurationChanged(Configuration newConfig) { 231 super.onConfigurationChanged(newConfig); 232 // We need to relayout the window as dialog width may be 233 // different in landscape vs portrait which affect the min 234 // window height needed to show all content. We have to 235 // re-add the window to force it to be resized if needed. 236 View decor = getWindow().getDecorView(); 237 if (decor.getParent() != null) { 238 getWindowManager().removeViewImmediate(decor); 239 getWindowManager().addView(decor, decor.getLayoutParams()); 240 if (mViewHandler instanceof GrantPermissionsViewHandlerImpl) { 241 ((GrantPermissionsViewHandlerImpl) mViewHandler).onConfigurationChanged(); 242 } 243 } 244 } 245 246 @Override dispatchTouchEvent(MotionEvent ev)247 public boolean dispatchTouchEvent(MotionEvent ev) { 248 View rootView = getWindow().getDecorView(); 249 if (rootView.getTop() != 0) { 250 // We are animating the top view, need to compensate for that in motion events. 251 ev.setLocation(ev.getX(), ev.getY() - rootView.getTop()); 252 } 253 return super.dispatchTouchEvent(ev); 254 } 255 256 @Override onSaveInstanceState(Bundle outState)257 protected void onSaveInstanceState(Bundle outState) { 258 super.onSaveInstanceState(outState); 259 mViewHandler.saveInstanceState(outState); 260 } 261 262 @Override onRestoreInstanceState(Bundle savedInstanceState)263 protected void onRestoreInstanceState(Bundle savedInstanceState) { 264 super.onRestoreInstanceState(savedInstanceState); 265 mViewHandler.loadInstanceState(savedInstanceState); 266 } 267 showNextPermissionGroupGrantRequest()268 private boolean showNextPermissionGroupGrantRequest() { 269 final int groupCount = mRequestGrantPermissionGroups.size(); 270 271 int currentIndex = 0; 272 for (GroupState groupState : mRequestGrantPermissionGroups.values()) { 273 if (groupState.mState == GroupState.STATE_UNKNOWN) { 274 CharSequence appLabel = mAppPermissions.getAppLabel(); 275 Spanned message = Html.fromHtml(getString(R.string.permission_warning_template, 276 appLabel, groupState.mGroup.getDescription()), 0); 277 // Set the permission message as the title so it can be announced. 278 setTitle(message); 279 280 // Set the new grant view 281 // TODO: Use a real message for the action. We need group action APIs 282 Resources resources; 283 try { 284 resources = getPackageManager().getResourcesForApplication( 285 groupState.mGroup.getIconPkg()); 286 } catch (NameNotFoundException e) { 287 // Fallback to system. 288 resources = Resources.getSystem(); 289 } 290 int icon = groupState.mGroup.getIconResId(); 291 292 mViewHandler.updateUi(groupState.mGroup.getName(), groupCount, currentIndex, 293 Icon.createWithResource(resources, icon), message, 294 groupState.mGroup.isUserSet()); 295 return true; 296 } 297 298 currentIndex++; 299 } 300 301 return false; 302 } 303 304 @Override onPermissionGrantResult(String name, boolean granted, boolean doNotAskAgain)305 public void onPermissionGrantResult(String name, boolean granted, boolean doNotAskAgain) { 306 KeyguardManager kgm = getSystemService(KeyguardManager.class); 307 308 if (kgm.isDeviceLocked()) { 309 kgm.requestDismissKeyguard(this, new KeyguardManager.KeyguardDismissCallback() { 310 @Override 311 public void onDismissError() { 312 Log.e(LOG_TAG, "Cannot dismiss keyguard perm=" + name + " granted=" 313 + granted + " doNotAskAgain=" + doNotAskAgain); 314 } 315 316 @Override 317 public void onDismissCancelled() { 318 // do nothing (i.e. stay at the current permission group) 319 } 320 321 @Override 322 public void onDismissSucceeded() { 323 // Now the keyguard is dismissed, hence the device is not locked 324 // anymore 325 onPermissionGrantResult(name, granted, doNotAskAgain); 326 } 327 }); 328 329 return; 330 } 331 332 GroupState groupState = mRequestGrantPermissionGroups.get(name); 333 if (groupState.mGroup != null) { 334 if (granted) { 335 groupState.mGroup.grantRuntimePermissions(doNotAskAgain, 336 groupState.affectedPermissions); 337 groupState.mState = GroupState.STATE_ALLOWED; 338 } else { 339 groupState.mGroup.revokeRuntimePermissions(doNotAskAgain, 340 groupState.affectedPermissions); 341 groupState.mState = GroupState.STATE_DENIED; 342 343 int numRequestedPermissions = mRequestedPermissions.length; 344 for (int i = 0; i < numRequestedPermissions; i++) { 345 String permission = mRequestedPermissions[i]; 346 347 if (groupState.mGroup.hasPermission(permission)) { 348 EventLogger.logPermissionDenied(this, permission, 349 mAppPermissions.getPackageInfo().packageName); 350 } 351 } 352 } 353 updateGrantResults(groupState.mGroup); 354 } 355 if (!showNextPermissionGroupGrantRequest()) { 356 setResultAndFinish(); 357 } 358 } 359 updateGrantResults(AppPermissionGroup group)360 private void updateGrantResults(AppPermissionGroup group) { 361 for (Permission permission : group.getPermissions()) { 362 final int index = ArrayUtils.indexOf( 363 mRequestedPermissions, permission.getName()); 364 if (index >= 0) { 365 mGrantResults[index] = permission.isGranted() ? PackageManager.PERMISSION_GRANTED 366 : PackageManager.PERMISSION_DENIED; 367 } 368 } 369 } 370 371 @Override onKeyDown(int keyCode, KeyEvent event)372 public boolean onKeyDown(int keyCode, KeyEvent event) { 373 // We do not allow backing out. 374 return keyCode == KeyEvent.KEYCODE_BACK; 375 } 376 377 @Override onKeyUp(int keyCode, KeyEvent event)378 public boolean onKeyUp(int keyCode, KeyEvent event) { 379 // We do not allow backing out. 380 return keyCode == KeyEvent.KEYCODE_BACK; 381 } 382 383 @Override finish()384 public void finish() { 385 setResultIfNeeded(RESULT_CANCELED); 386 super.finish(); 387 } 388 computePermissionGrantState(PackageInfo callingPackageInfo, String permission, int permissionPolicy)389 private int computePermissionGrantState(PackageInfo callingPackageInfo, 390 String permission, int permissionPolicy) { 391 boolean permissionRequested = false; 392 393 for (int i = 0; i < callingPackageInfo.requestedPermissions.length; i++) { 394 if (permission.equals(callingPackageInfo.requestedPermissions[i])) { 395 permissionRequested = true; 396 if ((callingPackageInfo.requestedPermissionsFlags[i] 397 & PackageInfo.REQUESTED_PERMISSION_GRANTED) != 0) { 398 return PERMISSION_GRANTED; 399 } 400 break; 401 } 402 } 403 404 if (!permissionRequested) { 405 return PERMISSION_DENIED; 406 } 407 408 try { 409 PermissionInfo pInfo = getPackageManager().getPermissionInfo(permission, 0); 410 if ((pInfo.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE) 411 != PermissionInfo.PROTECTION_DANGEROUS) { 412 return PERMISSION_DENIED; 413 } 414 if ((pInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_INSTANT) == 0 415 && callingPackageInfo.applicationInfo.isInstantApp()) { 416 return PERMISSION_DENIED; 417 } 418 if ((pInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY) != 0 419 && callingPackageInfo.applicationInfo.targetSdkVersion 420 < Build.VERSION_CODES.M) { 421 return PERMISSION_DENIED; 422 } 423 } catch (NameNotFoundException e) { 424 return PERMISSION_DENIED; 425 } 426 427 switch (permissionPolicy) { 428 case DevicePolicyManager.PERMISSION_POLICY_AUTO_GRANT: { 429 return PERMISSION_GRANTED; 430 } 431 default: { 432 return PERMISSION_DENIED; 433 } 434 } 435 } 436 getCallingPackageInfo()437 private PackageInfo getCallingPackageInfo() { 438 try { 439 return getPackageManager().getPackageInfo(getCallingPackage(), 440 PackageManager.GET_PERMISSIONS); 441 } catch (NameNotFoundException e) { 442 Log.i(LOG_TAG, "No package: " + getCallingPackage(), e); 443 return null; 444 } 445 } 446 updateDefaultResults(PackageInfo callingPackageInfo, int permissionPolicy)447 private void updateDefaultResults(PackageInfo callingPackageInfo, int permissionPolicy) { 448 final int requestedPermCount = mRequestedPermissions.length; 449 for (int i = 0; i < requestedPermCount; i++) { 450 String permission = mRequestedPermissions[i]; 451 mGrantResults[i] = callingPackageInfo != null 452 ? computePermissionGrantState(callingPackageInfo, permission, permissionPolicy) 453 : PERMISSION_DENIED; 454 } 455 } 456 setResultIfNeeded(int resultCode)457 private void setResultIfNeeded(int resultCode) { 458 if (!mResultSet) { 459 mResultSet = true; 460 logRequestedPermissionGroups(); 461 Intent result = new Intent(PackageManager.ACTION_REQUEST_PERMISSIONS); 462 result.putExtra(PackageManager.EXTRA_REQUEST_PERMISSIONS_NAMES, mRequestedPermissions); 463 result.putExtra(PackageManager.EXTRA_REQUEST_PERMISSIONS_RESULTS, mGrantResults); 464 setResult(resultCode, result); 465 } 466 } 467 setResultAndFinish()468 private void setResultAndFinish() { 469 setResultIfNeeded(RESULT_OK); 470 finish(); 471 } 472 logRequestedPermissionGroups()473 private void logRequestedPermissionGroups() { 474 if (mRequestGrantPermissionGroups.isEmpty()) { 475 return; 476 } 477 478 final int groupCount = mRequestGrantPermissionGroups.size(); 479 List<AppPermissionGroup> groups = new ArrayList<>(groupCount); 480 for (GroupState groupState : mRequestGrantPermissionGroups.values()) { 481 groups.add(groupState.mGroup); 482 } 483 484 SafetyNetLogger.logPermissionsRequested(mAppPermissions.getPackageInfo(), groups); 485 } 486 computeAffectedPermissions(PackageInfo callingPkg, String permission)487 private static String[] computeAffectedPermissions(PackageInfo callingPkg, 488 String permission) { 489 // For <= N_MR1 apps all permissions are affected. 490 if (callingPkg.applicationInfo.targetSdkVersion <= Build.VERSION_CODES.N_MR1) { 491 return null; 492 } 493 494 // For N_MR1+ apps only the requested permission is affected with addition 495 // to splits of this permission applicable to apps targeting N_MR1. 496 String[] permissions = new String[] {permission}; 497 for (PackageParser.SplitPermissionInfo splitPerm : PackageParser.SPLIT_PERMISSIONS) { 498 if (splitPerm.targetSdk <= Build.VERSION_CODES.N_MR1 499 || callingPkg.applicationInfo.targetSdkVersion >= splitPerm.targetSdk 500 || !permission.equals(splitPerm.rootPerm)) { 501 continue; 502 } 503 for (int i = 0; i < splitPerm.newPerms.length; i++) { 504 final String newPerm = splitPerm.newPerms[i]; 505 permissions = ArrayUtils.appendString(permissions, newPerm); 506 } 507 } 508 509 return permissions; 510 } 511 512 private static final class GroupState { 513 static final int STATE_UNKNOWN = 0; 514 static final int STATE_ALLOWED = 1; 515 static final int STATE_DENIED = 2; 516 517 final AppPermissionGroup mGroup; 518 int mState = STATE_UNKNOWN; 519 String[] affectedPermissions; 520 GroupState(AppPermissionGroup group)521 GroupState(AppPermissionGroup group) { 522 mGroup = group; 523 } 524 } 525 } 526