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