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)
    112                 != AppOpsManager.MODE_ALLOWED) {
    113             return PERMISSION_DENIED_APP_OP;
    114         }
    115 
    116         return PERMISSION_GRANTED;
    117     }
    118 
    119     /**
    120      * Checks whether your app has a given permission and whether the app op
    121      * that corresponds to this permission is allowed.
    122      *
    123      * @param context Context for accessing resources.
    124      * @param permission The permission to check.
    125      * @return The permission check result which is either {@link #PERMISSION_GRANTED}
    126      *     or {@link #PERMISSION_DENIED} or {@link #PERMISSION_DENIED_APP_OP}.
    127      */
    128     @PermissionResult
    129     public static int checkSelfPermission(@NonNull Context context,
    130             @NonNull String permission) {
    131         return checkPermission(context, permission, Process.myPid(),
    132                 Process.myUid(), context.getPackageName());
    133     }
    134 
    135     /**
    136      * Checks whether the IPC you are handling has a given permission and whether
    137      * the app op that corresponds to this permission is allowed.
    138      *
    139      * @param context Context for accessing resources.
    140      * @param permission The permission to check.
    141      * @param packageName The package name making the IPC. If null the
    142      *     the first package for the calling UID will be used.
    143      * @return The permission check result which is either {@link #PERMISSION_GRANTED}
    144      *     or {@link #PERMISSION_DENIED} or {@link #PERMISSION_DENIED_APP_OP}.
    145      */
    146     @PermissionResult
    147     public static int checkCallingPermission(@NonNull Context context,
    148             @NonNull String permission, @Nullable String packageName) {
    149         if (Binder.getCallingPid() == Process.myPid()) {
    150             return PERMISSION_DENIED;
    151         }
    152         return checkPermission(context, permission, Binder.getCallingPid(),
    153                 Binder.getCallingUid(), packageName);
    154     }
    155 
    156     /**
    157      * Checks whether the IPC you are handling or your app has a given permission
    158      * and whether the app op that corresponds to this permission is allowed.
    159      *
    160      * @param context Context for accessing resources.
    161      * @param permission The permission to check.
    162      * @return The permission check result which is either {@link #PERMISSION_GRANTED}
    163      *     or {@link #PERMISSION_DENIED} or {@link #PERMISSION_DENIED_APP_OP}.
    164      */
    165     @PermissionResult
    166     public static int checkCallingOrSelfPermission(@NonNull Context context,
    167             @NonNull String permission) {
    168         String packageName = (Binder.getCallingPid() == Process.myPid())
    169                 ? context.getPackageName() : null;
    170         return checkPermission(context, permission, Binder.getCallingPid(),
    171                 Binder.getCallingUid(), packageName);
    172     }
    173 }
    174