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