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.content.Intent; 24 import android.content.pm.PackageInfo; 25 import android.content.pm.PackageManager; 26 import android.content.pm.PackageManager.NameNotFoundException; 27 import android.content.pm.PermissionInfo; 28 import android.content.res.Resources; 29 import android.graphics.drawable.Icon; 30 import android.hardware.camera2.utils.ArrayUtils; 31 import android.os.Bundle; 32 import android.text.SpannableString; 33 import android.text.style.ForegroundColorSpan; 34 import android.util.Log; 35 import android.view.KeyEvent; 36 import android.view.MotionEvent; 37 import android.view.View; 38 import android.view.Window; 39 import android.view.WindowManager; 40 41 import com.android.packageinstaller.R; 42 import com.android.packageinstaller.permission.model.AppPermissionGroup; 43 import com.android.packageinstaller.permission.model.AppPermissions; 44 import com.android.packageinstaller.permission.model.Permission; 45 import com.android.packageinstaller.permission.utils.SafetyNetLogger; 46 import com.android.packageinstaller.permission.utils.Utils; 47 48 import java.util.ArrayList; 49 import java.util.LinkedHashMap; 50 import java.util.List; 51 52 public class GrantPermissionsActivity extends OverlayTouchActivity 53 implements GrantPermissionsViewHandler.ResultListener { 54 55 private static final String LOG_TAG = "GrantPermissionsActivity"; 56 57 private String[] mRequestedPermissions; 58 private int[] mGrantResults; 59 60 private LinkedHashMap<String, GroupState> mRequestGrantPermissionGroups = new LinkedHashMap<>(); 61 62 private GrantPermissionsViewHandler mViewHandler; 63 private AppPermissions mAppPermissions; 64 65 boolean mResultSet; 66 67 @Override onCreate(Bundle icicle)68 public void onCreate(Bundle icicle) { 69 super.onCreate(icicle); 70 setFinishOnTouchOutside(false); 71 72 setTitle(R.string.permission_request_title); 73 74 if (Utils.isTelevision(this)) { 75 mViewHandler = new GrantPermissionsTvViewHandler(this).setResultListener(this); 76 } else { 77 mViewHandler = new GrantPermissionsDefaultViewHandler(this).setResultListener(this); 78 } 79 80 mRequestedPermissions = getIntent().getStringArrayExtra( 81 PackageManager.EXTRA_REQUEST_PERMISSIONS_NAMES); 82 if (mRequestedPermissions == null) { 83 mRequestedPermissions = new String[0]; 84 } 85 86 final int requestedPermCount = mRequestedPermissions.length; 87 mGrantResults = new int[requestedPermCount]; 88 89 if (requestedPermCount == 0) { 90 setResultAndFinish(); 91 return; 92 } 93 94 PackageInfo callingPackageInfo = getCallingPackageInfo(); 95 96 DevicePolicyManager devicePolicyManager = getSystemService(DevicePolicyManager.class); 97 final int permissionPolicy = devicePolicyManager.getPermissionPolicy(null); 98 99 // If calling package is null we default to deny all. 100 updateDefaultResults(callingPackageInfo, permissionPolicy); 101 102 if (callingPackageInfo == null) { 103 setResultAndFinish(); 104 return; 105 } 106 107 mAppPermissions = new AppPermissions(this, callingPackageInfo, null, false, 108 new Runnable() { 109 @Override 110 public void run() { 111 setResultAndFinish(); 112 } 113 }); 114 115 for (AppPermissionGroup group : mAppPermissions.getPermissionGroups()) { 116 boolean groupHasRequestedPermission = false; 117 for (String requestedPermission : mRequestedPermissions) { 118 if (group.hasPermission(requestedPermission)) { 119 groupHasRequestedPermission = true; 120 break; 121 } 122 } 123 if (!groupHasRequestedPermission) { 124 continue; 125 } 126 // We allow the user to choose only non-fixed permissions. A permission 127 // is fixed either by device policy or the user denying with prejudice. 128 if (!group.isUserFixed() && !group.isPolicyFixed()) { 129 switch (permissionPolicy) { 130 case DevicePolicyManager.PERMISSION_POLICY_AUTO_GRANT: { 131 if (!group.areRuntimePermissionsGranted()) { 132 group.grantRuntimePermissions(false); 133 } 134 group.setPolicyFixed(); 135 } break; 136 137 case DevicePolicyManager.PERMISSION_POLICY_AUTO_DENY: { 138 if (group.areRuntimePermissionsGranted()) { 139 group.revokeRuntimePermissions(false); 140 } 141 group.setPolicyFixed(); 142 } break; 143 144 default: { 145 if (!group.areRuntimePermissionsGranted()) { 146 mRequestGrantPermissionGroups.put(group.getName(), 147 new GroupState(group)); 148 } else { 149 group.grantRuntimePermissions(false); 150 updateGrantResults(group); 151 } 152 } break; 153 } 154 } else { 155 // if the permission is fixed, ensure that we return the right request result 156 updateGrantResults(group); 157 } 158 } 159 160 setContentView(mViewHandler.createView()); 161 162 Window window = getWindow(); 163 WindowManager.LayoutParams layoutParams = window.getAttributes(); 164 mViewHandler.updateWindowAttributes(layoutParams); 165 window.setAttributes(layoutParams); 166 167 if (!showNextPermissionGroupGrantRequest()) { 168 setResultAndFinish(); 169 } 170 } 171 172 @Override dispatchTouchEvent(MotionEvent ev)173 public boolean dispatchTouchEvent(MotionEvent ev) { 174 View rootView = getWindow().getDecorView(); 175 if (rootView.getTop() != 0) { 176 // We are animating the top view, need to compensate for that in motion events. 177 ev.setLocation(ev.getX(), ev.getY() - rootView.getTop()); 178 } 179 return super.dispatchTouchEvent(ev); 180 } 181 182 @Override onSaveInstanceState(Bundle outState)183 protected void onSaveInstanceState(Bundle outState) { 184 super.onSaveInstanceState(outState); 185 mViewHandler.saveInstanceState(outState); 186 } 187 188 @Override onRestoreInstanceState(Bundle savedInstanceState)189 protected void onRestoreInstanceState(Bundle savedInstanceState) { 190 super.onRestoreInstanceState(savedInstanceState); 191 mViewHandler.loadInstanceState(savedInstanceState); 192 } 193 showNextPermissionGroupGrantRequest()194 private boolean showNextPermissionGroupGrantRequest() { 195 final int groupCount = mRequestGrantPermissionGroups.size(); 196 197 int currentIndex = 0; 198 for (GroupState groupState : mRequestGrantPermissionGroups.values()) { 199 if (groupState.mState == GroupState.STATE_UNKNOWN) { 200 CharSequence appLabel = mAppPermissions.getAppLabel(); 201 SpannableString message = new SpannableString(getString( 202 R.string.permission_warning_template, appLabel, 203 groupState.mGroup.getDescription())); 204 // Set the permission message as the title so it can be announced. 205 setTitle(message); 206 // Color the app name. 207 int appLabelStart = message.toString().indexOf(appLabel.toString(), 0); 208 int appLabelLength = appLabel.length(); 209 int color = getColor(R.color.grant_permissions_app_color); 210 message.setSpan(new ForegroundColorSpan(color), appLabelStart, 211 appLabelStart + appLabelLength, 0); 212 213 // Set the new grant view 214 // TODO: Use a real message for the action. We need group action APIs 215 Resources resources; 216 try { 217 resources = getPackageManager().getResourcesForApplication( 218 groupState.mGroup.getIconPkg()); 219 } catch (NameNotFoundException e) { 220 // Fallback to system. 221 resources = Resources.getSystem(); 222 } 223 int icon = groupState.mGroup.getIconResId(); 224 225 mViewHandler.updateUi(groupState.mGroup.getName(), groupCount, currentIndex, 226 Icon.createWithResource(resources, icon), message, 227 groupState.mGroup.isUserSet()); 228 return true; 229 } 230 231 currentIndex++; 232 } 233 234 return false; 235 } 236 237 @Override onPermissionGrantResult(String name, boolean granted, boolean doNotAskAgain)238 public void onPermissionGrantResult(String name, boolean granted, boolean doNotAskAgain) { 239 if (isObscuredTouch()) { 240 showOverlayDialog(); 241 finish(); 242 return; 243 } 244 GroupState groupState = mRequestGrantPermissionGroups.get(name); 245 if (groupState.mGroup != null) { 246 if (granted) { 247 groupState.mGroup.grantRuntimePermissions(doNotAskAgain); 248 groupState.mState = GroupState.STATE_ALLOWED; 249 } else { 250 groupState.mGroup.revokeRuntimePermissions(doNotAskAgain); 251 groupState.mState = GroupState.STATE_DENIED; 252 } 253 updateGrantResults(groupState.mGroup); 254 } 255 if (!showNextPermissionGroupGrantRequest()) { 256 setResultAndFinish(); 257 } 258 } 259 updateGrantResults(AppPermissionGroup group)260 private void updateGrantResults(AppPermissionGroup group) { 261 for (Permission permission : group.getPermissions()) { 262 final int index = ArrayUtils.getArrayIndex( 263 mRequestedPermissions, permission.getName()); 264 if (index >= 0) { 265 mGrantResults[index] = permission.isGranted() ? PackageManager.PERMISSION_GRANTED 266 : PackageManager.PERMISSION_DENIED; 267 } 268 } 269 } 270 271 @Override onKeyDown(int keyCode, KeyEvent event)272 public boolean onKeyDown(int keyCode, KeyEvent event) { 273 // We do not allow backing out. 274 return keyCode == KeyEvent.KEYCODE_BACK; 275 } 276 277 @Override onKeyUp(int keyCode, KeyEvent event)278 public boolean onKeyUp(int keyCode, KeyEvent event) { 279 // We do not allow backing out. 280 return keyCode == KeyEvent.KEYCODE_BACK; 281 } 282 283 @Override finish()284 public void finish() { 285 setResultIfNeeded(RESULT_CANCELED); 286 super.finish(); 287 } 288 computePermissionGrantState(PackageInfo callingPackageInfo, String permission, int permissionPolicy)289 private int computePermissionGrantState(PackageInfo callingPackageInfo, 290 String permission, int permissionPolicy) { 291 boolean permissionRequested = false; 292 293 for (int i = 0; i < callingPackageInfo.requestedPermissions.length; i++) { 294 if (permission.equals(callingPackageInfo.requestedPermissions[i])) { 295 permissionRequested = true; 296 if ((callingPackageInfo.requestedPermissionsFlags[i] 297 & PackageInfo.REQUESTED_PERMISSION_GRANTED) != 0) { 298 return PERMISSION_GRANTED; 299 } 300 break; 301 } 302 } 303 304 if (!permissionRequested) { 305 return PERMISSION_DENIED; 306 } 307 308 try { 309 PermissionInfo pInfo = getPackageManager().getPermissionInfo(permission, 0); 310 if ((pInfo.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE) 311 != PermissionInfo.PROTECTION_DANGEROUS) { 312 return PERMISSION_DENIED; 313 } 314 } catch (NameNotFoundException e) { 315 return PERMISSION_DENIED; 316 } 317 318 switch (permissionPolicy) { 319 case DevicePolicyManager.PERMISSION_POLICY_AUTO_GRANT: { 320 return PERMISSION_GRANTED; 321 } 322 default: { 323 return PERMISSION_DENIED; 324 } 325 } 326 } 327 getCallingPackageInfo()328 private PackageInfo getCallingPackageInfo() { 329 try { 330 return getPackageManager().getPackageInfo(getCallingPackage(), 331 PackageManager.GET_PERMISSIONS); 332 } catch (NameNotFoundException e) { 333 Log.i(LOG_TAG, "No package: " + getCallingPackage(), e); 334 return null; 335 } 336 } 337 updateDefaultResults(PackageInfo callingPackageInfo, int permissionPolicy)338 private void updateDefaultResults(PackageInfo callingPackageInfo, int permissionPolicy) { 339 final int requestedPermCount = mRequestedPermissions.length; 340 for (int i = 0; i < requestedPermCount; i++) { 341 String permission = mRequestedPermissions[i]; 342 mGrantResults[i] = callingPackageInfo != null 343 ? computePermissionGrantState(callingPackageInfo, permission, permissionPolicy) 344 : PERMISSION_DENIED; 345 } 346 } 347 setResultIfNeeded(int resultCode)348 private void setResultIfNeeded(int resultCode) { 349 if (!mResultSet) { 350 mResultSet = true; 351 logRequestedPermissionGroups(); 352 Intent result = new Intent(PackageManager.ACTION_REQUEST_PERMISSIONS); 353 result.putExtra(PackageManager.EXTRA_REQUEST_PERMISSIONS_NAMES, mRequestedPermissions); 354 result.putExtra(PackageManager.EXTRA_REQUEST_PERMISSIONS_RESULTS, mGrantResults); 355 setResult(resultCode, result); 356 } 357 } 358 setResultAndFinish()359 private void setResultAndFinish() { 360 setResultIfNeeded(RESULT_OK); 361 finish(); 362 } 363 logRequestedPermissionGroups()364 private void logRequestedPermissionGroups() { 365 if (mRequestGrantPermissionGroups.isEmpty()) { 366 return; 367 } 368 369 final int groupCount = mRequestGrantPermissionGroups.size(); 370 List<AppPermissionGroup> groups = new ArrayList<>(groupCount); 371 for (GroupState groupState : mRequestGrantPermissionGroups.values()) { 372 groups.add(groupState.mGroup); 373 } 374 375 SafetyNetLogger.logPermissionsRequested(mAppPermissions.getPackageInfo(), groups); 376 } 377 378 private static final class GroupState { 379 static final int STATE_UNKNOWN = 0; 380 static final int STATE_ALLOWED = 1; 381 static final int STATE_DENIED = 2; 382 383 final AppPermissionGroup mGroup; 384 int mState = STATE_UNKNOWN; 385 GroupState(AppPermissionGroup group)386 GroupState(AppPermissionGroup group) { 387 mGroup = group; 388 } 389 } 390 } 391