• 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.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
21 
22 import static com.android.packageinstaller.PermissionControllerStatsLog.GRANT_PERMISSIONS_ACTIVITY_BUTTON_ACTIONS;
23 import static com.android.packageinstaller.PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__AUTO_DENIED;
24 import static com.android.packageinstaller.PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__AUTO_GRANTED;
25 import static com.android.packageinstaller.PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__IGNORED;
26 import static com.android.packageinstaller.PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__IGNORED_POLICY_FIXED;
27 import static com.android.packageinstaller.PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__IGNORED_RESTRICTED_PERMISSION;
28 import static com.android.packageinstaller.PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__IGNORED_USER_FIXED;
29 import static com.android.packageinstaller.PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_DENIED;
30 import static com.android.packageinstaller.PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_DENIED_WITH_PREJUDICE;
31 import static com.android.packageinstaller.PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_GRANTED;
32 import static com.android.packageinstaller.permission.ui.GrantPermissionsViewHandler.DENIED;
33 import static com.android.packageinstaller.permission.ui.GrantPermissionsViewHandler.DENIED_DO_NOT_ASK_AGAIN;
34 import static com.android.packageinstaller.permission.ui.GrantPermissionsViewHandler.GRANTED_ALWAYS;
35 import static com.android.packageinstaller.permission.ui.GrantPermissionsViewHandler.GRANTED_FOREGROUND_ONLY;
36 import static com.android.packageinstaller.permission.utils.Utils.getRequestMessage;
37 
38 import android.app.Activity;
39 import android.app.KeyguardManager;
40 import android.app.admin.DevicePolicyManager;
41 import android.content.Intent;
42 import android.content.pm.PackageInfo;
43 import android.content.pm.PackageManager;
44 import android.content.pm.PackageManager.NameNotFoundException;
45 import android.content.res.Resources;
46 import android.graphics.drawable.Icon;
47 import android.os.Build;
48 import android.os.Bundle;
49 import android.os.UserHandle;
50 import android.permission.PermissionManager;
51 import android.text.Html;
52 import android.text.Spanned;
53 import android.util.ArrayMap;
54 import android.util.Log;
55 import android.util.Pair;
56 import android.view.KeyEvent;
57 import android.view.MotionEvent;
58 import android.view.View;
59 import android.view.Window;
60 import android.view.WindowManager;
61 
62 import androidx.annotation.NonNull;
63 import androidx.annotation.Nullable;
64 
65 import com.android.packageinstaller.DeviceUtils;
66 import com.android.packageinstaller.PermissionControllerStatsLog;
67 import com.android.packageinstaller.permission.model.AppPermissionGroup;
68 import com.android.packageinstaller.permission.model.AppPermissions;
69 import com.android.packageinstaller.permission.model.Permission;
70 import com.android.packageinstaller.permission.ui.auto.GrantPermissionsAutoViewHandler;
71 import com.android.packageinstaller.permission.utils.ArrayUtils;
72 import com.android.packageinstaller.permission.utils.PackageRemovalMonitor;
73 import com.android.packageinstaller.permission.utils.SafetyNetLogger;
74 import com.android.permissioncontroller.R;
75 
76 import java.util.ArrayList;
77 import java.util.List;
78 import java.util.Random;
79 
80 public class GrantPermissionsActivity extends Activity
81         implements GrantPermissionsViewHandler.ResultListener {
82 
83     private static final String LOG_TAG = "GrantPermissionsActivity";
84 
85     private static final String KEY_REQUEST_ID = GrantPermissionsActivity.class.getName()
86             + "_REQUEST_ID";
87 
88     public static int NUM_BUTTONS = 5;
89     public static int LABEL_ALLOW_BUTTON = 0;
90     public static int LABEL_ALLOW_ALWAYS_BUTTON = 1;
91     public static int LABEL_ALLOW_FOREGROUND_BUTTON = 2;
92     public static int LABEL_DENY_BUTTON = 3;
93     public static int LABEL_DENY_AND_DONT_ASK_AGAIN_BUTTON = 4;
94 
95     /** Unique Id of a request */
96     private long mRequestId;
97 
98     private String[] mRequestedPermissions;
99     private CharSequence[] mButtonLabels;
100 
101     private ArrayMap<Pair<String, Boolean>, GroupState> mRequestGrantPermissionGroups =
102             new ArrayMap<>();
103 
104     private GrantPermissionsViewHandler mViewHandler;
105     private AppPermissions mAppPermissions;
106 
107     boolean mResultSet;
108 
109     /**
110      * Listens for changes to the permission of the app the permissions are currently getting
111      * granted to. {@code null} when unregistered.
112      */
113     private @Nullable PackageManager.OnPermissionsChangedListener mPermissionChangeListener;
114 
115     /**
116      * Listens for changes to the app the permissions are currently getting granted to. {@code null}
117      * when unregistered.
118      */
119     private @Nullable PackageRemovalMonitor mPackageRemovalMonitor;
120 
121     /** Package that requested the permission grant */
122     private String mCallingPackage;
123     /** uid of {@link #mCallingPackage} */
124     private int mCallingUid;
125 
getPermissionPolicy()126     private int getPermissionPolicy() {
127         DevicePolicyManager devicePolicyManager = getSystemService(DevicePolicyManager.class);
128         return devicePolicyManager.getPermissionPolicy(null);
129     }
130 
131     /**
132      * Try to add a single permission that is requested to be granted.
133      *
134      * <p>This does <u>not</u> expand the permissions into the {@link #computeAffectedPermissions
135      * affected permissions}.
136      *
137      * @param group The group the permission belongs to (might be a background permission group)
138      * @param permName The name of the permission to add
139      * @param isFirstInstance Is this the first time the groupStates get created
140      */
addRequestedPermissions(AppPermissionGroup group, String permName, boolean isFirstInstance)141     private void addRequestedPermissions(AppPermissionGroup group, String permName,
142             boolean isFirstInstance) {
143         if (!group.isGrantingAllowed()) {
144             reportRequestResult(permName,
145                     PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__IGNORED);
146             // Skip showing groups that we know cannot be granted.
147             return;
148         }
149 
150         Permission permission = group.getPermission(permName);
151 
152         // If the permission is restricted it does not show in the UI and
153         // is not added to the group at all, so check that first.
154         if (permission == null && ArrayUtils.contains(
155                 mAppPermissions.getPackageInfo().requestedPermissions, permName)) {
156             reportRequestResult(permName,
157                   PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__IGNORED_RESTRICTED_PERMISSION);
158             return;
159         // We allow the user to choose only non-fixed permissions. A permission
160         // is fixed either by device policy or the user denying with prejudice.
161         } else if (group.isUserFixed()) {
162             reportRequestResult(permName,
163                     PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__IGNORED_USER_FIXED);
164             return;
165         } else if (group.isPolicyFixed() && !group.areRuntimePermissionsGranted()
166                 || permission.isPolicyFixed()) {
167             reportRequestResult(permName,
168                     PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__IGNORED_POLICY_FIXED);
169             return;
170         }
171 
172         Pair<String, Boolean> groupKey = new Pair<>(group.getName(),
173                 group.isBackgroundGroup());
174 
175         GroupState state = mRequestGrantPermissionGroups.get(groupKey);
176         if (state == null) {
177             state = new GroupState(group);
178             mRequestGrantPermissionGroups.put(groupKey, state);
179         }
180         state.affectedPermissions = ArrayUtils.appendString(
181                 state.affectedPermissions, permName);
182 
183         boolean skipGroup = false;
184         switch (getPermissionPolicy()) {
185             case DevicePolicyManager.PERMISSION_POLICY_AUTO_GRANT: {
186                 final String[] filterPermissions = new String[]{permName};
187                 group.grantRuntimePermissions(false, filterPermissions);
188                 group.setPolicyFixed(filterPermissions);
189                 state.mState = GroupState.STATE_ALLOWED;
190                 skipGroup = true;
191 
192                 reportRequestResult(permName,
193                         PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__AUTO_GRANTED);
194             } break;
195 
196             case DevicePolicyManager.PERMISSION_POLICY_AUTO_DENY: {
197                 final String[] filterPermissions = new String[]{permName};
198                 group.setPolicyFixed(filterPermissions);
199                 state.mState = GroupState.STATE_DENIED;
200                 skipGroup = true;
201 
202                 reportRequestResult(permName,
203                         PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__AUTO_DENIED);
204             } break;
205 
206             default: {
207                 if (group.areRuntimePermissionsGranted()) {
208                     group.grantRuntimePermissions(false, new String[]{permName});
209                     state.mState = GroupState.STATE_ALLOWED;
210                     skipGroup = true;
211 
212                     reportRequestResult(permName,
213                             PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__AUTO_GRANTED);
214                 }
215             } break;
216         }
217 
218         if (skipGroup && isFirstInstance) {
219             // Only allow to skip groups when this is the first time the dialog was created.
220             // Otherwise the number of groups changes between instances of the dialog.
221             state.mState = GroupState.STATE_SKIPPED;
222         }
223     }
224 
225     /**
226      * Report the result of a grant of a permission.
227      *
228      * @param permission The permission that was granted or denied
229      * @param result The permission grant result
230      */
reportRequestResult(@onNull String permission, int result)231     private void reportRequestResult(@NonNull String permission, int result) {
232         boolean isImplicit = !ArrayUtils.contains(mRequestedPermissions, permission);
233 
234         Log.v(LOG_TAG,
235                 "Permission grant result requestId=" + mRequestId + " callingUid=" + mCallingUid
236                         + " callingPackage=" + mCallingPackage + " permission=" + permission
237                         + " isImplicit=" + isImplicit + " result=" + result);
238 
239         PermissionControllerStatsLog.write(
240                 PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED, mRequestId,
241                 mCallingUid, mCallingPackage, permission, isImplicit, result);
242     }
243 
244     /**
245      * Report the result of a grant of a permission.
246      *
247      * @param permissions The permissions that were granted or denied
248      * @param result The permission grant result
249      */
reportRequestResult(@onNull String[] permissions, int result)250     private void reportRequestResult(@NonNull String[] permissions, int result) {
251         for (String permission : permissions) {
252             reportRequestResult(permission, result);
253         }
254     }
255 
256     @Override
onCreate(Bundle icicle)257     public void onCreate(Bundle icicle) {
258         super.onCreate(icicle);
259 
260         if (icicle == null) {
261             mRequestId = new Random().nextLong();
262         } else {
263             mRequestId = icicle.getLong(KEY_REQUEST_ID);
264         }
265 
266         getWindow().addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
267 
268         // Cache this as this can only read on onCreate, not later.
269         mCallingPackage = getCallingPackage();
270 
271         SafetyNetLogger.logIfHasUndefinedPermissionGroup(getPackageManager(), mCallingPackage);
272 
273         setFinishOnTouchOutside(false);
274 
275         setTitle(R.string.permission_request_title);
276 
277         mRequestedPermissions = getIntent().getStringArrayExtra(
278                 PackageManager.EXTRA_REQUEST_PERMISSIONS_NAMES);
279         if (mRequestedPermissions == null) {
280             mRequestedPermissions = new String[0];
281         }
282 
283         final int requestedPermCount = mRequestedPermissions.length;
284 
285         if (requestedPermCount == 0) {
286             setResultAndFinish();
287             return;
288         }
289 
290         PackageInfo callingPackageInfo = getCallingPackageInfo();
291 
292         if (callingPackageInfo == null || callingPackageInfo.requestedPermissions == null
293                 || callingPackageInfo.requestedPermissions.length <= 0) {
294             setResultAndFinish();
295             return;
296         }
297 
298         // Don't allow legacy apps to request runtime permissions.
299         if (callingPackageInfo.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M) {
300             // Returning empty arrays means a cancellation.
301             mRequestedPermissions = new String[0];
302             setResultAndFinish();
303             return;
304         }
305 
306         mCallingUid = callingPackageInfo.applicationInfo.uid;
307 
308         UserHandle userHandle = UserHandle.getUserHandleForUid(mCallingUid);
309 
310         if (DeviceUtils.isTelevision(this)) {
311             mViewHandler = new com.android.packageinstaller.permission.ui.television
312                     .GrantPermissionsViewHandlerImpl(this,
313                     mCallingPackage).setResultListener(this);
314         } else if (DeviceUtils.isWear(this)) {
315             mViewHandler = new GrantPermissionsWatchViewHandler(this).setResultListener(this);
316         } else if (DeviceUtils.isAuto(this)) {
317             mViewHandler = new GrantPermissionsAutoViewHandler(this, mCallingPackage, userHandle)
318                     .setResultListener(this);
319         } else {
320             mViewHandler = new com.android.packageinstaller.permission.ui.handheld
321                     .GrantPermissionsViewHandlerImpl(this, mCallingPackage, userHandle)
322                     .setResultListener(this);
323         }
324 
325         mAppPermissions = new AppPermissions(this, callingPackageInfo, false,
326                 new Runnable() {
327                     @Override
328                     public void run() {
329                         setResultAndFinish();
330                     }
331                 });
332 
333         for (String requestedPermission : mRequestedPermissions) {
334             if (requestedPermission == null) {
335                 continue;
336             }
337 
338             ArrayList<String> affectedPermissions =
339                     computeAffectedPermissions(requestedPermission);
340 
341             int numAffectedPermissions = affectedPermissions.size();
342             for (int i = 0; i < numAffectedPermissions; i++) {
343                 AppPermissionGroup group =
344                         mAppPermissions.getGroupForPermission(affectedPermissions.get(i));
345                 if (group == null) {
346                     reportRequestResult(affectedPermissions.get(i),
347                             PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__IGNORED);
348 
349                     continue;
350                 }
351 
352                 addRequestedPermissions(group, affectedPermissions.get(i), icicle == null);
353             }
354         }
355 
356         int numGroupStates = mRequestGrantPermissionGroups.size();
357         for (int groupStateNum = 0; groupStateNum < numGroupStates; groupStateNum++) {
358             GroupState groupState = mRequestGrantPermissionGroups.valueAt(groupStateNum);
359             AppPermissionGroup group = groupState.mGroup;
360 
361             // Restore permission group state after lifecycle events
362             if (icicle != null) {
363                 groupState.mState = icicle.getInt(
364                         getInstanceStateKey(mRequestGrantPermissionGroups.keyAt(groupStateNum)),
365                         groupState.mState);
366             }
367 
368             // Do not attempt to grant background access if foreground access is not either already
369             // granted or requested
370             if (group.isBackgroundGroup()) {
371                 // Check if a foreground permission is already granted
372                 boolean foregroundGroupAlreadyGranted = mAppPermissions.getPermissionGroup(
373                         group.getName()).areRuntimePermissionsGranted();
374                 boolean hasForegroundRequest = (getForegroundGroupState(group.getName()) != null);
375 
376                 if (!foregroundGroupAlreadyGranted && !hasForegroundRequest) {
377                     // The background permission cannot be granted at this time
378                     int numPermissions = groupState.affectedPermissions.length;
379                     for (int permissionNum = 0; permissionNum < numPermissions; permissionNum++) {
380                         Log.w(LOG_TAG,
381                                 "Cannot grant " + groupState.affectedPermissions[permissionNum]
382                                         + " as the matching foreground permission is not already "
383                                         + "granted.");
384                     }
385 
386                     groupState.mState = GroupState.STATE_SKIPPED;
387 
388                     reportRequestResult(groupState.affectedPermissions,
389                             PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__IGNORED);
390                 }
391             }
392         }
393 
394         setContentView(mViewHandler.createView());
395 
396         Window window = getWindow();
397         WindowManager.LayoutParams layoutParams = window.getAttributes();
398         mViewHandler.updateWindowAttributes(layoutParams);
399         window.setAttributes(layoutParams);
400 
401         // Restore UI state after lifecycle events. This has to be before
402         // showNextPermissionGroupGrantRequest is called. showNextPermissionGroupGrantRequest might
403         // update the UI and the UI behaves differently for updates and initial creations.
404         if (icicle != null) {
405             mViewHandler.loadInstanceState(icicle);
406         }
407 
408         if (!showNextPermissionGroupGrantRequest()) {
409             setResultAndFinish();
410         }
411     }
412 
413     /**
414      * Update the {@link #mRequestedPermissions} if the system reports them as granted.
415      *
416      * <p>This also updates the {@link #mAppPermissions} state and switches to the next group grant
417      * request if the current group becomes granted.
418      */
updateIfPermissionsWereGranted()419     private void updateIfPermissionsWereGranted() {
420         PackageManager pm = getPackageManager();
421 
422         boolean mightShowNextGroup = true;
423         int numGroupStates = mRequestGrantPermissionGroups.size();
424         for (int i = 0; i < numGroupStates; i++) {
425             GroupState groupState = mRequestGrantPermissionGroups.valueAt(i);
426 
427             if (groupState == null || groupState.mState != GroupState.STATE_UNKNOWN) {
428                 // Group has already been approved / denied via the UI by the user
429                 continue;
430             }
431 
432             boolean allAffectedPermissionsOfThisGroupAreGranted = true;
433 
434             if (groupState.affectedPermissions == null) {
435                 // It is not clear which permissions belong to this group, hence never skip this
436                 // view
437                 allAffectedPermissionsOfThisGroupAreGranted = false;
438             } else {
439                 for (int permNum = 0; permNum < groupState.affectedPermissions.length;
440                         permNum++) {
441                     if (pm.checkPermission(groupState.affectedPermissions[permNum], mCallingPackage)
442                             == PERMISSION_DENIED) {
443                         allAffectedPermissionsOfThisGroupAreGranted = false;
444                         break;
445                     }
446                 }
447             }
448 
449             if (allAffectedPermissionsOfThisGroupAreGranted) {
450                 groupState.mState = GroupState.STATE_ALLOWED;
451 
452                 if (mightShowNextGroup) {
453                     // The UI currently displays the first group with
454                     // mState == STATE_UNKNOWN. So we are switching to next group until we
455                     // could not allow a group that was still unknown
456                     if (!showNextPermissionGroupGrantRequest()) {
457                         setResultAndFinish();
458                     }
459                 }
460             } else {
461                 mightShowNextGroup = false;
462             }
463         }
464     }
465 
466     @Override
onStart()467     protected void onStart() {
468         super.onStart();
469 
470         try {
471             mPermissionChangeListener = new PermissionChangeListener();
472         } catch (NameNotFoundException e) {
473             setResultAndFinish();
474             return;
475         }
476         PackageManager pm = getPackageManager();
477         pm.addOnPermissionsChangeListener(mPermissionChangeListener);
478 
479         // get notified when the package is removed
480         mPackageRemovalMonitor = new PackageRemovalMonitor(this, mCallingPackage) {
481             @Override
482             public void onPackageRemoved() {
483                 Log.w(LOG_TAG, mCallingPackage + " was uninstalled");
484 
485                 finish();
486             }
487         };
488         mPackageRemovalMonitor.register();
489 
490         // check if the package was removed while this activity was not started
491         try {
492             pm.getPackageInfo(mCallingPackage, 0);
493         } catch (NameNotFoundException e) {
494             Log.w(LOG_TAG, mCallingPackage + " was uninstalled while this activity was stopped", e);
495             finish();
496         }
497 
498         updateIfPermissionsWereGranted();
499     }
500 
501     @Override
onStop()502     protected void onStop() {
503         super.onStop();
504 
505         if (mPackageRemovalMonitor != null) {
506             mPackageRemovalMonitor.unregister();
507             mPackageRemovalMonitor = null;
508         }
509 
510         if (mPermissionChangeListener != null) {
511             getPackageManager().removeOnPermissionsChangeListener(mPermissionChangeListener);
512             mPermissionChangeListener = null;
513         }
514     }
515 
516     @Override
dispatchTouchEvent(MotionEvent ev)517     public boolean dispatchTouchEvent(MotionEvent ev) {
518         View rootView = getWindow().getDecorView();
519         if (rootView.getTop() != 0) {
520             // We are animating the top view, need to compensate for that in motion events.
521             ev.setLocation(ev.getX(), ev.getY() - rootView.getTop());
522         }
523         return super.dispatchTouchEvent(ev);
524     }
525 
526     /**
527      * Compose a key that stores the GroupState.mState in the instance state.
528      *
529      * @param requestGrantPermissionGroupsKey The key of the permission group
530      *
531      * @return A unique key to be used in the instance state
532      */
getInstanceStateKey( Pair<String, Boolean> requestGrantPermissionGroupsKey)533     private static String getInstanceStateKey(
534             Pair<String, Boolean> requestGrantPermissionGroupsKey) {
535         return GrantPermissionsActivity.class.getName() + "_"
536                 + requestGrantPermissionGroupsKey.first + "_"
537                 + requestGrantPermissionGroupsKey.second;
538     }
539 
540     @Override
onSaveInstanceState(Bundle outState)541     protected void onSaveInstanceState(Bundle outState) {
542         super.onSaveInstanceState(outState);
543 
544         mViewHandler.saveInstanceState(outState);
545 
546         outState.putLong(KEY_REQUEST_ID, mRequestId);
547 
548         int numGroups = mRequestGrantPermissionGroups.size();
549         for (int i = 0; i < numGroups; i++) {
550             int state = mRequestGrantPermissionGroups.valueAt(i).mState;
551 
552             if (state != GroupState.STATE_UNKNOWN) {
553                 outState.putInt(getInstanceStateKey(mRequestGrantPermissionGroups.keyAt(i)), state);
554             }
555         }
556     }
557 
558     /**
559      * @return the background group state for the permission group with the {@code name}
560      */
getBackgroundGroupState(String name)561     private GroupState getBackgroundGroupState(String name) {
562         return mRequestGrantPermissionGroups.get(new Pair<>(name, true));
563     }
564 
565     /**
566      * @return the foreground group state for the permission group with the {@code name}
567      */
getForegroundGroupState(String name)568     private GroupState getForegroundGroupState(String name) {
569         return mRequestGrantPermissionGroups.get(new Pair<>(name, false));
570     }
571 
shouldShowRequestForGroupState(GroupState groupState)572     private boolean shouldShowRequestForGroupState(GroupState groupState) {
573         if (groupState.mState == GroupState.STATE_SKIPPED) {
574             return false;
575         }
576 
577         GroupState foregroundGroup = getForegroundGroupState(groupState.mGroup.getName());
578         if (groupState.mGroup.isBackgroundGroup()
579                 && (foregroundGroup != null && shouldShowRequestForGroupState(foregroundGroup))) {
580             // If an app requests both foreground and background permissions of the same group,
581             // we only show one request
582             return false;
583         }
584 
585         return true;
586     }
587 
showNextPermissionGroupGrantRequest()588     private boolean showNextPermissionGroupGrantRequest() {
589         int numGroupStates = mRequestGrantPermissionGroups.size();
590         int numGrantRequests = 0;
591         for (int i = 0; i < numGroupStates; i++) {
592             if (shouldShowRequestForGroupState(mRequestGrantPermissionGroups.valueAt(i))) {
593                 numGrantRequests++;
594             }
595         }
596 
597         int currentIndex = 0;
598         for (GroupState groupState : mRequestGrantPermissionGroups.values()) {
599             if (!shouldShowRequestForGroupState(groupState)) {
600                 continue;
601             }
602 
603             if (groupState.mState == GroupState.STATE_UNKNOWN) {
604                 GroupState foregroundGroupState;
605                 GroupState backgroundGroupState;
606                 if (groupState.mGroup.isBackgroundGroup()) {
607                     backgroundGroupState = groupState;
608                     foregroundGroupState = getForegroundGroupState(groupState.mGroup.getName());
609                 } else {
610                     foregroundGroupState = groupState;
611                     backgroundGroupState = getBackgroundGroupState(groupState.mGroup.getName());
612                 }
613 
614                 CharSequence appLabel = mAppPermissions.getAppLabel();
615 
616                 Icon icon;
617                 try {
618                     icon = Icon.createWithResource(groupState.mGroup.getIconPkg(),
619                             groupState.mGroup.getIconResId());
620                 } catch (Resources.NotFoundException e) {
621                     Log.e(LOG_TAG, "Cannot load icon for group" + groupState.mGroup.getName(), e);
622                     icon = null;
623                 }
624 
625                 // If no background permissions are granted yet, we need to ask for background
626                 // permissions
627                 boolean needBackgroundPermission = false;
628                 boolean isBackgroundPermissionUserSet = false;
629                 if (backgroundGroupState != null) {
630                     if (!backgroundGroupState.mGroup.areRuntimePermissionsGranted()) {
631                         needBackgroundPermission = true;
632                         isBackgroundPermissionUserSet = backgroundGroupState.mGroup.isUserSet();
633                     }
634                 }
635 
636                 // If no foreground permissions are granted yet, we need to ask for foreground
637                 // permissions
638                 boolean needForegroundPermission = false;
639                 boolean isForegroundPermissionUserSet = false;
640                 if (foregroundGroupState != null) {
641                     if (!foregroundGroupState.mGroup.areRuntimePermissionsGranted()) {
642                         needForegroundPermission = true;
643                         isForegroundPermissionUserSet = foregroundGroupState.mGroup.isUserSet();
644                     }
645                 }
646 
647                 // The button doesn't show when its label is null
648                 mButtonLabels = new CharSequence[NUM_BUTTONS];
649                 mButtonLabels[LABEL_ALLOW_BUTTON] = getString(R.string.grant_dialog_button_allow);
650                 mButtonLabels[LABEL_ALLOW_ALWAYS_BUTTON] = null;
651                 mButtonLabels[LABEL_ALLOW_FOREGROUND_BUTTON] = null;
652                 mButtonLabels[LABEL_DENY_BUTTON] = getString(R.string.grant_dialog_button_deny);
653                 if (isForegroundPermissionUserSet || isBackgroundPermissionUserSet) {
654                     mButtonLabels[LABEL_DENY_AND_DONT_ASK_AGAIN_BUTTON] =
655                             getString(R.string.grant_dialog_button_deny_and_dont_ask_again);
656                 } else {
657                     mButtonLabels[LABEL_DENY_AND_DONT_ASK_AGAIN_BUTTON] = null;
658                 }
659 
660                 int messageId;
661                 int detailMessageId = 0;
662                 if (needForegroundPermission) {
663                     messageId = groupState.mGroup.getRequest();
664 
665                     if (groupState.mGroup.hasPermissionWithBackgroundMode()) {
666                         mButtonLabels[LABEL_ALLOW_BUTTON] = null;
667                         mButtonLabels[LABEL_ALLOW_FOREGROUND_BUTTON] =
668                                 getString(R.string.grant_dialog_button_allow_foreground);
669                         if (needBackgroundPermission) {
670                             mButtonLabels[LABEL_ALLOW_ALWAYS_BUTTON] =
671                                     getString(R.string.grant_dialog_button_allow_always);
672                             if (isForegroundPermissionUserSet || isBackgroundPermissionUserSet) {
673                                 mButtonLabels[LABEL_DENY_BUTTON] = null;
674                             }
675                         }
676                     } else {
677                         detailMessageId = groupState.mGroup.getRequestDetail();
678                     }
679                 } else {
680                     if (needBackgroundPermission) {
681                         messageId = groupState.mGroup.getBackgroundRequest();
682                         detailMessageId = groupState.mGroup.getBackgroundRequestDetail();
683                         mButtonLabels[LABEL_ALLOW_BUTTON] =
684                                 getString(R.string.grant_dialog_button_allow_background);
685                         mButtonLabels[LABEL_DENY_BUTTON] =
686                                 getString(R.string.grant_dialog_button_deny_background);
687                         mButtonLabels[LABEL_DENY_AND_DONT_ASK_AGAIN_BUTTON] =
688                                 getString(R.string
689                                         .grant_dialog_button_deny_background_and_dont_ask_again);
690                     } else {
691                         // Not reached as the permissions should be auto-granted
692                         return false;
693                     }
694                 }
695 
696                 CharSequence message = getRequestMessage(appLabel, groupState.mGroup, this,
697                         messageId);
698 
699                 Spanned detailMessage = null;
700                 if (detailMessageId != 0) {
701                     try {
702                         detailMessage = Html.fromHtml(
703                                 getPackageManager().getResourcesForApplication(
704                                         groupState.mGroup.getDeclaringPackage()).getString(
705                                         detailMessageId), 0);
706                     } catch (NameNotFoundException ignored) {
707                     }
708                 }
709 
710                 // Set the permission message as the title so it can be announced.
711                 setTitle(message);
712 
713                 mViewHandler.updateUi(groupState.mGroup.getName(), numGrantRequests, currentIndex,
714                         icon, message, detailMessage, mButtonLabels);
715 
716                 return true;
717             }
718 
719             if (groupState.mState != GroupState.STATE_SKIPPED) {
720                 currentIndex++;
721             }
722         }
723 
724         return false;
725     }
726 
727     @Override
onPermissionGrantResult(String name, @GrantPermissionsViewHandler.Result int result)728     public void onPermissionGrantResult(String name,
729             @GrantPermissionsViewHandler.Result int result) {
730         logGrantPermissionActivityButtons(name, result);
731         GroupState foregroundGroupState = getForegroundGroupState(name);
732         GroupState backgroundGroupState = getBackgroundGroupState(name);
733 
734         if (result == GRANTED_ALWAYS || result == GRANTED_FOREGROUND_ONLY
735                 || result == DENIED_DO_NOT_ASK_AGAIN) {
736             KeyguardManager kgm = getSystemService(KeyguardManager.class);
737 
738             if (kgm.isDeviceLocked()) {
739                 kgm.requestDismissKeyguard(this, new KeyguardManager.KeyguardDismissCallback() {
740                             @Override
741                             public void onDismissError() {
742                                 Log.e(LOG_TAG, "Cannot dismiss keyguard perm=" + name + " result="
743                                         + result);
744                             }
745 
746                             @Override
747                             public void onDismissCancelled() {
748                                 // do nothing (i.e. stay at the current permission group)
749                             }
750 
751                             @Override
752                             public void onDismissSucceeded() {
753                                 // Now the keyguard is dismissed, hence the device is not locked
754                                 // anymore
755                                 onPermissionGrantResult(name, result);
756                             }
757                         });
758 
759                 return;
760             }
761         }
762 
763         switch (result) {
764             case GRANTED_ALWAYS :
765                 if (foregroundGroupState != null) {
766                     onPermissionGrantResultSingleState(foregroundGroupState, true, false);
767                 }
768                 if (backgroundGroupState != null) {
769                     onPermissionGrantResultSingleState(backgroundGroupState, true, false);
770                 }
771                 break;
772             case GRANTED_FOREGROUND_ONLY :
773                 if (foregroundGroupState != null) {
774                     onPermissionGrantResultSingleState(foregroundGroupState, true, false);
775                 }
776                 if (backgroundGroupState != null) {
777                     onPermissionGrantResultSingleState(backgroundGroupState, false, false);
778                 }
779                 break;
780             case DENIED :
781                 if (foregroundGroupState != null) {
782                     onPermissionGrantResultSingleState(foregroundGroupState, false, false);
783                 }
784                 if (backgroundGroupState != null) {
785                     onPermissionGrantResultSingleState(backgroundGroupState, false, false);
786                 }
787                 break;
788             case DENIED_DO_NOT_ASK_AGAIN :
789                 if (foregroundGroupState != null) {
790                     onPermissionGrantResultSingleState(foregroundGroupState, false, true);
791                 }
792                 if (backgroundGroupState != null) {
793                     onPermissionGrantResultSingleState(backgroundGroupState, false, true);
794                 }
795                 break;
796         }
797 
798         if (!showNextPermissionGroupGrantRequest()) {
799             setResultAndFinish();
800         }
801     }
802 
803     /**
804      * Grants or revoked the affected permissions for a single {@link groupState}.
805      *
806      * @param groupState The group state with the permissions to grant/revoke
807      * @param granted {@code true} if the permissions should be granted, {@code false} if they
808      *        should be revoked
809      * @param doNotAskAgain if the permissions should be revoked should be app be allowed to ask
810      *        again for the same permissions?
811      */
onPermissionGrantResultSingleState(GroupState groupState, boolean granted, boolean doNotAskAgain)812     private void onPermissionGrantResultSingleState(GroupState groupState, boolean granted,
813             boolean doNotAskAgain) {
814         if (groupState != null && groupState.mGroup != null
815                 && groupState.mState == GroupState.STATE_UNKNOWN) {
816             if (granted) {
817                 groupState.mGroup.grantRuntimePermissions(doNotAskAgain,
818                         groupState.affectedPermissions);
819                 groupState.mState = GroupState.STATE_ALLOWED;
820 
821                 reportRequestResult(groupState.affectedPermissions,
822                         PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_GRANTED);
823             } else {
824                 groupState.mGroup.revokeRuntimePermissions(doNotAskAgain,
825                         groupState.affectedPermissions);
826                 groupState.mState = GroupState.STATE_DENIED;
827 
828                 reportRequestResult(groupState.affectedPermissions, doNotAskAgain
829                         ?
830                         PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_DENIED_WITH_PREJUDICE
831                         : PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_DENIED);
832             }
833         }
834     }
835 
836     @Override
onKeyDown(int keyCode, KeyEvent event)837     public boolean onKeyDown(int keyCode, KeyEvent event)  {
838         // We do not allow backing out.
839         return keyCode == KeyEvent.KEYCODE_BACK;
840     }
841 
842     @Override
onKeyUp(int keyCode, KeyEvent event)843     public boolean onKeyUp(int keyCode, KeyEvent event)  {
844         // We do not allow backing out.
845         return keyCode == KeyEvent.KEYCODE_BACK;
846     }
847 
848     @Override
finish()849     public void finish() {
850         setResultIfNeeded(RESULT_CANCELED);
851         super.finish();
852     }
853 
getCallingPackageInfo()854     private PackageInfo getCallingPackageInfo() {
855         try {
856             return getPackageManager().getPackageInfo(mCallingPackage,
857                     PackageManager.GET_PERMISSIONS);
858         } catch (NameNotFoundException e) {
859             Log.i(LOG_TAG, "No package: " + mCallingPackage, e);
860             return null;
861         }
862     }
863 
setResultIfNeeded(int resultCode)864     private void setResultIfNeeded(int resultCode) {
865         if (!mResultSet) {
866             mResultSet = true;
867             logRequestedPermissionGroups();
868             Intent result = new Intent(PackageManager.ACTION_REQUEST_PERMISSIONS);
869             result.putExtra(PackageManager.EXTRA_REQUEST_PERMISSIONS_NAMES, mRequestedPermissions);
870 
871             PackageManager pm = getPackageManager();
872             int numRequestedPermissions = mRequestedPermissions.length;
873             int[] grantResults = new int[numRequestedPermissions];
874             for (int i = 0; i < numRequestedPermissions; i++) {
875                 grantResults[i] = pm.checkPermission(mRequestedPermissions[i], mCallingPackage);
876             }
877 
878             result.putExtra(PackageManager.EXTRA_REQUEST_PERMISSIONS_RESULTS, grantResults);
879             setResult(resultCode, result);
880         }
881     }
882 
setResultAndFinish()883     private void setResultAndFinish() {
884         setResultIfNeeded(RESULT_OK);
885         finish();
886     }
887 
logRequestedPermissionGroups()888     private void logRequestedPermissionGroups() {
889         if (mRequestGrantPermissionGroups.isEmpty()) {
890             return;
891         }
892 
893         final int groupCount = mRequestGrantPermissionGroups.size();
894         List<AppPermissionGroup> groups = new ArrayList<>(groupCount);
895         for (GroupState groupState : mRequestGrantPermissionGroups.values()) {
896             groups.add(groupState.mGroup);
897         }
898 
899         SafetyNetLogger.logPermissionsRequested(mAppPermissions.getPackageInfo(), groups);
900     }
901 
902     /**
903      * Get the actually requested permissions when a permission is requested.
904      *
905      * <p>>In some cases requesting to grant a single permission requires the system to grant
906      * additional permissions. E.g. before N-MR1 a single permission of a group caused the whole
907      * group to be granted. Another case are permissions that are split into two. For apps that
908      * target an SDK before the split, this method automatically adds the split off permission.
909      *
910      * @param permission The requested permission
911      *
912      * @return The actually requested permissions
913      */
computeAffectedPermissions(String permission)914     private ArrayList<String> computeAffectedPermissions(String permission) {
915         int requestingAppTargetSDK =
916                 mAppPermissions.getPackageInfo().applicationInfo.targetSdkVersion;
917 
918         // If a permission is split, all permissions the original permission is split into are
919         // affected
920         ArrayList<String> extendedBySplitPerms = new ArrayList<>();
921         extendedBySplitPerms.add(permission);
922 
923         List<PermissionManager.SplitPermissionInfo> splitPerms = getSystemService(
924                 PermissionManager.class).getSplitPermissions();
925         int numSplitPerms = splitPerms.size();
926         for (int i = 0; i < numSplitPerms; i++) {
927             PermissionManager.SplitPermissionInfo splitPerm = splitPerms.get(i);
928 
929             if (requestingAppTargetSDK < splitPerm.getTargetSdk()
930                     && permission.equals(splitPerm.getSplitPermission())) {
931                 extendedBySplitPerms.addAll(splitPerm.getNewPermissions());
932             }
933         }
934 
935         // For <= N_MR1 apps all permissions of the groups of the requested permissions are affected
936         if (requestingAppTargetSDK <= Build.VERSION_CODES.N_MR1) {
937             ArrayList<String> extendedBySplitPermsAndGroup = new ArrayList<>();
938 
939             int numExtendedBySplitPerms = extendedBySplitPerms.size();
940             for (int splitPermNum = 0; splitPermNum < numExtendedBySplitPerms; splitPermNum++) {
941                 AppPermissionGroup group = mAppPermissions.getGroupForPermission(
942                         extendedBySplitPerms.get(splitPermNum));
943 
944                 if (group == null) {
945                     continue;
946                 }
947 
948                 ArrayList<Permission> permissionsInGroup = group.getPermissions();
949                 int numPermissionsInGroup = permissionsInGroup.size();
950                 for (int permNum = 0; permNum < numPermissionsInGroup; permNum++) {
951                     extendedBySplitPermsAndGroup.add(permissionsInGroup.get(permNum).getName());
952                 }
953             }
954 
955             return extendedBySplitPermsAndGroup;
956         } else {
957             return extendedBySplitPerms;
958         }
959     }
960 
logGrantPermissionActivityButtons(String permissionGroupName, int grantResult)961     private void logGrantPermissionActivityButtons(String permissionGroupName, int grantResult) {
962         int clickedButton = 0;
963         int presentedButtons = getButtonState();
964         switch (grantResult) {
965             case GRANTED_ALWAYS:
966                 if ((presentedButtons & (1 << LABEL_ALLOW_BUTTON)) != 0) {
967                     clickedButton = 1 << LABEL_ALLOW_BUTTON;
968                 } else {
969                     clickedButton = 1 << LABEL_ALLOW_ALWAYS_BUTTON;
970                 }
971                 break;
972             case GRANTED_FOREGROUND_ONLY:
973                 clickedButton = 1 << LABEL_ALLOW_FOREGROUND_BUTTON;
974                 break;
975             case DENIED:
976                 clickedButton = 1 << LABEL_DENY_BUTTON;
977                 break;
978             case DENIED_DO_NOT_ASK_AGAIN:
979                 clickedButton = 1 << LABEL_DENY_AND_DONT_ASK_AGAIN_BUTTON;
980                 break;
981             default:
982                 break;
983         }
984 
985         PermissionControllerStatsLog.write(GRANT_PERMISSIONS_ACTIVITY_BUTTON_ACTIONS,
986                 permissionGroupName, mCallingUid, mCallingPackage, presentedButtons,
987                 clickedButton);
988         Log.v(LOG_TAG, "Logged buttons presented and clicked permissionGroupName="
989                 + permissionGroupName + " uid=" + mCallingUid + " package=" + mCallingPackage
990                 + " presentedButtons=" + presentedButtons + " clickedButton=" + clickedButton);
991     }
992 
getButtonState()993     private int getButtonState() {
994         if (mButtonLabels == null) {
995             return 0;
996         }
997         int buttonState = 0;
998         for (int i = NUM_BUTTONS - 1; i >= 0; i--) {
999             buttonState *= 2;
1000             if (mButtonLabels[i] != null) {
1001                 buttonState++;
1002             }
1003         }
1004         return buttonState;
1005     }
1006 
1007     private static final class GroupState {
1008         static final int STATE_UNKNOWN = 0;
1009         static final int STATE_ALLOWED = 1;
1010         static final int STATE_DENIED = 2;
1011         static final int STATE_SKIPPED = 3;
1012 
1013         final AppPermissionGroup mGroup;
1014         int mState = STATE_UNKNOWN;
1015 
1016         /** Permissions of this group that need to be granted, null == no permissions of group */
1017         String[] affectedPermissions;
1018 
GroupState(AppPermissionGroup group)1019         GroupState(AppPermissionGroup group) {
1020             mGroup = group;
1021         }
1022     }
1023 
1024     private class PermissionChangeListener implements PackageManager.OnPermissionsChangedListener {
1025         final int mCallingPackageUid;
1026 
PermissionChangeListener()1027         PermissionChangeListener() throws NameNotFoundException {
1028             mCallingPackageUid = getPackageManager().getPackageUid(mCallingPackage, 0);
1029         }
1030 
1031         @Override
onPermissionsChanged(int uid)1032         public void onPermissionsChanged(int uid) {
1033             if (uid == mCallingPackageUid) {
1034                 updateIfPermissionsWereGranted();
1035             }
1036         }
1037     }
1038 }
1039