• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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.role.service;
18 
19 import android.app.role.RoleControllerService;
20 import android.app.role.RoleManager;
21 import android.content.pm.ApplicationInfo;
22 import android.os.Process;
23 import android.os.UserHandle;
24 import android.util.ArrayMap;
25 import android.util.ArraySet;
26 import android.util.Log;
27 
28 import androidx.annotation.NonNull;
29 import androidx.annotation.WorkerThread;
30 
31 import com.android.permissioncontroller.permission.utils.CollectionUtils;
32 import com.android.permissioncontroller.role.model.Role;
33 import com.android.permissioncontroller.role.model.Roles;
34 import com.android.permissioncontroller.role.utils.PackageUtils;
35 
36 import java.util.ArrayList;
37 import java.util.List;
38 import java.util.Objects;
39 
40 /**
41  * Implementation of {@link RoleControllerService}.
42  */
43 public class RoleControllerServiceImpl extends RoleControllerService {
44 
45     private static final String LOG_TAG = RoleControllerServiceImpl.class.getSimpleName();
46 
47     private static final boolean DEBUG = false;
48 
49     private RoleManager mRoleManager;
50 
51     @Override
onCreate()52     public void onCreate() {
53         super.onCreate();
54 
55         mRoleManager = getSystemService(RoleManager.class);
56     }
57 
58     @Override
59     @WorkerThread
onGrantDefaultRoles()60     public boolean onGrantDefaultRoles() {
61         if (DEBUG) {
62             Log.i(LOG_TAG, "Granting default roles, user: " + UserHandle.myUserId());
63         }
64 
65         // Gather the available roles for current user.
66         ArrayMap<String, Role> roleMap = Roles.get(this);
67         List<Role> roles = new ArrayList<>();
68         List<String> roleNames = new ArrayList<>();
69         ArraySet<String> addedRoleNames = new ArraySet<>();
70         int roleMapSize = roleMap.size();
71         for (int i = 0; i < roleMapSize; i++) {
72             Role role = roleMap.valueAt(i);
73 
74             if (!role.isAvailable(this)) {
75                 continue;
76             }
77             roles.add(role);
78             String roleName = role.getName();
79             roleNames.add(roleName);
80             if (!mRoleManager.isRoleAvailable(roleName)) {
81                 addedRoleNames.add(roleName);
82             }
83         }
84 
85         // TODO: Clean up holders of roles that will be removed.
86 
87         // Set the available role names in RoleManager.
88         mRoleManager.setRoleNamesFromController(roleNames);
89 
90         int addedRoleNamesSize = addedRoleNames.size();
91         for (int i = 0; i < addedRoleNamesSize; i++) {
92             String roleName = addedRoleNames.valueAt(i);
93 
94             Role role = roleMap.get(roleName);
95             role.onRoleAdded(this);
96         }
97 
98         // Go through the holders of all roles.
99         int rolesSize = roles.size();
100         for (int rolesIndex = 0; rolesIndex < rolesSize; rolesIndex++) {
101             Role role = roles.get(rolesIndex);
102 
103             String roleName = role.getName();
104 
105             // For each of the current holders, check if it is still qualified, redo grant if so, or
106             // remove it otherwise.
107             List<String> currentPackageNames = mRoleManager.getRoleHolders(roleName);
108             int currentPackageNamesSize = currentPackageNames.size();
109             for (int currentPackageNamesIndex = 0;
110                     currentPackageNamesIndex < currentPackageNamesSize;
111                     currentPackageNamesIndex++) {
112                 String packageName = currentPackageNames.get(currentPackageNamesIndex);
113 
114                 if (role.isPackageQualified(packageName, this)) {
115                     // We should not override user set or fixed permissions because we are only
116                     // redoing the grant here. Otherwise, user won't be able to revoke permissions
117                     // granted by role.
118                     addRoleHolderInternal(role, packageName, false, false, true);
119                 } else {
120                     Log.i(LOG_TAG, "Removing package that no longer qualifies for the role,"
121                             + " package: " + packageName + ", role: " + roleName);
122                     removeRoleHolderInternal(role, packageName, false);
123                 }
124             }
125 
126             // If there is no holder for a role now, or the role is static, we need to add default
127             // or fallback holders, if any.
128             currentPackageNames = mRoleManager.getRoleHolders(roleName);
129             currentPackageNamesSize = currentPackageNames.size();
130             boolean isStaticRole = role.isStatic();
131             if (currentPackageNamesSize == 0 || isStaticRole) {
132                 List<String> packageNamesToAdd = null;
133                 if (addedRoleNames.contains(roleName) || isStaticRole) {
134                     packageNamesToAdd = role.getDefaultHolders(this);
135                 }
136                 if (packageNamesToAdd == null || packageNamesToAdd.isEmpty()) {
137                     packageNamesToAdd = CollectionUtils.singletonOrEmpty(role.getFallbackHolder(
138                             this));
139                 }
140 
141                 int packageNamesToAddSize = packageNamesToAdd.size();
142                 for (int packageNamesToAddIndex = 0; packageNamesToAddIndex < packageNamesToAddSize;
143                         packageNamesToAddIndex++) {
144                     String packageName = packageNamesToAdd.get(packageNamesToAddIndex);
145 
146                     if (currentPackageNames.contains(packageName)) {
147                         // This may happen when we are ensuring all default holders are added for
148                         // static roles.
149                         continue;
150                     }
151                     if (!role.isPackageQualified(packageName, this)) {
152                         Log.e(LOG_TAG, "Default/fallback role holder package doesn't qualify for"
153                                 + " the role, package: " + packageName + ", role: " + roleName);
154                         continue;
155                     }
156                     Log.i(LOG_TAG, "Adding package as default/fallback role holder, package: "
157                             + packageName + ", role: " + roleName);
158                     // TODO: If we don't override user here, user might end up missing incoming
159                     // phone calls or SMS, so we just keep the old behavior. But overriding user
160                     // choice about permission without explicit user action is bad, so maybe we
161                     // should at least show a notification?
162                     addRoleHolderInternal(role, packageName, role.shouldOverrideUserWhenGranting());
163                 }
164             }
165 
166             // Ensure that an exclusive role has at most one holder.
167             currentPackageNames = mRoleManager.getRoleHolders(roleName);
168             currentPackageNamesSize = currentPackageNames.size();
169             if (role.isExclusive() && currentPackageNamesSize > 1) {
170                 Log.w(LOG_TAG, "Multiple packages holding an exclusive role, role: "
171                         + roleName);
172                 // No good way to determine who should be the only one, just keep the first one.
173                 for (int currentPackageNamesIndex = 1;
174                         currentPackageNamesIndex < currentPackageNamesSize;
175                         currentPackageNamesIndex++) {
176                     String packageName = currentPackageNames.get(currentPackageNamesIndex);
177 
178                     Log.i(LOG_TAG, "Removing extraneous package for an exclusive role, package: "
179                             + packageName + ", role: " + roleName);
180                     removeRoleHolderInternal(role, packageName, false);
181                 }
182             }
183         }
184 
185         return true;
186     }
187 
188     @Override
189     @WorkerThread
onAddRoleHolder(@onNull String roleName, @NonNull String packageName, int flags)190     public boolean onAddRoleHolder(@NonNull String roleName, @NonNull String packageName,
191             int flags) {
192         if (!checkFlags(flags, RoleManager.MANAGE_HOLDERS_FLAG_DONT_KILL_APP)) {
193             return false;
194         }
195 
196         Role role = Roles.get(this).get(roleName);
197         if (role == null) {
198             Log.e(LOG_TAG, "Unknown role: " + roleName);
199             return false;
200         }
201         if (!role.isAvailable(this)) {
202             Log.e(LOG_TAG, "Role is unavailable: " + roleName);
203             return false;
204         }
205 
206         if (!role.isPackageQualified(packageName, this)) {
207             Log.e(LOG_TAG, "Package does not qualify for the role, package: " + packageName
208                     + ", role: " + roleName);
209             return false;
210         }
211 
212         boolean added = false;
213         if (role.isExclusive()) {
214             List<String> currentPackageNames = mRoleManager.getRoleHolders(roleName);
215             int currentPackageNamesSize = currentPackageNames.size();
216             for (int i = 0; i < currentPackageNamesSize; i++) {
217                 String currentPackageName = currentPackageNames.get(i);
218 
219                 if (Objects.equals(currentPackageName, packageName)) {
220                     Log.i(LOG_TAG, "Package is already a role holder, package: " + packageName
221                             + ", role: " + roleName);
222                     added = true;
223                     continue;
224                 }
225 
226                 boolean removed = removeRoleHolderInternal(role, currentPackageName, false);
227                 if (!removed) {
228                     // TODO: Clean up?
229                     return false;
230                 }
231             }
232         }
233 
234         boolean dontKillApp = hasFlag(flags, RoleManager.MANAGE_HOLDERS_FLAG_DONT_KILL_APP);
235         added = addRoleHolderInternal(role, packageName, dontKillApp,
236                 role.shouldOverrideUserWhenGranting(), added);
237         if (!added) {
238             return false;
239         }
240 
241         role.onHolderAddedAsUser(packageName, Process.myUserHandle(), this);
242         role.onHolderChangedAsUser(Process.myUserHandle(), this);
243 
244         return true;
245     }
246 
247     @Override
248     @WorkerThread
onRemoveRoleHolder(@onNull String roleName, @NonNull String packageName, int flags)249     public boolean onRemoveRoleHolder(@NonNull String roleName, @NonNull String packageName,
250             int flags) {
251         if (!checkFlags(flags, RoleManager.MANAGE_HOLDERS_FLAG_DONT_KILL_APP)) {
252             return false;
253         }
254 
255         Role role = Roles.get(this).get(roleName);
256         if (role == null) {
257             Log.e(LOG_TAG, "Unknown role: " + roleName);
258             return false;
259         }
260         if (!role.isAvailable(this)) {
261             Log.e(LOG_TAG, "Role is unavailable: " + roleName);
262             return false;
263         }
264 
265         boolean dontKillApp = hasFlag(flags, RoleManager.MANAGE_HOLDERS_FLAG_DONT_KILL_APP);
266         boolean removed = removeRoleHolderInternal(role, packageName, dontKillApp);
267         if (!removed) {
268             return false;
269         }
270 
271         // TODO: Should we consider this successful regardless?
272         boolean fallbackSuccessful = addFallbackRoleHolderMaybe(role);
273         if (!fallbackSuccessful) {
274             return false;
275         }
276 
277         role.onHolderChangedAsUser(Process.myUserHandle(), this);
278 
279         return true;
280     }
281 
282     @Override
283     @WorkerThread
onClearRoleHolders(@onNull String roleName, int flags)284     public boolean onClearRoleHolders(@NonNull String roleName, int flags) {
285         if (!checkFlags(flags, RoleManager.MANAGE_HOLDERS_FLAG_DONT_KILL_APP)) {
286             return false;
287         }
288 
289         Role role = Roles.get(this).get(roleName);
290         if (role == null) {
291             Log.e(LOG_TAG, "Unknown role: " + roleName);
292             return false;
293         }
294         if (!role.isAvailable(this)) {
295             Log.e(LOG_TAG, "Role is unavailable: " + roleName);
296             return false;
297         }
298 
299         boolean dontKillApp = hasFlag(flags, RoleManager.MANAGE_HOLDERS_FLAG_DONT_KILL_APP);
300         boolean cleared = clearRoleHoldersInternal(role, dontKillApp);
301         if (!cleared) {
302             return false;
303         }
304 
305         // TODO: Should we consider this successful regardless?
306         boolean fallbackSuccessful = addFallbackRoleHolderMaybe(role);
307         if (!fallbackSuccessful) {
308             return false;
309         }
310 
311         role.onHolderChangedAsUser(Process.myUserHandle(), this);
312 
313         return true;
314     }
315 
316     @WorkerThread
addRoleHolderInternal(@onNull Role role, @NonNull String packageName, boolean overrideUserSetAndFixedPermissions)317     private boolean addRoleHolderInternal(@NonNull Role role, @NonNull String packageName,
318             boolean overrideUserSetAndFixedPermissions) {
319         return addRoleHolderInternal(role, packageName, false, overrideUserSetAndFixedPermissions,
320                 false);
321     }
322 
323     @WorkerThread
addRoleHolderInternal(@onNull Role role, @NonNull String packageName, boolean dontKillApp, boolean overrideUserSetAndFixedPermissions, boolean added)324     private boolean addRoleHolderInternal(@NonNull Role role, @NonNull String packageName,
325             boolean dontKillApp, boolean overrideUserSetAndFixedPermissions, boolean added) {
326         role.grant(packageName, dontKillApp, overrideUserSetAndFixedPermissions, this);
327 
328         String roleName = role.getName();
329         if (!added) {
330             added = mRoleManager.addRoleHolderFromController(roleName, packageName);
331         }
332         if (!added) {
333             Log.e(LOG_TAG, "Failed to add role holder in RoleManager, package: " + packageName
334                     + ", role: " + roleName);
335         }
336         return added;
337     }
338 
339     @WorkerThread
removeRoleHolderInternal(@onNull Role role, @NonNull String packageName, boolean dontKillApp)340     private boolean removeRoleHolderInternal(@NonNull Role role, @NonNull String packageName,
341             boolean dontKillApp) {
342         ApplicationInfo applicationInfo = PackageUtils.getApplicationInfo(packageName, this);
343         if (applicationInfo == null) {
344             Log.w(LOG_TAG, "Cannot get ApplicationInfo for package: " + packageName);
345         }
346 
347         if (applicationInfo != null) {
348             role.revoke(packageName, dontKillApp, false, this);
349         }
350 
351         String roleName = role.getName();
352         boolean removed = mRoleManager.removeRoleHolderFromController(roleName, packageName);
353         if (!removed) {
354             Log.e(LOG_TAG, "Failed to remove role holder in RoleManager," + " package: "
355                     + packageName + ", role: " + roleName);
356         }
357         return removed;
358     }
359 
360     @WorkerThread
clearRoleHoldersInternal(@onNull Role role, boolean dontKillApp)361     private boolean clearRoleHoldersInternal(@NonNull Role role, boolean dontKillApp) {
362         String roleName = role.getName();
363         List<String> packageNames = mRoleManager.getRoleHolders(roleName);
364         boolean cleared = true;
365 
366         int packageNamesSize = packageNames.size();
367         for (int i = 0; i < packageNamesSize; i++) {
368             String packageName = packageNames.get(i);
369             boolean removed = removeRoleHolderInternal(role, packageName, dontKillApp);
370             if (!removed) {
371                 cleared = false;
372             }
373         }
374 
375         if (!cleared) {
376             Log.e(LOG_TAG, "Failed to clear role holders, role: " + roleName);
377         }
378         return cleared;
379     }
380 
381     @WorkerThread
addFallbackRoleHolderMaybe(@onNull Role role)382     private boolean addFallbackRoleHolderMaybe(@NonNull Role role) {
383         String roleName = role.getName();
384         List<String> currentPackageNames = mRoleManager.getRoleHolders(roleName);
385         if (!currentPackageNames.isEmpty()) {
386             return true;
387         }
388 
389         String fallbackPackageName = role.getFallbackHolder(this);
390         if (fallbackPackageName == null) {
391             return true;
392         }
393 
394         if (!role.isPackageQualified(fallbackPackageName, this)) {
395             Log.e(LOG_TAG, "Fallback role holder package doesn't qualify for the role, package: "
396                     + fallbackPackageName + ", role: " + roleName);
397             return false;
398         }
399 
400         Log.i(LOG_TAG, "Adding package as fallback role holder, package: " + fallbackPackageName
401                 + ", role: " + roleName);
402         // TODO: If we don't override user here, user might end up missing incoming
403         // phone calls or SMS, so we just keep the old behavior. But overriding user
404         // choice about permission without explicit user action is bad, so maybe we
405         // should at least show a notification?
406         return addRoleHolderInternal(role, fallbackPackageName,
407                 role.shouldOverrideUserWhenGranting());
408     }
409 
410     @Override
onIsApplicationQualifiedForRole(@onNull String roleName, @NonNull String packageName)411     public boolean onIsApplicationQualifiedForRole(@NonNull String roleName,
412             @NonNull String packageName) {
413         // This API has been deprecated and Settings has been using onIsApplicationVisibleForRole()
414         // instead.
415         return false;
416     }
417 
418     @Override
onIsApplicationVisibleForRole(@onNull String roleName, @NonNull String packageName)419     public boolean onIsApplicationVisibleForRole(@NonNull String roleName,
420             @NonNull String packageName) {
421         Role role = Roles.get(this).get(roleName);
422         if (role == null) {
423             return false;
424         }
425         if (!role.isAvailable(this)) {
426             return false;
427         }
428         if (!role.isPackageQualified(packageName, this)) {
429             return false;
430         }
431         ApplicationInfo applicationInfo = PackageUtils.getApplicationInfo(packageName, this);
432         if (applicationInfo == null || !role.isApplicationVisibleAsUser(applicationInfo,
433                 Process.myUserHandle(), this)) {
434             return false;
435         }
436         return true;
437     }
438 
439     @Override
onIsRoleVisible(@onNull String roleName)440     public boolean onIsRoleVisible(@NonNull String roleName) {
441         Role role = Roles.get(this).get(roleName);
442         if (role == null) {
443             return false;
444         }
445         if (!role.isAvailable(this)) {
446             return false;
447         }
448         return role.isVisibleAsUser(Process.myUserHandle(), this);
449     }
450 
checkFlags(int flags, int allowedFlags)451     private static boolean checkFlags(int flags, int allowedFlags) {
452         if ((flags & allowedFlags) != flags) {
453             Log.e(LOG_TAG, "flags is invalid, flags: 0x" + Integer.toHexString(flags)
454                     + ", allowed flags: 0x" + Integer.toHexString(allowedFlags));
455             return false;
456         }
457         return true;
458     }
459 
hasFlag(int flags, int flag)460     private static boolean hasFlag(int flags, int flag) {
461         return (flags & flag) == flag;
462     }
463 }
464