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.permission;
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.AttributionSourceState;
24 import android.content.Context;
25 import android.content.pm.PackageManager;
26 import android.os.RemoteException;
27 import android.os.ServiceManager;
28 
29 import java.lang.annotation.Retention;
30 import java.lang.annotation.RetentionPolicy;
31 import java.util.Objects;
32 
33 /**
34  * Manager for checking runtime and app op permissions. This is a temporary
35  * class and we may fold its function in the PermissionManager once the
36  * permission re-architecture starts falling into place. The main benefit
37  * of this class is to allow context level caching.
38  *
39  * @hide
40  */
41 public class PermissionCheckerManager {
42 
43     /**
44      * The permission is granted.
45      */
46     public static final int PERMISSION_GRANTED = IPermissionChecker.PERMISSION_GRANTED;
47 
48     /**
49      * The permission is denied. Applicable only to runtime and app op permissions.
50      *
51      * <p>Returned when:
52      * <ul>
53      *   <li>the runtime permission is granted, but the corresponding app op is denied
54      *       for runtime permissions.</li>
55      *   <li>the app ops is ignored for app op permissions.</li>
56      * </ul>
57      */
58     public static final int PERMISSION_SOFT_DENIED = IPermissionChecker.PERMISSION_SOFT_DENIED;
59 
60     /**
61      * The permission is denied.
62      *
63      * <p>Returned when:
64      * <ul>
65      *   <li>the permission is denied for non app op permissions.</li>
66      *   <li>the app op is denied or app op is {@link AppOpsManager#MODE_DEFAULT}
67      *   and permission is denied.</li>
68      * </ul>
69      */
70     public static final int PERMISSION_HARD_DENIED = IPermissionChecker.PERMISSION_HARD_DENIED;
71 
72     /** @hide */
73     @IntDef({PERMISSION_GRANTED,
74             PERMISSION_SOFT_DENIED,
75             PERMISSION_HARD_DENIED})
76     @Retention(RetentionPolicy.SOURCE)
77     public @interface PermissionResult {}
78 
79     @NonNull
80     private final Context mContext;
81 
82     @NonNull
83     private final IPermissionChecker mService;
84 
85     @NonNull
86     private final PackageManager mPackageManager;
87 
PermissionCheckerManager(@onNull Context context)88     public PermissionCheckerManager(@NonNull Context context)
89             throws ServiceManager.ServiceNotFoundException {
90         mContext = context;
91         mService = IPermissionChecker.Stub.asInterface(ServiceManager.getServiceOrThrow(
92                 Context.PERMISSION_CHECKER_SERVICE));
93         mPackageManager = context.getPackageManager();
94     }
95 
96     /**
97      * Checks a permission by validating the entire attribution source chain. If the
98      * permission is associated with an app op the op is also noted/started for the
99      * entire attribution chain.
100      *
101      * @param permission The permission
102      * @param attributionSource The attribution chain to check.
103      * @param message Message associated with the permission if permission has an app op
104      * @param forDataDelivery Whether the check is for delivering data if permission has an app op
105      * @param startDataDelivery Whether to start data delivery (start op) if permission has
106      *     an app op
107      * @param fromDatasource Whether the check is by a datasource (skip checks for the
108      *     first attribution source in the chain as this is the datasource)
109      * @param attributedOp Alternative app op to attribute
110      * @return The permission check result.
111      */
112     @PermissionResult
checkPermission(@onNull String permission, @NonNull AttributionSourceState attributionSource, @Nullable String message, boolean forDataDelivery, boolean startDataDelivery, boolean fromDatasource, int attributedOp)113     public int checkPermission(@NonNull String permission,
114             @NonNull AttributionSourceState attributionSource, @Nullable String message,
115             boolean forDataDelivery, boolean startDataDelivery, boolean fromDatasource,
116             int attributedOp) {
117         Objects.requireNonNull(permission);
118         Objects.requireNonNull(attributionSource);
119         // Fast path for non-runtime, non-op permissions where the attribution chain has
120         // length one. This is the majority of the cases and we want these to be fast by
121         // hitting the local in process permission cache.
122         if (AppOpsManager.permissionToOpCode(permission) == AppOpsManager.OP_NONE) {
123             if (fromDatasource) {
124                 if (attributionSource.next != null && attributionSource.next.length > 0) {
125                     return mContext.checkPermission(permission, attributionSource.next[0].pid,
126                             attributionSource.next[0].uid) == PackageManager.PERMISSION_GRANTED
127                             ? PERMISSION_GRANTED : PERMISSION_HARD_DENIED;
128                 }
129             } else {
130                 return (mContext.checkPermission(permission, attributionSource.pid,
131                             attributionSource.uid) == PackageManager.PERMISSION_GRANTED)
132                         ? PERMISSION_GRANTED : PERMISSION_HARD_DENIED;
133             }
134         }
135         try {
136             return mService.checkPermission(permission, attributionSource, message, forDataDelivery,
137                     startDataDelivery, fromDatasource, attributedOp);
138         } catch (RemoteException e) {
139             e.rethrowFromSystemServer();
140         }
141         return PERMISSION_HARD_DENIED;
142     }
143 
144     /**
145      * Finishes an app op by validating the entire attribution source chain.
146      *
147      * @param op The op to finish.
148      * @param attributionSource The attribution chain to finish.
149      * @param fromDatasource Whether the finish is by a datasource (skip finish for the
150      *     first attribution source in the chain as this is the datasource)
151      */
finishDataDelivery(int op, @NonNull AttributionSourceState attributionSource, boolean fromDatasource)152     public void finishDataDelivery(int op, @NonNull AttributionSourceState attributionSource,
153             boolean fromDatasource) {
154         Objects.requireNonNull(attributionSource);
155         try {
156             mService.finishDataDelivery(op, attributionSource, fromDatasource);
157         } catch (RemoteException e) {
158             e.rethrowFromSystemServer();
159         }
160     }
161 
162     /**
163      * Checks an app op by validating the entire attribution source chain. The op is
164      * also noted/started for the entire attribution chain.
165      *
166      * @param op The op to check.
167      * @param attributionSource The attribution chain to check.
168      * @param message Message associated with the permission if permission has an app op
169      * @param forDataDelivery Whether the check is for delivering data if permission has an app op
170      * @param startDataDelivery Whether to start data delivery (start op) if permission has
171      *     an app op
172      * @return The op check result.
173      */
174     @PermissionResult
checkOp(int op, @NonNull AttributionSourceState attributionSource, @Nullable String message, boolean forDataDelivery, boolean startDataDelivery)175     public int checkOp(int op, @NonNull AttributionSourceState attributionSource,
176             @Nullable String message, boolean forDataDelivery, boolean startDataDelivery) {
177         Objects.requireNonNull(attributionSource);
178         try {
179             return mService.checkOp(op, attributionSource, message, forDataDelivery,
180                     startDataDelivery);
181         } catch (RemoteException e) {
182             e.rethrowFromSystemServer();
183         }
184         return PERMISSION_HARD_DENIED;
185     }
186 }
187