• 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.content.Intent;
24 import android.content.pm.PackageInfo;
25 import android.content.pm.PackageManager;
26 import android.content.pm.PackageManager.NameNotFoundException;
27 import android.content.pm.PackageParser;
28 import android.content.pm.PermissionInfo;
29 import android.content.res.Configuration;
30 import android.content.res.Resources;
31 import android.graphics.drawable.Icon;
32 import android.os.Build;
33 import android.os.Bundle;
34 import android.text.Html;
35 import android.text.Spanned;
36 import android.util.Log;
37 import android.view.KeyEvent;
38 import android.view.MotionEvent;
39 import android.view.View;
40 import android.view.Window;
41 import android.view.WindowManager;
42 
43 import com.android.packageinstaller.DeviceUtils;
44 import com.android.packageinstaller.R;
45 import com.android.packageinstaller.permission.model.AppPermissionGroup;
46 import com.android.packageinstaller.permission.model.AppPermissions;
47 import com.android.packageinstaller.permission.model.Permission;
48 import com.android.packageinstaller.permission.ui.auto.GrantPermissionsAutoViewHandler;
49 import com.android.packageinstaller.permission.ui.handheld.GrantPermissionsViewHandlerImpl;
50 import com.android.packageinstaller.permission.utils.ArrayUtils;
51 import com.android.packageinstaller.permission.utils.EventLogger;
52 import com.android.packageinstaller.permission.utils.SafetyNetLogger;
53 
54 import java.util.ArrayList;
55 import java.util.Arrays;
56 import java.util.LinkedHashMap;
57 import java.util.List;
58 
59 public class GrantPermissionsActivity extends OverlayTouchActivity
60         implements GrantPermissionsViewHandler.ResultListener {
61 
62     private static final String LOG_TAG = "GrantPermissionsActivity";
63 
64     private String[] mRequestedPermissions;
65     private int[] mGrantResults;
66 
67     private LinkedHashMap<String, GroupState> mRequestGrantPermissionGroups = new LinkedHashMap<>();
68 
69     private GrantPermissionsViewHandler mViewHandler;
70     private AppPermissions mAppPermissions;
71 
72     boolean mResultSet;
73 
74     @Override
onCreate(Bundle icicle)75     public void onCreate(Bundle icicle) {
76         super.onCreate(icicle);
77         setFinishOnTouchOutside(false);
78 
79         getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
80         getWindow().addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
81 
82         setTitle(R.string.permission_request_title);
83 
84         if (DeviceUtils.isTelevision(this)) {
85             mViewHandler = new com.android.packageinstaller.permission.ui.television
86                     .GrantPermissionsViewHandlerImpl(this,
87                     getCallingPackage()).setResultListener(this);
88         } else if (DeviceUtils.isWear(this)) {
89             mViewHandler = new GrantPermissionsWatchViewHandler(this).setResultListener(this);
90         } else if (DeviceUtils.isAuto(this)) {
91             mViewHandler = new GrantPermissionsAutoViewHandler(this, getCallingPackage())
92                     .setResultListener(this);
93         } else {
94             mViewHandler = new com.android.packageinstaller.permission.ui.handheld
95                     .GrantPermissionsViewHandlerImpl(this, getCallingPackage())
96                     .setResultListener(this);
97         }
98 
99         mRequestedPermissions = getIntent().getStringArrayExtra(
100                 PackageManager.EXTRA_REQUEST_PERMISSIONS_NAMES);
101         if (mRequestedPermissions == null) {
102             mRequestedPermissions = new String[0];
103         }
104 
105         final int requestedPermCount = mRequestedPermissions.length;
106         mGrantResults = new int[requestedPermCount];
107         Arrays.fill(mGrantResults, PackageManager.PERMISSION_DENIED);
108 
109         if (requestedPermCount == 0) {
110             setResultAndFinish();
111             return;
112         }
113 
114         PackageInfo callingPackageInfo = getCallingPackageInfo();
115 
116         if (callingPackageInfo == null || callingPackageInfo.requestedPermissions == null
117                 || callingPackageInfo.requestedPermissions.length <= 0) {
118             setResultAndFinish();
119             return;
120         }
121 
122         // Don't allow legacy apps to request runtime permissions.
123         if (callingPackageInfo.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M) {
124             // Returning empty arrays means a cancellation.
125             mRequestedPermissions = new String[0];
126             mGrantResults = new int[0];
127             setResultAndFinish();
128             return;
129         }
130 
131         DevicePolicyManager devicePolicyManager = getSystemService(DevicePolicyManager.class);
132         final int permissionPolicy = devicePolicyManager.getPermissionPolicy(null);
133 
134         // If calling package is null we default to deny all.
135         updateDefaultResults(callingPackageInfo, permissionPolicy);
136 
137         mAppPermissions = new AppPermissions(this, callingPackageInfo, null, false,
138                 new Runnable() {
139                     @Override
140                     public void run() {
141                         setResultAndFinish();
142                     }
143                 });
144 
145         for (String requestedPermission : mRequestedPermissions) {
146             AppPermissionGroup group = null;
147             for (AppPermissionGroup nextGroup : mAppPermissions.getPermissionGroups()) {
148                 if (nextGroup.hasPermission(requestedPermission)) {
149                     group = nextGroup;
150                     break;
151                 }
152             }
153             if (group == null) {
154                 continue;
155             }
156             if (!group.isGrantingAllowed()) {
157                 // Skip showing groups that we know cannot be granted.
158                 continue;
159             }
160             // We allow the user to choose only non-fixed permissions. A permission
161             // is fixed either by device policy or the user denying with prejudice.
162             if (!group.isUserFixed() && !group.isPolicyFixed()) {
163                 switch (permissionPolicy) {
164                     case DevicePolicyManager.PERMISSION_POLICY_AUTO_GRANT: {
165                         if (!group.areRuntimePermissionsGranted()) {
166                             group.grantRuntimePermissions(false, computeAffectedPermissions(
167                                     callingPackageInfo, requestedPermission));
168                         }
169                         group.setPolicyFixed();
170                     } break;
171 
172                     case DevicePolicyManager.PERMISSION_POLICY_AUTO_DENY: {
173                         if (group.areRuntimePermissionsGranted()) {
174                             group.revokeRuntimePermissions(false, computeAffectedPermissions(
175                                     callingPackageInfo, requestedPermission));
176                         }
177                         group.setPolicyFixed();
178                     } break;
179 
180                     default: {
181                         if (!group.areRuntimePermissionsGranted()) {
182                             GroupState state = mRequestGrantPermissionGroups.get(group.getName());
183                             if (state == null) {
184                                 state = new GroupState(group);
185                                 mRequestGrantPermissionGroups.put(group.getName(), state);
186                             }
187                             String[] affectedPermissions = computeAffectedPermissions(
188                                     callingPackageInfo, requestedPermission);
189                             if (affectedPermissions != null) {
190                                 for (String affectedPermission : affectedPermissions) {
191                                     state.affectedPermissions = ArrayUtils.appendString(
192                                             state.affectedPermissions, affectedPermission);
193                                 }
194                             }
195                         } else {
196                             group.grantRuntimePermissions(false, computeAffectedPermissions(
197                                     callingPackageInfo, requestedPermission));
198                             updateGrantResults(group);
199                         }
200                     } break;
201                 }
202             } else {
203                 // if the permission is fixed, ensure that we return the right request result
204                 updateGrantResults(group);
205             }
206         }
207 
208         setContentView(mViewHandler.createView());
209 
210         Window window = getWindow();
211         WindowManager.LayoutParams layoutParams = window.getAttributes();
212         mViewHandler.updateWindowAttributes(layoutParams);
213         window.setAttributes(layoutParams);
214 
215         if (!showNextPermissionGroupGrantRequest()) {
216             setResultAndFinish();
217         } else if (icicle == null) {
218             int numRequestedPermissions = mRequestedPermissions.length;
219             for (int permissionNum = 0; permissionNum < numRequestedPermissions; permissionNum++) {
220                 String permission = mRequestedPermissions[permissionNum];
221 
222                 EventLogger.logPermissionRequested(this, permission,
223                         mAppPermissions.getPackageInfo().packageName);
224             }
225         }
226     }
227 
228     @Override
onConfigurationChanged(Configuration newConfig)229     public void onConfigurationChanged(Configuration newConfig) {
230         super.onConfigurationChanged(newConfig);
231         // We need to relayout the window as dialog width may be
232         // different in landscape vs portrait which affect the min
233         // window height needed to show all content. We have to
234         // re-add the window to force it to be resized if needed.
235         View decor = getWindow().getDecorView();
236         if (decor.getParent() != null) {
237             getWindowManager().removeViewImmediate(decor);
238             getWindowManager().addView(decor, decor.getLayoutParams());
239             if (mViewHandler instanceof GrantPermissionsViewHandlerImpl) {
240                 ((GrantPermissionsViewHandlerImpl) mViewHandler).onConfigurationChanged();
241             }
242         }
243     }
244 
245     @Override
dispatchTouchEvent(MotionEvent ev)246     public boolean dispatchTouchEvent(MotionEvent ev) {
247         View rootView = getWindow().getDecorView();
248         if (rootView.getTop() != 0) {
249             // We are animating the top view, need to compensate for that in motion events.
250             ev.setLocation(ev.getX(), ev.getY() - rootView.getTop());
251         }
252         return super.dispatchTouchEvent(ev);
253     }
254 
255     @Override
onSaveInstanceState(Bundle outState)256     protected void onSaveInstanceState(Bundle outState) {
257         super.onSaveInstanceState(outState);
258         mViewHandler.saveInstanceState(outState);
259     }
260 
261     @Override
onRestoreInstanceState(Bundle savedInstanceState)262     protected void onRestoreInstanceState(Bundle savedInstanceState) {
263         super.onRestoreInstanceState(savedInstanceState);
264         mViewHandler.loadInstanceState(savedInstanceState);
265     }
266 
showNextPermissionGroupGrantRequest()267     private boolean showNextPermissionGroupGrantRequest() {
268         final int groupCount = mRequestGrantPermissionGroups.size();
269 
270         int currentIndex = 0;
271         for (GroupState groupState : mRequestGrantPermissionGroups.values()) {
272             if (groupState.mState == GroupState.STATE_UNKNOWN) {
273                 CharSequence appLabel = mAppPermissions.getAppLabel();
274                 Spanned message = Html.fromHtml(getString(R.string.permission_warning_template,
275                         appLabel, groupState.mGroup.getDescription()), 0);
276                 // Set the permission message as the title so it can be announced.
277                 setTitle(message);
278 
279                 // Set the new grant view
280                 // TODO: Use a real message for the action. We need group action APIs
281                 Resources resources;
282                 try {
283                     resources = getPackageManager().getResourcesForApplication(
284                             groupState.mGroup.getIconPkg());
285                 } catch (NameNotFoundException e) {
286                     // Fallback to system.
287                     resources = Resources.getSystem();
288                 }
289                 int icon = groupState.mGroup.getIconResId();
290 
291                 mViewHandler.updateUi(groupState.mGroup.getName(), groupCount, currentIndex,
292                         Icon.createWithResource(resources, icon), message,
293                         groupState.mGroup.isUserSet());
294                 return true;
295             }
296 
297             currentIndex++;
298         }
299 
300         return false;
301     }
302 
303     @Override
onPermissionGrantResult(String name, boolean granted, boolean doNotAskAgain)304     public void onPermissionGrantResult(String name, boolean granted, boolean doNotAskAgain) {
305         GroupState groupState = mRequestGrantPermissionGroups.get(name);
306         if (groupState.mGroup != null) {
307             if (granted) {
308                 groupState.mGroup.grantRuntimePermissions(doNotAskAgain,
309                         groupState.affectedPermissions);
310                 groupState.mState = GroupState.STATE_ALLOWED;
311             } else {
312                 groupState.mGroup.revokeRuntimePermissions(doNotAskAgain,
313                         groupState.affectedPermissions);
314                 groupState.mState = GroupState.STATE_DENIED;
315 
316                 int numRequestedPermissions = mRequestedPermissions.length;
317                 for (int i = 0; i < numRequestedPermissions; i++) {
318                     String permission = mRequestedPermissions[i];
319 
320                     if (groupState.mGroup.hasPermission(permission)) {
321                         EventLogger.logPermissionDenied(this, permission,
322                                 mAppPermissions.getPackageInfo().packageName);
323                     }
324                 }
325             }
326             updateGrantResults(groupState.mGroup);
327         }
328         if (!showNextPermissionGroupGrantRequest()) {
329             setResultAndFinish();
330         }
331     }
332 
updateGrantResults(AppPermissionGroup group)333     private void updateGrantResults(AppPermissionGroup group) {
334         for (Permission permission : group.getPermissions()) {
335             final int index = ArrayUtils.indexOf(
336                     mRequestedPermissions, permission.getName());
337             if (index >= 0) {
338                 mGrantResults[index] = permission.isGranted() ? PackageManager.PERMISSION_GRANTED
339                         : PackageManager.PERMISSION_DENIED;
340             }
341         }
342     }
343 
344     @Override
onKeyDown(int keyCode, KeyEvent event)345     public boolean onKeyDown(int keyCode, KeyEvent event)  {
346         // We do not allow backing out.
347         return keyCode == KeyEvent.KEYCODE_BACK;
348     }
349 
350     @Override
onKeyUp(int keyCode, KeyEvent event)351     public boolean onKeyUp(int keyCode, KeyEvent event)  {
352         // We do not allow backing out.
353         return keyCode == KeyEvent.KEYCODE_BACK;
354     }
355 
356     @Override
finish()357     public void finish() {
358         setResultIfNeeded(RESULT_CANCELED);
359         super.finish();
360     }
361 
computePermissionGrantState(PackageInfo callingPackageInfo, String permission, int permissionPolicy)362     private int computePermissionGrantState(PackageInfo callingPackageInfo,
363             String permission, int permissionPolicy) {
364         boolean permissionRequested = false;
365 
366         for (int i = 0; i < callingPackageInfo.requestedPermissions.length; i++) {
367             if (permission.equals(callingPackageInfo.requestedPermissions[i])) {
368                 permissionRequested = true;
369                 if ((callingPackageInfo.requestedPermissionsFlags[i]
370                         & PackageInfo.REQUESTED_PERMISSION_GRANTED) != 0) {
371                     return PERMISSION_GRANTED;
372                 }
373                 break;
374             }
375         }
376 
377         if (!permissionRequested) {
378             return PERMISSION_DENIED;
379         }
380 
381         try {
382             PermissionInfo pInfo = getPackageManager().getPermissionInfo(permission, 0);
383             if ((pInfo.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE)
384                     != PermissionInfo.PROTECTION_DANGEROUS) {
385                 return PERMISSION_DENIED;
386             }
387             if ((pInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_EPHEMERAL) == 0
388                     && callingPackageInfo.applicationInfo.isInstantApp()) {
389                 return PERMISSION_DENIED;
390             }
391             if ((pInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY) != 0
392                     && callingPackageInfo.applicationInfo.targetSdkVersion
393                     < Build.VERSION_CODES.M) {
394                 return PERMISSION_DENIED;
395             }
396         } catch (NameNotFoundException e) {
397             return PERMISSION_DENIED;
398         }
399 
400         switch (permissionPolicy) {
401             case DevicePolicyManager.PERMISSION_POLICY_AUTO_GRANT: {
402                 return PERMISSION_GRANTED;
403             }
404             default: {
405                 return PERMISSION_DENIED;
406             }
407         }
408     }
409 
getCallingPackageInfo()410     private PackageInfo getCallingPackageInfo() {
411         try {
412             return getPackageManager().getPackageInfo(getCallingPackage(),
413                     PackageManager.GET_PERMISSIONS);
414         } catch (NameNotFoundException e) {
415             Log.i(LOG_TAG, "No package: " + getCallingPackage(), e);
416             return null;
417         }
418     }
419 
updateDefaultResults(PackageInfo callingPackageInfo, int permissionPolicy)420     private void updateDefaultResults(PackageInfo callingPackageInfo, int permissionPolicy) {
421         final int requestedPermCount = mRequestedPermissions.length;
422         for (int i = 0; i < requestedPermCount; i++) {
423             String permission = mRequestedPermissions[i];
424             mGrantResults[i] = callingPackageInfo != null
425                     ? computePermissionGrantState(callingPackageInfo, permission, permissionPolicy)
426                     : PERMISSION_DENIED;
427         }
428     }
429 
setResultIfNeeded(int resultCode)430     private void setResultIfNeeded(int resultCode) {
431         if (!mResultSet) {
432             mResultSet = true;
433             logRequestedPermissionGroups();
434             Intent result = new Intent(PackageManager.ACTION_REQUEST_PERMISSIONS);
435             result.putExtra(PackageManager.EXTRA_REQUEST_PERMISSIONS_NAMES, mRequestedPermissions);
436             result.putExtra(PackageManager.EXTRA_REQUEST_PERMISSIONS_RESULTS, mGrantResults);
437             setResult(resultCode, result);
438         }
439     }
440 
setResultAndFinish()441     private void setResultAndFinish() {
442         setResultIfNeeded(RESULT_OK);
443         finish();
444     }
445 
logRequestedPermissionGroups()446     private void logRequestedPermissionGroups() {
447         if (mRequestGrantPermissionGroups.isEmpty()) {
448             return;
449         }
450 
451         final int groupCount = mRequestGrantPermissionGroups.size();
452         List<AppPermissionGroup> groups = new ArrayList<>(groupCount);
453         for (GroupState groupState : mRequestGrantPermissionGroups.values()) {
454             groups.add(groupState.mGroup);
455         }
456 
457         SafetyNetLogger.logPermissionsRequested(mAppPermissions.getPackageInfo(), groups);
458     }
459 
computeAffectedPermissions(PackageInfo callingPkg, String permission)460     private static String[] computeAffectedPermissions(PackageInfo callingPkg,
461             String permission) {
462         // For <= N_MR1 apps all permissions are affected.
463         if (callingPkg.applicationInfo.targetSdkVersion <= Build.VERSION_CODES.N_MR1) {
464             return null;
465         }
466 
467         // For N_MR1+ apps only the requested permission is affected with addition
468         // to splits of this permission applicable to apps targeting N_MR1.
469         String[] permissions = new String[] {permission};
470         for (PackageParser.SplitPermissionInfo splitPerm : PackageParser.SPLIT_PERMISSIONS) {
471             if (splitPerm.targetSdk <= Build.VERSION_CODES.N_MR1
472                     || callingPkg.applicationInfo.targetSdkVersion >= splitPerm.targetSdk
473                     || !permission.equals(splitPerm.rootPerm)) {
474                 continue;
475             }
476             for (int i = 0; i < splitPerm.newPerms.length; i++) {
477                 final String newPerm = splitPerm.newPerms[i];
478                 permissions = ArrayUtils.appendString(permissions, newPerm);
479             }
480         }
481 
482         return permissions;
483     }
484 
485     private static final class GroupState {
486         static final int STATE_UNKNOWN = 0;
487         static final int STATE_ALLOWED = 1;
488         static final int STATE_DENIED = 2;
489 
490         final AppPermissionGroup mGroup;
491         int mState = STATE_UNKNOWN;
492         String[] affectedPermissions;
493 
GroupState(AppPermissionGroup group)494         GroupState(AppPermissionGroup group) {
495             mGroup = group;
496         }
497     }
498 }
499