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         setFinishOnTouchOutside(false);
272 
273         setTitle(R.string.permission_request_title);
274 
275         mRequestedPermissions = getIntent().getStringArrayExtra(
276                 PackageManager.EXTRA_REQUEST_PERMISSIONS_NAMES);
277         if (mRequestedPermissions == null) {
278             mRequestedPermissions = new String[0];
279         }
280 
281         final int requestedPermCount = mRequestedPermissions.length;
282 
283         if (requestedPermCount == 0) {
284             setResultAndFinish();
285             return;
286         }
287 
288         PackageInfo callingPackageInfo = getCallingPackageInfo();
289 
290         if (callingPackageInfo == null || callingPackageInfo.requestedPermissions == null
291                 || callingPackageInfo.requestedPermissions.length <= 0) {
292             setResultAndFinish();
293             return;
294         }
295 
296         // Don't allow legacy apps to request runtime permissions.
297         if (callingPackageInfo.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M) {
298             // Returning empty arrays means a cancellation.
299             mRequestedPermissions = new String[0];
300             setResultAndFinish();
301             return;
302         }
303 
304         mCallingUid = callingPackageInfo.applicationInfo.uid;
305 
306         UserHandle userHandle = UserHandle.getUserHandleForUid(mCallingUid);
307 
308         if (DeviceUtils.isTelevision(this)) {
309             mViewHandler = new com.android.packageinstaller.permission.ui.television
310                     .GrantPermissionsViewHandlerImpl(this,
311                     mCallingPackage).setResultListener(this);
312         } else if (DeviceUtils.isWear(this)) {
313             mViewHandler = new GrantPermissionsWatchViewHandler(this).setResultListener(this);
314         } else if (DeviceUtils.isAuto(this)) {
315             mViewHandler = new GrantPermissionsAutoViewHandler(this, mCallingPackage, userHandle)
316                     .setResultListener(this);
317         } else {
318             mViewHandler = new com.android.packageinstaller.permission.ui.handheld
319                     .GrantPermissionsViewHandlerImpl(this, mCallingPackage, userHandle)
320                     .setResultListener(this);
321         }
322 
323         mAppPermissions = new AppPermissions(this, callingPackageInfo, false,
324                 new Runnable() {
325                     @Override
326                     public void run() {
327                         setResultAndFinish();
328                     }
329                 });
330 
331         for (String requestedPermission : mRequestedPermissions) {
332             if (requestedPermission == null) {
333                 continue;
334             }
335 
336             ArrayList<String> affectedPermissions =
337                     computeAffectedPermissions(requestedPermission);
338 
339             int numAffectedPermissions = affectedPermissions.size();
340             for (int i = 0; i < numAffectedPermissions; i++) {
341                 AppPermissionGroup group =
342                         mAppPermissions.getGroupForPermission(affectedPermissions.get(i));
343                 if (group == null) {
344                     reportRequestResult(affectedPermissions.get(i),
345                             PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__IGNORED);
346 
347                     continue;
348                 }
349 
350                 addRequestedPermissions(group, affectedPermissions.get(i), icicle == null);
351             }
352         }
353 
354         int numGroupStates = mRequestGrantPermissionGroups.size();
355         for (int groupStateNum = 0; groupStateNum < numGroupStates; groupStateNum++) {
356             GroupState groupState = mRequestGrantPermissionGroups.valueAt(groupStateNum);
357             AppPermissionGroup group = groupState.mGroup;
358 
359             // Restore permission group state after lifecycle events
360             if (icicle != null) {
361                 groupState.mState = icicle.getInt(
362                         getInstanceStateKey(mRequestGrantPermissionGroups.keyAt(groupStateNum)),
363                         groupState.mState);
364             }
365 
366             // Do not attempt to grant background access if foreground access is not either already
367             // granted or requested
368             if (group.isBackgroundGroup()) {
369                 // Check if a foreground permission is already granted
370                 boolean foregroundGroupAlreadyGranted = mAppPermissions.getPermissionGroup(
371                         group.getName()).areRuntimePermissionsGranted();
372                 boolean hasForegroundRequest = (getForegroundGroupState(group.getName()) != null);
373 
374                 if (!foregroundGroupAlreadyGranted && !hasForegroundRequest) {
375                     // The background permission cannot be granted at this time
376                     int numPermissions = groupState.affectedPermissions.length;
377                     for (int permissionNum = 0; permissionNum < numPermissions; permissionNum++) {
378                         Log.w(LOG_TAG,
379                                 "Cannot grant " + groupState.affectedPermissions[permissionNum]
380                                         + " as the matching foreground permission is not already "
381                                         + "granted.");
382                     }
383 
384                     groupState.mState = GroupState.STATE_SKIPPED;
385 
386                     reportRequestResult(groupState.affectedPermissions,
387                             PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__IGNORED);
388                 }
389             }
390         }
391 
392         setContentView(mViewHandler.createView());
393 
394         Window window = getWindow();
395         WindowManager.LayoutParams layoutParams = window.getAttributes();
396         mViewHandler.updateWindowAttributes(layoutParams);
397         window.setAttributes(layoutParams);
398 
399         // Restore UI state after lifecycle events. This has to be before
400         // showNextPermissionGroupGrantRequest is called. showNextPermissionGroupGrantRequest might
401         // update the UI and the UI behaves differently for updates and initial creations.
402         if (icicle != null) {
403             mViewHandler.loadInstanceState(icicle);
404         }
405 
406         if (!showNextPermissionGroupGrantRequest()) {
407             setResultAndFinish();
408         }
409     }
410 
411     /**
412      * Update the {@link #mRequestedPermissions} if the system reports them as granted.
413      *
414      * <p>This also updates the {@link #mAppPermissions} state and switches to the next group grant
415      * request if the current group becomes granted.
416      */
updateIfPermissionsWereGranted()417     private void updateIfPermissionsWereGranted() {
418         PackageManager pm = getPackageManager();
419 
420         boolean mightShowNextGroup = true;
421         int numGroupStates = mRequestGrantPermissionGroups.size();
422         for (int i = 0; i < numGroupStates; i++) {
423             GroupState groupState = mRequestGrantPermissionGroups.valueAt(i);
424 
425             if (groupState == null || groupState.mState != GroupState.STATE_UNKNOWN) {
426                 // Group has already been approved / denied via the UI by the user
427                 continue;
428             }
429 
430             boolean allAffectedPermissionsOfThisGroupAreGranted = true;
431 
432             if (groupState.affectedPermissions == null) {
433                 // It is not clear which permissions belong to this group, hence never skip this
434                 // view
435                 allAffectedPermissionsOfThisGroupAreGranted = false;
436             } else {
437                 for (int permNum = 0; permNum < groupState.affectedPermissions.length;
438                         permNum++) {
439                     if (pm.checkPermission(groupState.affectedPermissions[permNum], mCallingPackage)
440                             == PERMISSION_DENIED) {
441                         allAffectedPermissionsOfThisGroupAreGranted = false;
442                         break;
443                     }
444                 }
445             }
446 
447             if (allAffectedPermissionsOfThisGroupAreGranted) {
448                 groupState.mState = GroupState.STATE_ALLOWED;
449 
450                 if (mightShowNextGroup) {
451                     // The UI currently displays the first group with
452                     // mState == STATE_UNKNOWN. So we are switching to next group until we
453                     // could not allow a group that was still unknown
454                     if (!showNextPermissionGroupGrantRequest()) {
455                         setResultAndFinish();
456                     }
457                 }
458             } else {
459                 mightShowNextGroup = false;
460             }
461         }
462     }
463 
464     @Override
onStart()465     protected void onStart() {
466         super.onStart();
467 
468         try {
469             mPermissionChangeListener = new PermissionChangeListener();
470         } catch (NameNotFoundException e) {
471             setResultAndFinish();
472             return;
473         }
474         PackageManager pm = getPackageManager();
475         pm.addOnPermissionsChangeListener(mPermissionChangeListener);
476 
477         // get notified when the package is removed
478         mPackageRemovalMonitor = new PackageRemovalMonitor(this, mCallingPackage) {
479             @Override
480             public void onPackageRemoved() {
481                 Log.w(LOG_TAG, mCallingPackage + " was uninstalled");
482 
483                 finish();
484             }
485         };
486         mPackageRemovalMonitor.register();
487 
488         // check if the package was removed while this activity was not started
489         try {
490             pm.getPackageInfo(mCallingPackage, 0);
491         } catch (NameNotFoundException e) {
492             Log.w(LOG_TAG, mCallingPackage + " was uninstalled while this activity was stopped", e);
493             finish();
494         }
495 
496         updateIfPermissionsWereGranted();
497     }
498 
499     @Override
onStop()500     protected void onStop() {
501         super.onStop();
502 
503         if (mPackageRemovalMonitor != null) {
504             mPackageRemovalMonitor.unregister();
505             mPackageRemovalMonitor = null;
506         }
507 
508         if (mPermissionChangeListener != null) {
509             getPackageManager().removeOnPermissionsChangeListener(mPermissionChangeListener);
510             mPermissionChangeListener = null;
511         }
512     }
513 
514     @Override
dispatchTouchEvent(MotionEvent ev)515     public boolean dispatchTouchEvent(MotionEvent ev) {
516         View rootView = getWindow().getDecorView();
517         if (rootView.getTop() != 0) {
518             // We are animating the top view, need to compensate for that in motion events.
519             ev.setLocation(ev.getX(), ev.getY() - rootView.getTop());
520         }
521         return super.dispatchTouchEvent(ev);
522     }
523 
524     /**
525      * Compose a key that stores the GroupState.mState in the instance state.
526      *
527      * @param requestGrantPermissionGroupsKey The key of the permission group
528      *
529      * @return A unique key to be used in the instance state
530      */
getInstanceStateKey( Pair<String, Boolean> requestGrantPermissionGroupsKey)531     private static String getInstanceStateKey(
532             Pair<String, Boolean> requestGrantPermissionGroupsKey) {
533         return GrantPermissionsActivity.class.getName() + "_"
534                 + requestGrantPermissionGroupsKey.first + "_"
535                 + requestGrantPermissionGroupsKey.second;
536     }
537 
538     @Override
onSaveInstanceState(Bundle outState)539     protected void onSaveInstanceState(Bundle outState) {
540         super.onSaveInstanceState(outState);
541 
542         mViewHandler.saveInstanceState(outState);
543 
544         outState.putLong(KEY_REQUEST_ID, mRequestId);
545 
546         int numGroups = mRequestGrantPermissionGroups.size();
547         for (int i = 0; i < numGroups; i++) {
548             int state = mRequestGrantPermissionGroups.valueAt(i).mState;
549 
550             if (state != GroupState.STATE_UNKNOWN) {
551                 outState.putInt(getInstanceStateKey(mRequestGrantPermissionGroups.keyAt(i)), state);
552             }
553         }
554     }
555 
556     /**
557      * @return the background group state for the permission group with the {@code name}
558      */
getBackgroundGroupState(String name)559     private GroupState getBackgroundGroupState(String name) {
560         return mRequestGrantPermissionGroups.get(new Pair<>(name, true));
561     }
562 
563     /**
564      * @return the foreground group state for the permission group with the {@code name}
565      */
getForegroundGroupState(String name)566     private GroupState getForegroundGroupState(String name) {
567         return mRequestGrantPermissionGroups.get(new Pair<>(name, false));
568     }
569 
shouldShowRequestForGroupState(GroupState groupState)570     private boolean shouldShowRequestForGroupState(GroupState groupState) {
571         if (groupState.mState == GroupState.STATE_SKIPPED) {
572             return false;
573         }
574 
575         GroupState foregroundGroup = getForegroundGroupState(groupState.mGroup.getName());
576         if (groupState.mGroup.isBackgroundGroup()
577                 && (foregroundGroup != null && shouldShowRequestForGroupState(foregroundGroup))) {
578             // If an app requests both foreground and background permissions of the same group,
579             // we only show one request
580             return false;
581         }
582 
583         return true;
584     }
585 
showNextPermissionGroupGrantRequest()586     private boolean showNextPermissionGroupGrantRequest() {
587         int numGroupStates = mRequestGrantPermissionGroups.size();
588         int numGrantRequests = 0;
589         for (int i = 0; i < numGroupStates; i++) {
590             if (shouldShowRequestForGroupState(mRequestGrantPermissionGroups.valueAt(i))) {
591                 numGrantRequests++;
592             }
593         }
594 
595         int currentIndex = 0;
596         for (GroupState groupState : mRequestGrantPermissionGroups.values()) {
597             if (!shouldShowRequestForGroupState(groupState)) {
598                 continue;
599             }
600 
601             if (groupState.mState == GroupState.STATE_UNKNOWN) {
602                 GroupState foregroundGroupState;
603                 GroupState backgroundGroupState;
604                 if (groupState.mGroup.isBackgroundGroup()) {
605                     backgroundGroupState = groupState;
606                     foregroundGroupState = getForegroundGroupState(groupState.mGroup.getName());
607                 } else {
608                     foregroundGroupState = groupState;
609                     backgroundGroupState = getBackgroundGroupState(groupState.mGroup.getName());
610                 }
611 
612                 CharSequence appLabel = mAppPermissions.getAppLabel();
613 
614                 Icon icon;
615                 try {
616                     icon = Icon.createWithResource(groupState.mGroup.getIconPkg(),
617                             groupState.mGroup.getIconResId());
618                 } catch (Resources.NotFoundException e) {
619                     Log.e(LOG_TAG, "Cannot load icon for group" + groupState.mGroup.getName(), e);
620                     icon = null;
621                 }
622 
623                 // If no background permissions are granted yet, we need to ask for background
624                 // permissions
625                 boolean needBackgroundPermission = false;
626                 boolean isBackgroundPermissionUserSet = false;
627                 if (backgroundGroupState != null) {
628                     if (!backgroundGroupState.mGroup.areRuntimePermissionsGranted()) {
629                         needBackgroundPermission = true;
630                         isBackgroundPermissionUserSet = backgroundGroupState.mGroup.isUserSet();
631                     }
632                 }
633 
634                 // If no foreground permissions are granted yet, we need to ask for foreground
635                 // permissions
636                 boolean needForegroundPermission = false;
637                 boolean isForegroundPermissionUserSet = false;
638                 if (foregroundGroupState != null) {
639                     if (!foregroundGroupState.mGroup.areRuntimePermissionsGranted()) {
640                         needForegroundPermission = true;
641                         isForegroundPermissionUserSet = foregroundGroupState.mGroup.isUserSet();
642                     }
643                 }
644 
645                 // The button doesn't show when its label is null
646                 mButtonLabels = new CharSequence[NUM_BUTTONS];
647                 mButtonLabels[LABEL_ALLOW_BUTTON] = getString(R.string.grant_dialog_button_allow);
648                 mButtonLabels[LABEL_ALLOW_ALWAYS_BUTTON] = null;
649                 mButtonLabels[LABEL_ALLOW_FOREGROUND_BUTTON] = null;
650                 mButtonLabels[LABEL_DENY_BUTTON] = getString(R.string.grant_dialog_button_deny);
651                 if (isForegroundPermissionUserSet || isBackgroundPermissionUserSet) {
652                     mButtonLabels[LABEL_DENY_AND_DONT_ASK_AGAIN_BUTTON] =
653                             getString(R.string.grant_dialog_button_deny_and_dont_ask_again);
654                 } else {
655                     mButtonLabels[LABEL_DENY_AND_DONT_ASK_AGAIN_BUTTON] = null;
656                 }
657 
658                 int messageId;
659                 int detailMessageId = 0;
660                 if (needForegroundPermission) {
661                     messageId = groupState.mGroup.getRequest();
662 
663                     if (groupState.mGroup.hasPermissionWithBackgroundMode()) {
664                         mButtonLabels[LABEL_ALLOW_BUTTON] = null;
665                         mButtonLabels[LABEL_ALLOW_FOREGROUND_BUTTON] =
666                                 getString(R.string.grant_dialog_button_allow_foreground);
667                         if (needBackgroundPermission) {
668                             mButtonLabels[LABEL_ALLOW_ALWAYS_BUTTON] =
669                                     getString(R.string.grant_dialog_button_allow_always);
670                             if (isForegroundPermissionUserSet || isBackgroundPermissionUserSet) {
671                                 mButtonLabels[LABEL_DENY_BUTTON] = null;
672                             }
673                         }
674                     } else {
675                         detailMessageId = groupState.mGroup.getRequestDetail();
676                     }
677                 } else {
678                     if (needBackgroundPermission) {
679                         messageId = groupState.mGroup.getBackgroundRequest();
680                         detailMessageId = groupState.mGroup.getBackgroundRequestDetail();
681                         mButtonLabels[LABEL_ALLOW_BUTTON] =
682                                 getString(R.string.grant_dialog_button_allow_background);
683                         mButtonLabels[LABEL_DENY_BUTTON] =
684                                 getString(R.string.grant_dialog_button_deny_background);
685                         mButtonLabels[LABEL_DENY_AND_DONT_ASK_AGAIN_BUTTON] =
686                                 getString(R.string
687                                         .grant_dialog_button_deny_background_and_dont_ask_again);
688                     } else {
689                         // Not reached as the permissions should be auto-granted
690                         return false;
691                     }
692                 }
693 
694                 CharSequence message = getRequestMessage(appLabel, groupState.mGroup, this,
695                         messageId);
696 
697                 Spanned detailMessage = null;
698                 if (detailMessageId != 0) {
699                     try {
700                         detailMessage = Html.fromHtml(
701                                 getPackageManager().getResourcesForApplication(
702                                         groupState.mGroup.getDeclaringPackage()).getString(
703                                         detailMessageId), 0);
704                     } catch (NameNotFoundException ignored) {
705                     }
706                 }
707 
708                 // Set the permission message as the title so it can be announced.
709                 setTitle(message);
710 
711                 mViewHandler.updateUi(groupState.mGroup.getName(), numGrantRequests, currentIndex,
712                         icon, message, detailMessage, mButtonLabels);
713 
714                 return true;
715             }
716 
717             if (groupState.mState != GroupState.STATE_SKIPPED) {
718                 currentIndex++;
719             }
720         }
721 
722         return false;
723     }
724 
725     @Override
onPermissionGrantResult(String name, @GrantPermissionsViewHandler.Result int result)726     public void onPermissionGrantResult(String name,
727             @GrantPermissionsViewHandler.Result int result) {
728         logGrantPermissionActivityButtons(name, result);
729         GroupState foregroundGroupState = getForegroundGroupState(name);
730         GroupState backgroundGroupState = getBackgroundGroupState(name);
731 
732         if (result == GRANTED_ALWAYS || result == GRANTED_FOREGROUND_ONLY
733                 || result == DENIED_DO_NOT_ASK_AGAIN) {
734             KeyguardManager kgm = getSystemService(KeyguardManager.class);
735 
736             if (kgm.isDeviceLocked()) {
737                 kgm.requestDismissKeyguard(this, new KeyguardManager.KeyguardDismissCallback() {
738                             @Override
739                             public void onDismissError() {
740                                 Log.e(LOG_TAG, "Cannot dismiss keyguard perm=" + name + " result="
741                                         + result);
742                             }
743 
744                             @Override
745                             public void onDismissCancelled() {
746                                 // do nothing (i.e. stay at the current permission group)
747                             }
748 
749                             @Override
750                             public void onDismissSucceeded() {
751                                 // Now the keyguard is dismissed, hence the device is not locked
752                                 // anymore
753                                 onPermissionGrantResult(name, result);
754                             }
755                         });
756 
757                 return;
758             }
759         }
760 
761         switch (result) {
762             case GRANTED_ALWAYS :
763                 if (foregroundGroupState != null) {
764                     onPermissionGrantResultSingleState(foregroundGroupState, true, false);
765                 }
766                 if (backgroundGroupState != null) {
767                     onPermissionGrantResultSingleState(backgroundGroupState, true, false);
768                 }
769                 break;
770             case GRANTED_FOREGROUND_ONLY :
771                 if (foregroundGroupState != null) {
772                     onPermissionGrantResultSingleState(foregroundGroupState, true, false);
773                 }
774                 if (backgroundGroupState != null) {
775                     onPermissionGrantResultSingleState(backgroundGroupState, false, false);
776                 }
777                 break;
778             case DENIED :
779                 if (foregroundGroupState != null) {
780                     onPermissionGrantResultSingleState(foregroundGroupState, false, false);
781                 }
782                 if (backgroundGroupState != null) {
783                     onPermissionGrantResultSingleState(backgroundGroupState, false, false);
784                 }
785                 break;
786             case DENIED_DO_NOT_ASK_AGAIN :
787                 if (foregroundGroupState != null) {
788                     onPermissionGrantResultSingleState(foregroundGroupState, false, true);
789                 }
790                 if (backgroundGroupState != null) {
791                     onPermissionGrantResultSingleState(backgroundGroupState, false, true);
792                 }
793                 break;
794         }
795 
796         if (!showNextPermissionGroupGrantRequest()) {
797             setResultAndFinish();
798         }
799     }
800 
801     /**
802      * Grants or revoked the affected permissions for a single {@link groupState}.
803      *
804      * @param groupState The group state with the permissions to grant/revoke
805      * @param granted {@code true} if the permissions should be granted, {@code false} if they
806      *        should be revoked
807      * @param doNotAskAgain if the permissions should be revoked should be app be allowed to ask
808      *        again for the same permissions?
809      */
onPermissionGrantResultSingleState(GroupState groupState, boolean granted, boolean doNotAskAgain)810     private void onPermissionGrantResultSingleState(GroupState groupState, boolean granted,
811             boolean doNotAskAgain) {
812         if (groupState != null && groupState.mGroup != null
813                 && groupState.mState == GroupState.STATE_UNKNOWN) {
814             if (granted) {
815                 groupState.mGroup.grantRuntimePermissions(doNotAskAgain,
816                         groupState.affectedPermissions);
817                 groupState.mState = GroupState.STATE_ALLOWED;
818 
819                 reportRequestResult(groupState.affectedPermissions,
820                         PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_GRANTED);
821             } else {
822                 groupState.mGroup.revokeRuntimePermissions(doNotAskAgain,
823                         groupState.affectedPermissions);
824                 groupState.mState = GroupState.STATE_DENIED;
825 
826                 reportRequestResult(groupState.affectedPermissions, doNotAskAgain
827                         ?
828                         PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_DENIED_WITH_PREJUDICE
829                         : PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_DENIED);
830             }
831         }
832     }
833 
834     @Override
onKeyDown(int keyCode, KeyEvent event)835     public boolean onKeyDown(int keyCode, KeyEvent event)  {
836         // We do not allow backing out.
837         return keyCode == KeyEvent.KEYCODE_BACK;
838     }
839 
840     @Override
onKeyUp(int keyCode, KeyEvent event)841     public boolean onKeyUp(int keyCode, KeyEvent event)  {
842         // We do not allow backing out.
843         return keyCode == KeyEvent.KEYCODE_BACK;
844     }
845 
846     @Override
finish()847     public void finish() {
848         setResultIfNeeded(RESULT_CANCELED);
849         super.finish();
850     }
851 
getCallingPackageInfo()852     private PackageInfo getCallingPackageInfo() {
853         try {
854             return getPackageManager().getPackageInfo(mCallingPackage,
855                     PackageManager.GET_PERMISSIONS);
856         } catch (NameNotFoundException e) {
857             Log.i(LOG_TAG, "No package: " + mCallingPackage, e);
858             return null;
859         }
860     }
861 
setResultIfNeeded(int resultCode)862     private void setResultIfNeeded(int resultCode) {
863         if (!mResultSet) {
864             mResultSet = true;
865             logRequestedPermissionGroups();
866             Intent result = new Intent(PackageManager.ACTION_REQUEST_PERMISSIONS);
867             result.putExtra(PackageManager.EXTRA_REQUEST_PERMISSIONS_NAMES, mRequestedPermissions);
868 
869             PackageManager pm = getPackageManager();
870             int numRequestedPermissions = mRequestedPermissions.length;
871             int[] grantResults = new int[numRequestedPermissions];
872             for (int i = 0; i < numRequestedPermissions; i++) {
873                 grantResults[i] = pm.checkPermission(mRequestedPermissions[i], mCallingPackage);
874             }
875 
876             result.putExtra(PackageManager.EXTRA_REQUEST_PERMISSIONS_RESULTS, grantResults);
877             setResult(resultCode, result);
878         }
879     }
880 
setResultAndFinish()881     private void setResultAndFinish() {
882         setResultIfNeeded(RESULT_OK);
883         finish();
884     }
885 
logRequestedPermissionGroups()886     private void logRequestedPermissionGroups() {
887         if (mRequestGrantPermissionGroups.isEmpty()) {
888             return;
889         }
890 
891         final int groupCount = mRequestGrantPermissionGroups.size();
892         List<AppPermissionGroup> groups = new ArrayList<>(groupCount);
893         for (GroupState groupState : mRequestGrantPermissionGroups.values()) {
894             groups.add(groupState.mGroup);
895         }
896 
897         SafetyNetLogger.logPermissionsRequested(mAppPermissions.getPackageInfo(), groups);
898     }
899 
900     /**
901      * Get the actually requested permissions when a permission is requested.
902      *
903      * <p>>In some cases requesting to grant a single permission requires the system to grant
904      * additional permissions. E.g. before N-MR1 a single permission of a group caused the whole
905      * group to be granted. Another case are permissions that are split into two. For apps that
906      * target an SDK before the split, this method automatically adds the split off permission.
907      *
908      * @param permission The requested permission
909      *
910      * @return The actually requested permissions
911      */
computeAffectedPermissions(String permission)912     private ArrayList<String> computeAffectedPermissions(String permission) {
913         int requestingAppTargetSDK =
914                 mAppPermissions.getPackageInfo().applicationInfo.targetSdkVersion;
915 
916         // If a permission is split, all permissions the original permission is split into are
917         // affected
918         ArrayList<String> extendedBySplitPerms = new ArrayList<>();
919         extendedBySplitPerms.add(permission);
920 
921         List<PermissionManager.SplitPermissionInfo> splitPerms = getSystemService(
922                 PermissionManager.class).getSplitPermissions();
923         int numSplitPerms = splitPerms.size();
924         for (int i = 0; i < numSplitPerms; i++) {
925             PermissionManager.SplitPermissionInfo splitPerm = splitPerms.get(i);
926 
927             if (requestingAppTargetSDK < splitPerm.getTargetSdk()
928                     && permission.equals(splitPerm.getSplitPermission())) {
929                 extendedBySplitPerms.addAll(splitPerm.getNewPermissions());
930             }
931         }
932 
933         // For <= N_MR1 apps all permissions of the groups of the requested permissions are affected
934         if (requestingAppTargetSDK <= Build.VERSION_CODES.N_MR1) {
935             ArrayList<String> extendedBySplitPermsAndGroup = new ArrayList<>();
936 
937             int numExtendedBySplitPerms = extendedBySplitPerms.size();
938             for (int splitPermNum = 0; splitPermNum < numExtendedBySplitPerms; splitPermNum++) {
939                 AppPermissionGroup group = mAppPermissions.getGroupForPermission(
940                         extendedBySplitPerms.get(splitPermNum));
941 
942                 if (group == null) {
943                     continue;
944                 }
945 
946                 ArrayList<Permission> permissionsInGroup = group.getPermissions();
947                 int numPermissionsInGroup = permissionsInGroup.size();
948                 for (int permNum = 0; permNum < numPermissionsInGroup; permNum++) {
949                     extendedBySplitPermsAndGroup.add(permissionsInGroup.get(permNum).getName());
950                 }
951             }
952 
953             return extendedBySplitPermsAndGroup;
954         } else {
955             return extendedBySplitPerms;
956         }
957     }
958 
logGrantPermissionActivityButtons(String permissionGroupName, int grantResult)959     private void logGrantPermissionActivityButtons(String permissionGroupName, int grantResult) {
960         int clickedButton = 0;
961         int presentedButtons = getButtonState();
962         switch (grantResult) {
963             case GRANTED_ALWAYS:
964                 if ((presentedButtons & (1 << LABEL_ALLOW_BUTTON)) != 0) {
965                     clickedButton = 1 << LABEL_ALLOW_BUTTON;
966                 } else {
967                     clickedButton = 1 << LABEL_ALLOW_ALWAYS_BUTTON;
968                 }
969                 break;
970             case GRANTED_FOREGROUND_ONLY:
971                 clickedButton = 1 << LABEL_ALLOW_FOREGROUND_BUTTON;
972                 break;
973             case DENIED:
974                 clickedButton = 1 << LABEL_DENY_BUTTON;
975                 break;
976             case DENIED_DO_NOT_ASK_AGAIN:
977                 clickedButton = 1 << LABEL_DENY_AND_DONT_ASK_AGAIN_BUTTON;
978                 break;
979             default:
980                 break;
981         }
982 
983         PermissionControllerStatsLog.write(GRANT_PERMISSIONS_ACTIVITY_BUTTON_ACTIONS,
984                 permissionGroupName, mCallingUid, mCallingPackage, presentedButtons,
985                 clickedButton);
986         Log.v(LOG_TAG, "Logged buttons presented and clicked permissionGroupName="
987                 + permissionGroupName + " uid=" + mCallingUid + " package=" + mCallingPackage
988                 + " presentedButtons=" + presentedButtons + " clickedButton=" + clickedButton);
989     }
990 
getButtonState()991     private int getButtonState() {
992         if (mButtonLabels == null) {
993             return 0;
994         }
995         int buttonState = 0;
996         for (int i = NUM_BUTTONS - 1; i >= 0; i--) {
997             buttonState *= 2;
998             if (mButtonLabels[i] != null) {
999                 buttonState++;
1000             }
1001         }
1002         return buttonState;
1003     }
1004 
1005     private static final class GroupState {
1006         static final int STATE_UNKNOWN = 0;
1007         static final int STATE_ALLOWED = 1;
1008         static final int STATE_DENIED = 2;
1009         static final int STATE_SKIPPED = 3;
1010 
1011         final AppPermissionGroup mGroup;
1012         int mState = STATE_UNKNOWN;
1013 
1014         /** Permissions of this group that need to be granted, null == no permissions of group */
1015         String[] affectedPermissions;
1016 
GroupState(AppPermissionGroup group)1017         GroupState(AppPermissionGroup group) {
1018             mGroup = group;
1019         }
1020     }
1021 
1022     private class PermissionChangeListener implements PackageManager.OnPermissionsChangedListener {
1023         final int mCallingPackageUid;
1024 
PermissionChangeListener()1025         PermissionChangeListener() throws NameNotFoundException {
1026             mCallingPackageUid = getPackageManager().getPackageUid(mCallingPackage, 0);
1027         }
1028 
1029         @Override
onPermissionsChanged(int uid)1030         public void onPermissionsChanged(int uid) {
1031             if (uid == mCallingPackageUid) {
1032                 updateIfPermissionsWereGranted();
1033             }
1034         }
1035     }
1036 }
1037