• 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.utils.PackageUtils;
33 import com.android.permissioncontroller.role.utils.RoleUiBehaviorUtils;
34 import com.android.role.controller.model.Role;
35 import com.android.role.controller.model.Roles;
36 
37 import java.util.ArrayList;
38 import java.util.List;
39 import java.util.Objects;
40 
41 /**
42  * Implementation of {@link RoleControllerService}.
43  */
44 public class RoleControllerServiceImpl extends RoleControllerService {
45 
46     private static final String LOG_TAG = RoleControllerServiceImpl.class.getSimpleName();
47 
48     private static final boolean DEBUG = false;
49 
50     private RoleManager mRoleManager;
51 
52     @Override
onCreate()53     public void onCreate() {
54         super.onCreate();
55 
56         mRoleManager = getSystemService(RoleManager.class);
57     }
58 
59     @Override
60     @WorkerThread
onGrantDefaultRoles()61     public boolean onGrantDefaultRoles() {
62         if (DEBUG) {
63             Log.i(LOG_TAG, "Granting default roles, user: " + UserHandle.myUserId());
64         }
65 
66         // Gather the available roles for current user.
67         ArrayMap<String, Role> roleMap = Roles.get(this);
68         List<Role> roles = new ArrayList<>();
69         List<String> roleNames = new ArrayList<>();
70         ArraySet<String> addedRoleNames = new ArraySet<>();
71         int roleMapSize = roleMap.size();
72         for (int i = 0; i < roleMapSize; i++) {
73             Role role = roleMap.valueAt(i);
74 
75             if (!role.isAvailable(this)) {
76                 continue;
77             }
78             roles.add(role);
79             String roleName = role.getName();
80             roleNames.add(roleName);
81             if (!mRoleManager.isRoleAvailable(roleName)) {
82                 addedRoleNames.add(roleName);
83             }
84         }
85 
86         // TODO: Clean up holders of roles that will be removed.
87 
88         // Set the available role names in RoleManager.
89         mRoleManager.setRoleNamesFromController(roleNames);
90 
91         int addedRoleNamesSize = addedRoleNames.size();
92         for (int i = 0; i < addedRoleNamesSize; i++) {
93             String roleName = addedRoleNames.valueAt(i);
94 
95             Role role = roleMap.get(roleName);
96             role.onRoleAdded(this);
97         }
98 
99         // Go through the holders of all roles.
100         int rolesSize = roles.size();
101         for (int rolesIndex = 0; rolesIndex < rolesSize; rolesIndex++) {
102             Role role = roles.get(rolesIndex);
103 
104             String roleName = role.getName();
105 
106             // For each of the current holders, check if it is still qualified, redo grant if so, or
107             // remove it otherwise.
108             List<String> currentPackageNames = mRoleManager.getRoleHolders(roleName);
109             int currentPackageNamesSize = currentPackageNames.size();
110             for (int currentPackageNamesIndex = 0;
111                     currentPackageNamesIndex < currentPackageNamesSize;
112                     currentPackageNamesIndex++) {
113                 String packageName = currentPackageNames.get(currentPackageNamesIndex);
114 
115                 if (role.isPackageQualified(packageName, this)) {
116                     // We should not override user set or fixed permissions because we are only
117                     // redoing the grant here. Otherwise, user won't be able to revoke permissions
118                     // granted by role.
119                     addRoleHolderInternal(role, packageName, false, false, true);
120                 } else {
121                     Log.i(LOG_TAG, "Removing package that no longer qualifies for the role,"
122                             + " package: " + packageName + ", role: " + roleName);
123                     removeRoleHolderInternal(role, packageName, false);
124                 }
125             }
126 
127             // If there is no holder for a role now, or the role is static, we need to add default
128             // or fallback holders, if any.
129             currentPackageNames = mRoleManager.getRoleHolders(roleName);
130             currentPackageNamesSize = currentPackageNames.size();
131             boolean isStaticRole = role.isStatic();
132             if (currentPackageNamesSize == 0 || isStaticRole) {
133                 List<String> packageNamesToAdd = null;
134                 if (addedRoleNames.contains(roleName) || isStaticRole) {
135                     packageNamesToAdd = role.getDefaultHolders(this);
136                 }
137                 if (packageNamesToAdd == null || packageNamesToAdd.isEmpty()) {
138                     packageNamesToAdd = CollectionUtils.singletonOrEmpty(role.getFallbackHolder(
139                             this));
140                 }
141 
142                 int packageNamesToAddSize = packageNamesToAdd.size();
143                 for (int packageNamesToAddIndex = 0; packageNamesToAddIndex < packageNamesToAddSize;
144                         packageNamesToAddIndex++) {
145                     String packageName = packageNamesToAdd.get(packageNamesToAddIndex);
146 
147                     if (currentPackageNames.contains(packageName)) {
148                         // This may happen when we are ensuring all default holders are added for
149                         // static roles.
150                         continue;
151                     }
152                     if (!role.isPackageQualified(packageName, this)) {
153                         Log.e(LOG_TAG, "Default/fallback role holder package doesn't qualify for"
154                                 + " the role, package: " + packageName + ", role: " + roleName);
155                         continue;
156                     }
157                     Log.i(LOG_TAG, "Adding package as default/fallback role holder, package: "
158                             + packageName + ", role: " + roleName);
159                     // TODO: If we don't override user here, user might end up missing incoming
160                     // phone calls or SMS, so we just keep the old behavior. But overriding user
161                     // choice about permission without explicit user action is bad, so maybe we
162                     // should at least show a notification?
163                     addRoleHolderInternal(role, packageName, role.shouldOverrideUserWhenGranting());
164                 }
165             }
166 
167             // Ensure that an exclusive role has at most one holder.
168             currentPackageNames = mRoleManager.getRoleHolders(roleName);
169             currentPackageNamesSize = currentPackageNames.size();
170             if (role.isExclusive() && currentPackageNamesSize > 1) {
171                 Log.w(LOG_TAG, "Multiple packages holding an exclusive role, role: "
172                         + roleName);
173                 // No good way to determine who should be the only one, just keep the first one.
174                 for (int currentPackageNamesIndex = 1;
175                         currentPackageNamesIndex < currentPackageNamesSize;
176                         currentPackageNamesIndex++) {
177                     String packageName = currentPackageNames.get(currentPackageNamesIndex);
178 
179                     Log.i(LOG_TAG, "Removing extraneous package for an exclusive role, package: "
180                             + packageName + ", role: " + roleName);
181                     removeRoleHolderInternal(role, packageName, false);
182                 }
183             }
184         }
185 
186         return true;
187     }
188 
189     @Override
190     @WorkerThread
onAddRoleHolder(@onNull String roleName, @NonNull String packageName, int flags)191     public boolean onAddRoleHolder(@NonNull String roleName, @NonNull String packageName,
192             int flags) {
193         if (!checkFlags(flags, RoleManager.MANAGE_HOLDERS_FLAG_DONT_KILL_APP)) {
194             return false;
195         }
196 
197         Role role = Roles.get(this).get(roleName);
198         if (role == null) {
199             Log.e(LOG_TAG, "Unknown role: " + roleName);
200             return false;
201         }
202         if (!role.isAvailable(this)) {
203             Log.e(LOG_TAG, "Role is unavailable: " + roleName);
204             return false;
205         }
206 
207         if (!role.isPackageQualified(packageName, this)) {
208             Log.e(LOG_TAG, "Package does not qualify for the role, package: " + packageName
209                     + ", role: " + roleName);
210             return false;
211         }
212 
213         boolean added = false;
214         if (role.isExclusive()) {
215             List<String> currentPackageNames = mRoleManager.getRoleHolders(roleName);
216             int currentPackageNamesSize = currentPackageNames.size();
217             for (int i = 0; i < currentPackageNamesSize; i++) {
218                 String currentPackageName = currentPackageNames.get(i);
219 
220                 if (Objects.equals(currentPackageName, packageName)) {
221                     Log.i(LOG_TAG, "Package is already a role holder, package: " + packageName
222                             + ", role: " + roleName);
223                     added = true;
224                     continue;
225                 }
226 
227                 boolean removed = removeRoleHolderInternal(role, currentPackageName, false);
228                 if (!removed) {
229                     // TODO: Clean up?
230                     return false;
231                 }
232             }
233         }
234 
235         boolean dontKillApp = hasFlag(flags, RoleManager.MANAGE_HOLDERS_FLAG_DONT_KILL_APP);
236         added = addRoleHolderInternal(role, packageName, dontKillApp,
237                 role.shouldOverrideUserWhenGranting(), added);
238         if (!added) {
239             return false;
240         }
241 
242         role.onHolderAddedAsUser(packageName, Process.myUserHandle(), this);
243         role.onHolderChangedAsUser(Process.myUserHandle(), this);
244 
245         return true;
246     }
247 
248     @Override
249     @WorkerThread
onRemoveRoleHolder(@onNull String roleName, @NonNull String packageName, int flags)250     public boolean onRemoveRoleHolder(@NonNull String roleName, @NonNull String packageName,
251             int flags) {
252         if (!checkFlags(flags, RoleManager.MANAGE_HOLDERS_FLAG_DONT_KILL_APP)) {
253             return false;
254         }
255 
256         Role role = Roles.get(this).get(roleName);
257         if (role == null) {
258             Log.e(LOG_TAG, "Unknown role: " + roleName);
259             return false;
260         }
261         if (!role.isAvailable(this)) {
262             Log.e(LOG_TAG, "Role is unavailable: " + roleName);
263             return false;
264         }
265 
266         boolean dontKillApp = hasFlag(flags, RoleManager.MANAGE_HOLDERS_FLAG_DONT_KILL_APP);
267         boolean removed = removeRoleHolderInternal(role, packageName, dontKillApp);
268         if (!removed) {
269             return false;
270         }
271 
272         // TODO: Should we consider this successful regardless?
273         boolean fallbackSuccessful = addFallbackRoleHolderMaybe(role);
274         if (!fallbackSuccessful) {
275             return false;
276         }
277 
278         role.onHolderChangedAsUser(Process.myUserHandle(), this);
279 
280         return true;
281     }
282 
283     @Override
284     @WorkerThread
onClearRoleHolders(@onNull String roleName, int flags)285     public boolean onClearRoleHolders(@NonNull String roleName, int flags) {
286         if (!checkFlags(flags, RoleManager.MANAGE_HOLDERS_FLAG_DONT_KILL_APP)) {
287             return false;
288         }
289 
290         Role role = Roles.get(this).get(roleName);
291         if (role == null) {
292             Log.e(LOG_TAG, "Unknown role: " + roleName);
293             return false;
294         }
295         if (!role.isAvailable(this)) {
296             Log.e(LOG_TAG, "Role is unavailable: " + roleName);
297             return false;
298         }
299 
300         boolean dontKillApp = hasFlag(flags, RoleManager.MANAGE_HOLDERS_FLAG_DONT_KILL_APP);
301         boolean cleared = clearRoleHoldersInternal(role, dontKillApp);
302         if (!cleared) {
303             return false;
304         }
305 
306         // TODO: Should we consider this successful regardless?
307         boolean fallbackSuccessful = addFallbackRoleHolderMaybe(role);
308         if (!fallbackSuccessful) {
309             return false;
310         }
311 
312         role.onHolderChangedAsUser(Process.myUserHandle(), this);
313 
314         return true;
315     }
316 
317     @WorkerThread
addRoleHolderInternal(@onNull Role role, @NonNull String packageName, boolean overrideUser)318     private boolean addRoleHolderInternal(@NonNull Role role, @NonNull String packageName,
319             boolean overrideUser) {
320         return addRoleHolderInternal(role, packageName, false, overrideUser, false);
321     }
322 
323     @WorkerThread
addRoleHolderInternal(@onNull Role role, @NonNull String packageName, boolean dontKillApp, boolean overrideUser, boolean added)324     private boolean addRoleHolderInternal(@NonNull Role role, @NonNull String packageName,
325             boolean dontKillApp, boolean overrideUser, boolean added) {
326         role.grant(packageName, dontKillApp, overrideUser, 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 || !RoleUiBehaviorUtils.isApplicationVisibleAsUser(role,
433                 applicationInfo, 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 
449         return RoleUiBehaviorUtils.isVisibleAsUser(role, Process.myUserHandle(), this);
450     }
451 
checkFlags(int flags, int allowedFlags)452     private static boolean checkFlags(int flags, int allowedFlags) {
453         if ((flags & allowedFlags) != flags) {
454             Log.e(LOG_TAG, "flags is invalid, flags: 0x" + Integer.toHexString(flags)
455                     + ", allowed flags: 0x" + Integer.toHexString(allowedFlags));
456             return false;
457         }
458         return true;
459     }
460 
hasFlag(int flags, int flag)461     private static boolean hasFlag(int flags, int flag) {
462         return (flags & flag) == flag;
463     }
464 }
465