• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 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.permission.service;
18 
19 import static android.app.admin.DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT;
20 import static android.app.admin.DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED;
21 import static android.app.admin.DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED;
22 import static android.content.pm.PackageManager.GET_PERMISSIONS;
23 import static android.permission.PermissionControllerManager.REASON_INSTALLER_POLICY_VIOLATION;
24 import static android.permission.PermissionControllerManager.REASON_MALWARE;
25 import static android.util.Xml.newSerializer;
26 
27 import static com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__AUTO_ONE_TIME_PERMISSION_REVOKED;
28 
29 import static java.nio.charset.StandardCharsets.UTF_8;
30 
31 import android.Manifest;
32 import android.app.admin.DevicePolicyManager;
33 import android.content.Intent;
34 import android.content.pm.PackageInfo;
35 import android.content.pm.PackageManager;
36 import android.os.AsyncTask;
37 import android.os.Build;
38 import android.os.Handler;
39 import android.os.Looper;
40 import android.os.Process;
41 import android.os.UserHandle;
42 import android.os.UserManager;
43 import android.permission.AdminPermissionControlParams;
44 import android.permission.PermissionManager;
45 import android.permission.RuntimePermissionPresentationInfo;
46 import android.permission.RuntimePermissionUsageInfo;
47 import android.util.ArrayMap;
48 import android.util.ArraySet;
49 import android.util.Log;
50 import android.util.Xml;
51 
52 import androidx.annotation.NonNull;
53 import androidx.annotation.Nullable;
54 import androidx.annotation.RequiresApi;
55 
56 import com.android.permissioncontroller.PermissionControllerProto.PermissionControllerDumpProto;
57 import com.android.permissioncontroller.PermissionControllerStatsLog;
58 import com.android.permissioncontroller.permission.model.AppPermissionGroup;
59 import com.android.permissioncontroller.permission.model.AppPermissions;
60 import com.android.permissioncontroller.permission.model.Permission;
61 import com.android.permissioncontroller.permission.model.livedatatypes.AppPermGroupUiInfo;
62 import com.android.permissioncontroller.permission.model.livedatatypes.AppPermGroupUiInfo.PermGrantState;
63 import com.android.permissioncontroller.permission.ui.AutoGrantPermissionsNotifier;
64 import com.android.permissioncontroller.permission.utils.ArrayUtils;
65 import com.android.permissioncontroller.permission.utils.KotlinUtils;
66 import com.android.permissioncontroller.permission.utils.PermissionMapping;
67 import com.android.permissioncontroller.permission.utils.UserSensitiveFlagsUtils;
68 import com.android.permissioncontroller.permission.utils.Utils;
69 import com.android.permissioncontroller.permission.utils.v31.AdminRestrictedPermissionsUtils;
70 import com.android.role.controller.model.Role;
71 import com.android.role.controller.model.Roles;
72 
73 import kotlin.Pair;
74 
75 import org.xmlpull.v1.XmlPullParser;
76 import org.xmlpull.v1.XmlSerializer;
77 
78 import java.io.FileDescriptor;
79 import java.io.FileOutputStream;
80 import java.io.IOException;
81 import java.io.InputStream;
82 import java.io.OutputStream;
83 import java.io.PrintWriter;
84 import java.nio.charset.StandardCharsets;
85 import java.util.ArrayList;
86 import java.util.Collections;
87 import java.util.HashSet;
88 import java.util.List;
89 import java.util.Map;
90 import java.util.Set;
91 import java.util.concurrent.Executor;
92 import java.util.function.Consumer;
93 import java.util.function.IntConsumer;
94 import java.util.stream.Collectors;
95 
96 import kotlinx.coroutines.BuildersKt;
97 import kotlinx.coroutines.GlobalScope;
98 
99 /**
100  * Calls from the system into the permission controller.
101  *
102  * All reading methods are called async, and all writing method are called on the AsyncTask single
103  * thread executor so that multiple writes won't override each other concurrently.
104  */
105 public final class PermissionControllerServiceImpl extends PermissionControllerLifecycleService {
106     private static final String LOG_TAG = PermissionControllerServiceImpl.class.getSimpleName();
107     public static final String ONE_TIME_PERMISSION_REVOKED_REASON = "one-time permission revoked";
108     private static final int MAX_RETRY_ATTEMPTS = 3;
109     private static final long RETRY_DELAY_MS = 500;
110 
111 
112     private final PermissionControllerServiceModel mServiceModel = new
113             PermissionControllerServiceModel(this);
114 
115     @Override
onUnbind(@ullable Intent intent)116     public boolean onUnbind(@Nullable Intent intent) {
117         mServiceModel.removeObservers();
118         return super.onUnbind(intent);
119     }
120 
121     @Override
dump(FileDescriptor fd, PrintWriter writer, String[] args)122     public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
123         PermissionControllerDumpProto dump;
124         try {
125             dump = BuildersKt.runBlocking(
126                     GlobalScope.INSTANCE.getCoroutineContext(),
127                     (coroutineScope, continuation) -> mServiceModel.onDump(continuation));
128         } catch (Exception e) {
129             Log.e(LOG_TAG, "Cannot produce dump", e);
130             return;
131         }
132 
133         if (ArrayUtils.contains(args, "--proto")) {
134             try (OutputStream out = new FileOutputStream(fd)) {
135                 dump.writeTo(out);
136             } catch (IOException e) {
137                 Log.e(LOG_TAG, "Cannot write dump", e);
138             }
139         } else {
140             writer.println(dump.toString());
141             writer.flush();
142         }
143     }
144 
145     /**
146      * Expand {@code perms} by split permissions for an app with the given targetSDK.
147      *
148      * @param perms The permissions that should be expanded
149      * @param targetSDK The target SDK to expand for
150      *
151      * @return The expanded permissions
152      */
addSplitPermissions(@onNull List<String> perms, int targetSDK)153     private @NonNull ArrayList<String> addSplitPermissions(@NonNull List<String> perms,
154             int targetSDK) {
155         List<PermissionManager.SplitPermissionInfo> splitPerms =
156                 getSystemService(PermissionManager.class).getSplitPermissions();
157 
158         // Add split permissions to the request
159         ArrayList<String> expandedPerms = new ArrayList<>(perms);
160         int numReqPerms = perms.size();
161         for (int reqPermNum = 0; reqPermNum < numReqPerms; reqPermNum++) {
162             String reqPerm = perms.get(reqPermNum);
163 
164             int numSplitPerms = splitPerms.size();
165             for (int splitPermNum = 0; splitPermNum < numSplitPerms; splitPermNum++) {
166                 PermissionManager.SplitPermissionInfo splitPerm = splitPerms.get(splitPermNum);
167 
168                 if (targetSDK < splitPerm.getTargetSdk()
169                         && splitPerm.getSplitPermission().equals(reqPerm)) {
170                     expandedPerms.addAll(splitPerm.getNewPermissions());
171                 }
172             }
173         }
174 
175         return expandedPerms;
176     }
177 
178     /**
179      * Get the package info for a package.
180      *
181      * @param pkg The package name
182      *
183      * @return the package info or {@code null} if the package could not be found
184      */
getPkgInfo(@onNull String pkg)185     private @Nullable PackageInfo getPkgInfo(@NonNull String pkg) {
186         try {
187             return getPackageManager().getPackageInfo(pkg, GET_PERMISSIONS);
188         } catch (PackageManager.NameNotFoundException e) {
189             Log.w(LOG_TAG, pkg + " not found", e);
190             return null;
191         }
192     }
193 
194     /**
195      * Given a set of permissions, find all permission groups of an app that can be revoked and that
196      * contain any of the permissions.
197      *
198      * @param permissions The permissions to revoke
199      * @param appPerms The {@link AppPermissions} for the app that is currently investigated
200      *
201      * @return The groups to revoke
202      */
getRevocableGroupsForPermissions( @onNull ArrayList<String> permissions, @NonNull AppPermissions appPerms)203     private @NonNull ArrayList<AppPermissionGroup> getRevocableGroupsForPermissions(
204             @NonNull ArrayList<String> permissions, @NonNull AppPermissions appPerms) {
205         ArrayList<AppPermissionGroup> groupsToRevoke = new ArrayList<>();
206         int numGroups = appPerms.getPermissionGroups().size();
207         for (int groupNum = 0; groupNum < numGroups; groupNum++) {
208             AppPermissionGroup group = appPerms.getPermissionGroups().get(groupNum);
209 
210             // Do not override fixed permissions
211             if (group.isPolicyFixed() || group.isSystemFixed()) {
212                 continue;
213             }
214 
215             int numPerms = permissions.size();
216             for (int permNum = 0; permNum < numPerms; permNum++) {
217                 String reqPerm = permissions.get(permNum);
218 
219                 if (group.hasPermission(reqPerm)) {
220                     groupsToRevoke.add(group);
221 
222                     // If fg permissions get revoked also revoke bg permissions as bg
223                     // permissions require fg permissions.
224                     AppPermissionGroup bgPerms = group.getBackgroundPermissions();
225                     if (bgPerms != null) {
226                         groupsToRevoke.add(bgPerms);
227                     }
228                 } else {
229                     AppPermissionGroup bgPerms = group.getBackgroundPermissions();
230                     if (bgPerms != null && bgPerms.hasPermission(reqPerm)) {
231                         groupsToRevoke.add(bgPerms);
232                     }
233                 }
234             }
235         }
236 
237         return groupsToRevoke;
238     }
239 
240     /**
241      * Revoke all permissions of some groups.
242      *
243      * @param groupsToRevoke The groups
244      *
245      * @return The permissions that were revoked
246      */
revokePermissionGroups( @onNull ArrayList<AppPermissionGroup> groupsToRevoke)247     private @NonNull ArrayList<String> revokePermissionGroups(
248             @NonNull ArrayList<AppPermissionGroup> groupsToRevoke) {
249         ArrayList<String> revokedPerms = new ArrayList<>();
250 
251         int numGroupsToRevoke = groupsToRevoke.size();
252         for (int groupsToRevokeNum = 0; groupsToRevokeNum < numGroupsToRevoke;
253                 groupsToRevokeNum++) {
254             AppPermissionGroup group = groupsToRevoke.get(groupsToRevokeNum);
255             ArrayList<Permission> perms = group.getPermissions();
256 
257             // Mark the permissions as reviewed as we don't want to use to accidentally grant
258             // the permission during review
259             group.unsetReviewRequired();
260 
261             int numPerms = perms.size();
262             for (int permNum = 0; permNum < numPerms; permNum++) {
263                 Permission perm = perms.get(permNum);
264 
265                 // Only count individual permissions that are actually revoked
266                 if (perm.isGrantedIncludingAppOp()) {
267                     revokedPerms.add(perm.getName());
268                 }
269             }
270 
271             group.revokeRuntimePermissions(false);
272         }
273 
274         return revokedPerms;
275     }
276 
277     @Override
onRevokeRuntimePermissions(@onNull Map<String, List<String>> request, boolean doDryRun, int reason, @NonNull String callerPackageName, @NonNull Consumer<Map<String, List<String>>> callback)278     public void onRevokeRuntimePermissions(@NonNull Map<String, List<String>> request,
279             boolean doDryRun, int reason, @NonNull String callerPackageName,
280             @NonNull Consumer<Map<String, List<String>>> callback) {
281         AsyncTask.execute(() -> callback.accept(onRevokeRuntimePermissions(request, doDryRun,
282                 reason, callerPackageName)));
283     }
284 
onRevokeRuntimePermissions( @onNull Map<String, List<String>> request, boolean doDryRun, int reason, @NonNull String callerPackageName)285     private @NonNull Map<String, List<String>> onRevokeRuntimePermissions(
286             @NonNull Map<String, List<String>> request, boolean doDryRun,
287             int reason, @NonNull String callerPackageName) {
288         // The reason parameter is not checked by platform code as this might need to be updated
289         // async to platform releases.
290         if (reason != REASON_MALWARE && reason != REASON_INSTALLER_POLICY_VIOLATION) {
291             Log.e(LOG_TAG, "Invalid reason " + reason);
292             return Collections.emptyMap();
293         }
294 
295         PackageManager pm = getPackageManager();
296 
297         PackageInfo callerPkgInfo = getPkgInfo(callerPackageName);
298         if (callerPkgInfo == null) {
299             return Collections.emptyMap();
300         }
301         int callerTargetSdk = callerPkgInfo.applicationInfo.targetSdkVersion;
302 
303         Map<String, List<String>> actuallyRevokedPerms = new ArrayMap<>();
304         ArrayList<AppPermissions> appsWithRevokedPerms = new ArrayList<>();
305 
306         for (Map.Entry<String, List<String>> appRequest : request.entrySet()) {
307             PackageInfo requestedPkgInfo = getPkgInfo(appRequest.getKey());
308             if (requestedPkgInfo == null) {
309                 continue;
310             }
311 
312             // Permissions are per UID. Hence permissions will be removed from all apps sharing an
313             // UID.
314             String[] pkgNames = pm.getPackagesForUid(requestedPkgInfo.applicationInfo.uid);
315             if (pkgNames == null) {
316                 continue;
317             }
318 
319             int numPkgNames = pkgNames.length;
320             for (int pkgNum = 0; pkgNum < numPkgNames; pkgNum++) {
321                 String pkgName = pkgNames[pkgNum];
322 
323                 PackageInfo pkgInfo = getPkgInfo(pkgName);
324                 if (pkgInfo == null) {
325                     continue;
326                 }
327 
328                 // If the revocation is because of a market policy violation only the installer can
329                 // revoke the permissions.
330                 if (reason == REASON_INSTALLER_POLICY_VIOLATION
331                         && !callerPackageName.equals(pm.getInstallerPackageName(pkgName))) {
332                     Log.i(LOG_TAG, "Ignoring " + pkgName + " as it is not installed by "
333                             + callerPackageName);
334                     continue;
335                 }
336 
337                 // In rare cases the caller does not know about the permissions that have been added
338                 // due to splits. Hence add them now.
339                 ArrayList<String> expandedPerms = addSplitPermissions(appRequest.getValue(),
340                         callerTargetSdk);
341 
342                 AppPermissions appPerms = new AppPermissions(this, pkgInfo, false, true, null);
343 
344                 // First find the groups that should be revoked and then revoke all permissions of
345                 // these groups. This is needed as soon as a single permission in the group is
346                 // granted, all other permissions get auto-granted on request.
347                 ArrayList<AppPermissionGroup> groupsToRevoke = getRevocableGroupsForPermissions(
348                         expandedPerms, appPerms);
349                 ArrayList<String> revokedPerms = revokePermissionGroups(groupsToRevoke);
350 
351                 // In racy conditions the group might not have had granted permissions anymore
352                 if (!revokedPerms.isEmpty()) {
353                     actuallyRevokedPerms.put(pkgName, revokedPerms);
354                     appsWithRevokedPerms.add(appPerms);
355                 }
356             }
357         }
358 
359         // Persist changes after we computed everything to remove
360         // This is necessary as we would otherwise only look at the first app of a shared UID.
361         if (!doDryRun) {
362             int numChangedApps = appsWithRevokedPerms.size();
363             for (int i = 0; i < numChangedApps; i++) {
364                 appsWithRevokedPerms.get(i).persistChanges(true);
365             }
366         }
367 
368         return actuallyRevokedPerms;
369     }
370 
371     @Override
onGetRuntimePermissionsBackup(@onNull UserHandle user, @NonNull OutputStream backup, @NonNull Runnable callback)372     public void onGetRuntimePermissionsBackup(@NonNull UserHandle user,
373             @NonNull OutputStream backup, @NonNull Runnable callback) {
374         AsyncTask.execute(() -> {
375             onGetRuntimePermissionsBackup(user, backup);
376             callback.run();
377         });
378     }
379 
onGetRuntimePermissionsBackup(@onNull UserHandle user, @NonNull OutputStream backup)380     private void onGetRuntimePermissionsBackup(@NonNull UserHandle user,
381             @NonNull OutputStream backup) {
382         BackupHelper backupHelper = new BackupHelper(this, user);
383 
384         try {
385             XmlSerializer serializer = newSerializer();
386             serializer.setOutput(backup, UTF_8.name());
387 
388             backupHelper.writeState(serializer);
389             serializer.flush();
390         } catch (Exception e) {
391             Log.e(LOG_TAG, "Unable to write permissions backup", e);
392         }
393     }
394 
395     @Override
onStageAndApplyRuntimePermissionsBackup(@onNull UserHandle user, @NonNull InputStream backup, @NonNull Runnable callback)396     public void onStageAndApplyRuntimePermissionsBackup(@NonNull UserHandle user,
397             @NonNull InputStream backup, @NonNull Runnable callback) {
398         AsyncTask.execute(() -> {
399             onRestoreRuntimePermissionsBackup(user, backup);
400             callback.run();
401         });
402     }
403 
onRestoreRuntimePermissionsBackup(@onNull UserHandle user, @NonNull InputStream backup)404     private void onRestoreRuntimePermissionsBackup(@NonNull UserHandle user,
405             @NonNull InputStream backup) {
406         try {
407             XmlPullParser parser = Xml.newPullParser();
408             parser.setInput(backup, StandardCharsets.UTF_8.name());
409 
410             new BackupHelper(this, user).restoreState(parser);
411         } catch (Exception e) {
412             Log.e(LOG_TAG, "Exception restoring permissions: " + e.getMessage());
413         }
414     }
415 
416     @Override
onApplyStagedRuntimePermissionBackup(@onNull String packageName, @NonNull UserHandle user, @NonNull Consumer<Boolean> callback)417     public void onApplyStagedRuntimePermissionBackup(@NonNull String packageName,
418             @NonNull UserHandle user, @NonNull Consumer<Boolean> callback) {
419         AsyncTask.execute(() -> callback.accept(
420                 onRestoreDelayedRuntimePermissionsBackup(packageName, user)));
421     }
422 
onRestoreDelayedRuntimePermissionsBackup(@onNull String packageName, @NonNull UserHandle user)423     private boolean onRestoreDelayedRuntimePermissionsBackup(@NonNull String packageName,
424             @NonNull UserHandle user) {
425         try {
426             return new BackupHelper(this, user).restoreDelayedState(packageName);
427         } catch (Exception e) {
428             Log.e(LOG_TAG, "Exception restoring delayed permissions: " + e.getMessage());
429             return false;
430         }
431     }
432 
433     @Override
onGetAppPermissions(@onNull String packageName, @NonNull Consumer<List<RuntimePermissionPresentationInfo>> callback)434     public void onGetAppPermissions(@NonNull String packageName,
435             @NonNull Consumer<List<RuntimePermissionPresentationInfo>> callback) {
436         mServiceModel.onGetAppPermissions(packageName, (groupUiInfos) -> {
437             List<RuntimePermissionPresentationInfo> permissions = new ArrayList<>();
438 
439             for (Pair<String, AppPermGroupUiInfo> groupNameAndUiInfo : groupUiInfos) {
440                 String groupName = groupNameAndUiInfo.getFirst();
441                 AppPermGroupUiInfo uiInfo = groupNameAndUiInfo.getSecond();
442                 boolean isPlatform =
443                         PermissionMapping.getPlatformPermissionGroups().contains(groupName);
444                 CharSequence label = KotlinUtils.INSTANCE.getPermGroupLabel(this, groupName);
445 
446                 RuntimePermissionPresentationInfo permission =
447                         new RuntimePermissionPresentationInfo(label,
448                                 uiInfo.getPermGrantState() != PermGrantState.PERMS_DENIED
449                                         && uiInfo.getPermGrantState() != PermGrantState.PERMS_ASK,
450                                 isPlatform);
451                 permissions.add(permission);
452             }
453             callback.accept(permissions);
454         });
455     }
456 
457     @Override
onRevokeRuntimePermission(@onNull String packageName, @NonNull String permissionName, @NonNull Runnable callback)458     public void onRevokeRuntimePermission(@NonNull String packageName,
459             @NonNull String permissionName, @NonNull Runnable callback) {
460         AsyncTask.execute(() -> {
461             onRevokeRuntimePermission(packageName, permissionName);
462             callback.run();
463         });
464     }
465 
onRevokeRuntimePermission(@onNull String packageName, @NonNull String permissionName)466     private void onRevokeRuntimePermission(@NonNull String packageName,
467             @NonNull String permissionName) {
468         try {
469             final PackageInfo packageInfo = getPackageManager().getPackageInfo(packageName,
470                     GET_PERMISSIONS);
471             final AppPermissions appPermissions = new AppPermissions(this, packageInfo, false,
472                     null);
473 
474             final AppPermissionGroup appPermissionGroup = appPermissions.getGroupForPermission(
475                     permissionName);
476 
477             if (appPermissionGroup != null) {
478                 appPermissionGroup.revokeRuntimePermissions(false);
479             }
480         } catch (PackageManager.NameNotFoundException e) {
481             Log.e(LOG_TAG, "Error getting package:" + packageName, e);
482         }
483     }
484 
485     @Override
onCountPermissionApps(@onNull List<String> permissionNames, int flags, @NonNull IntConsumer callback)486     public void onCountPermissionApps(@NonNull List<String> permissionNames, int flags,
487             @NonNull IntConsumer callback) {
488         // There is no data processing needed, so we just directly pass the result onto the callback
489         mServiceModel.onCountPermissionAppsLiveData(permissionNames, flags,
490                 callback);
491     }
492 
493     /**
494      * Deprecated api call, only returns null.
495      */
496     @Override
497     @Deprecated
onGetPermissionUsages(boolean countSystem, long numMillis, @NonNull Consumer<List<RuntimePermissionUsageInfo>> callback)498     public void onGetPermissionUsages(boolean countSystem, long numMillis,
499             @NonNull Consumer<List<RuntimePermissionUsageInfo>> callback) {
500         callback.accept(null);
501     }
502 
503     @Override
onSetRuntimePermissionGrantStateByDeviceAdmin(@onNull String callerPackageName, @NonNull String packageName, @NonNull String unexpandedPermission, int grantState, @NonNull Consumer<Boolean> callback)504     public void onSetRuntimePermissionGrantStateByDeviceAdmin(@NonNull String callerPackageName,
505             @NonNull String packageName, @NonNull String unexpandedPermission, int grantState,
506             @NonNull Consumer<Boolean> callback) {
507         AsyncTask.execute(() -> callback.accept(onSetRuntimePermissionGrantStateByDeviceAdmin(
508                 callerPackageName, packageName, unexpandedPermission, grantState, true)));
509     }
510 
511     /**
512      * Admin control based on params.
513      */
514     @Override
onSetRuntimePermissionGrantStateByDeviceAdmin( @onNull String callerPackageName, @NonNull AdminPermissionControlParams params, @NonNull Consumer<Boolean> callback)515     public void onSetRuntimePermissionGrantStateByDeviceAdmin(
516             @NonNull String callerPackageName, @NonNull AdminPermissionControlParams params,
517             @NonNull Consumer<Boolean> callback) {
518         AsyncTask.execute(() -> callback.accept(onSetRuntimePermissionGrantStateByDeviceAdmin(
519                 callerPackageName, params.getGranteePackageName(), params.getPermission(),
520                 params.getGrantState(), params.canAdminGrantSensorsPermissions())));
521     }
522 
onSetRuntimePermissionGrantStateByDeviceAdmin(@onNull String callerPackageName, @NonNull String packageName, @NonNull String unexpandedPermission, int grantState, boolean canAdminGrantSensorsPermissions)523     private boolean onSetRuntimePermissionGrantStateByDeviceAdmin(@NonNull String callerPackageName,
524             @NonNull String packageName, @NonNull String unexpandedPermission, int grantState,
525             boolean canAdminGrantSensorsPermissions) {
526         PackageInfo callerPkgInfo = getPkgInfo(callerPackageName);
527         if (callerPkgInfo == null) {
528             Log.w(LOG_TAG, "Cannot fix " + unexpandedPermission + " as admin "
529                     + callerPackageName + " cannot be found");
530             return false;
531         }
532 
533         PackageInfo pkgInfo = getPkgInfo(packageName);
534         if (pkgInfo == null) {
535             Log.w(LOG_TAG, "Cannot fix " + unexpandedPermission + " as " + packageName
536                     + " cannot be found");
537             return false;
538         }
539 
540         ArrayList<String> expandedPermissions = addSplitPermissions(
541                 Collections.singletonList(unexpandedPermission),
542                 callerPkgInfo.applicationInfo.targetSdkVersion);
543 
544         AppPermissions app = new AppPermissions(this, pkgInfo, false, true, null);
545         AutoGrantPermissionsNotifier autoGrantPermissionsNotifier =
546                 new AutoGrantPermissionsNotifier(this, pkgInfo);
547 
548         final boolean isManagedProfile = getSystemService(UserManager.class).isManagedProfile();
549         DevicePolicyManager dpm = getSystemService(DevicePolicyManager.class);
550         int numPerms = expandedPermissions.size();
551         for (int i = 0; i < numPerms; i++) {
552             String permName = expandedPermissions.get(i);
553             AppPermissionGroup group = app.getGroupForPermission(permName);
554             if (group == null || group.isSystemFixed()) {
555                 continue;
556             }
557 
558             Permission perm = group.getPermission(permName);
559             if (perm == null) {
560                 continue;
561             }
562 
563             switch (grantState) {
564                 case PERMISSION_GRANT_STATE_GRANTED:
565                     if (AdminRestrictedPermissionsUtils.mayAdminGrantPermission(perm.getName(),
566                             canAdminGrantSensorsPermissions, isManagedProfile, dpm)) {
567                         perm.setPolicyFixed(true);
568                         group.grantRuntimePermissions(false, false, new String[]{permName});
569                         autoGrantPermissionsNotifier.onPermissionAutoGranted(permName);
570                     } else {
571                         // similar to PERMISSION_GRANT_STATE_DEFAULT
572                         perm.setPolicyFixed(false);
573                     }
574                     break;
575                 case PERMISSION_GRANT_STATE_DENIED:
576                     perm.setPolicyFixed(true);
577                     group.revokeRuntimePermissions(false, new String[]{permName});
578                     break;
579                 case PERMISSION_GRANT_STATE_DEFAULT:
580                     perm.setPolicyFixed(false);
581                     break;
582                 default:
583                     return false;
584             }
585         }
586 
587         app.persistChanges(grantState == PERMISSION_GRANT_STATE_DENIED
588                 || !callerPackageName.equals(packageName));
589         autoGrantPermissionsNotifier.notifyOfAutoGrantPermissions(false);
590 
591         return true;
592     }
593 
594     @Override
onGrantOrUpgradeDefaultRuntimePermissions(@onNull Runnable callback)595     public void onGrantOrUpgradeDefaultRuntimePermissions(@NonNull Runnable callback) {
596         performDefaultPermissionGrants();
597         RuntimePermissionsUpgradeController.INSTANCE.upgradeIfNeeded(this, () -> {
598             callback.run();
599         });
600     }
601 
performDefaultPermissionGrants()602     private void performDefaultPermissionGrants() {
603         // TODO: Default permission grants should go here
604     }
605 
606     @Override
onUpdateUserSensitivePermissionFlags(int uid, Executor executor, Runnable callback)607     public void onUpdateUserSensitivePermissionFlags(int uid, Executor executor,
608             Runnable callback) {
609         onUpdateUserSensistivePermissionFlagsWithRetry(uid, executor, callback, 0);
610     }
611 
onUpdateUserSensistivePermissionFlagsWithRetry(int uid, Executor executor, Runnable callback, int numAttempts)612     private void onUpdateUserSensistivePermissionFlagsWithRetry(int uid, Executor executor,
613             Runnable callback, int numAttempts) {
614         String idString = uid == Process.INVALID_UID
615                 ? "user " + Process.myUserHandle().getIdentifier() : "uid " + uid;
616         try {
617             Log.i(LOG_TAG, "Updating user sensitive for " + idString);
618             if (uid == Process.INVALID_UID) {
619                 UserSensitiveFlagsUtils.updateUserSensitiveForUser(Process.myUserHandle(),
620                         () -> executor.execute(callback));
621             } else {
622                 UserSensitiveFlagsUtils.updateUserSensitiveForUid(uid,
623                         () -> executor.execute(callback));
624             }
625         } catch (Exception e) {
626             // We specifically want to catch DeadSystemExceptions, but cannot explicitly request
627             // them, as it results in a compiler error
628             Log.w(LOG_TAG, "Failed to complete user sensitive update for " + idString
629                     + ", attempt number " + (numAttempts + 1) + " of " + MAX_RETRY_ATTEMPTS, e);
630             if (numAttempts == MAX_RETRY_ATTEMPTS) {
631                 throw e;
632             } else {
633                 int attempts = numAttempts + 1;
634                 Handler h = new Handler(Looper.getMainLooper());
635                 h.postDelayed(() -> onUpdateUserSensistivePermissionFlagsWithRetry(uid,
636                         executor, callback, attempts), RETRY_DELAY_MS);
637             }
638         }
639 
640     }
641 
642     @Override
onOneTimePermissionSessionTimeout(@onNull String packageName)643     public void onOneTimePermissionSessionTimeout(@NonNull String packageName) {
644         PackageManager pm = getPackageManager();
645         PackageInfo packageInfo;
646         int uid;
647         try {
648             packageInfo = pm.getPackageInfo(packageName, GET_PERMISSIONS);
649             uid = pm.getPackageUid(packageName, 0);
650         } catch (PackageManager.NameNotFoundException e) {
651             throw new RuntimeException(e);
652         }
653 
654         String[] permissions = packageInfo.requestedPermissions;
655         if (permissions == null) {
656             return;
657         }
658 
659         Set<AppPermissionGroup> groups = new ArraySet<>();
660         for (String permission : permissions) {
661             AppPermissionGroup group = AppPermissionGroup.create(this, packageInfo, permission,
662                     true);
663             if (group != null) {
664                 AppPermissionGroup bgGroup = group.getBackgroundPermissions();
665                 boolean isBgGroupOneTime = bgGroup != null && bgGroup.isOneTime();
666                 if (group.isOneTime() || isBgGroupOneTime) {
667                     groups.add(group);
668                 }
669             }
670         }
671         long requestId = Utils.getValidSessionId();
672         for (AppPermissionGroup group : groups) {
673             AppPermissionGroup bgGroup = group.getBackgroundPermissions();
674             if (group.areRuntimePermissionsGranted(null, true, false)) {
675                 logOneTimeSessionRevoke(packageName, uid, group, requestId);
676                 // Revoke only one time granted permissions if not all
677                 List<String> oneTimeGrantedPermissions = group.getPermissions().stream()
678                         .filter(Permission::isOneTime).filter(Permission::isGranted)
679                         .map(Permission::getName).collect(Collectors.toList());
680                 if (group.getPermissions().size() == oneTimeGrantedPermissions.size()) {
681                     group.revokeRuntimePermissions(false);
682                 } else {
683                     group.revokeRuntimePermissions(false,
684                             oneTimeGrantedPermissions.toArray(new String[0]));
685                 }
686                 for (String permissionName : oneTimeGrantedPermissions) {
687                     // We only reset the USER_SET and REVOKED_COMPAT flags if the permission was
688                     // granted.
689                     Permission permission = group.getPermission(permissionName);
690                     if (permission != null) {
691                         permission.setUserSet(false);
692                         if (!permission.isGranted() && permission.isRevokedCompat()) {
693                             // If we revoked the permission, but the Revoked Compat flag is set,
694                             // reset it
695                             permission.setRevokedCompat(false);
696                         }
697                     }
698                 }
699                 if (bgGroup != null) {
700                     // We also revoke background permissions if all foreground permissions are
701                     // getting revoked.
702                     if (group.getPermissions().size() == oneTimeGrantedPermissions.size()) {
703                         bgGroup.revokeRuntimePermissions(false);
704                     } else {
705                         bgGroup.revokeRuntimePermissions(false,
706                                 bgGroup.getPermissions().stream()
707                                         .filter(Permission::isOneTime).filter(Permission::isGranted)
708                                         .map(Permission::getName).toArray(String[]::new));
709                     }
710                 }
711             }
712             if (!group.supportsOneTimeGrant()) {
713                 group.setOneTime(false);
714             }
715             group.persistChanges(false, ONE_TIME_PERMISSION_REVOKED_REASON);
716             if (bgGroup != null) {
717                 if (!bgGroup.supportsOneTimeGrant()) {
718                     bgGroup.setOneTime(false);
719                 }
720                 bgGroup.persistChanges(false, ONE_TIME_PERMISSION_REVOKED_REASON);
721             }
722         }
723     }
724 
logOneTimeSessionRevoke(@onNull String packageName, int uid, AppPermissionGroup group, long requestId)725     private void logOneTimeSessionRevoke(@NonNull String packageName, int uid,
726             AppPermissionGroup group, long requestId) {
727         // used to keep lines below 100 chars
728         int r = PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__AUTO_ONE_TIME_PERMISSION_REVOKED;
729 
730         for (Permission permission : group.getPermissions()) {
731             if (permission.isGranted()) {
732                 String permName = permission.getName();
733                 Log.v(LOG_TAG,
734                         "Permission grant result requestId=" + requestId + " callingUid="
735                                 + uid + " callingPackage=" + packageName + " permission="
736                                 + permName + " isImplicit=false" + " result=" + r);
737 
738                 PermissionControllerStatsLog.write(
739                         PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED,
740                         requestId, uid, packageName, permName, false, r,
741                         /* permission_rationale_shown = */ false);
742             }
743         }
744     }
745 
746     @Override
getPrivilegesDescriptionStringForProfile(@onNull String deviceProfileName)747     public String getPrivilegesDescriptionStringForProfile(@NonNull String deviceProfileName) {
748         Role role = Roles.get(this).get(deviceProfileName);
749         if (role == null) {
750             throw new IllegalArgumentException("No such role: " + deviceProfileName);
751         }
752         return getString(role.getDescriptionResource(), "APP_NAME");
753     }
754 
755     @Override
onGetPlatformPermissionsForGroup(@onNull String permissionGroupName, @NonNull Consumer<List<String>> callback)756     public void onGetPlatformPermissionsForGroup(@NonNull String permissionGroupName,
757             @NonNull Consumer<List<String>> callback) {
758         callback.accept(PermissionMapping.getPlatformPermissionNamesOfGroup(permissionGroupName));
759     }
760 
761     @Override
onGetGroupOfPlatformPermission(@onNull String permissionName, @NonNull Consumer<String> callback)762     public void onGetGroupOfPlatformPermission(@NonNull String permissionName,
763             @NonNull Consumer<String> callback) {
764         callback.accept(PermissionMapping.getGroupOfPlatformPermission(permissionName));
765     }
766 
767     @Override
onGetUnusedAppCount(@onNull IntConsumer callback)768     public void onGetUnusedAppCount(@NonNull IntConsumer callback) {
769         mServiceModel.onCountUnusedApps(callback);
770     }
771 
772     @Override
onGetHibernationEligibility(@onNull String packageName, @NonNull IntConsumer callback)773     public void onGetHibernationEligibility(@NonNull String packageName,
774             @NonNull IntConsumer callback) {
775         mServiceModel.onGetHibernationEligibility(packageName, callback);
776     }
777 
778     @Override
779     @RequiresApi(Build.VERSION_CODES.TIRAMISU)
onRevokeSelfPermissionsOnKill(@onNull String packageName, @NonNull List<String> permissions, @NonNull Runnable callback)780     public void onRevokeSelfPermissionsOnKill(@NonNull String packageName,
781             @NonNull List<String> permissions, @NonNull Runnable callback) {
782         PackageInfo pkgInfo = getPkgInfo(packageName);
783         if (pkgInfo == null) {
784             throw new SecurityException("Cannot revoke permission " + String.join(",", permissions)
785                     + " for package " + packageName);
786         }
787         Set<AppPermissionGroup> groups = new HashSet<>();
788         AppPermissions app = new AppPermissions(this, pkgInfo, false, true, null);
789         for (String permName : permissions) {
790             AppPermissionGroup group = app.getGroupForPermission(permName);
791             if (group == null) {
792                 throw new IllegalArgumentException("Cannot revoke permission " + permName
793                         + " for package " + packageName + " since " + permName
794                         + " does not belong to a permission group");
795             }
796             if (!group.doesSupportRuntimePermissions()) {
797                 throw new IllegalArgumentException("Cannot revoke permission " + permName
798                         + " for package " + packageName + " since it is not a runtime permission");
799             }
800             Permission perm = group.getPermission(permName);
801             if (!perm.isGranted()) {
802                 continue;
803             }
804             perm.setOneTime(true);
805             groups.add(group);
806 
807             if (permName.equals(Manifest.permission.ACCESS_COARSE_LOCATION)) {
808                 group.getPermission(Manifest.permission.ACCESS_FINE_LOCATION).setOneTime(true);
809             } else if (permName.equals(Manifest.permission.ACCESS_FINE_LOCATION)) {
810                 // Set coarse as the selected location accuracy
811                 perm.setSelectedLocationAccuracy(false);
812                 group.getPermission(Manifest.permission.ACCESS_COARSE_LOCATION)
813                         .setSelectedLocationAccuracy(true);
814             }
815 
816             if (group.isStrictlyOneTime()) {
817                 // All remaining permissions in the group are one-time, we should also revoke
818                 // the background permissions if there are any
819                 Permission bgPerm = perm.getBackgroundPermission();
820                 if (bgPerm != null && bgPerm.isGranted()) {
821                     bgPerm.setOneTime(true);
822                     AppPermissionGroup bgGroup = group.getBackgroundPermissions();
823                     groups.add(bgGroup);
824                 }
825             }
826         }
827         for (AppPermissionGroup group : groups) {
828             group.setSelfRevoked();
829             group.persistChanges(false);
830         }
831         getMainExecutor().execute(callback);
832     }
833 }
834