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