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.permissioncontroller.permission.model;
18 
19 import android.content.Context;
20 import android.content.pm.PackageInfo;
21 import android.content.pm.PackageManager;
22 import android.os.UserHandle;
23 import android.util.ArrayMap;
24 
25 import com.android.permissioncontroller.permission.utils.Utils;
26 
27 import java.util.ArrayList;
28 import java.util.Collections;
29 import java.util.List;
30 
31 /**
32  * An app that requests permissions.
33  *
34  * <p>Allows to query all permission groups of the app and which permission belongs to which group.
35  */
36 public final class AppPermissions {
37     /**
38      * All permission groups the app requests. Background permission groups are attached to their
39      * foreground groups.
40      */
41     private final ArrayList<AppPermissionGroup> mGroups = new ArrayList<>();
42 
43     /** Cache: group name -> group */
44     private final ArrayMap<String, AppPermissionGroup> mGroupNameToGroup = new ArrayMap<>();
45 
46     /** Cache: permission name -> group. Might point to background group */
47     private final ArrayMap<String, AppPermissionGroup> mPermissionNameToGroup = new ArrayMap<>();
48 
49     private final Context mContext;
50 
51     private final CharSequence mAppLabel;
52 
53     private final Runnable mOnErrorCallback;
54 
55     private final boolean mSortGroups;
56 
57     /** Do not actually commit changes to the platform until {@link #persistChanges} is called */
58     private final boolean mDelayChanges;
59 
60     private PackageInfo mPackageInfo;
61 
AppPermissions(Context context, PackageInfo packageInfo, boolean sortGroups, Runnable onErrorCallback)62     public AppPermissions(Context context, PackageInfo packageInfo, boolean sortGroups,
63             Runnable onErrorCallback) {
64         this(context, packageInfo, sortGroups, false, onErrorCallback);
65     }
66 
AppPermissions(Context context, PackageInfo packageInfo, boolean sortGroups, boolean delayChanges, Runnable onErrorCallback)67     public AppPermissions(Context context, PackageInfo packageInfo, boolean sortGroups,
68             boolean delayChanges, Runnable onErrorCallback) {
69         mContext = context;
70         mPackageInfo = packageInfo;
71         mAppLabel = Utils.getAppLabel(packageInfo.applicationInfo, context);
72         mSortGroups = sortGroups;
73         mDelayChanges = delayChanges;
74         mOnErrorCallback = onErrorCallback;
75         loadPermissionGroups();
76     }
77 
getPackageInfo()78     public PackageInfo getPackageInfo() {
79         return mPackageInfo;
80     }
81 
refresh()82     public void refresh() {
83         loadPackageInfo();
84         loadPermissionGroups();
85     }
86 
getAppLabel()87     public CharSequence getAppLabel() {
88         return mAppLabel;
89     }
90 
getPermissionGroup(String name)91     public AppPermissionGroup getPermissionGroup(String name) {
92         return mGroupNameToGroup.get(name);
93     }
94 
getPermissionGroups()95     public List<AppPermissionGroup> getPermissionGroups() {
96         return mGroups;
97     }
98 
isReviewRequired()99     public boolean isReviewRequired() {
100         final int groupCount = mGroups.size();
101         for (int i = 0; i < groupCount; i++) {
102             AppPermissionGroup group = mGroups.get(i);
103             if (group.isReviewRequired()) {
104                 return true;
105             }
106         }
107         return false;
108     }
109 
loadPackageInfo()110     private void loadPackageInfo() {
111         try {
112             mPackageInfo = mContext.createPackageContextAsUser(mPackageInfo.packageName, 0,
113                     UserHandle.getUserHandleForUid(mPackageInfo.applicationInfo.uid))
114                     .getPackageManager().getPackageInfo(mPackageInfo.packageName,
115                             PackageManager.GET_PERMISSIONS);
116         } catch (PackageManager.NameNotFoundException e) {
117             if (mOnErrorCallback != null) {
118                 mOnErrorCallback.run();
119             }
120         }
121     }
122 
123     /**
124      * Add all individual permissions of the {@code group} to the {@link #mPermissionNameToGroup}
125      * lookup table.
126      *
127      * @param group The group of permissions to add
128      */
addAllPermissions(AppPermissionGroup group)129     private void addAllPermissions(AppPermissionGroup group) {
130         ArrayList<Permission> perms = group.getPermissions();
131 
132         int numPerms = perms.size();
133         for (int permNum = 0; permNum < numPerms; permNum++) {
134             mPermissionNameToGroup.put(perms.get(permNum).getName(), group);
135         }
136     }
137 
loadPermissionGroups()138     private void loadPermissionGroups() {
139         mGroups.clear();
140         mGroupNameToGroup.clear();
141         mPermissionNameToGroup.clear();
142 
143         if (mPackageInfo.requestedPermissions != null) {
144             for (String requestedPerm : mPackageInfo.requestedPermissions) {
145                 if (getGroupForPermission(requestedPerm) == null) {
146                     AppPermissionGroup group = AppPermissionGroup.create(mContext, mPackageInfo,
147                             requestedPerm, mDelayChanges);
148                     if (group == null) {
149                         continue;
150                     }
151 
152                     mGroups.add(group);
153                     mGroupNameToGroup.put(group.getName(), group);
154 
155                     addAllPermissions(group);
156 
157                     AppPermissionGroup backgroundGroup = group.getBackgroundPermissions();
158                     if (backgroundGroup != null) {
159                         addAllPermissions(backgroundGroup);
160                     }
161                 }
162             }
163 
164             if (mSortGroups) {
165                 Collections.sort(mGroups);
166             }
167         }
168     }
169 
170     /**
171      * Find the group a permission belongs to.
172      *
173      * <p>The group found might be a background group.
174      *
175      * @param permission The name of the permission
176      *
177      * @return The group the permission belongs to
178      */
getGroupForPermission(String permission)179     public AppPermissionGroup getGroupForPermission(String permission) {
180         return mPermissionNameToGroup.get(permission);
181     }
182 
183     /**
184      * If the changes to the permission groups were delayed, persist them now.
185      *
186      * @param mayKillBecauseOfAppOpsChange If the app may be killed if app ops change. If this is
187      *                                     set to {@code false} the caller has to make sure to kill
188      *                                     the app if needed.
189      */
persistChanges(boolean mayKillBecauseOfAppOpsChange)190     public void persistChanges(boolean mayKillBecauseOfAppOpsChange) {
191         if (mDelayChanges) {
192             int numGroups = mGroups.size();
193 
194             for (int i = 0; i < numGroups; i++) {
195                 AppPermissionGroup group = mGroups.get(i);
196                 group.persistChanges(mayKillBecauseOfAppOpsChange);
197 
198                 AppPermissionGroup backgroundGroup = group.getBackgroundPermissions();
199                 if (backgroundGroup != null) {
200                     backgroundGroup.persistChanges(mayKillBecauseOfAppOpsChange);
201                 }
202             }
203         }
204     }
205 }
206