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 android.permission;
18 
19 import android.Manifest;
20 import android.annotation.CallbackExecutor;
21 import android.annotation.IntRange;
22 import android.annotation.NonNull;
23 import android.annotation.Nullable;
24 import android.annotation.RequiresPermission;
25 import android.annotation.SystemApi;
26 import android.annotation.SystemService;
27 import android.annotation.TestApi;
28 import android.app.ActivityManager;
29 import android.app.ActivityThread;
30 import android.app.IActivityManager;
31 import android.app.PropertyInvalidatedCache;
32 import android.content.Context;
33 import android.content.pm.IPackageManager;
34 import android.content.pm.PackageManager;
35 import android.content.pm.permission.SplitPermissionInfoParcelable;
36 import android.os.Process;
37 import android.os.RemoteException;
38 import android.os.ServiceManager;
39 import android.os.UserHandle;
40 import android.util.Slog;
41 
42 import com.android.internal.annotations.Immutable;
43 import com.android.internal.annotations.VisibleForTesting;
44 import com.android.internal.util.CollectionUtils;
45 
46 import java.util.ArrayList;
47 import java.util.Collections;
48 import java.util.List;
49 import java.util.Objects;
50 import java.util.Set;
51 import java.util.concurrent.Executor;
52 import java.util.function.Consumer;
53 
54 /**
55  * System level service for accessing the permission capabilities of the platform.
56  *
57  * @hide
58  */
59 @TestApi
60 @SystemApi
61 @SystemService(Context.PERMISSION_SERVICE)
62 public final class PermissionManager {
63     private static final String TAG = PermissionManager.class.getName();
64 
65     /** @hide */
66     public static final String KILL_APP_REASON_PERMISSIONS_REVOKED =
67             "permissions revoked";
68     /** @hide */
69     public static final String KILL_APP_REASON_GIDS_CHANGED =
70             "permission grant or revoke changed gids";
71 
72     private final @NonNull Context mContext;
73 
74     private final IPackageManager mPackageManager;
75 
76     private final IPermissionManager mPermissionManager;
77 
78     private List<SplitPermissionInfo> mSplitPermissionInfos;
79 
80     /**
81      * Creates a new instance.
82      *
83      * @param context The current context in which to operate.
84      * @hide
85      */
PermissionManager(@onNull Context context, IPackageManager packageManager)86     public PermissionManager(@NonNull Context context, IPackageManager packageManager)
87             throws ServiceManager.ServiceNotFoundException {
88         this(context, packageManager, IPermissionManager.Stub.asInterface(
89                 ServiceManager.getServiceOrThrow("permissionmgr")));
90     }
91 
92     /**
93      * Creates a new instance with the provided instantiation of the IPermissionManager.
94      *
95      * @param context           the current context in which to operate
96      * @param packageManager    package manager service to be used for package related permission
97      *                          requests
98      * @param permissionManager injectable permission manager service
99      * @hide
100      */
101     @VisibleForTesting
PermissionManager(@onNull Context context, IPackageManager packageManager, IPermissionManager permissionManager)102     public PermissionManager(@NonNull Context context, IPackageManager packageManager,
103             IPermissionManager permissionManager) {
104         mContext = context;
105         mPackageManager = packageManager;
106         mPermissionManager = permissionManager;
107     }
108 
109     /**
110      * Gets the version of the runtime permission database.
111      *
112      * @return The database version, -1 when this is an upgrade from pre-Q, 0 when this is a fresh
113      * install.
114      *
115      * @hide
116      */
117     @TestApi
118     @SystemApi
119     @RequiresPermission(anyOf = {
120             Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY,
121             Manifest.permission.UPGRADE_RUNTIME_PERMISSIONS
122     })
getRuntimePermissionsVersion()123     public @IntRange(from = 0) int getRuntimePermissionsVersion() {
124         try {
125             return mPackageManager.getRuntimePermissionsVersion(mContext.getUserId());
126         } catch (RemoteException e) {
127             throw e.rethrowFromSystemServer();
128         }
129     }
130 
131     /**
132      * Sets the version of the runtime permission database.
133      *
134      * @param version The new version.
135      *
136      * @hide
137      */
138     @TestApi
139     @SystemApi
140     @RequiresPermission(anyOf = {
141             Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY,
142             Manifest.permission.UPGRADE_RUNTIME_PERMISSIONS
143     })
setRuntimePermissionsVersion(@ntRangefrom = 0) int version)144     public void setRuntimePermissionsVersion(@IntRange(from = 0) int version) {
145         try {
146             mPackageManager.setRuntimePermissionsVersion(version, mContext.getUserId());
147         } catch (RemoteException e) {
148             throw e.rethrowFromSystemServer();
149         }
150     }
151 
152     /**
153      * Get set of permissions that have been split into more granular or dependent permissions.
154      *
155      * <p>E.g. before {@link android.os.Build.VERSION_CODES#Q} an app that was granted
156      * {@link Manifest.permission#ACCESS_COARSE_LOCATION} could access he location while it was in
157      * foreground and background. On platforms after {@link android.os.Build.VERSION_CODES#Q}
158      * the location permission only grants location access while the app is in foreground. This
159      * would break apps that target before {@link android.os.Build.VERSION_CODES#Q}. Hence whenever
160      * such an old app asks for a location permission (i.e. the
161      * {@link SplitPermissionInfo#getSplitPermission()}), then the
162      * {@link Manifest.permission#ACCESS_BACKGROUND_LOCATION} permission (inside
163      * {@link SplitPermissionInfo#getNewPermissions}) is added.
164      *
165      * <p>Note: Regular apps do not have to worry about this. The platform and permission controller
166      * automatically add the new permissions where needed.
167      *
168      * @return All permissions that are split.
169      */
getSplitPermissions()170     public @NonNull List<SplitPermissionInfo> getSplitPermissions() {
171         if (mSplitPermissionInfos != null) {
172             return mSplitPermissionInfos;
173         }
174 
175         List<SplitPermissionInfoParcelable> parcelableList;
176         try {
177             parcelableList = ActivityThread.getPermissionManager().getSplitPermissions();
178         } catch (RemoteException e) {
179             Slog.e(TAG, "Error getting split permissions", e);
180             return Collections.emptyList();
181         }
182 
183         mSplitPermissionInfos = splitPermissionInfoListToNonParcelableList(parcelableList);
184 
185         return mSplitPermissionInfos;
186     }
187 
188     /**
189      * Grant default permissions to currently active LUI app
190      * @param packageName The package name for the LUI app
191      * @param user The user handle
192      * @param executor The executor for the callback
193      * @param callback The callback provided by caller to be notified when grant completes
194      * @hide
195      */
196     @RequiresPermission(Manifest.permission.GRANT_RUNTIME_PERMISSIONS_TO_TELEPHONY_DEFAULTS)
grantDefaultPermissionsToLuiApp( @onNull String packageName, @NonNull UserHandle user, @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback)197     public void grantDefaultPermissionsToLuiApp(
198             @NonNull String packageName, @NonNull UserHandle user,
199             @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback) {
200         try {
201             mPermissionManager.grantDefaultPermissionsToActiveLuiApp(
202                     packageName, user.getIdentifier());
203             executor.execute(() -> callback.accept(true));
204         } catch (RemoteException e) {
205             e.rethrowFromSystemServer();
206         }
207     }
208 
209     /**
210      * Revoke default permissions to currently active LUI app
211      * @param packageNames The package names for the LUI apps
212      * @param user The user handle
213      * @param executor The executor for the callback
214      * @param callback The callback provided by caller to be notified when grant completes
215      * @hide
216      */
217     @RequiresPermission(Manifest.permission.GRANT_RUNTIME_PERMISSIONS_TO_TELEPHONY_DEFAULTS)
revokeDefaultPermissionsFromLuiApps( @onNull String[] packageNames, @NonNull UserHandle user, @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback)218     public void revokeDefaultPermissionsFromLuiApps(
219             @NonNull String[] packageNames, @NonNull UserHandle user,
220             @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback) {
221         try {
222             mPermissionManager.revokeDefaultPermissionsFromLuiApps(
223                     packageNames, user.getIdentifier());
224             executor.execute(() -> callback.accept(true));
225         } catch (RemoteException e) {
226             e.rethrowFromSystemServer();
227         }
228     }
229 
230     /**
231      * Grant default permissions to currently active Ims services
232      * @param packageNames The package names for the Ims services
233      * @param user The user handle
234      * @param executor The executor for the callback
235      * @param callback The callback provided by caller to be notified when grant completes
236      * @hide
237      */
238     @RequiresPermission(Manifest.permission.GRANT_RUNTIME_PERMISSIONS_TO_TELEPHONY_DEFAULTS)
grantDefaultPermissionsToEnabledImsServices( @onNull String[] packageNames, @NonNull UserHandle user, @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback)239     public void grantDefaultPermissionsToEnabledImsServices(
240             @NonNull String[] packageNames, @NonNull UserHandle user,
241             @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback) {
242         try {
243             mPermissionManager.grantDefaultPermissionsToEnabledImsServices(
244                     packageNames, user.getIdentifier());
245             executor.execute(() -> callback.accept(true));
246         } catch (RemoteException e) {
247             e.rethrowFromSystemServer();
248         }
249     }
250 
251     /**
252      * Grant default permissions to currently enabled telephony data services
253      * @param packageNames The package name for the services
254      * @param user The user handle
255      * @param executor The executor for the callback
256      * @param callback The callback provided by caller to be notified when grant completes
257      * @hide
258      */
259     @RequiresPermission(Manifest.permission.GRANT_RUNTIME_PERMISSIONS_TO_TELEPHONY_DEFAULTS)
grantDefaultPermissionsToEnabledTelephonyDataServices( @onNull String[] packageNames, @NonNull UserHandle user, @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback)260     public void grantDefaultPermissionsToEnabledTelephonyDataServices(
261             @NonNull String[] packageNames, @NonNull UserHandle user,
262             @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback) {
263         try {
264             mPermissionManager.grantDefaultPermissionsToEnabledTelephonyDataServices(
265                     packageNames, user.getIdentifier());
266             executor.execute(() -> callback.accept(true));
267         } catch (RemoteException e) {
268             e.rethrowFromSystemServer();
269         }
270     }
271 
272     /**
273      * Revoke default permissions to currently active telephony data services
274      * @param packageNames The package name for the services
275      * @param user The user handle
276      * @param executor The executor for the callback
277      * @param callback The callback provided by caller to be notified when revoke completes
278      * @hide
279      */
280     @RequiresPermission(Manifest.permission.GRANT_RUNTIME_PERMISSIONS_TO_TELEPHONY_DEFAULTS)
revokeDefaultPermissionsFromDisabledTelephonyDataServices( @onNull String[] packageNames, @NonNull UserHandle user, @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback)281     public void revokeDefaultPermissionsFromDisabledTelephonyDataServices(
282             @NonNull String[] packageNames, @NonNull UserHandle user,
283             @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback) {
284         try {
285             mPermissionManager.revokeDefaultPermissionsFromDisabledTelephonyDataServices(
286                     packageNames, user.getIdentifier());
287             executor.execute(() -> callback.accept(true));
288         } catch (RemoteException e) {
289             e.rethrowFromSystemServer();
290         }
291     }
292 
293     /**
294      * Grant default permissions to currently enabled carrier apps
295      * @param packageNames Package names of the apps to be granted permissions
296      * @param user The user handle
297      * @param executor The executor for the callback
298      * @param callback The callback provided by caller to be notified when grant completes
299      * @hide
300      */
301     @RequiresPermission(Manifest.permission.GRANT_RUNTIME_PERMISSIONS_TO_TELEPHONY_DEFAULTS)
grantDefaultPermissionsToEnabledCarrierApps(@onNull String[] packageNames, @NonNull UserHandle user, @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback)302     public void grantDefaultPermissionsToEnabledCarrierApps(@NonNull String[] packageNames,
303             @NonNull UserHandle user, @NonNull @CallbackExecutor Executor executor,
304             @NonNull Consumer<Boolean> callback) {
305         try {
306             mPermissionManager.grantDefaultPermissionsToEnabledCarrierApps(packageNames,
307                     user.getIdentifier());
308             executor.execute(() -> callback.accept(true));
309         } catch (RemoteException e) {
310             e.rethrowFromSystemServer();
311         }
312     }
313 
314     /**
315      * Gets the list of packages that have permissions that specified
316      * {@code requestDontAutoRevokePermissions=true} in their
317      * {@code application} manifest declaration.
318      *
319      * @return the list of packages for current user
320      * @hide
321      */
322     @SystemApi
323     @NonNull
324     @RequiresPermission(Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY)
getAutoRevokeExemptionRequestedPackages()325     public Set<String> getAutoRevokeExemptionRequestedPackages() {
326         try {
327             return CollectionUtils.toSet(mPermissionManager.getAutoRevokeExemptionRequestedPackages(
328                     mContext.getUser().getIdentifier()));
329         } catch (RemoteException e) {
330             throw e.rethrowFromSystemServer();
331         }
332     }
333 
334     /**
335      * Gets the list of packages that have permissions that specified
336      * {@code autoRevokePermissions=disallowed} in their
337      * {@code application} manifest declaration.
338      *
339      * @return the list of packages for current user
340      * @hide
341      */
342     @SystemApi
343     @NonNull
344     @RequiresPermission(Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY)
getAutoRevokeExemptionGrantedPackages()345     public Set<String> getAutoRevokeExemptionGrantedPackages() {
346         try {
347             return CollectionUtils.toSet(mPermissionManager.getAutoRevokeExemptionGrantedPackages(
348                     mContext.getUser().getIdentifier()));
349         } catch (RemoteException e) {
350             throw e.rethrowFromSystemServer();
351         }
352     }
353 
splitPermissionInfoListToNonParcelableList( List<SplitPermissionInfoParcelable> parcelableList)354     private List<SplitPermissionInfo> splitPermissionInfoListToNonParcelableList(
355             List<SplitPermissionInfoParcelable> parcelableList) {
356         final int size = parcelableList.size();
357         List<SplitPermissionInfo> list = new ArrayList<>(size);
358         for (int i = 0; i < size; i++) {
359             list.add(new SplitPermissionInfo(parcelableList.get(i)));
360         }
361         return list;
362     }
363 
364     /**
365      * Converts a {@link List} of {@link SplitPermissionInfo} into a List of
366      * {@link SplitPermissionInfoParcelable} and returns it.
367      * @hide
368      */
splitPermissionInfoListToParcelableList( List<SplitPermissionInfo> splitPermissionsList)369     public static List<SplitPermissionInfoParcelable> splitPermissionInfoListToParcelableList(
370             List<SplitPermissionInfo> splitPermissionsList) {
371         final int size = splitPermissionsList.size();
372         List<SplitPermissionInfoParcelable> outList = new ArrayList<>(size);
373         for (int i = 0; i < size; i++) {
374             SplitPermissionInfo info = splitPermissionsList.get(i);
375             outList.add(new SplitPermissionInfoParcelable(
376                     info.getSplitPermission(), info.getNewPermissions(), info.getTargetSdk()));
377         }
378         return outList;
379     }
380 
381     /**
382      * A permission that was added in a previous API level might have split into several
383      * permissions. This object describes one such split.
384      */
385     @Immutable
386     public static final class SplitPermissionInfo {
387         private @NonNull final SplitPermissionInfoParcelable mSplitPermissionInfoParcelable;
388 
389         @Override
equals(@ullable Object o)390         public boolean equals(@Nullable Object o) {
391             if (this == o) return true;
392             if (o == null || getClass() != o.getClass()) return false;
393             SplitPermissionInfo that = (SplitPermissionInfo) o;
394             return mSplitPermissionInfoParcelable.equals(that.mSplitPermissionInfoParcelable);
395         }
396 
397         @Override
hashCode()398         public int hashCode() {
399             return mSplitPermissionInfoParcelable.hashCode();
400         }
401 
402         /**
403          * Get the permission that is split.
404          */
getSplitPermission()405         public @NonNull String getSplitPermission() {
406             return mSplitPermissionInfoParcelable.getSplitPermission();
407         }
408 
409         /**
410          * Get the permissions that are added.
411          */
getNewPermissions()412         public @NonNull List<String> getNewPermissions() {
413             return mSplitPermissionInfoParcelable.getNewPermissions();
414         }
415 
416         /**
417          * Get the target API level when the permission was split.
418          */
getTargetSdk()419         public int getTargetSdk() {
420             return mSplitPermissionInfoParcelable.getTargetSdk();
421         }
422 
423         /**
424          * Constructs a split permission.
425          *
426          * @param splitPerm old permission that will be split
427          * @param newPerms list of new permissions that {@code rootPerm} will be split into
428          * @param targetSdk apps targetting SDK versions below this will have {@code rootPerm}
429          * split into {@code newPerms}
430          * @hide
431          */
SplitPermissionInfo(@onNull String splitPerm, @NonNull List<String> newPerms, int targetSdk)432         public SplitPermissionInfo(@NonNull String splitPerm, @NonNull List<String> newPerms,
433                 int targetSdk) {
434             this(new SplitPermissionInfoParcelable(splitPerm, newPerms, targetSdk));
435         }
436 
SplitPermissionInfo(@onNull SplitPermissionInfoParcelable parcelable)437         private SplitPermissionInfo(@NonNull SplitPermissionInfoParcelable parcelable) {
438             mSplitPermissionInfoParcelable = parcelable;
439         }
440     }
441 
442     /**
443      * Starts a one-time permission session for a given package. A one-time permission session is
444      * ended if app becomes inactive. Inactivity is defined as the package's uid importance level
445      * staying > importanceToResetTimer for timeoutMillis milliseconds. If the package's uid
446      * importance level goes <= importanceToResetTimer then the timer is reset and doesn't start
447      * until going > importanceToResetTimer.
448      * <p>
449      * When this timeoutMillis is reached if the importance level is <= importanceToKeepSessionAlive
450      * then the session is extended until either the importance goes above
451      * importanceToKeepSessionAlive which will end the session or <= importanceToResetTimer which
452      * will continue the session and reset the timer.
453      * </p>
454      * <p>
455      * Importance levels are defined in {@link android.app.ActivityManager.RunningAppProcessInfo}.
456      * </p>
457      * <p>
458      * Once the session ends
459      * {@link PermissionControllerService#onOneTimePermissionSessionTimeout(String)} is invoked.
460      * </p>
461      * <p>
462      * Note that if there is currently an active session for a package a new one isn't created and
463      * the existing one isn't changed.
464      * </p>
465      * @param packageName The package to start a one-time permission session for
466      * @param timeoutMillis Number of milliseconds for an app to be in an inactive state
467      * @param importanceToResetTimer The least important level to uid must be to reset the timer
468      * @param importanceToKeepSessionAlive The least important level the uid must be to keep the
469      *                                    session alive
470      *
471      * @hide
472      */
473     @SystemApi
474     @RequiresPermission(Manifest.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS)
startOneTimePermissionSession(@onNull String packageName, long timeoutMillis, @ActivityManager.RunningAppProcessInfo.Importance int importanceToResetTimer, @ActivityManager.RunningAppProcessInfo.Importance int importanceToKeepSessionAlive)475     public void startOneTimePermissionSession(@NonNull String packageName, long timeoutMillis,
476             @ActivityManager.RunningAppProcessInfo.Importance int importanceToResetTimer,
477             @ActivityManager.RunningAppProcessInfo.Importance int importanceToKeepSessionAlive) {
478         try {
479             mPermissionManager.startOneTimePermissionSession(packageName, mContext.getUserId(),
480                     timeoutMillis, importanceToResetTimer, importanceToKeepSessionAlive);
481         } catch (RemoteException e) {
482             e.rethrowFromSystemServer();
483         }
484     }
485 
486     /**
487      * Stops the one-time permission session for the package. The callback to the end of session is
488      * not invoked. If there is no one-time session for the package then nothing happens.
489      *
490      * @param packageName Package to stop the one-time permission session for
491      *
492      * @hide
493      */
494     @SystemApi
495     @RequiresPermission(Manifest.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS)
stopOneTimePermissionSession(@onNull String packageName)496     public void stopOneTimePermissionSession(@NonNull String packageName) {
497         try {
498             mPermissionManager.stopOneTimePermissionSession(packageName,
499                     mContext.getUserId());
500         } catch (RemoteException e) {
501             e.rethrowFromSystemServer();
502         }
503     }
504 
505     /**
506      * Checks whether the package with the given pid/uid can read device identifiers.
507      *
508      * @param packageName      the name of the package to be checked for identifier access
509      * @param message          the message to be used for logging during identifier access
510      *                         verification
511      * @param callingFeatureId the feature in the package
512      * @param pid              the process id of the package to be checked
513      * @param uid              the uid of the package to be checked
514      * @return {@link PackageManager#PERMISSION_GRANTED} if the package is allowed identifier
515      * access, {@link PackageManager#PERMISSION_DENIED} otherwise
516      * @hide
517      */
518     @SystemApi
checkDeviceIdentifierAccess(@ullable String packageName, @Nullable String message, @Nullable String callingFeatureId, int pid, int uid)519     public int checkDeviceIdentifierAccess(@Nullable String packageName, @Nullable String message,
520             @Nullable String callingFeatureId, int pid, int uid) {
521         try {
522             return mPermissionManager.checkDeviceIdentifierAccess(packageName, message,
523                     callingFeatureId, pid, uid);
524         } catch (RemoteException e) {
525             throw e.rethrowFromSystemServer();
526         }
527     }
528 
529     /* @hide */
checkPermissionUncached(@ullable String permission, int pid, int uid)530     private static int checkPermissionUncached(@Nullable String permission, int pid, int uid) {
531         final IActivityManager am = ActivityManager.getService();
532         if (am == null) {
533             // Well this is super awkward; we somehow don't have an active ActivityManager
534             // instance. If we're testing a root or system UID, then they totally have whatever
535             // permission this is.
536             final int appId = UserHandle.getAppId(uid);
537             if (appId == Process.ROOT_UID || appId == Process.SYSTEM_UID) {
538                 Slog.w(TAG, "Missing ActivityManager; assuming " + uid + " holds " + permission);
539                 return PackageManager.PERMISSION_GRANTED;
540             }
541             Slog.w(TAG, "Missing ActivityManager; assuming " + uid + " does not hold "
542                     + permission);
543             return PackageManager.PERMISSION_DENIED;
544         }
545         try {
546             return am.checkPermission(permission, pid, uid);
547         } catch (RemoteException e) {
548             throw e.rethrowFromSystemServer();
549         }
550     }
551 
552     /**
553      * Identifies a permission query.
554      *
555      * N.B. we include the checking pid for tracking purposes but don't include it in the equality
556      * comparison: we use only uid for the actual security check, so comparing pid would result
557      * in spurious misses.
558      *
559      * @hide
560      */
561     @Immutable
562     private static final class PermissionQuery {
563         final String permission;
564         final int pid;
565         final int uid;
566 
PermissionQuery(@ullable String permission, int pid, int uid)567         PermissionQuery(@Nullable String permission, int pid, int uid) {
568             this.permission = permission;
569             this.pid = pid;
570             this.uid = uid;
571         }
572 
573         @Override
toString()574         public String toString() {
575             return String.format("PermissionQuery(permission=\"%s\", pid=%s, uid=%s)",
576                     permission, pid, uid);
577         }
578 
579         @Override
hashCode()580         public int hashCode() {
581             // N.B. pid doesn't count toward equality and therefore shouldn't count for
582             // hashing either.
583             int hash = Objects.hashCode(permission);
584             hash = hash * 13 + Objects.hashCode(uid);
585             return hash;
586         }
587 
588         @Override
equals(Object rval)589         public boolean equals(Object rval) {
590             // N.B. pid doesn't count toward equality!
591             if (rval == null) {
592                 return false;
593             }
594             PermissionQuery other;
595             try {
596                 other = (PermissionQuery) rval;
597             } catch (ClassCastException ex) {
598                 return false;
599             }
600             return uid == other.uid
601                     && Objects.equals(permission, other.permission);
602         }
603     }
604 
605     /** @hide */
606     public static final String CACHE_KEY_PACKAGE_INFO = "cache_key.package_info";
607 
608     /** @hide */
609     private static final PropertyInvalidatedCache<PermissionQuery, Integer> sPermissionCache =
610             new PropertyInvalidatedCache<PermissionQuery, Integer>(
611                     16, CACHE_KEY_PACKAGE_INFO) {
612                 @Override
613                 protected Integer recompute(PermissionQuery query) {
614                     return checkPermissionUncached(query.permission, query.pid, query.uid);
615                 }
616             };
617 
618     /** @hide */
checkPermission(@ullable String permission, int pid, int uid)619     public static int checkPermission(@Nullable String permission, int pid, int uid) {
620         return sPermissionCache.query(new PermissionQuery(permission, pid, uid));
621     }
622 
623     /**
624      * Make checkPermission() above bypass the permission cache in this process.
625      *
626      * @hide
627      */
disablePermissionCache()628     public static void disablePermissionCache() {
629         sPermissionCache.disableLocal();
630     }
631 
632     /**
633      * Like PermissionQuery, but for permission checks based on a package name instead of
634      * a UID.
635      */
636     @Immutable
637     private static final class PackageNamePermissionQuery {
638         final String permName;
639         final String pkgName;
640         final int uid;
641 
PackageNamePermissionQuery(@ullable String permName, @Nullable String pkgName, int uid)642         PackageNamePermissionQuery(@Nullable String permName, @Nullable String pkgName, int uid) {
643             this.permName = permName;
644             this.pkgName = pkgName;
645             this.uid = uid;
646         }
647 
648         @Override
toString()649         public String toString() {
650             return String.format(
651                     "PackageNamePermissionQuery(pkgName=\"%s\", permName=\"%s, uid=%s\")",
652                     pkgName, permName, uid);
653         }
654 
655         @Override
hashCode()656         public int hashCode() {
657             return Objects.hash(permName, pkgName, uid);
658         }
659 
660         @Override
equals(Object rval)661         public boolean equals(Object rval) {
662             if (rval == null) {
663                 return false;
664             }
665             PackageNamePermissionQuery other;
666             try {
667                 other = (PackageNamePermissionQuery) rval;
668             } catch (ClassCastException ex) {
669                 return false;
670             }
671             return Objects.equals(permName, other.permName)
672                     && Objects.equals(pkgName, other.pkgName)
673                     && uid == other.uid;
674         }
675     }
676 
677     /* @hide */
checkPackageNamePermissionUncached( String permName, String pkgName, int uid)678     private static int checkPackageNamePermissionUncached(
679             String permName, String pkgName, int uid) {
680         try {
681             return ActivityThread.getPermissionManager().checkPermission(
682                     permName, pkgName, uid);
683         } catch (RemoteException e) {
684             throw e.rethrowFromSystemServer();
685         }
686     }
687 
688     /* @hide */
689     private static PropertyInvalidatedCache<PackageNamePermissionQuery, Integer>
690             sPackageNamePermissionCache =
691             new PropertyInvalidatedCache<PackageNamePermissionQuery, Integer>(
692                     16, CACHE_KEY_PACKAGE_INFO) {
693                 @Override
694                 protected Integer recompute(PackageNamePermissionQuery query) {
695                     return checkPackageNamePermissionUncached(
696                             query.permName, query.pkgName, query.uid);
697                 }
698             };
699 
700     /**
701      * Check whether a package has a permission.
702      *
703      * @hide
704      */
checkPackageNamePermission(String permName, String pkgName, int uid)705     public static int checkPackageNamePermission(String permName, String pkgName, int uid) {
706         return sPackageNamePermissionCache.query(
707                 new PackageNamePermissionQuery(permName, pkgName, uid));
708     }
709 
710     /**
711      * Make checkPackageNamePermission() bypass the cache in this process.
712      *
713      * @hide
714      */
disablePackageNamePermissionCache()715     public static void disablePackageNamePermissionCache() {
716         sPackageNamePermissionCache.disableLocal();
717     }
718 
719 }
720