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