1 /*
2  * Copyright (C) 2024 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 com.android.server.pm.permission;
18 
19 import static android.os.Process.INVALID_UID;
20 
21 import android.annotation.NonNull;
22 import android.annotation.Nullable;
23 import android.annotation.UserIdInt;
24 import android.app.AppOpsManager;
25 import android.app.AppOpsManager.AttributionFlags;
26 import android.app.AppOpsManagerInternal.CheckOpsDelegate;
27 import android.app.SyncNotedAppOp;
28 import android.content.AttributionSource;
29 import android.content.pm.PackageManager;
30 import android.content.pm.PackageManagerInternal;
31 import android.os.Binder;
32 import android.os.IBinder;
33 import android.os.Process;
34 import android.os.UserHandle;
35 import android.text.TextUtils;
36 import android.util.ArrayMap;
37 import android.util.SparseArray;
38 
39 import com.android.internal.util.ArrayUtils;
40 import com.android.internal.util.function.DodecFunction;
41 import com.android.internal.util.function.HexConsumer;
42 import com.android.internal.util.function.HexFunction;
43 import com.android.internal.util.function.OctFunction;
44 import com.android.internal.util.function.QuadFunction;
45 import com.android.internal.util.function.TriFunction;
46 import com.android.internal.util.function.UndecFunction;
47 import com.android.server.LocalServices;
48 import com.android.server.pm.permission.PermissionManagerServiceInternal.CheckPermissionDelegate;
49 
50 import java.util.List;
51 import java.util.Map;
52 
53 /**
54  * Interface to intercept incoming parameters and outgoing results to permission and appop checks
55  */
56 public interface AccessCheckDelegate extends CheckPermissionDelegate, CheckOpsDelegate {
57 
58     /**
59      * Assigns the package whose permissions are delegated the state of Shell's permissions.
60      *
61      * @param delegateUid the UID whose permissions are delegated to shell
62      * @param packageName the name of the package whose permissions are delegated to shell
63      * @param permissions the set of permissions to delegate to shell. If null then all
64      *                    permission will be delegated
65      */
setShellPermissionDelegate(int delegateUid, @NonNull String packageName, @Nullable String[] permissions)66     void setShellPermissionDelegate(int delegateUid, @NonNull String packageName,
67             @Nullable String[] permissions);
68 
69     /**
70      * Removes the assigned Shell permission delegate.
71      */
removeShellPermissionDelegate()72     void removeShellPermissionDelegate();
73 
74     /**
75      * @return a list of permissions delegated to Shell's permission state
76      */
77     @NonNull
getDelegatedPermissionNames()78     List<String> getDelegatedPermissionNames();
79 
80     /**
81      * @return whether there exists a Shell permission delegate
82      */
hasShellPermissionDelegate()83     boolean hasShellPermissionDelegate();
84 
85     /**
86      * @param uid the UID to check
87      * @return whether the UID's permissions are delegated to Shell's and the owner of overrides
88      */
isDelegateAndOwnerUid(int uid)89     boolean isDelegateAndOwnerUid(int uid);
90 
91     /**
92      * @param uid the UID to check
93      * @param packageName the package to check
94      * @return whether the UID and package combination's permissions are delegated to Shell's
95      * permissions
96      */
isDelegatePackage(int uid, @NonNull String packageName)97     boolean isDelegatePackage(int uid, @NonNull String packageName);
98 
99     /**
100      * Adds permission to be overridden to the given state.
101      *
102      * @param ownerUid the UID of the app who assigned the permission override
103      * @param uid The UID of the app whose permission will be overridden
104      * @param permission The permission whose state will be overridden
105      * @param result The state to override the permission to
106      */
addOverridePermissionState(int ownerUid, int uid, @NonNull String permission, int result)107     void addOverridePermissionState(int ownerUid, int uid, @NonNull String permission,
108             int result);
109 
110     /**
111      * Removes overridden permission. UiAutomation must be connected to root user.
112      *
113      * @param uid The UID of the app whose permission is overridden
114      * @param permission The permission whose state will no longer be overridden
115      *
116      * @hide
117      */
removeOverridePermissionState(int uid, @NonNull String permission)118     void removeOverridePermissionState(int uid, @NonNull String permission);
119 
120     /**
121      * Clears all overridden permissions for the given UID.
122      *
123      * @param uid The UID of the app whose permissions will no longer be overridden
124      */
clearOverridePermissionStates(int uid)125     void clearOverridePermissionStates(int uid);
126 
127     /**
128      * Clears all overridden permissions on the device.
129      */
clearAllOverridePermissionStates()130     void clearAllOverridePermissionStates();
131 
132     /**
133      * @return whether there exists any permission overrides
134      */
hasOverriddenPermissions()135     boolean hasOverriddenPermissions();
136 
137     /**
138      * @return whether there exists permissions delegated to Shell's permissions or overridden
139      */
hasDelegateOrOverrides()140     boolean hasDelegateOrOverrides();
141 
142     class AccessCheckDelegateImpl implements AccessCheckDelegate {
143         public static final String SHELL_PKG = "com.android.shell";
144         private int mDelegateAndOwnerUid = INVALID_UID;
145         @Nullable
146         private String mDelegatePackage;
147         @Nullable
148         private String[] mDelegatePermissions;
149         boolean mDelegateAllPermissions;
150         @Nullable
151         private SparseArray<ArrayMap<String, Integer>> mOverridePermissionStates;
152 
153         @Override
setShellPermissionDelegate(int uid, @NonNull String packageName, @Nullable String[] permissions)154         public void setShellPermissionDelegate(int uid, @NonNull String packageName,
155                 @Nullable String[] permissions) {
156             mDelegateAndOwnerUid = uid;
157             mDelegatePackage = packageName;
158             mDelegatePermissions = permissions;
159             mDelegateAllPermissions = permissions == null;
160             PackageManager.invalidatePackageInfoCache();
161         }
162 
163         @Override
removeShellPermissionDelegate()164         public void removeShellPermissionDelegate() {
165             mDelegatePackage = null;
166             mDelegatePermissions = null;
167             mDelegateAllPermissions = false;
168             PackageManager.invalidatePackageInfoCache();
169         }
170 
171         @Override
addOverridePermissionState(int ownerUid, int uid, @NonNull String permission, int state)172         public void addOverridePermissionState(int ownerUid, int uid, @NonNull String permission,
173                 int state) {
174             if (mOverridePermissionStates == null) {
175                 mDelegateAndOwnerUid = ownerUid;
176                 mOverridePermissionStates = new SparseArray<>();
177             }
178 
179             int uidIdx = mOverridePermissionStates.indexOfKey(uid);
180             ArrayMap<String, Integer> perUidOverrides;
181             if (uidIdx < 0) {
182                 perUidOverrides = new ArrayMap<>();
183                 mOverridePermissionStates.put(uid, perUidOverrides);
184             } else {
185                 perUidOverrides = mOverridePermissionStates.valueAt(uidIdx);
186             }
187 
188             perUidOverrides.put(permission, state);
189             PackageManager.invalidatePackageInfoCache();
190         }
191 
192         @Override
removeOverridePermissionState(int uid, @NonNull String permission)193         public void removeOverridePermissionState(int uid, @NonNull String permission) {
194             if (mOverridePermissionStates == null) {
195                 return;
196             }
197 
198             ArrayMap<String, Integer> perUidOverrides = mOverridePermissionStates.get(uid);
199 
200             if (perUidOverrides == null) {
201                 return;
202             }
203 
204             perUidOverrides.remove(permission);
205             PackageManager.invalidatePackageInfoCache();
206 
207             if (perUidOverrides.isEmpty()) {
208                 mOverridePermissionStates.remove(uid);
209             }
210             if (mOverridePermissionStates.size() == 0) {
211                 mOverridePermissionStates = null;
212             }
213         }
214 
215         @Override
clearOverridePermissionStates(int uid)216         public void clearOverridePermissionStates(int uid) {
217             if (mOverridePermissionStates == null) {
218                 return;
219             }
220 
221             mOverridePermissionStates.remove(uid);
222             PackageManager.invalidatePackageInfoCache();
223 
224             if (mOverridePermissionStates.size() == 0) {
225                 mOverridePermissionStates = null;
226             }
227         }
228 
229         @Override
clearAllOverridePermissionStates()230         public void clearAllOverridePermissionStates() {
231             mOverridePermissionStates = null;
232             PackageManager.invalidatePackageInfoCache();
233         }
234 
235         @Override
getDelegatedPermissionNames()236         public List<String> getDelegatedPermissionNames() {
237             return mDelegatePermissions == null ? null : List.of(mDelegatePermissions);
238         }
239 
240         @Override
hasShellPermissionDelegate()241         public boolean hasShellPermissionDelegate() {
242             return mDelegateAllPermissions || mDelegatePermissions != null;
243         }
244 
245         @Override
isDelegatePackage(int uid, @NonNull String packageName)246         public boolean isDelegatePackage(int uid, @NonNull String packageName) {
247             return mDelegateAndOwnerUid == uid && TextUtils.equals(mDelegatePackage, packageName);
248         }
249 
250         @Override
hasOverriddenPermissions()251         public boolean hasOverriddenPermissions() {
252             return mOverridePermissionStates != null;
253         }
254 
255         @Override
isDelegateAndOwnerUid(int uid)256         public boolean isDelegateAndOwnerUid(int uid) {
257             return uid == mDelegateAndOwnerUid;
258         }
259 
260         @Override
hasDelegateOrOverrides()261         public boolean hasDelegateOrOverrides() {
262             return hasShellPermissionDelegate() || hasOverriddenPermissions();
263         }
264 
265         @Override
checkPermission(@onNull String packageName, @NonNull String permissionName, @NonNull String persistentDeviceId, @UserIdInt int userId, @NonNull QuadFunction<String, String, String, Integer, Integer> superImpl)266         public int checkPermission(@NonNull String packageName, @NonNull String permissionName,
267                 @NonNull String persistentDeviceId, @UserIdInt int userId,
268                 @NonNull QuadFunction<String, String, String, Integer, Integer> superImpl) {
269             if (TextUtils.equals(mDelegatePackage, packageName) && !SHELL_PKG.equals(packageName)) {
270                 if (isDelegatePermission(permissionName)) {
271                     final long identity = Binder.clearCallingIdentity();
272                     try {
273                         return checkPermission(SHELL_PKG, permissionName, persistentDeviceId,
274                                 userId, superImpl);
275                     } finally {
276                         Binder.restoreCallingIdentity(identity);
277                     }
278                 }
279             }
280             if (mOverridePermissionStates != null) {
281                 int uid = LocalServices.getService(PackageManagerInternal.class)
282                                 .getPackageUid(packageName, 0, userId);
283                 if (uid >= 0) {
284                     Map<String, Integer> permissionGrants = mOverridePermissionStates.get(uid);
285                     if (permissionGrants != null && permissionGrants.containsKey(permissionName)) {
286                         return permissionGrants.get(permissionName);
287                     }
288                 }
289             }
290             return superImpl.apply(packageName, permissionName, persistentDeviceId, userId);
291         }
292 
293         @Override
checkUidPermission(int uid, @NonNull String permissionName, @NonNull String persistentDeviceId, @NonNull TriFunction<Integer, String, String, Integer> superImpl)294         public int checkUidPermission(int uid, @NonNull String permissionName,
295                 @NonNull String persistentDeviceId,
296                 @NonNull TriFunction<Integer, String, String, Integer> superImpl) {
297             if (uid == mDelegateAndOwnerUid && uid != Process.SHELL_UID) {
298                 if (isDelegatePermission(permissionName)) {
299                     final long identity = Binder.clearCallingIdentity();
300                     try {
301                         return checkUidPermission(Process.SHELL_UID, permissionName,
302                                 persistentDeviceId, superImpl);
303                     } finally {
304                         Binder.restoreCallingIdentity(identity);
305                     }
306                 }
307             }
308             if (mOverridePermissionStates != null) {
309                 Map<String, Integer> permissionGrants = mOverridePermissionStates.get(uid);
310                 if (permissionGrants != null && permissionGrants.containsKey(permissionName)) {
311                     return permissionGrants.get(permissionName);
312                 }
313             }
314             return superImpl.apply(uid, permissionName, persistentDeviceId);
315         }
316 
317         @Override
checkOperation(int code, int uid, @Nullable String packageName, @Nullable String attributionTag, int virtualDeviceId, boolean raw, @NonNull HexFunction<Integer, Integer, String, String, Integer, Boolean, Integer> superImpl)318         public int checkOperation(int code, int uid, @Nullable String packageName,
319                 @Nullable String attributionTag, int virtualDeviceId, boolean raw,
320                 @NonNull HexFunction<Integer, Integer, String, String, Integer, Boolean, Integer>
321                         superImpl) {
322             if (uid == mDelegateAndOwnerUid && isDelegateOp(code)) {
323                 final int shellUid = UserHandle.getUid(UserHandle.getUserId(uid),
324                         Process.SHELL_UID);
325                 final long identity = Binder.clearCallingIdentity();
326                 try {
327                     return superImpl.apply(code, shellUid, SHELL_PKG, null, virtualDeviceId, raw);
328                 } finally {
329                     Binder.restoreCallingIdentity(identity);
330                 }
331             }
332             return superImpl.apply(code, uid, packageName, attributionTag, virtualDeviceId, raw);
333         }
334 
335         @Override
checkAudioOperation(int code, int usage, int uid, @Nullable String packageName, @NonNull QuadFunction<Integer, Integer, Integer, String, Integer> superImpl)336         public int checkAudioOperation(int code, int usage, int uid, @Nullable String packageName,
337                 @NonNull QuadFunction<Integer, Integer, Integer, String, Integer> superImpl) {
338             if (uid == mDelegateAndOwnerUid && isDelegateOp(code)) {
339                 final int shellUid = UserHandle.getUid(UserHandle.getUserId(uid),
340                         Process.SHELL_UID);
341                 final long identity = Binder.clearCallingIdentity();
342                 try {
343                     return superImpl.apply(code, usage, shellUid, SHELL_PKG);
344                 } finally {
345                     Binder.restoreCallingIdentity(identity);
346                 }
347             }
348             return superImpl.apply(code, usage, uid, packageName);
349         }
350 
351         @Override
noteOperation(int code, int uid, @Nullable String packageName, @Nullable String featureId, int virtualDeviceId, boolean shouldCollectAsyncNotedOp, @Nullable String message, boolean shouldCollectMessage, @NonNull OctFunction<Integer, Integer, String, String, Integer, Boolean, String, Boolean, SyncNotedAppOp> superImpl)352         public SyncNotedAppOp noteOperation(int code, int uid, @Nullable String packageName,
353                 @Nullable String featureId, int virtualDeviceId, boolean shouldCollectAsyncNotedOp,
354                 @Nullable String message, boolean shouldCollectMessage,
355                 @NonNull OctFunction<Integer, Integer, String, String, Integer, Boolean, String,
356                         Boolean, SyncNotedAppOp> superImpl) {
357             if (uid == mDelegateAndOwnerUid && isDelegateOp(code)) {
358                 final int shellUid = UserHandle.getUid(UserHandle.getUserId(uid),
359                         Process.SHELL_UID);
360                 final long identity = Binder.clearCallingIdentity();
361                 try {
362                     return superImpl.apply(code, shellUid, SHELL_PKG, featureId, virtualDeviceId,
363                             shouldCollectAsyncNotedOp, message, shouldCollectMessage);
364                 } finally {
365                     Binder.restoreCallingIdentity(identity);
366                 }
367             }
368             return superImpl.apply(code, uid, packageName, featureId, virtualDeviceId,
369                     shouldCollectAsyncNotedOp, message, shouldCollectMessage);
370         }
371 
372         @Override
noteProxyOperation(int code, @NonNull AttributionSource attributionSource, boolean shouldCollectAsyncNotedOp, @Nullable String message, boolean shouldCollectMessage, boolean skiProxyOperation, @NonNull HexFunction<Integer, AttributionSource, Boolean, String, Boolean, Boolean, SyncNotedAppOp> superImpl)373         public SyncNotedAppOp noteProxyOperation(int code,
374                 @NonNull AttributionSource attributionSource, boolean shouldCollectAsyncNotedOp,
375                 @Nullable String message, boolean shouldCollectMessage, boolean skiProxyOperation,
376                 @NonNull HexFunction<Integer, AttributionSource, Boolean, String, Boolean,
377                         Boolean, SyncNotedAppOp> superImpl) {
378             if (attributionSource.getUid() == mDelegateAndOwnerUid && isDelegateOp(code)) {
379                 final int shellUid = UserHandle.getUid(
380                         UserHandle.getUserId(attributionSource.getUid()), Process.SHELL_UID);
381                 final long identity = Binder.clearCallingIdentity();
382                 try {
383                     return superImpl.apply(code,
384                             new AttributionSource(shellUid, Process.INVALID_PID, SHELL_PKG,
385                                     attributionSource.getAttributionTag(),
386                                     attributionSource.getToken(), /*renouncedPermissions*/ null,
387                                     attributionSource.getDeviceId(), attributionSource.getNext()),
388                             shouldCollectAsyncNotedOp, message, shouldCollectMessage,
389                             skiProxyOperation);
390                 } finally {
391                     Binder.restoreCallingIdentity(identity);
392                 }
393             }
394             return superImpl.apply(code, attributionSource, shouldCollectAsyncNotedOp,
395                     message, shouldCollectMessage, skiProxyOperation);
396         }
397 
398         @Override
startOperation(@onNull IBinder token, int code, int uid, @Nullable String packageName, @Nullable String attributionTag, int virtualDeviceId, boolean startIfModeDefault, boolean shouldCollectAsyncNotedOp, @Nullable String message, boolean shouldCollectMessage, @AttributionFlags int attributionFlags, int attributionChainId, @NonNull DodecFunction<IBinder, Integer, Integer, String, String, Integer, Boolean, Boolean, String, Boolean, Integer, Integer, SyncNotedAppOp> superImpl)399         public SyncNotedAppOp startOperation(@NonNull IBinder token, int code, int uid,
400                 @Nullable String packageName, @Nullable String attributionTag, int virtualDeviceId,
401                 boolean startIfModeDefault, boolean shouldCollectAsyncNotedOp,
402                 @Nullable String message, boolean shouldCollectMessage,
403                 @AttributionFlags int attributionFlags, int attributionChainId,
404                 @NonNull DodecFunction<IBinder, Integer, Integer, String, String, Integer, Boolean,
405                         Boolean, String, Boolean, Integer, Integer, SyncNotedAppOp> superImpl) {
406             if (uid == mDelegateAndOwnerUid && isDelegateOp(code)) {
407                 final int shellUid = UserHandle.getUid(UserHandle.getUserId(uid),
408                         Process.SHELL_UID);
409                 final long identity = Binder.clearCallingIdentity();
410                 try {
411                     return superImpl.apply(token, code, shellUid, SHELL_PKG, attributionTag,
412                             virtualDeviceId, startIfModeDefault, shouldCollectAsyncNotedOp, message,
413                             shouldCollectMessage, attributionFlags, attributionChainId);
414                 } finally {
415                     Binder.restoreCallingIdentity(identity);
416                 }
417             }
418             return superImpl.apply(token, code, uid, packageName, attributionTag, virtualDeviceId,
419                     startIfModeDefault, shouldCollectAsyncNotedOp, message, shouldCollectMessage,
420                     attributionFlags, attributionChainId);
421         }
422 
423         @Override
startProxyOperation(@onNull IBinder clientId, int code, @NonNull AttributionSource attributionSource, boolean startIfModeDefault, boolean shouldCollectAsyncNotedOp, @Nullable String message, boolean shouldCollectMessage, boolean skipProxyOperation, @AttributionFlags int proxyAttributionFlags, @AttributionFlags int proxiedAttributionFlags, int attributionChainId, @NonNull UndecFunction<IBinder, Integer, AttributionSource, Boolean, Boolean, String, Boolean, Boolean, Integer, Integer, Integer, SyncNotedAppOp> superImpl)424         public SyncNotedAppOp startProxyOperation(@NonNull IBinder clientId, int code,
425                 @NonNull AttributionSource attributionSource, boolean startIfModeDefault,
426                 boolean shouldCollectAsyncNotedOp, @Nullable String message,
427                 boolean shouldCollectMessage, boolean skipProxyOperation,
428                 @AttributionFlags int proxyAttributionFlags,
429                 @AttributionFlags int proxiedAttributionFlags, int attributionChainId,
430                 @NonNull UndecFunction<IBinder, Integer, AttributionSource, Boolean,
431                         Boolean, String, Boolean, Boolean, Integer, Integer, Integer,
432                         SyncNotedAppOp> superImpl) {
433             if (attributionSource.getUid() == mDelegateAndOwnerUid && isDelegateOp(code)) {
434                 final int shellUid = UserHandle.getUid(UserHandle.getUserId(
435                         attributionSource.getUid()), Process.SHELL_UID);
436                 final long identity = Binder.clearCallingIdentity();
437                 try {
438                     return superImpl.apply(clientId, code,
439                             new AttributionSource(shellUid, Process.INVALID_PID, SHELL_PKG,
440                                     attributionSource.getAttributionTag(),
441                                     attributionSource.getToken(), /*renouncedPermissions*/ null,
442                                     attributionSource.getDeviceId(), attributionSource.getNext()),
443                             startIfModeDefault, shouldCollectAsyncNotedOp, message,
444                             shouldCollectMessage, skipProxyOperation, proxyAttributionFlags,
445                             proxiedAttributionFlags, attributionChainId);
446                 } finally {
447                     Binder.restoreCallingIdentity(identity);
448                 }
449             }
450             return superImpl.apply(clientId, code, attributionSource, startIfModeDefault,
451                     shouldCollectAsyncNotedOp, message, shouldCollectMessage, skipProxyOperation,
452                     proxyAttributionFlags, proxiedAttributionFlags, attributionChainId);
453         }
454 
455         @Override
finishProxyOperation(@onNull IBinder clientId, int code, @NonNull AttributionSource attributionSource, boolean skipProxyOperation, @NonNull QuadFunction<IBinder, Integer, AttributionSource, Boolean, Void> superImpl)456         public void finishProxyOperation(@NonNull IBinder clientId, int code,
457                 @NonNull AttributionSource attributionSource, boolean skipProxyOperation,
458                 @NonNull QuadFunction<IBinder, Integer, AttributionSource, Boolean,
459                         Void> superImpl) {
460             if (attributionSource.getUid() == mDelegateAndOwnerUid && isDelegateOp(code)) {
461                 final int shellUid = UserHandle.getUid(UserHandle.getUserId(
462                         attributionSource.getUid()), Process.SHELL_UID);
463                 final long identity = Binder.clearCallingIdentity();
464                 try {
465                     superImpl.apply(clientId, code,
466                             new AttributionSource(shellUid, Process.INVALID_PID, SHELL_PKG,
467                                     attributionSource.getAttributionTag(),
468                                     attributionSource.getToken(), /*renouncedPermissions*/ null,
469                                     attributionSource.getDeviceId(), attributionSource.getNext()),
470                             skipProxyOperation);
471                     return;
472                 } finally {
473                     Binder.restoreCallingIdentity(identity);
474                 }
475             }
476             superImpl.apply(clientId, code, attributionSource, skipProxyOperation);
477         }
478 
479         @Override
finishOperation(IBinder clientId, int code, int uid, String packageName, String attributionTag, int virtualDeviceId, @NonNull HexConsumer<IBinder, Integer, Integer, String, String, Integer> superImpl)480         public void finishOperation(IBinder clientId, int code, int uid, String packageName,
481                 String attributionTag, int virtualDeviceId, @NonNull HexConsumer<IBinder, Integer,
482                                         Integer, String, String, Integer> superImpl) {
483             if (uid == mDelegateAndOwnerUid && isDelegateOp(code)) {
484                 final int shellUid =
485                         UserHandle.getUid(UserHandle.getUserId(uid), Process.SHELL_UID);
486                 final long identity = Binder.clearCallingIdentity();
487                 try {
488                     superImpl.accept(clientId, code, shellUid, SHELL_PKG, attributionTag,
489                             virtualDeviceId);
490                     return;
491                 } finally {
492                     Binder.restoreCallingIdentity(identity);
493                 }
494             }
495             superImpl.accept(clientId, code, uid, packageName, attributionTag,
496                     virtualDeviceId);
497         }
498 
isDelegatePermission(@onNull String permission)499         private boolean isDelegatePermission(@NonNull String permission) {
500             // null permissions means all permissions are delegated
501             return mDelegateAndOwnerUid != INVALID_UID
502                     && (mDelegateAllPermissions
503                     || ArrayUtils.contains(mDelegatePermissions, permission));
504         }
505 
isDelegateOp(int code)506         private boolean isDelegateOp(int code) {
507             if (mDelegateAllPermissions) {
508                 return true;
509             }
510             // no permission for the op means the op is targeted
511             final String permission = AppOpsManager.opToPermission(code);
512             if (permission == null) {
513                 return true;
514             }
515             return isDelegatePermission(permission);
516         }
517     }
518 }
519