• 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         getWindowManager().removeViewImmediate(decor);
200         getWindowManager().addView(decor, decor.getLayoutParams());
201         if (mViewHandler instanceof GrantPermissionsViewHandlerImpl) {
202             ((GrantPermissionsViewHandlerImpl) mViewHandler).onConfigurationChanged();
203         }
204     }
205 
206     @Override
dispatchTouchEvent(MotionEvent ev)207     public boolean dispatchTouchEvent(MotionEvent ev) {
208         View rootView = getWindow().getDecorView();
209         if (rootView.getTop() != 0) {
210             // We are animating the top view, need to compensate for that in motion events.
211             ev.setLocation(ev.getX(), ev.getY() - rootView.getTop());
212         }
213         return super.dispatchTouchEvent(ev);
214     }
215 
216     @Override
onSaveInstanceState(Bundle outState)217     protected void onSaveInstanceState(Bundle outState) {
218         super.onSaveInstanceState(outState);
219         mViewHandler.saveInstanceState(outState);
220     }
221 
222     @Override
onRestoreInstanceState(Bundle savedInstanceState)223     protected void onRestoreInstanceState(Bundle savedInstanceState) {
224         super.onRestoreInstanceState(savedInstanceState);
225         mViewHandler.loadInstanceState(savedInstanceState);
226     }
227 
showNextPermissionGroupGrantRequest()228     private boolean showNextPermissionGroupGrantRequest() {
229         final int groupCount = mRequestGrantPermissionGroups.size();
230 
231         int currentIndex = 0;
232         for (GroupState groupState : mRequestGrantPermissionGroups.values()) {
233             if (groupState.mState == GroupState.STATE_UNKNOWN) {
234                 CharSequence appLabel = mAppPermissions.getAppLabel();
235                 Spanned message = Html.fromHtml(getString(R.string.permission_warning_template,
236                         appLabel, groupState.mGroup.getDescription()), 0);
237                 // Set the permission message as the title so it can be announced.
238                 setTitle(message);
239 
240                 // Set the new grant view
241                 // TODO: Use a real message for the action. We need group action APIs
242                 Resources resources;
243                 try {
244                     resources = getPackageManager().getResourcesForApplication(
245                             groupState.mGroup.getIconPkg());
246                 } catch (NameNotFoundException e) {
247                     // Fallback to system.
248                     resources = Resources.getSystem();
249                 }
250                 int icon = groupState.mGroup.getIconResId();
251 
252                 mViewHandler.updateUi(groupState.mGroup.getName(), groupCount, currentIndex,
253                         Icon.createWithResource(resources, icon), message,
254                         groupState.mGroup.isUserSet());
255                 return true;
256             }
257 
258             currentIndex++;
259         }
260 
261         return false;
262     }
263 
264     @Override
onPermissionGrantResult(String name, boolean granted, boolean doNotAskAgain)265     public void onPermissionGrantResult(String name, boolean granted, boolean doNotAskAgain) {
266         GroupState groupState = mRequestGrantPermissionGroups.get(name);
267         if (groupState.mGroup != null) {
268             if (granted) {
269                 groupState.mGroup.grantRuntimePermissions(doNotAskAgain);
270                 groupState.mState = GroupState.STATE_ALLOWED;
271             } else {
272                 groupState.mGroup.revokeRuntimePermissions(doNotAskAgain);
273                 groupState.mState = GroupState.STATE_DENIED;
274             }
275             updateGrantResults(groupState.mGroup);
276         }
277         if (!showNextPermissionGroupGrantRequest()) {
278             setResultAndFinish();
279         }
280     }
281 
updateGrantResults(AppPermissionGroup group)282     private void updateGrantResults(AppPermissionGroup group) {
283         for (Permission permission : group.getPermissions()) {
284             final int index = ArrayUtils.getArrayIndex(
285                     mRequestedPermissions, permission.getName());
286             if (index >= 0) {
287                 mGrantResults[index] = permission.isGranted() ? PackageManager.PERMISSION_GRANTED
288                         : PackageManager.PERMISSION_DENIED;
289             }
290         }
291     }
292 
293     @Override
onKeyDown(int keyCode, KeyEvent event)294     public boolean onKeyDown(int keyCode, KeyEvent event)  {
295         // We do not allow backing out.
296         return keyCode == KeyEvent.KEYCODE_BACK;
297     }
298 
299     @Override
onKeyUp(int keyCode, KeyEvent event)300     public boolean onKeyUp(int keyCode, KeyEvent event)  {
301         // We do not allow backing out.
302         return keyCode == KeyEvent.KEYCODE_BACK;
303     }
304 
305     @Override
finish()306     public void finish() {
307         setResultIfNeeded(RESULT_CANCELED);
308         super.finish();
309     }
310 
computePermissionGrantState(PackageInfo callingPackageInfo, String permission, int permissionPolicy)311     private int computePermissionGrantState(PackageInfo callingPackageInfo,
312             String permission, int permissionPolicy) {
313         boolean permissionRequested = false;
314 
315         for (int i = 0; i < callingPackageInfo.requestedPermissions.length; i++) {
316             if (permission.equals(callingPackageInfo.requestedPermissions[i])) {
317                 permissionRequested = true;
318                 if ((callingPackageInfo.requestedPermissionsFlags[i]
319                         & PackageInfo.REQUESTED_PERMISSION_GRANTED) != 0) {
320                     return PERMISSION_GRANTED;
321                 }
322                 break;
323             }
324         }
325 
326         if (!permissionRequested) {
327             return PERMISSION_DENIED;
328         }
329 
330         try {
331             PermissionInfo pInfo = getPackageManager().getPermissionInfo(permission, 0);
332             if ((pInfo.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE)
333                     != PermissionInfo.PROTECTION_DANGEROUS) {
334                 return PERMISSION_DENIED;
335             }
336         } catch (NameNotFoundException e) {
337             return PERMISSION_DENIED;
338         }
339 
340         switch (permissionPolicy) {
341             case DevicePolicyManager.PERMISSION_POLICY_AUTO_GRANT: {
342                 return PERMISSION_GRANTED;
343             }
344             default: {
345                 return PERMISSION_DENIED;
346             }
347         }
348     }
349 
getCallingPackageInfo()350     private PackageInfo getCallingPackageInfo() {
351         try {
352             return getPackageManager().getPackageInfo(getCallingPackage(),
353                     PackageManager.GET_PERMISSIONS);
354         } catch (NameNotFoundException e) {
355             Log.i(LOG_TAG, "No package: " + getCallingPackage(), e);
356             return null;
357         }
358     }
359 
updateDefaultResults(PackageInfo callingPackageInfo, int permissionPolicy)360     private void updateDefaultResults(PackageInfo callingPackageInfo, int permissionPolicy) {
361         final int requestedPermCount = mRequestedPermissions.length;
362         for (int i = 0; i < requestedPermCount; i++) {
363             String permission = mRequestedPermissions[i];
364             mGrantResults[i] = callingPackageInfo != null
365                     ? computePermissionGrantState(callingPackageInfo, permission, permissionPolicy)
366                     : PERMISSION_DENIED;
367         }
368     }
369 
setResultIfNeeded(int resultCode)370     private void setResultIfNeeded(int resultCode) {
371         if (!mResultSet) {
372             mResultSet = true;
373             logRequestedPermissionGroups();
374             Intent result = new Intent(PackageManager.ACTION_REQUEST_PERMISSIONS);
375             result.putExtra(PackageManager.EXTRA_REQUEST_PERMISSIONS_NAMES, mRequestedPermissions);
376             result.putExtra(PackageManager.EXTRA_REQUEST_PERMISSIONS_RESULTS, mGrantResults);
377             setResult(resultCode, result);
378         }
379     }
380 
setResultAndFinish()381     private void setResultAndFinish() {
382         setResultIfNeeded(RESULT_OK);
383         finish();
384     }
385 
logRequestedPermissionGroups()386     private void logRequestedPermissionGroups() {
387         if (mRequestGrantPermissionGroups.isEmpty()) {
388             return;
389         }
390 
391         final int groupCount = mRequestGrantPermissionGroups.size();
392         List<AppPermissionGroup> groups = new ArrayList<>(groupCount);
393         for (GroupState groupState : mRequestGrantPermissionGroups.values()) {
394             groups.add(groupState.mGroup);
395         }
396 
397         SafetyNetLogger.logPermissionsRequested(mAppPermissions.getPackageInfo(), groups);
398     }
399 
400     private static final class GroupState {
401         static final int STATE_UNKNOWN = 0;
402         static final int STATE_ALLOWED = 1;
403         static final int STATE_DENIED = 2;
404 
405         final AppPermissionGroup mGroup;
406         int mState = STATE_UNKNOWN;
407 
GroupState(AppPermissionGroup group)408         GroupState(AppPermissionGroup group) {
409             mGroup = group;
410         }
411     }
412 }
413