/* * Copyright (C) 2018 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.permission; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.AppOpsManager; import android.content.AttributionSourceState; import android.content.Context; import android.content.pm.PackageManager; import android.os.RemoteException; import android.os.ServiceManager; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Objects; /** * Manager for checking runtime and app op permissions. This is a temporary * class and we may fold its function in the PermissionManager once the * permission re-architecture starts falling into place. The main benefit * of this class is to allow context level caching. * * @hide */ public class PermissionCheckerManager { /** * The permission is granted. */ public static final int PERMISSION_GRANTED = IPermissionChecker.PERMISSION_GRANTED; /** * The permission is denied. Applicable only to runtime and app op permissions. * *

Returned when: *

*/ public static final int PERMISSION_SOFT_DENIED = IPermissionChecker.PERMISSION_SOFT_DENIED; /** * The permission is denied. * *

Returned when: *

*/ public static final int PERMISSION_HARD_DENIED = IPermissionChecker.PERMISSION_HARD_DENIED; /** @hide */ @IntDef({PERMISSION_GRANTED, PERMISSION_SOFT_DENIED, PERMISSION_HARD_DENIED}) @Retention(RetentionPolicy.SOURCE) public @interface PermissionResult {} @NonNull private final Context mContext; @NonNull private final IPermissionChecker mService; @NonNull private final PackageManager mPackageManager; public PermissionCheckerManager(@NonNull Context context) throws ServiceManager.ServiceNotFoundException { mContext = context; mService = IPermissionChecker.Stub.asInterface(ServiceManager.getServiceOrThrow( Context.PERMISSION_CHECKER_SERVICE)); mPackageManager = context.getPackageManager(); } /** * Checks a permission by validating the entire attribution source chain. If the * permission is associated with an app op the op is also noted/started for the * entire attribution chain. * * @param permission The permission * @param attributionSource The attribution chain to check. * @param message Message associated with the permission if permission has an app op * @param forDataDelivery Whether the check is for delivering data if permission has an app op * @param startDataDelivery Whether to start data delivery (start op) if permission has * an app op * @param fromDatasource Whether the check is by a datasource (skip checks for the * first attribution source in the chain as this is the datasource) * @param attributedOp Alternative app op to attribute * @return The permission check result. */ @PermissionResult public int checkPermission(@NonNull String permission, @NonNull AttributionSourceState attributionSource, @Nullable String message, boolean forDataDelivery, boolean startDataDelivery, boolean fromDatasource, int attributedOp) { Objects.requireNonNull(permission); Objects.requireNonNull(attributionSource); // Fast path for non-runtime, non-op permissions where the attribution chain has // length one. This is the majority of the cases and we want these to be fast by // hitting the local in process permission cache. if (AppOpsManager.permissionToOpCode(permission) == AppOpsManager.OP_NONE) { if (fromDatasource) { if (attributionSource.next != null && attributionSource.next.length > 0) { return mContext.checkPermission(permission, attributionSource.next[0].pid, attributionSource.next[0].uid) == PackageManager.PERMISSION_GRANTED ? PERMISSION_GRANTED : PERMISSION_HARD_DENIED; } } else { return (mContext.checkPermission(permission, attributionSource.pid, attributionSource.uid) == PackageManager.PERMISSION_GRANTED) ? PERMISSION_GRANTED : PERMISSION_HARD_DENIED; } } try { return mService.checkPermission(permission, attributionSource, message, forDataDelivery, startDataDelivery, fromDatasource, attributedOp); } catch (RemoteException e) { e.rethrowFromSystemServer(); } return PERMISSION_HARD_DENIED; } /** * Finishes an app op by validating the entire attribution source chain. * * @param op The op to finish. * @param attributionSource The attribution chain to finish. * @param fromDatasource Whether the finish is by a datasource (skip finish for the * first attribution source in the chain as this is the datasource) */ public void finishDataDelivery(int op, @NonNull AttributionSourceState attributionSource, boolean fromDatasource) { Objects.requireNonNull(attributionSource); try { mService.finishDataDelivery(op, attributionSource, fromDatasource); } catch (RemoteException e) { e.rethrowFromSystemServer(); } } /** * Checks an app op by validating the entire attribution source chain. The op is * also noted/started for the entire attribution chain. * * @param op The op to check. * @param attributionSource The attribution chain to check. * @param message Message associated with the permission if permission has an app op * @param forDataDelivery Whether the check is for delivering data if permission has an app op * @param startDataDelivery Whether to start data delivery (start op) if permission has * an app op * @return The op check result. */ @PermissionResult public int checkOp(int op, @NonNull AttributionSourceState attributionSource, @Nullable String message, boolean forDataDelivery, boolean startDataDelivery) { Objects.requireNonNull(attributionSource); try { return mService.checkOp(op, attributionSource, message, forDataDelivery, startDataDelivery); } catch (RemoteException e) { e.rethrowFromSystemServer(); } return PERMISSION_HARD_DENIED; } }