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