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