• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.packageinstaller.permission.ui;
18 
19 import static android.content.pm.PackageManager.PERMISSION_DENIED;
20 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
21 
22 import android.app.admin.DevicePolicyManager;
23 import android.app.KeyguardManager;
24 import android.content.Intent;
25 import android.content.pm.PackageInfo;
26 import android.content.pm.PackageManager;
27 import android.content.pm.PackageManager.NameNotFoundException;
28 import android.content.pm.PackageParser;
29 import android.content.pm.PermissionInfo;
30 import android.content.res.Configuration;
31 import android.content.res.Resources;
32 import android.graphics.drawable.Icon;
33 import android.os.Build;
34 import android.os.Bundle;
35 import android.text.Html;
36 import android.text.Spanned;
37 import android.util.ArraySet;
38 import android.util.Log;
39 import android.view.KeyEvent;
40 import android.view.MotionEvent;
41 import android.view.View;
42 import android.view.Window;
43 import android.view.WindowManager;
44 
45 import com.android.internal.content.PackageMonitor;
46 import com.android.internal.logging.nano.MetricsProto;
47 import com.android.packageinstaller.DeviceUtils;
48 import com.android.packageinstaller.R;
49 import com.android.packageinstaller.permission.model.AppPermissionGroup;
50 import com.android.packageinstaller.permission.model.AppPermissions;
51 import com.android.packageinstaller.permission.model.Permission;
52 import com.android.packageinstaller.permission.ui.auto.GrantPermissionsAutoViewHandler;
53 import com.android.packageinstaller.permission.ui.handheld.GrantPermissionsViewHandlerImpl;
54 import com.android.packageinstaller.permission.utils.ArrayUtils;
55 import com.android.packageinstaller.permission.utils.EventLogger;
56 import com.android.packageinstaller.permission.utils.SafetyNetLogger;
57 
58 import java.util.ArrayList;
59 import java.util.Arrays;
60 import java.util.LinkedHashMap;
61 import java.util.List;
62 
63 public class GrantPermissionsActivity extends OverlayTouchActivity
64         implements GrantPermissionsViewHandler.ResultListener {
65 
66     private static final String LOG_TAG = "GrantPermissionsActivity";
67 
68     private String[] mRequestedPermissions;
69     private int[] mGrantResults;
70 
71     private LinkedHashMap<String, GroupState> mRequestGrantPermissionGroups = new LinkedHashMap<>();
72 
73     private GrantPermissionsViewHandler mViewHandler;
74     private AppPermissions mAppPermissions;
75 
76     boolean mResultSet;
77 
78     private PackageManager.OnPermissionsChangedListener mPermissionChangeListener;
79     private PackageMonitor mPackageMonitor;
80 
81     private String mCallingPackage;
82 
getPermissionPolicy()83     private int getPermissionPolicy() {
84         DevicePolicyManager devicePolicyManager = getSystemService(DevicePolicyManager.class);
85         return devicePolicyManager.getPermissionPolicy(null);
86     }
87 
88     @Override
onCreate(Bundle icicle)89     public void onCreate(Bundle icicle) {
90         super.onCreate(icicle);
91 
92         // Cache this as this can only read on onCreate, not later.
93         mCallingPackage = getCallingPackage();
94 
95         mPackageMonitor = new PackageMonitor() {
96             @Override
97             public void onPackageRemoved(String packageName, int uid) {
98                 if (mCallingPackage.equals(packageName)) {
99                     Log.w(LOG_TAG, mCallingPackage + " was uninstalled");
100 
101                     finish();
102                 }
103             }
104         };
105 
106         setFinishOnTouchOutside(false);
107 
108         getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
109         getWindow().addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
110 
111         setTitle(R.string.permission_request_title);
112 
113         if (DeviceUtils.isTelevision(this)) {
114             mViewHandler = new com.android.packageinstaller.permission.ui.television
115                     .GrantPermissionsViewHandlerImpl(this,
116                     mCallingPackage).setResultListener(this);
117         } else if (DeviceUtils.isWear(this)) {
118             mViewHandler = new GrantPermissionsWatchViewHandler(this).setResultListener(this);
119         } else if (DeviceUtils.isAuto(this)) {
120             mViewHandler = new GrantPermissionsAutoViewHandler(this, mCallingPackage)
121                     .setResultListener(this);
122         } else {
123             mViewHandler = new com.android.packageinstaller.permission.ui.handheld
124                     .GrantPermissionsViewHandlerImpl(this, mCallingPackage)
125                     .setResultListener(this);
126         }
127 
128         mRequestedPermissions = getIntent().getStringArrayExtra(
129                 PackageManager.EXTRA_REQUEST_PERMISSIONS_NAMES);
130         if (mRequestedPermissions == null) {
131             mRequestedPermissions = new String[0];
132         }
133 
134         final int requestedPermCount = mRequestedPermissions.length;
135         mGrantResults = new int[requestedPermCount];
136         Arrays.fill(mGrantResults, PackageManager.PERMISSION_DENIED);
137 
138         if (requestedPermCount == 0) {
139             setResultAndFinish();
140             return;
141         }
142 
143         try {
144             mPermissionChangeListener = new PermissionChangeListener();
145         } catch (NameNotFoundException e) {
146             setResultAndFinish();
147             return;
148         }
149 
150         PackageInfo callingPackageInfo = getCallingPackageInfo();
151 
152         if (callingPackageInfo == null || callingPackageInfo.requestedPermissions == null
153                 || callingPackageInfo.requestedPermissions.length <= 0) {
154             setResultAndFinish();
155             return;
156         }
157 
158         // Don't allow legacy apps to request runtime permissions.
159         if (callingPackageInfo.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M) {
160             // Returning empty arrays means a cancellation.
161             mRequestedPermissions = new String[0];
162             mGrantResults = new int[0];
163             setResultAndFinish();
164             return;
165         }
166 
167         updateAlreadyGrantedPermissions(callingPackageInfo, getPermissionPolicy());
168 
169         mAppPermissions = new AppPermissions(this, callingPackageInfo, null, false,
170                 new Runnable() {
171                     @Override
172                     public void run() {
173                         setResultAndFinish();
174                     }
175                 });
176 
177         for (String requestedPermission : mRequestedPermissions) {
178             AppPermissionGroup group = null;
179             for (AppPermissionGroup nextGroup : mAppPermissions.getPermissionGroups()) {
180                 if (nextGroup.hasPermission(requestedPermission)) {
181                     group = nextGroup;
182                     break;
183                 }
184             }
185             if (group == null) {
186                 continue;
187             }
188             if (!group.isGrantingAllowed()) {
189                 // Skip showing groups that we know cannot be granted.
190                 continue;
191             }
192             // We allow the user to choose only non-fixed permissions. A permission
193             // is fixed either by device policy or the user denying with prejudice.
194             if (!group.isUserFixed() && !group.isPolicyFixed()) {
195                 switch (getPermissionPolicy()) {
196                     case DevicePolicyManager.PERMISSION_POLICY_AUTO_GRANT: {
197                         if (!group.areRuntimePermissionsGranted()) {
198                             group.grantRuntimePermissions(false, computeAffectedPermissions(
199                                     callingPackageInfo, requestedPermission));
200                         }
201                         group.setPolicyFixed();
202                     } break;
203 
204                     case DevicePolicyManager.PERMISSION_POLICY_AUTO_DENY: {
205                         if (group.areRuntimePermissionsGranted()) {
206                             group.revokeRuntimePermissions(false, computeAffectedPermissions(
207                                     callingPackageInfo, requestedPermission));
208                         }
209                         group.setPolicyFixed();
210                     } break;
211 
212                     default: {
213                         if (!group.areRuntimePermissionsGranted()) {
214                             GroupState state = mRequestGrantPermissionGroups.get(group.getName());
215                             if (state == null) {
216                                 state = new GroupState(group);
217                                 mRequestGrantPermissionGroups.put(group.getName(), state);
218                             }
219                             String[] affectedPermissions = computeAffectedPermissions(
220                                     callingPackageInfo, requestedPermission);
221                             if (affectedPermissions != null) {
222                                 for (String affectedPermission : affectedPermissions) {
223                                     state.affectedPermissions = ArrayUtils.appendString(
224                                             state.affectedPermissions, affectedPermission);
225                                 }
226                             }
227                         } else {
228                             group.grantRuntimePermissions(false, computeAffectedPermissions(
229                                     callingPackageInfo, requestedPermission));
230                             updateGrantResults(group);
231                         }
232                     } break;
233                 }
234             } else {
235                 // if the permission is fixed, ensure that we return the right request result
236                 updateGrantResults(group);
237             }
238         }
239 
240         setContentView(mViewHandler.createView());
241 
242         Window window = getWindow();
243         WindowManager.LayoutParams layoutParams = window.getAttributes();
244         mViewHandler.updateWindowAttributes(layoutParams);
245         window.setAttributes(layoutParams);
246 
247         if (!showNextPermissionGroupGrantRequest()) {
248             setResultAndFinish();
249         } else if (icicle == null) {
250             int numRequestedPermissions = mRequestedPermissions.length;
251             for (int permissionNum = 0; permissionNum < numRequestedPermissions; permissionNum++) {
252                 String permission = mRequestedPermissions[permissionNum];
253 
254                 EventLogger.logPermission(
255                         MetricsProto.MetricsEvent.ACTION_PERMISSION_REQUESTED, permission,
256                         mAppPermissions.getPackageInfo().packageName);
257             }
258         }
259     }
260 
261 
262     /**
263      * Update the {@link #mRequestedPermissions} if the system reports them as granted.
264      *
265      * <p>This also updates the {@link #mAppPermissions} state and switches to the next group grant
266      * request if the current group becomes granted.
267      */
updateIfPermissionsWereGranted()268     private void updateIfPermissionsWereGranted() {
269         updateAlreadyGrantedPermissions(getCallingPackageInfo(), getPermissionPolicy());
270 
271         ArraySet<String> grantedPermissionNames = new ArraySet<>(mRequestedPermissions.length);
272         for (int i = 0; i < mRequestedPermissions.length; i++) {
273             if (mGrantResults[i] == PERMISSION_GRANTED) {
274                 grantedPermissionNames.add(mRequestedPermissions[i]);
275             }
276         }
277 
278         boolean mightShowNextGroup = true;
279         int numGroups = mAppPermissions.getPermissionGroups().size();
280         for (int groupNum = 0; groupNum < numGroups; groupNum++) {
281             AppPermissionGroup group = mAppPermissions.getPermissionGroups().get(groupNum);
282             GroupState groupState = mRequestGrantPermissionGroups.get(group.getName());
283 
284             if (groupState == null || groupState.mState != GroupState.STATE_UNKNOWN) {
285                 // Group has already been approved / denied via the UI by the user
286                 continue;
287             }
288 
289             boolean allAffectedPermissionsOfThisGroupAreGranted = true;
290 
291             if (groupState.affectedPermissions == null) {
292                 // It is not clear which permissions belong to this group, hence never skip this
293                 // view
294                 allAffectedPermissionsOfThisGroupAreGranted = false;
295             } else {
296                 for (int permNum = 0; permNum < groupState.affectedPermissions.length;
297                         permNum++) {
298                     if (!grantedPermissionNames.contains(
299                             groupState.affectedPermissions[permNum])) {
300                         allAffectedPermissionsOfThisGroupAreGranted = false;
301                         break;
302                     }
303                 }
304             }
305 
306             if (allAffectedPermissionsOfThisGroupAreGranted) {
307                 groupState.mState = GroupState.STATE_ALLOWED;
308 
309                 if (mightShowNextGroup) {
310                     // The UI currently displays the first group with
311                     // mState == STATE_UNKNOWN. So we are switching to next group until we
312                     // could not allow a group that was still unknown
313                     if (!showNextPermissionGroupGrantRequest()) {
314                         setResultAndFinish();
315                     }
316                 }
317             } else {
318                 mightShowNextGroup = false;
319             }
320         }
321     }
322 
323     @Override
onStart()324     protected void onStart() {
325         super.onStart();
326 
327         PackageManager pm = getPackageManager();
328         pm.addOnPermissionsChangeListener(mPermissionChangeListener);
329 
330         // get notified when the package is removed
331         mPackageMonitor.register(this, getMainLooper(), false);
332 
333         // check if the package was removed while this activity was not started
334         try {
335             pm.getPackageInfo(mCallingPackage, 0);
336         } catch (NameNotFoundException e) {
337             Log.w(LOG_TAG, mCallingPackage + " was uninstalled while this activity was stopped", e);
338             finish();
339         }
340 
341         updateIfPermissionsWereGranted();
342     }
343 
344     @Override
onStop()345     protected void onStop() {
346         super.onStop();
347 
348         mPackageMonitor.unregister();
349 
350         getPackageManager().removeOnPermissionsChangeListener(mPermissionChangeListener);
351     }
352 
353     @Override
onConfigurationChanged(Configuration newConfig)354     public void onConfigurationChanged(Configuration newConfig) {
355         super.onConfigurationChanged(newConfig);
356         // We need to relayout the window as dialog width may be
357         // different in landscape vs portrait which affect the min
358         // window height needed to show all content. We have to
359         // re-add the window to force it to be resized if needed.
360         View decor = getWindow().getDecorView();
361         if (decor.getParent() != null) {
362             getWindowManager().removeViewImmediate(decor);
363             getWindowManager().addView(decor, decor.getLayoutParams());
364             if (mViewHandler instanceof GrantPermissionsViewHandlerImpl) {
365                 ((GrantPermissionsViewHandlerImpl) mViewHandler).onConfigurationChanged();
366             }
367         }
368     }
369 
370     @Override
dispatchTouchEvent(MotionEvent ev)371     public boolean dispatchTouchEvent(MotionEvent ev) {
372         View rootView = getWindow().getDecorView();
373         if (rootView.getTop() != 0) {
374             // We are animating the top view, need to compensate for that in motion events.
375             ev.setLocation(ev.getX(), ev.getY() - rootView.getTop());
376         }
377         return super.dispatchTouchEvent(ev);
378     }
379 
380     @Override
onSaveInstanceState(Bundle outState)381     protected void onSaveInstanceState(Bundle outState) {
382         super.onSaveInstanceState(outState);
383         mViewHandler.saveInstanceState(outState);
384     }
385 
386     @Override
onRestoreInstanceState(Bundle savedInstanceState)387     protected void onRestoreInstanceState(Bundle savedInstanceState) {
388         super.onRestoreInstanceState(savedInstanceState);
389         mViewHandler.loadInstanceState(savedInstanceState);
390     }
391 
showNextPermissionGroupGrantRequest()392     private boolean showNextPermissionGroupGrantRequest() {
393         final int groupCount = mRequestGrantPermissionGroups.size();
394 
395         int currentIndex = 0;
396         for (GroupState groupState : mRequestGrantPermissionGroups.values()) {
397             if (groupState.mState == GroupState.STATE_UNKNOWN) {
398                 CharSequence appLabel = mAppPermissions.getAppLabel();
399 
400                 Spanned message = null;
401                 int requestMessageId = groupState.mGroup.getRequest();
402                 if (requestMessageId != 0) {
403                     try {
404                         message = Html.fromHtml(getPackageManager().getResourcesForApplication(
405                                 groupState.mGroup.getDeclaringPackage()).getString(requestMessageId,
406                                 appLabel), 0);
407                     } catch (NameNotFoundException ignored) {
408                     }
409                 }
410 
411                 if (message == null) {
412                     message = Html.fromHtml(getString(R.string.permission_warning_template,
413                             appLabel, groupState.mGroup.getDescription()), 0);
414                 }
415 
416                 // Set the permission message as the title so it can be announced.
417                 setTitle(message);
418 
419                 // Set the new grant view
420                 // TODO: Use a real message for the action. We need group action APIs
421                 Resources resources;
422                 try {
423                     resources = getPackageManager().getResourcesForApplication(
424                             groupState.mGroup.getIconPkg());
425                 } catch (NameNotFoundException e) {
426                     // Fallback to system.
427                     resources = Resources.getSystem();
428                 }
429 
430                 Icon icon;
431                 try {
432                     icon = Icon.createWithResource(resources, groupState.mGroup.getIconResId());
433                 } catch (Resources.NotFoundException e) {
434                     Log.e(LOG_TAG, "Cannot load icon for group" + groupState.mGroup.getName(), e);
435                     icon = null;
436                 }
437 
438                 mViewHandler.updateUi(groupState.mGroup.getName(), groupCount, currentIndex,
439                         icon, message, groupState.mGroup.isUserSet());
440                 return true;
441             }
442 
443             currentIndex++;
444         }
445 
446         return false;
447     }
448 
449     @Override
onPermissionGrantResult(String name, boolean granted, boolean doNotAskAgain)450     public void onPermissionGrantResult(String name, boolean granted, boolean doNotAskAgain) {
451         KeyguardManager kgm = getSystemService(KeyguardManager.class);
452 
453         if (kgm.isDeviceLocked()) {
454             kgm.requestDismissKeyguard(this, new KeyguardManager.KeyguardDismissCallback() {
455                         @Override
456                         public void onDismissError() {
457                             Log.e(LOG_TAG, "Cannot dismiss keyguard perm=" + name + " granted="
458                                    + granted + " doNotAskAgain=" + doNotAskAgain);
459                         }
460 
461                         @Override
462                         public void onDismissCancelled() {
463                             // do nothing (i.e. stay at the current permission group)
464                         }
465 
466                         @Override
467                         public void onDismissSucceeded() {
468                             // Now the keyguard is dismissed, hence the device is not locked
469                             // anymore
470                             onPermissionGrantResult(name, granted, doNotAskAgain);
471                         }
472                     });
473 
474             return;
475         }
476 
477         GroupState groupState = mRequestGrantPermissionGroups.get(name);
478         if (groupState != null && groupState.mGroup != null) {
479             if (granted) {
480                 groupState.mGroup.grantRuntimePermissions(doNotAskAgain,
481                         groupState.affectedPermissions);
482                 groupState.mState = GroupState.STATE_ALLOWED;
483             } else {
484                 groupState.mGroup.revokeRuntimePermissions(doNotAskAgain,
485                         groupState.affectedPermissions);
486                 groupState.mState = GroupState.STATE_DENIED;
487 
488                 int numRequestedPermissions = mRequestedPermissions.length;
489                 for (int i = 0; i < numRequestedPermissions; i++) {
490                     String permission = mRequestedPermissions[i];
491 
492                     if (groupState.mGroup.hasPermission(permission)) {
493                         EventLogger.logPermission(
494                                 MetricsProto.MetricsEvent.ACTION_PERMISSION_DENIED, permission,
495                                 mAppPermissions.getPackageInfo().packageName);
496                     }
497                 }
498             }
499             updateGrantResults(groupState.mGroup);
500         }
501         if (!showNextPermissionGroupGrantRequest()) {
502             setResultAndFinish();
503         }
504     }
505 
updateGrantResults(AppPermissionGroup group)506     private void updateGrantResults(AppPermissionGroup group) {
507         for (Permission permission : group.getPermissions()) {
508             final int index = ArrayUtils.indexOf(
509                     mRequestedPermissions, permission.getName());
510             if (index >= 0) {
511                 mGrantResults[index] = permission.isGranted() ? PackageManager.PERMISSION_GRANTED
512                         : PackageManager.PERMISSION_DENIED;
513             }
514         }
515     }
516 
517     @Override
onKeyDown(int keyCode, KeyEvent event)518     public boolean onKeyDown(int keyCode, KeyEvent event)  {
519         // We do not allow backing out.
520         return keyCode == KeyEvent.KEYCODE_BACK;
521     }
522 
523     @Override
onKeyUp(int keyCode, KeyEvent event)524     public boolean onKeyUp(int keyCode, KeyEvent event)  {
525         // We do not allow backing out.
526         return keyCode == KeyEvent.KEYCODE_BACK;
527     }
528 
529     @Override
finish()530     public void finish() {
531         setResultIfNeeded(RESULT_CANCELED);
532         super.finish();
533     }
534 
computePermissionGrantState(PackageInfo callingPackageInfo, String permission, int permissionPolicy)535     private int computePermissionGrantState(PackageInfo callingPackageInfo,
536             String permission, int permissionPolicy) {
537         boolean permissionRequested = false;
538 
539         for (int i = 0; i < callingPackageInfo.requestedPermissions.length; i++) {
540             if (permission.equals(callingPackageInfo.requestedPermissions[i])) {
541                 permissionRequested = true;
542                 if ((callingPackageInfo.requestedPermissionsFlags[i]
543                         & PackageInfo.REQUESTED_PERMISSION_GRANTED) != 0) {
544                     return PERMISSION_GRANTED;
545                 }
546                 break;
547             }
548         }
549 
550         if (!permissionRequested) {
551             return PERMISSION_DENIED;
552         }
553 
554         try {
555             PermissionInfo pInfo = getPackageManager().getPermissionInfo(permission, 0);
556             if ((pInfo.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE)
557                     != PermissionInfo.PROTECTION_DANGEROUS) {
558                 return PERMISSION_DENIED;
559             }
560             if ((pInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_INSTANT) == 0
561                     && callingPackageInfo.applicationInfo.isInstantApp()) {
562                 return PERMISSION_DENIED;
563             }
564             if ((pInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY) != 0
565                     && callingPackageInfo.applicationInfo.targetSdkVersion
566                     < Build.VERSION_CODES.M) {
567                 return PERMISSION_DENIED;
568             }
569         } catch (NameNotFoundException e) {
570             return PERMISSION_DENIED;
571         }
572 
573         switch (permissionPolicy) {
574             case DevicePolicyManager.PERMISSION_POLICY_AUTO_GRANT: {
575                 return PERMISSION_GRANTED;
576             }
577             default: {
578                 return PERMISSION_DENIED;
579             }
580         }
581     }
582 
getCallingPackageInfo()583     private PackageInfo getCallingPackageInfo() {
584         try {
585             return getPackageManager().getPackageInfo(mCallingPackage,
586                     PackageManager.GET_PERMISSIONS);
587         } catch (NameNotFoundException e) {
588             Log.i(LOG_TAG, "No package: " + mCallingPackage, e);
589             return null;
590         }
591     }
592 
updateAlreadyGrantedPermissions(PackageInfo callingPackageInfo, int permissionPolicy)593     private void updateAlreadyGrantedPermissions(PackageInfo callingPackageInfo,
594             int permissionPolicy) {
595         final int requestedPermCount = mRequestedPermissions.length;
596         for (int i = 0; i < requestedPermCount; i++) {
597             String permission = mRequestedPermissions[i];
598 
599             if (permission != null) {
600                 if (computePermissionGrantState(callingPackageInfo, permission, permissionPolicy)
601                         == PERMISSION_GRANTED) {
602                     mGrantResults[i] = PERMISSION_GRANTED;
603                 }
604             }
605         }
606     }
607 
setResultIfNeeded(int resultCode)608     private void setResultIfNeeded(int resultCode) {
609         if (!mResultSet) {
610             mResultSet = true;
611             logRequestedPermissionGroups();
612             Intent result = new Intent(PackageManager.ACTION_REQUEST_PERMISSIONS);
613             result.putExtra(PackageManager.EXTRA_REQUEST_PERMISSIONS_NAMES, mRequestedPermissions);
614             result.putExtra(PackageManager.EXTRA_REQUEST_PERMISSIONS_RESULTS, mGrantResults);
615             setResult(resultCode, result);
616         }
617     }
618 
setResultAndFinish()619     private void setResultAndFinish() {
620         setResultIfNeeded(RESULT_OK);
621         finish();
622     }
623 
logRequestedPermissionGroups()624     private void logRequestedPermissionGroups() {
625         if (mRequestGrantPermissionGroups.isEmpty()) {
626             return;
627         }
628 
629         final int groupCount = mRequestGrantPermissionGroups.size();
630         List<AppPermissionGroup> groups = new ArrayList<>(groupCount);
631         for (GroupState groupState : mRequestGrantPermissionGroups.values()) {
632             groups.add(groupState.mGroup);
633         }
634 
635         SafetyNetLogger.logPermissionsRequested(mAppPermissions.getPackageInfo(), groups);
636     }
637 
computeAffectedPermissions(PackageInfo callingPkg, String permission)638     private static String[] computeAffectedPermissions(PackageInfo callingPkg,
639             String permission) {
640         // For <= N_MR1 apps all permissions are affected.
641         if (callingPkg.applicationInfo.targetSdkVersion <= Build.VERSION_CODES.N_MR1) {
642             return null;
643         }
644 
645         // For N_MR1+ apps only the requested permission is affected with addition
646         // to splits of this permission applicable to apps targeting N_MR1.
647         String[] permissions = new String[] {permission};
648         for (PackageParser.SplitPermissionInfo splitPerm : PackageParser.SPLIT_PERMISSIONS) {
649             if (splitPerm.targetSdk <= Build.VERSION_CODES.N_MR1
650                     || callingPkg.applicationInfo.targetSdkVersion >= splitPerm.targetSdk
651                     || !permission.equals(splitPerm.rootPerm)) {
652                 continue;
653             }
654             for (int i = 0; i < splitPerm.newPerms.length; i++) {
655                 final String newPerm = splitPerm.newPerms[i];
656                 permissions = ArrayUtils.appendString(permissions, newPerm);
657             }
658         }
659 
660         return permissions;
661     }
662 
663     private static final class GroupState {
664         static final int STATE_UNKNOWN = 0;
665         static final int STATE_ALLOWED = 1;
666         static final int STATE_DENIED = 2;
667 
668         final AppPermissionGroup mGroup;
669         int mState = STATE_UNKNOWN;
670 
671         /** Permissions of this group that need to be granted, null == all permissions of group */
672         String[] affectedPermissions;
673 
GroupState(AppPermissionGroup group)674         GroupState(AppPermissionGroup group) {
675             mGroup = group;
676         }
677     }
678 
679     private class PermissionChangeListener implements PackageManager.OnPermissionsChangedListener {
680         final int mCallingPackageUid;
681 
PermissionChangeListener()682         PermissionChangeListener() throws NameNotFoundException {
683             mCallingPackageUid = getPackageManager().getPackageUid(mCallingPackage, 0);
684         }
685 
686         @Override
onPermissionsChanged(int uid)687         public void onPermissionsChanged(int uid) {
688             if (uid == mCallingPackageUid) {
689                 updateIfPermissionsWereGranted();
690             }
691         }
692     }
693 }
694