1 /*
2  * Copyright (C) 2019 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.policy;
18 
19 import static android.app.AppOpsManager.MODE_ALLOWED;
20 import static android.app.AppOpsManager.MODE_FOREGROUND;
21 import static android.app.AppOpsManager.MODE_IGNORED;
22 import static android.app.AppOpsManager.OP_NONE;
23 import static android.content.pm.PackageManager.FLAG_PERMISSION_APPLY_RESTRICTION;
24 import static android.content.pm.PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED;
25 import static android.content.pm.PackageManager.FLAG_PERMISSION_REVOKED_COMPAT;
26 import static android.content.pm.PackageManager.GET_PERMISSIONS;
27 
28 import android.annotation.NonNull;
29 import android.annotation.Nullable;
30 import android.annotation.UserIdInt;
31 import android.app.AppOpsManager;
32 import android.app.AppOpsManagerInternal;
33 import android.content.BroadcastReceiver;
34 import android.content.ContentResolver;
35 import android.content.Context;
36 import android.content.Intent;
37 import android.content.IntentFilter;
38 import android.content.pm.ApplicationInfo;
39 import android.content.pm.PackageInfo;
40 import android.content.pm.PackageManager;
41 import android.content.pm.PackageManager.NameNotFoundException;
42 import android.content.pm.PackageManagerInternal;
43 import android.content.pm.PackageManagerInternal.PackageListObserver;
44 import android.content.pm.PermissionInfo;
45 import android.os.Build;
46 import android.os.Process;
47 import android.os.RemoteException;
48 import android.os.ServiceManager;
49 import android.os.UserHandle;
50 import android.permission.PermissionControllerManager;
51 import android.provider.Settings;
52 import android.provider.Telephony;
53 import android.telecom.TelecomManager;
54 import android.util.ArrayMap;
55 import android.util.ArraySet;
56 import android.util.LongSparseLongArray;
57 import android.util.Pair;
58 import android.util.Slog;
59 import android.util.SparseBooleanArray;
60 
61 import com.android.internal.annotations.GuardedBy;
62 import com.android.internal.app.IAppOpsCallback;
63 import com.android.internal.app.IAppOpsService;
64 import com.android.internal.infra.AndroidFuture;
65 import com.android.internal.util.IntPair;
66 import com.android.internal.util.function.pooled.PooledLambda;
67 import com.android.server.FgThread;
68 import com.android.server.LocalServices;
69 import com.android.server.SystemService;
70 import com.android.server.pm.UserManagerInternal;
71 import com.android.server.pm.parsing.pkg.AndroidPackage;
72 import com.android.server.pm.permission.PermissionManagerServiceInternal;
73 import com.android.server.policy.PermissionPolicyInternal.OnInitializedCallback;
74 
75 import java.util.ArrayList;
76 import java.util.Collections;
77 import java.util.HashMap;
78 import java.util.List;
79 import java.util.Map;
80 import java.util.concurrent.ExecutionException;
81 
82 /**
83  * This is a permission policy that governs over all permission mechanism
84  * such as permissions, app ops, etc. For example, the policy ensures that
85  * permission state and app ops is synchronized for cases where there is a
86  * dependency between permission state (permissions or permission flags)
87  * and app ops - and vise versa.
88  */
89 public final class PermissionPolicyService extends SystemService {
90     private static final String LOG_TAG = PermissionPolicyService.class.getSimpleName();
91     private static final boolean DEBUG = false;
92     private static final long USER_SENSITIVE_UPDATE_DELAY_MS = 60000;
93 
94     private final Object mLock = new Object();
95 
96     private IAppOpsCallback mAppOpsCallback;
97 
98     /** Whether the user is started but not yet stopped */
99     @GuardedBy("mLock")
100     private final SparseBooleanArray mIsStarted = new SparseBooleanArray();
101 
102     /** Callbacks for when a user is initialized */
103     @GuardedBy("mLock")
104     private OnInitializedCallback mOnInitializedCallback;
105 
106     /**
107      * Whether an async {@link #synchronizePackagePermissionsAndAppOpsForUser} is currently
108      * scheduled for a package/user.
109      */
110     @GuardedBy("mLock")
111     private final ArraySet<Pair<String, Integer>> mIsPackageSyncsScheduled = new ArraySet<>();
112 
113     /**
114      * Whether an async {@link #resetAppOpPermissionsIfNotRequestedForUid} is currently
115      * scheduled for a uid.
116      */
117     @GuardedBy("mLock")
118     private final SparseBooleanArray mIsUidSyncScheduled = new SparseBooleanArray();
119 
120     private List<String> mAppOpPermissions;
121 
PermissionPolicyService(@onNull Context context)122     public PermissionPolicyService(@NonNull Context context) {
123         super(context);
124 
125         LocalServices.addService(PermissionPolicyInternal.class, new Internal());
126     }
127 
128     @Override
onStart()129     public void onStart() {
130         final PackageManagerInternal packageManagerInternal = LocalServices.getService(
131                 PackageManagerInternal.class);
132         final PermissionManagerServiceInternal permissionManagerInternal = LocalServices.getService(
133                 PermissionManagerServiceInternal.class);
134         final IAppOpsService appOpsService = IAppOpsService.Stub.asInterface(
135                 ServiceManager.getService(Context.APP_OPS_SERVICE));
136 
137         packageManagerInternal.getPackageList(new PackageListObserver() {
138             @Override
139             public void onPackageAdded(String packageName, int uid) {
140                 final int userId = UserHandle.getUserId(uid);
141                 if (isStarted(userId)) {
142                     synchronizePackagePermissionsAndAppOpsForUser(packageName, userId);
143                 }
144             }
145 
146             @Override
147             public void onPackageChanged(String packageName, int uid) {
148                 final int userId = UserHandle.getUserId(uid);
149                 if (isStarted(userId)) {
150                     synchronizePackagePermissionsAndAppOpsForUser(packageName, userId);
151                     resetAppOpPermissionsIfNotRequestedForUid(uid);
152                 }
153             }
154 
155             @Override
156             public void onPackageRemoved(String packageName, int uid) {
157                 final int userId = UserHandle.getUserId(uid);
158                 if (isStarted(userId)) {
159                     resetAppOpPermissionsIfNotRequestedForUid(uid);
160                 }
161             }
162         });
163 
164         permissionManagerInternal.addOnRuntimePermissionStateChangedListener(
165                 this::synchronizePackagePermissionsAndAppOpsAsyncForUser);
166 
167         mAppOpsCallback = new IAppOpsCallback.Stub() {
168             public void opChanged(int op, int uid, String packageName) {
169                 synchronizePackagePermissionsAndAppOpsAsyncForUser(packageName,
170                         UserHandle.getUserId(uid));
171                 resetAppOpPermissionsIfNotRequestedForUidAsync(uid);
172             }
173         };
174 
175         final ArrayList<PermissionInfo> dangerousPerms =
176                 permissionManagerInternal.getAllPermissionsWithProtection(
177                         PermissionInfo.PROTECTION_DANGEROUS);
178         try {
179             int numDangerousPerms = dangerousPerms.size();
180             for (int i = 0; i < numDangerousPerms; i++) {
181                 PermissionInfo perm = dangerousPerms.get(i);
182 
183                 if (perm.isRuntime()) {
184                     appOpsService.startWatchingMode(getSwitchOp(perm.name), null, mAppOpsCallback);
185                 }
186                 if (perm.isSoftRestricted()) {
187                     SoftRestrictedPermissionPolicy policy =
188                             SoftRestrictedPermissionPolicy.forPermission(null, null, null,
189                                     null, perm.name);
190                     int extraAppOp = policy.getExtraAppOpCode();
191                     if (extraAppOp != OP_NONE) {
192                         appOpsService.startWatchingMode(extraAppOp, null, mAppOpsCallback);
193                     }
194                 }
195             }
196         } catch (RemoteException doesNotHappen) {
197             Slog.wtf(LOG_TAG, "Cannot set up app-ops listener");
198         }
199 
200         final List<PermissionInfo> appOpPermissionInfos =
201                 permissionManagerInternal.getAllPermissionsWithProtectionFlags(
202                         PermissionInfo.PROTECTION_FLAG_APPOP);
203         mAppOpPermissions = new ArrayList<>();
204         final int appOpPermissionInfosSize = appOpPermissionInfos.size();
205         for (int i = 0; i < appOpPermissionInfosSize; i++) {
206             final PermissionInfo appOpPermissionInfo = appOpPermissionInfos.get(i);
207 
208             switch (appOpPermissionInfo.name) {
209                 case android.Manifest.permission.ACCESS_NOTIFICATIONS:
210                 case android.Manifest.permission.MANAGE_IPSEC_TUNNELS:
211                     continue;
212                 case android.Manifest.permission.REQUEST_INSTALL_PACKAGES:
213                     // Settings allows the user to control the app op if it's not in the default
214                     // mode, regardless of whether the app has requested the permission, so we
215                     // should not reset it.
216                     continue;
217                 default:
218                     final int appOpCode = AppOpsManager.permissionToOpCode(
219                             appOpPermissionInfo.name);
220                     if (appOpCode != OP_NONE) {
221                         mAppOpPermissions.add(appOpPermissionInfo.name);
222                         try {
223                             appOpsService.startWatchingMode(appOpCode, null, mAppOpsCallback);
224                         } catch (RemoteException e) {
225                             Slog.wtf(LOG_TAG, "Cannot set up app-ops listener", e);
226                         }
227                     }
228             }
229         }
230 
231         IntentFilter intentFilter = new IntentFilter();
232         intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
233         intentFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
234         intentFilter.addDataScheme("package");
235 
236         getContext().registerReceiverAsUser(new BroadcastReceiver() {
237             final List<Integer> mUserSetupUids = new ArrayList<>(200);
238             final Map<UserHandle, PermissionControllerManager> mPermControllerManagers =
239                     new HashMap<>();
240 
241             @Override
242             public void onReceive(Context context, Intent intent) {
243                 boolean hasSetupRun = true;
244                 try {
245                     final ContentResolver cr = getContext().getContentResolver();
246                     hasSetupRun = Settings.Secure.getIntForUser(cr,
247                             Settings.Secure.USER_SETUP_COMPLETE, cr.getUserId()) != 0;
248                 } catch (Settings.SettingNotFoundException e) {
249                     // Ignore error, assume setup has run
250                 }
251                 int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
252                 // If there is no valid package for the given UID, return immediately
253                 if (packageManagerInternal.getPackage(uid) == null) {
254                     return;
255                 }
256 
257                 if (hasSetupRun) {
258                     if (!mUserSetupUids.isEmpty()) {
259                         synchronized (mUserSetupUids) {
260                             for (int i = mUserSetupUids.size() - 1; i >= 0; i--) {
261                                 updateUid(mUserSetupUids.get(i));
262                             }
263                             mUserSetupUids.clear();
264                         }
265                     }
266                     updateUid(uid);
267                 } else {
268                     synchronized (mUserSetupUids) {
269                         if (!mUserSetupUids.contains(uid)) {
270                             mUserSetupUids.add(uid);
271                         }
272                     }
273                 }
274             }
275 
276             private void updateUid(int uid) {
277                 UserHandle user = UserHandle.getUserHandleForUid(uid);
278                 PermissionControllerManager manager = mPermControllerManagers.get(user);
279                 if (manager == null) {
280                     manager = new PermissionControllerManager(
281                             getUserContext(getContext(), user), FgThread.getHandler());
282                     mPermControllerManagers.put(user, manager);
283                 }
284                 manager.updateUserSensitiveForApp(uid);
285             }
286         }, UserHandle.ALL, intentFilter, null, null);
287 
288         PermissionControllerManager manager = new PermissionControllerManager(
289                 getUserContext(getContext(), Process.myUserHandle()), FgThread.getHandler());
290         FgThread.getHandler().postDelayed(manager::updateUserSensitive,
291                 USER_SENSITIVE_UPDATE_DELAY_MS);
292     }
293 
294     /**
295      * Get op that controls the access related to the permission.
296      *
297      * <p>Usually the permission-op relationship is 1:1 but some permissions (e.g. fine location)
298      * {@link AppOpsManager#sOpToSwitch share an op} to control the access.
299      *
300      * @param permission The permission
301      * @return The op that controls the access of the permission
302      */
getSwitchOp(@onNull String permission)303     private static int getSwitchOp(@NonNull String permission) {
304         int op = AppOpsManager.permissionToOpCode(permission);
305         if (op == OP_NONE) {
306             return OP_NONE;
307         }
308 
309         return AppOpsManager.opToSwitch(op);
310     }
311 
synchronizePackagePermissionsAndAppOpsAsyncForUser(@onNull String packageName, @UserIdInt int changedUserId)312     private void synchronizePackagePermissionsAndAppOpsAsyncForUser(@NonNull String packageName,
313             @UserIdInt int changedUserId) {
314         if (isStarted(changedUserId)) {
315             synchronized (mLock) {
316                 if (mIsPackageSyncsScheduled.add(new Pair<>(packageName, changedUserId))) {
317                     FgThread.getHandler().sendMessage(PooledLambda.obtainMessage(
318                             PermissionPolicyService
319                                     ::synchronizePackagePermissionsAndAppOpsForUser,
320                             this, packageName, changedUserId));
321                 } else {
322                     if (DEBUG) {
323                         Slog.v(LOG_TAG, "sync for " + packageName + "/" + changedUserId
324                                 + " already scheduled");
325                     }
326                 }
327             }
328         }
329     }
330 
331     @Override
onBootPhase(int phase)332     public void onBootPhase(int phase) {
333         if (DEBUG) Slog.i(LOG_TAG, "onBootPhase(" + phase + ")");
334 
335         if (phase == PHASE_ACTIVITY_MANAGER_READY) {
336             final UserManagerInternal um = LocalServices.getService(UserManagerInternal.class);
337 
338             // For some users we might not receive a onStartUser, hence force one here
339             for (int userId : um.getUserIds()) {
340                 if (um.isUserRunning(userId)) {
341                     onStartUser(userId);
342                 }
343             }
344         }
345     }
346 
347     /**
348      * @return Whether the user is started but not yet stopped
349      */
isStarted(@serIdInt int userId)350     private boolean isStarted(@UserIdInt int userId) {
351         synchronized (mLock) {
352             return mIsStarted.get(userId);
353         }
354     }
355 
356     @Override
onUserStarting(@onNull TargetUser user)357     public void onUserStarting(@NonNull TargetUser user) {
358         onStartUser(user.getUserIdentifier());
359     }
360 
onStartUser(@serIdInt int userId)361     private void onStartUser(@UserIdInt int userId) {
362         if (DEBUG) Slog.i(LOG_TAG, "onStartUser(" + userId + ")");
363 
364         if (isStarted(userId)) {
365             return;
366         }
367 
368         grantOrUpgradeDefaultRuntimePermissionsIfNeeded(userId);
369 
370         final OnInitializedCallback callback;
371 
372         synchronized (mLock) {
373             mIsStarted.put(userId, true);
374             callback = mOnInitializedCallback;
375         }
376 
377         // Force synchronization as permissions might have changed
378         synchronizePermissionsAndAppOpsForUser(userId);
379 
380         // Tell observers we are initialized for this user.
381         if (callback != null) {
382             callback.onInitialized(userId);
383         }
384     }
385 
386     @Override
onUserStopping(@onNull TargetUser user)387     public void onUserStopping(@NonNull TargetUser user) {
388         if (DEBUG) Slog.i(LOG_TAG, "onStopUser(" + user + ")");
389 
390         synchronized (mLock) {
391             mIsStarted.delete(user.getUserIdentifier());
392         }
393     }
394 
grantOrUpgradeDefaultRuntimePermissionsIfNeeded(@serIdInt int userId)395     private void grantOrUpgradeDefaultRuntimePermissionsIfNeeded(@UserIdInt int userId) {
396         if (DEBUG) Slog.i(LOG_TAG, "grantOrUpgradeDefaultPermsIfNeeded(" + userId + ")");
397 
398         final PackageManagerInternal packageManagerInternal =
399                 LocalServices.getService(PackageManagerInternal.class);
400         final PermissionManagerServiceInternal permissionManagerInternal =
401                 LocalServices.getService(PermissionManagerServiceInternal.class);
402         if (packageManagerInternal.isPermissionUpgradeNeeded(userId)) {
403             if (DEBUG) Slog.i(LOG_TAG, "defaultPermsWereGrantedSinceBoot(" + userId + ")");
404 
405             // Now call into the permission controller to apply policy around permissions
406             final AndroidFuture<Boolean> future = new AndroidFuture<>();
407 
408             // We need to create a local manager that does not schedule work on the main
409             // there as we are on the main thread and want to block until the work is
410             // completed or we time out.
411             final PermissionControllerManager permissionControllerManager =
412                     new PermissionControllerManager(
413                             getUserContext(getContext(), UserHandle.of(userId)),
414                             FgThread.getHandler());
415             permissionControllerManager.grantOrUpgradeDefaultRuntimePermissions(
416                     FgThread.getExecutor(), successful -> {
417                         if (successful) {
418                             future.complete(null);
419                         } else {
420                             // We are in an undefined state now, let us crash and have
421                             // rescue party suggest a wipe to recover to a good one.
422                             final String message = "Error granting/upgrading runtime permissions"
423                                     + " for user " + userId;
424                             Slog.wtf(LOG_TAG, message);
425                             future.completeExceptionally(new IllegalStateException(message));
426                         }
427                     });
428             try {
429                 future.get();
430             } catch (InterruptedException | ExecutionException e) {
431                 throw new IllegalStateException(e);
432             }
433 
434             permissionControllerManager.updateUserSensitive();
435 
436             packageManagerInternal.updateRuntimePermissionsFingerprint(userId);
437         }
438     }
439 
getUserContext(@onNull Context context, @Nullable UserHandle user)440     private static @Nullable Context getUserContext(@NonNull Context context,
441             @Nullable UserHandle user) {
442         if (context.getUser().equals(user)) {
443             return context;
444         } else {
445             try {
446                 return context.createPackageContextAsUser(context.getPackageName(), 0, user);
447             } catch (NameNotFoundException e) {
448                 Slog.e(LOG_TAG, "Cannot create context for user " + user, e);
449                 return null;
450             }
451         }
452     }
453 
454     /**
455      * Synchronize a single package.
456      */
synchronizePackagePermissionsAndAppOpsForUser(@onNull String packageName, @UserIdInt int userId)457     private void synchronizePackagePermissionsAndAppOpsForUser(@NonNull String packageName,
458             @UserIdInt int userId) {
459         synchronized (mLock) {
460             mIsPackageSyncsScheduled.remove(new Pair<>(packageName, userId));
461         }
462 
463         if (DEBUG) {
464             Slog.v(LOG_TAG,
465                     "synchronizePackagePermissionsAndAppOpsForUser(" + packageName + ", "
466                             + userId + ")");
467         }
468 
469         final PackageManagerInternal packageManagerInternal = LocalServices.getService(
470                 PackageManagerInternal.class);
471         final PackageInfo pkg = packageManagerInternal.getPackageInfo(packageName, 0,
472                 Process.SYSTEM_UID, userId);
473         if (pkg == null) {
474             return;
475         }
476         final PermissionToOpSynchroniser synchroniser = new PermissionToOpSynchroniser(
477                 getUserContext(getContext(), UserHandle.of(userId)));
478         synchroniser.addPackage(pkg.packageName);
479         final String[] sharedPkgNames = packageManagerInternal.getSharedUserPackagesForPackage(
480                 pkg.packageName, userId);
481 
482         for (String sharedPkgName : sharedPkgNames) {
483             final AndroidPackage sharedPkg = packageManagerInternal
484                     .getPackage(sharedPkgName);
485             if (sharedPkg != null) {
486                 synchroniser.addPackage(sharedPkg.getPackageName());
487             }
488         }
489         synchroniser.syncPackages();
490     }
491 
492     /**
493      * Synchronize all packages
494      */
synchronizePermissionsAndAppOpsForUser(@serIdInt int userId)495     private void synchronizePermissionsAndAppOpsForUser(@UserIdInt int userId) {
496         if (DEBUG) Slog.i(LOG_TAG, "synchronizePermissionsAndAppOpsForUser(" + userId + ")");
497 
498         final PackageManagerInternal packageManagerInternal = LocalServices.getService(
499                 PackageManagerInternal.class);
500         final PermissionToOpSynchroniser synchronizer = new PermissionToOpSynchroniser(
501                 getUserContext(getContext(), UserHandle.of(userId)));
502         packageManagerInternal.forEachPackage(
503                 (pkg) -> synchronizer.addPackage(pkg.getPackageName()));
504         synchronizer.syncPackages();
505     }
506 
resetAppOpPermissionsIfNotRequestedForUidAsync(int uid)507     private void resetAppOpPermissionsIfNotRequestedForUidAsync(int uid) {
508         if (isStarted(UserHandle.getUserId(uid))) {
509             synchronized (mLock) {
510                 if (!mIsUidSyncScheduled.get(uid)) {
511                     mIsUidSyncScheduled.put(uid, true);
512                     FgThread.getHandler().sendMessage(PooledLambda.obtainMessage(
513                             PermissionPolicyService::resetAppOpPermissionsIfNotRequestedForUid,
514                             this, uid));
515                 }
516             }
517         }
518     }
519 
resetAppOpPermissionsIfNotRequestedForUid(int uid)520     private void resetAppOpPermissionsIfNotRequestedForUid(int uid) {
521         synchronized (mLock) {
522             mIsUidSyncScheduled.delete(uid);
523         }
524 
525         final Context context = getContext();
526         final PackageManager userPackageManager = getUserContext(context,
527                 UserHandle.getUserHandleForUid(uid)).getPackageManager();
528         final String[] packageNames = userPackageManager.getPackagesForUid(uid);
529         if (packageNames == null || packageNames.length == 0) {
530             return;
531         }
532 
533         final ArraySet<String> requestedPermissions = new ArraySet<>();
534         for (String packageName : packageNames) {
535             final PackageInfo packageInfo;
536             try {
537                 packageInfo = userPackageManager.getPackageInfo(packageName, GET_PERMISSIONS);
538             } catch (NameNotFoundException e) {
539                 continue;
540             }
541             if (packageInfo == null || packageInfo.requestedPermissions == null) {
542                 continue;
543             }
544             Collections.addAll(requestedPermissions, packageInfo.requestedPermissions);
545         }
546 
547         final AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class);
548         final AppOpsManagerInternal appOpsManagerInternal = LocalServices.getService(
549                 AppOpsManagerInternal.class);
550         final int appOpPermissionsSize = mAppOpPermissions.size();
551         for (int i = 0; i < appOpPermissionsSize; i++) {
552             final String appOpPermission = mAppOpPermissions.get(i);
553 
554             if (!requestedPermissions.contains(appOpPermission)) {
555                 final int appOpCode = AppOpsManager.permissionToOpCode(appOpPermission);
556                 final int defaultAppOpMode = AppOpsManager.opToDefaultMode(appOpCode);
557                 for (String packageName : packageNames) {
558                     final int appOpMode = appOpsManager.unsafeCheckOpRawNoThrow(appOpCode, uid,
559                             packageName);
560                     if (appOpMode != defaultAppOpMode) {
561                         appOpsManagerInternal.setUidModeFromPermissionPolicy(appOpCode, uid,
562                                 defaultAppOpMode, mAppOpsCallback);
563                         appOpsManagerInternal.setModeFromPermissionPolicy(appOpCode, uid,
564                                 packageName, defaultAppOpMode, mAppOpsCallback);
565                     }
566                 }
567             }
568         }
569     }
570 
571     /**
572      * Synchronizes permission to app ops. You *must* always sync all packages
573      * in a shared UID at the same time to ensure proper synchronization.
574      */
575     private class PermissionToOpSynchroniser {
576         private final @NonNull Context mContext;
577         private final @NonNull PackageManager mPackageManager;
578         private final @NonNull AppOpsManager mAppOpsManager;
579         private final @NonNull AppOpsManagerInternal mAppOpsManagerInternal;
580 
581         private final @NonNull ArrayMap<String, PermissionInfo> mRuntimeAndTheirBgPermissionInfos;
582 
583         /**
584          * All ops that need to be flipped to allow.
585          *
586          * @see #syncPackages
587          */
588         private final @NonNull ArrayList<OpToChange> mOpsToAllow = new ArrayList<>();
589 
590         /**
591          * All ops that need to be flipped to ignore.
592          *
593          * @see #syncPackages
594          */
595         private final @NonNull ArrayList<OpToChange> mOpsToIgnore = new ArrayList<>();
596 
597         /**
598          * All ops that need to be flipped to ignore if not allowed.
599          *
600          * Currently, only used by soft restricted permissions logic.
601          *
602          * @see #syncPackages
603          */
604         private final @NonNull ArrayList<OpToChange> mOpsToIgnoreIfNotAllowed = new ArrayList<>();
605 
606         /**
607          * All ops that need to be flipped to foreground.
608          *
609          * Currently, only used by the foreground/background permissions logic.
610          *
611          * @see #syncPackages
612          */
613         private final @NonNull ArrayList<OpToChange> mOpsToForeground = new ArrayList<>();
614 
PermissionToOpSynchroniser(@onNull Context context)615         PermissionToOpSynchroniser(@NonNull Context context) {
616             mContext = context;
617             mPackageManager = context.getPackageManager();
618             mAppOpsManager = context.getSystemService(AppOpsManager.class);
619             mAppOpsManagerInternal = LocalServices.getService(AppOpsManagerInternal.class);
620 
621             mRuntimeAndTheirBgPermissionInfos = new ArrayMap<>();
622             PermissionManagerServiceInternal permissionManagerInternal = LocalServices.getService(
623                     PermissionManagerServiceInternal.class);
624             List<PermissionInfo> permissionInfos =
625                     permissionManagerInternal.getAllPermissionsWithProtection(
626                             PermissionInfo.PROTECTION_DANGEROUS);
627             int permissionInfosSize = permissionInfos.size();
628             for (int i = 0; i < permissionInfosSize; i++) {
629                 PermissionInfo permissionInfo = permissionInfos.get(i);
630                 mRuntimeAndTheirBgPermissionInfos.put(permissionInfo.name, permissionInfo);
631                 // Make sure we scoop up all background permissions as they may not be runtime
632                 if (permissionInfo.backgroundPermission != null) {
633                     String backgroundNonRuntimePermission = permissionInfo.backgroundPermission;
634                     for (int j = 0; j < permissionInfosSize; j++) {
635                         PermissionInfo bgPermissionCandidate = permissionInfos.get(j);
636                         if (permissionInfo.backgroundPermission.equals(
637                                 bgPermissionCandidate.name)) {
638                             backgroundNonRuntimePermission = null;
639                             break;
640                         }
641                     }
642                     if (backgroundNonRuntimePermission != null) {
643                         try {
644                             PermissionInfo backgroundPermissionInfo = mPackageManager
645                                     .getPermissionInfo(backgroundNonRuntimePermission, 0);
646                             mRuntimeAndTheirBgPermissionInfos.put(backgroundPermissionInfo.name,
647                                     backgroundPermissionInfo);
648                         } catch (NameNotFoundException e) {
649                             Slog.w(LOG_TAG, "Unknown background permission: "
650                                     + backgroundNonRuntimePermission);
651                         }
652                     }
653                 }
654             }
655         }
656 
657         /**
658          * Set app ops that were added in {@link #addPackage}.
659          *
660          * <p>This processes ops previously added by {@link #addAppOps(PackageInfo, String)}
661          */
syncPackages()662         private void syncPackages() {
663             // Remember which ops were already set. This makes sure that we always set the most
664             // permissive mode if two OpChanges are scheduled. This can e.g. happen if two
665             // permissions change the same op. See {@link #getSwitchOp}.
666             LongSparseLongArray alreadySetAppOps = new LongSparseLongArray();
667 
668             final int allowCount = mOpsToAllow.size();
669             for (int i = 0; i < allowCount; i++) {
670                 final OpToChange op = mOpsToAllow.get(i);
671 
672                 setUidModeAllowed(op.code, op.uid, op.packageName);
673                 alreadySetAppOps.put(IntPair.of(op.uid, op.code), 1);
674             }
675 
676             final int foregroundCount = mOpsToForeground.size();
677             for (int i = 0; i < foregroundCount; i++) {
678                 final OpToChange op = mOpsToForeground.get(i);
679                 if (alreadySetAppOps.indexOfKey(IntPair.of(op.uid, op.code)) >= 0) {
680                     continue;
681                 }
682 
683                 setUidModeForeground(op.code, op.uid, op.packageName);
684                 alreadySetAppOps.put(IntPair.of(op.uid, op.code), 1);
685             }
686 
687             final int ignoreCount = mOpsToIgnore.size();
688             for (int i = 0; i < ignoreCount; i++) {
689                 final OpToChange op = mOpsToIgnore.get(i);
690                 if (alreadySetAppOps.indexOfKey(IntPair.of(op.uid, op.code)) >= 0) {
691                     continue;
692                 }
693 
694                 setUidModeIgnored(op.code, op.uid, op.packageName);
695                 alreadySetAppOps.put(IntPair.of(op.uid, op.code), 1);
696             }
697 
698             final int ignoreIfNotAllowedCount = mOpsToIgnoreIfNotAllowed.size();
699             for (int i = 0; i < ignoreIfNotAllowedCount; i++) {
700                 final OpToChange op = mOpsToIgnoreIfNotAllowed.get(i);
701                 if (alreadySetAppOps.indexOfKey(IntPair.of(op.uid, op.code)) >= 0) {
702                     continue;
703                 }
704 
705                 boolean wasSet = setUidModeIgnoredIfNotAllowed(op.code, op.uid, op.packageName);
706                 if (wasSet) {
707                     alreadySetAppOps.put(IntPair.of(op.uid, op.code), 1);
708                 }
709             }
710         }
711 
712         /**
713          * Note: Called with the package lock held. Do <u>not</u> call into app-op manager.
714          */
addAppOps(@onNull PackageInfo packageInfo, @NonNull AndroidPackage pkg, @NonNull String permissionName)715         private void addAppOps(@NonNull PackageInfo packageInfo, @NonNull AndroidPackage pkg,
716                 @NonNull String permissionName) {
717             PermissionInfo permissionInfo = mRuntimeAndTheirBgPermissionInfos.get(permissionName);
718             if (permissionInfo == null) {
719                 return;
720             }
721             addPermissionAppOp(packageInfo, pkg, permissionInfo);
722             addExtraAppOp(packageInfo, pkg, permissionInfo);
723         }
724 
addPermissionAppOp(@onNull PackageInfo packageInfo, @NonNull AndroidPackage pkg, @NonNull PermissionInfo permissionInfo)725         private void addPermissionAppOp(@NonNull PackageInfo packageInfo,
726                 @NonNull AndroidPackage pkg, @NonNull PermissionInfo permissionInfo) {
727             if (!permissionInfo.isRuntime()) {
728                 return;
729             }
730 
731             String permissionName = permissionInfo.name;
732             String packageName = packageInfo.packageName;
733             int permissionFlags = mPackageManager.getPermissionFlags(permissionName,
734                     packageName, mContext.getUser());
735             boolean isReviewRequired = (permissionFlags & FLAG_PERMISSION_REVIEW_REQUIRED) != 0;
736             if (isReviewRequired) {
737                 return;
738             }
739 
740             // TODO: COARSE_LOCATION and FINE_LOCATION shares the same app op. We are solving this
741             //  with switch op but once we start syncing single permission this won't work.
742             int appOpCode = getSwitchOp(permissionName);
743             if (appOpCode == OP_NONE) {
744                 // Note that background permissions don't have an associated app op.
745                 return;
746             }
747 
748             int appOpMode;
749             boolean shouldGrantAppOp = shouldGrantAppOp(packageInfo, pkg, permissionInfo);
750             if (shouldGrantAppOp) {
751                 if (permissionInfo.backgroundPermission != null) {
752                     PermissionInfo backgroundPermissionInfo = mRuntimeAndTheirBgPermissionInfos.get(
753                             permissionInfo.backgroundPermission);
754                     boolean shouldGrantBackgroundAppOp = backgroundPermissionInfo != null
755                             && shouldGrantAppOp(packageInfo, pkg, backgroundPermissionInfo);
756                     appOpMode = shouldGrantBackgroundAppOp ? MODE_ALLOWED : MODE_FOREGROUND;
757                 } else {
758                     appOpMode = MODE_ALLOWED;
759                 }
760             } else {
761                 appOpMode = MODE_IGNORED;
762             }
763 
764             int uid = packageInfo.applicationInfo.uid;
765             OpToChange opToChange = new OpToChange(uid, packageName, appOpCode);
766             switch (appOpMode) {
767                 case MODE_ALLOWED:
768                     mOpsToAllow.add(opToChange);
769                     break;
770                 case MODE_FOREGROUND:
771                     mOpsToForeground.add(opToChange);
772                     break;
773                 case MODE_IGNORED:
774                     mOpsToIgnore.add(opToChange);
775                     break;
776             }
777         }
778 
shouldGrantAppOp(@onNull PackageInfo packageInfo, @NonNull AndroidPackage pkg, @NonNull PermissionInfo permissionInfo)779         private boolean shouldGrantAppOp(@NonNull PackageInfo packageInfo,
780                 @NonNull AndroidPackage pkg, @NonNull PermissionInfo permissionInfo) {
781             String permissionName = permissionInfo.name;
782             String packageName = packageInfo.packageName;
783             boolean isGranted = mPackageManager.checkPermission(permissionName, packageName)
784                     == PackageManager.PERMISSION_GRANTED;
785             if (!isGranted) {
786                 return false;
787             }
788 
789             int permissionFlags = mPackageManager.getPermissionFlags(permissionName, packageName,
790                     mContext.getUser());
791             boolean isRevokedCompat = (permissionFlags & FLAG_PERMISSION_REVOKED_COMPAT)
792                     == FLAG_PERMISSION_REVOKED_COMPAT;
793             if (isRevokedCompat) {
794                 return false;
795             }
796 
797             if (permissionInfo.isHardRestricted()) {
798                 boolean shouldApplyRestriction =
799                         (permissionFlags & FLAG_PERMISSION_APPLY_RESTRICTION)
800                                 == FLAG_PERMISSION_APPLY_RESTRICTION;
801                 return !shouldApplyRestriction;
802             } else if (permissionInfo.isSoftRestricted()) {
803                 SoftRestrictedPermissionPolicy policy =
804                         SoftRestrictedPermissionPolicy.forPermission(mContext,
805                                 packageInfo.applicationInfo, pkg, mContext.getUser(),
806                                 permissionName);
807                 return policy.mayGrantPermission();
808             } else {
809                 return true;
810             }
811         }
812 
addExtraAppOp(@onNull PackageInfo packageInfo, @NonNull AndroidPackage pkg, @NonNull PermissionInfo permissionInfo)813         private void addExtraAppOp(@NonNull PackageInfo packageInfo, @NonNull AndroidPackage pkg,
814                 @NonNull PermissionInfo permissionInfo) {
815             if (!permissionInfo.isSoftRestricted()) {
816                 return;
817             }
818 
819             String permissionName = permissionInfo.name;
820             SoftRestrictedPermissionPolicy policy =
821                     SoftRestrictedPermissionPolicy.forPermission(mContext,
822                             packageInfo.applicationInfo, pkg, mContext.getUser(), permissionName);
823             int extraOpCode = policy.getExtraAppOpCode();
824             if (extraOpCode == OP_NONE) {
825                 return;
826             }
827 
828             int uid = packageInfo.applicationInfo.uid;
829             String packageName = packageInfo.packageName;
830             OpToChange extraOpToChange = new OpToChange(uid, packageName, extraOpCode);
831             if (policy.mayAllowExtraAppOp()) {
832                 mOpsToAllow.add(extraOpToChange);
833             } else {
834                 if (policy.mayDenyExtraAppOpIfGranted()) {
835                     mOpsToIgnore.add(extraOpToChange);
836                 } else {
837                     mOpsToIgnoreIfNotAllowed.add(extraOpToChange);
838                 }
839             }
840         }
841 
842         /**
843          * Add a package for {@link #syncPackages() processing} later.
844          *
845          * <p>Note: Called with the package lock held. Do <u>not</u> call into app-op manager.
846          *
847          * @param pkgName The package to add for later processing.
848          */
addPackage(@onNull String pkgName)849         void addPackage(@NonNull String pkgName) {
850             PackageManagerInternal pmInternal =
851                     LocalServices.getService(PackageManagerInternal.class);
852             final PackageInfo pkgInfo;
853             final AndroidPackage pkg;
854             try {
855                 pkgInfo = mPackageManager.getPackageInfo(pkgName, GET_PERMISSIONS);
856                 pkg = pmInternal.getPackage(pkgName);
857             } catch (NameNotFoundException e) {
858                 return;
859             }
860 
861             if (pkgInfo == null || pkg == null || pkgInfo.applicationInfo == null
862                     || pkgInfo.requestedPermissions == null) {
863                 return;
864             }
865 
866             final int uid = pkgInfo.applicationInfo.uid;
867             if (uid == Process.ROOT_UID || uid == Process.SYSTEM_UID) {
868                 // Root and system server always pass permission checks, so don't touch their app
869                 // ops to keep compatibility.
870                 return;
871             }
872 
873             for (String permission : pkgInfo.requestedPermissions) {
874                 addAppOps(pkgInfo, pkg, permission);
875             }
876         }
877 
setUidModeAllowed(int opCode, int uid, @NonNull String packageName)878         private void setUidModeAllowed(int opCode, int uid, @NonNull String packageName) {
879             setUidMode(opCode, uid, MODE_ALLOWED, packageName);
880         }
881 
setUidModeForeground(int opCode, int uid, @NonNull String packageName)882         private void setUidModeForeground(int opCode, int uid, @NonNull String packageName) {
883             setUidMode(opCode, uid, MODE_FOREGROUND, packageName);
884         }
885 
setUidModeIgnored(int opCode, int uid, @NonNull String packageName)886         private void setUidModeIgnored(int opCode, int uid, @NonNull String packageName) {
887             setUidMode(opCode, uid, MODE_IGNORED, packageName);
888         }
889 
setUidModeIgnoredIfNotAllowed(int opCode, int uid, @NonNull String packageName)890         private boolean setUidModeIgnoredIfNotAllowed(int opCode, int uid,
891                 @NonNull String packageName) {
892             final int currentMode = mAppOpsManager.unsafeCheckOpRaw(AppOpsManager.opToPublicName(
893                     opCode), uid, packageName);
894             if (currentMode != MODE_ALLOWED) {
895                 if (currentMode != MODE_IGNORED) {
896                     mAppOpsManagerInternal.setUidModeFromPermissionPolicy(opCode, uid, MODE_IGNORED,
897                             mAppOpsCallback);
898                 }
899                 return true;
900             }
901             return false;
902         }
903 
setUidMode(int opCode, int uid, int mode, @NonNull String packageName)904         private void setUidMode(int opCode, int uid, int mode,
905                 @NonNull String packageName) {
906             final int oldMode = mAppOpsManager.unsafeCheckOpRaw(AppOpsManager.opToPublicName(
907                     opCode), uid, packageName);
908             if (oldMode != mode) {
909                 mAppOpsManagerInternal.setUidModeFromPermissionPolicy(opCode, uid, mode,
910                         mAppOpsCallback);
911                 final int newMode = mAppOpsManager.unsafeCheckOpRaw(AppOpsManager.opToPublicName(
912                         opCode), uid, packageName);
913                 if (newMode != mode) {
914                     // Work around incorrectly-set package mode. It never makes sense for app ops
915                     // related to runtime permissions, but can get in the way and we have to reset
916                     // it.
917                     mAppOpsManagerInternal.setModeFromPermissionPolicy(opCode, uid, packageName,
918                             AppOpsManager.opToDefaultMode(opCode), mAppOpsCallback);
919                 }
920             }
921         }
922 
923         private class OpToChange {
924             final int uid;
925             final @NonNull String packageName;
926             final int code;
927 
OpToChange(int uid, @NonNull String packageName, int code)928             OpToChange(int uid, @NonNull String packageName, int code) {
929                 this.uid = uid;
930                 this.packageName = packageName;
931                 this.code = code;
932             }
933         }
934     }
935 
936     private class Internal extends PermissionPolicyInternal {
937 
938         @Override
checkStartActivity(@onNull Intent intent, int callingUid, @Nullable String callingPackage)939         public boolean checkStartActivity(@NonNull Intent intent, int callingUid,
940                 @Nullable String callingPackage) {
941             if (callingPackage != null && isActionRemovedForCallingPackage(intent, callingUid,
942                     callingPackage)) {
943                 Slog.w(LOG_TAG, "Action Removed: starting " + intent.toString() + " from "
944                         + callingPackage + " (uid=" + callingUid + ")");
945                 return false;
946             }
947             return true;
948         }
949 
950         @Override
isInitialized(int userId)951         public boolean isInitialized(int userId) {
952             return isStarted(userId);
953         }
954 
955         @Override
setOnInitializedCallback(@onNull OnInitializedCallback callback)956         public void setOnInitializedCallback(@NonNull OnInitializedCallback callback) {
957             synchronized (mLock) {
958                 mOnInitializedCallback = callback;
959             }
960         }
961 
962         /**
963          * Check if the intent action is removed for the calling package (often based on target SDK
964          * version). If the action is removed, we'll silently cancel the activity launch.
965          */
isActionRemovedForCallingPackage(@onNull Intent intent, int callingUid, @NonNull String callingPackage)966         private boolean isActionRemovedForCallingPackage(@NonNull Intent intent, int callingUid,
967                 @NonNull String callingPackage) {
968             String action = intent.getAction();
969             if (action == null) {
970                 return false;
971             }
972             switch (action) {
973                 case TelecomManager.ACTION_CHANGE_DEFAULT_DIALER:
974                 case Telephony.Sms.Intents.ACTION_CHANGE_DEFAULT: {
975                     ApplicationInfo applicationInfo;
976                     try {
977                         applicationInfo = getContext().getPackageManager().getApplicationInfoAsUser(
978                                 callingPackage, 0, UserHandle.getUserId(callingUid));
979                         if (applicationInfo.targetSdkVersion >= Build.VERSION_CODES.Q) {
980                             // Applications targeting Q or higher should use
981                             // RoleManager.createRequestRoleIntent() instead.
982                             return true;
983                         }
984                     } catch (PackageManager.NameNotFoundException e) {
985                         Slog.i(LOG_TAG, "Cannot find application info for " + callingPackage);
986                     }
987                     // Make sure RequestRoleActivity can know the calling package if we allow it.
988                     intent.putExtra(Intent.EXTRA_CALLING_PACKAGE, callingPackage);
989                     return false;
990                 }
991                 default:
992                     return false;
993             }
994         }
995     }
996 }
997