• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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