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