/* * Copyright (C) 2015 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.support.v4.content; import android.content.Context; import android.content.pm.PackageManager; import android.os.Binder; import android.os.Process; import android.support.annotation.IntDef; import android.support.annotation.NonNull; import android.support.v4.app.AppOpsManagerCompat; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; /** * This class provides permission check APIs that verify both the * permission and the associated app op for this permission if * such is defined. *
* In the new permission model permissions with protection level * dangerous are runtime permissions. For apps targeting {@link android.os.Build.VERSION_CODES#M} * and above the user may not grant such permissions or revoke * them at any time. For apps targeting API lower than {@link android.os.Build.VERSION_CODES#M} * these permissions are always granted as such apps do not expect * permission revocations and would crash. Therefore, when the * user disables a permission for a legacy app in the UI the * platform disables the APIs guarded by this permission making * them a no-op which is doing nothing or returning an empty * result or default error. *
** It is important that when you perform an operation on behalf of * another app you use these APIs to check for permissions as the * app may be a legacy app that does not participate in the new * permission model for which the user had disabled the "permission" * which is achieved by disallowing the corresponding app op. *
*/ public final class PermissionChecker { /** Permission result: The permission is granted. */ public static final int PERMISSION_GRANTED = PackageManager.PERMISSION_GRANTED; /** Permission result: The permission is denied. */ public static final int PERMISSION_DENIED = PackageManager.PERMISSION_DENIED; /** Permission result: The permission is denied because the app op is not allowed. */ public static final int PERMISSION_DENIED_APP_OP = PackageManager.PERMISSION_DENIED - 1; @IntDef({PERMISSION_GRANTED, PERMISSION_DENIED, PERMISSION_DENIED_APP_OP}) @Retention(RetentionPolicy.SOURCE) public @interface PermissionResult {} private PermissionChecker() { /* do nothing */ } /** * Checks whether a given package in a UID and PID has a given permission * and whether the app op that corresponds to this permission is allowed. * * @param context Context for accessing resources. * @param permission The permission to check. * @param pid The process id for which to check. * @param uid The uid for which to check. * @param packageName The package name for which to check. If null the * the first package for the calling UID will be used. * @return The permission check result which is either {@link #PERMISSION_GRANTED} * or {@link #PERMISSION_DENIED} or {@link #PERMISSION_DENIED_APP_OP}. */ public static int checkPermission(@NonNull Context context, @NonNull String permission, int pid, int uid, String packageName) { if (context.checkPermission(permission, pid, uid) == PackageManager.PERMISSION_DENIED) { return PERMISSION_DENIED; } String op = AppOpsManagerCompat.permissionToOp(permission); if (op == null) { return PERMISSION_GRANTED; } if (packageName == null) { String[] packageNames = context.getPackageManager().getPackagesForUid(uid); if (packageNames == null || packageNames.length <= 0) { return PERMISSION_DENIED; } packageName = packageNames[0]; } if (AppOpsManagerCompat.noteProxyOp(context, op, packageName) != AppOpsManagerCompat.MODE_ALLOWED) { return PERMISSION_DENIED_APP_OP; } return PERMISSION_GRANTED; } /** * Checks whether your app has a given permission and whether the app op * that corresponds to this permission is allowed. * * @param context Context for accessing resources. * @param permission The permission to check. * @return The permission check result which is either {@link #PERMISSION_GRANTED} * or {@link #PERMISSION_DENIED} or {@link #PERMISSION_DENIED_APP_OP}. */ public static int checkSelfPermission(@NonNull Context context, @NonNull String permission) { return checkPermission(context, permission, android.os.Process.myPid(), android.os.Process.myUid(), context.getPackageName()); } /** * Checks whether the IPC you are handling has a given permission and whether * the app op that corresponds to this permission is allowed. * * @param context Context for accessing resources. * @param permission The permission to check. * @param packageName The package name making the IPC. If null the * the first package for the calling UID will be used. * @return The permission check result which is either {@link #PERMISSION_GRANTED} * or {@link #PERMISSION_DENIED} or {@link #PERMISSION_DENIED_APP_OP}. */ public static int checkCallingPermission(@NonNull Context context, @NonNull String permission, String packageName) { if (Binder.getCallingPid() == Process.myPid()) { return PackageManager.PERMISSION_DENIED; } return checkPermission(context, permission, Binder.getCallingPid(), Binder.getCallingUid(), packageName); } /** * Checks whether the IPC you are handling or your app has a given permission * and whether the app op that corresponds to this permission is allowed. * * @param context Context for accessing resources. * @param permission The permission to check. * @return The permission check result which is either {@link #PERMISSION_GRANTED} * or {@link #PERMISSION_DENIED} or {@link #PERMISSION_DENIED_APP_OP}. */ public static int checkCallingOrSelfPermission(@NonNull Context context, @NonNull String permission) { String packageName = (Binder.getCallingPid() == Process.myPid()) ? context.getPackageName() : null; return checkPermission(context, permission, Binder.getCallingPid(), Binder.getCallingUid(), packageName); } }