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