Home | History | Annotate | Download | only in content
      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.content;
     18 
     19 import android.annotation.IntDef;
     20 import android.annotation.NonNull;
     21 import android.annotation.Nullable;
     22 import android.app.AppOpsManager;
     23 import android.content.pm.PackageManager;
     24 import android.os.Binder;
     25 import android.os.Process;
     26 
     27 import java.lang.annotation.Retention;
     28 import java.lang.annotation.RetentionPolicy;
     29 
     30 /**
     31  * This class provides permission check APIs that verify both the
     32  * permission and the associated app op for this permission if
     33  * such is defined.
     34  * <p>
     35  * In the new permission model permissions with protection level
     36  * dangerous are runtime permissions. For apps targeting {@link android.os.Build.VERSION_CODES#M}
     37  * and above the user may not grant such permissions or revoke
     38  * them at any time. For apps targeting API lower than {@link android.os.Build.VERSION_CODES#M}
     39  * these permissions are always granted as such apps do not expect
     40  * permission revocations and would crash. Therefore, when the
     41  * user disables a permission for a legacy app in the UI the
     42  * platform disables the APIs guarded by this permission making
     43  * them a no-op which is doing nothing or returning an empty
     44  * result or default error.
     45  * </p>
     46  * <p>
     47  * It is important that when you perform an operation on behalf of
     48  * another app you use these APIs to check for permissions as the
     49  * app may be a legacy app that does not participate in the new
     50  * permission model for which the user had disabled the "permission"
     51  * which is achieved by disallowing the corresponding app op.
     52  * </p>
     53  *
     54  * @hide
     55  */
     56 public final class PermissionChecker {
     57     /** Permission result: The permission is granted. */
     58     public static final int PERMISSION_GRANTED =  PackageManager.PERMISSION_GRANTED;
     59 
     60     /** Permission result: The permission is denied. */
     61     public static final int PERMISSION_DENIED =  PackageManager.PERMISSION_DENIED;
     62 
     63     /** Permission result: The permission is denied because the app op is not allowed. */
     64     public static final int PERMISSION_DENIED_APP_OP =  PackageManager.PERMISSION_DENIED  - 1;
     65 
     66     /** @hide */
     67     @IntDef({PERMISSION_GRANTED,
     68             PERMISSION_DENIED,
     69             PERMISSION_DENIED_APP_OP})
     70     @Retention(RetentionPolicy.SOURCE)
     71     public @interface PermissionResult {}
     72 
     73     private PermissionChecker() {
     74         /* do nothing */
     75     }
     76 
     77     /**
     78      * Checks whether a given package in a UID and PID has a given permission
     79      * and whether the app op that corresponds to this permission is allowed.
     80      *
     81      * @param context Context for accessing resources.
     82      * @param permission The permission to check.
     83      * @param pid The process id for which to check.
     84      * @param uid The uid for which to check.
     85      * @param packageName The package name for which to check. If null the
     86      *     the first package for the calling UID will be used.
     87      * @return The permission check result which is either {@link #PERMISSION_GRANTED}
     88      *     or {@link #PERMISSION_DENIED} or {@link #PERMISSION_DENIED_APP_OP}.
     89      */
     90     @PermissionResult
     91     public static int checkPermission(@NonNull Context context, @NonNull String permission,
     92             int pid, int uid, @Nullable String packageName) {
     93         if (context.checkPermission(permission, pid, uid) == PackageManager.PERMISSION_DENIED) {
     94             return PERMISSION_DENIED;
     95         }
     96 
     97         AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class);
     98         String op = appOpsManager.permissionToOp(permission);
     99         if (op == null) {
    100             return PERMISSION_GRANTED;
    101         }
    102 
    103         if (packageName == null) {
    104             String[] packageNames = context.getPackageManager().getPackagesForUid(uid);
    105             if (packageNames == null || packageNames.length <= 0) {
    106                 return PERMISSION_DENIED;
    107             }
    108             packageName = packageNames[0];
    109         }
    110 
    111         if (appOpsManager.noteProxyOpNoThrow(op, packageName, uid) != AppOpsManager.MODE_ALLOWED) {
    112             return PERMISSION_DENIED_APP_OP;
    113         }
    114 
    115         return PERMISSION_GRANTED;
    116     }
    117 
    118     /**
    119      * Checks whether your app has a given permission and whether the app op
    120      * that corresponds to this permission is allowed.
    121      *
    122      * <p>This API assumes the the {@link Binder#getCallingUid()} is the same as
    123      * {@link Process#myUid()}.
    124      *
    125      * @param context Context for accessing resources.
    126      * @param permission The permission to check.
    127      * @return The permission check result which is either {@link #PERMISSION_GRANTED}
    128      *     or {@link #PERMISSION_DENIED} or {@link #PERMISSION_DENIED_APP_OP}.
    129      */
    130     @PermissionResult
    131     public static int checkSelfPermission(@NonNull Context context,
    132             @NonNull String permission) {
    133         return checkPermission(context, permission, Process.myPid(),
    134                 Process.myUid(), context.getPackageName());
    135     }
    136 
    137     /**
    138      * Checks whether the IPC you are handling has a given permission and whether
    139      * the app op that corresponds to this permission is allowed.
    140      *
    141      * @param context Context for accessing resources.
    142      * @param permission The permission to check.
    143      * @param packageName The package name making the IPC. If null the
    144      *     the first package for the calling UID will be used.
    145      * @return The permission check result which is either {@link #PERMISSION_GRANTED}
    146      *     or {@link #PERMISSION_DENIED} or {@link #PERMISSION_DENIED_APP_OP}.
    147      */
    148     @PermissionResult
    149     public static int checkCallingPermission(@NonNull Context context,
    150             @NonNull String permission, @Nullable String packageName) {
    151         if (Binder.getCallingPid() == Process.myPid()) {
    152             return PERMISSION_DENIED;
    153         }
    154         return checkPermission(context, permission, Binder.getCallingPid(),
    155                 Binder.getCallingUid(), packageName);
    156     }
    157 
    158     /**
    159      * Checks whether the IPC you are handling or your app has a given permission
    160      * and whether the app op that corresponds to this permission is allowed.
    161      *
    162      * @param context Context for accessing resources.
    163      * @param permission The permission to check.
    164      * @return The permission check result which is either {@link #PERMISSION_GRANTED}
    165      *     or {@link #PERMISSION_DENIED} or {@link #PERMISSION_DENIED_APP_OP}.
    166      */
    167     @PermissionResult
    168     public static int checkCallingOrSelfPermission(@NonNull Context context,
    169             @NonNull String permission) {
    170         String packageName = (Binder.getCallingPid() == Process.myPid())
    171                 ? context.getPackageName() : null;
    172         return checkPermission(context, permission, Binder.getCallingPid(),
    173                 Binder.getCallingUid(), packageName);
    174     }
    175 }
    176