blob: f3a60185bfb637b86567401f610f0090093b72eb [file] [log] [blame]
Philip P. Moltmannfaa788a2019-05-29 16:18:18 -07001/*
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
17package com.android.server.policy;
18
19import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
20import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
Philip P. Moltmannfaa788a2019-05-29 16:18:18 -070021import static android.app.AppOpsManager.OP_LEGACY_STORAGE;
22import static android.app.AppOpsManager.OP_NONE;
23import static android.content.pm.PackageManager.FLAG_PERMISSION_APPLY_RESTRICTION;
Philip P. Moltmann8625cdd2019-05-30 08:27:19 -070024import static android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT;
25import static android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT;
26import static android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT;
Philip P. Moltmannfaa788a2019-05-29 16:18:18 -070027
Philip P. Moltmannf75bb772019-07-17 08:35:47 -070028import static java.lang.Integer.min;
29
Philip P. Moltmannfaa788a2019-05-29 16:18:18 -070030import android.annotation.NonNull;
Philip P. Moltmannfad1a8f2019-06-14 09:02:24 -070031import android.annotation.Nullable;
Philip P. Moltmannfaa788a2019-05-29 16:18:18 -070032import android.app.AppOpsManager;
33import android.content.Context;
34import android.content.pm.ApplicationInfo;
Philip P. Moltmannf75bb772019-07-17 08:35:47 -070035import android.content.pm.PackageManager;
Philip P. Moltmann8625cdd2019-05-30 08:27:19 -070036import android.os.Build;
Philip P. Moltmann69b645f2019-06-17 14:28:11 -070037import android.os.UserHandle;
Philip P. Moltmannfaa788a2019-05-29 16:18:18 -070038
39/**
40 * The behavior of soft restricted permissions is different for each permission. This class collects
41 * the policies in one place.
Philip P. Moltmannf4de94f2019-05-30 11:14:33 -070042 *
43 * This is the twin of
44 * {@link com.android.packageinstaller.permission.utils.SoftRestrictedPermissionPolicy}
Philip P. Moltmannfaa788a2019-05-29 16:18:18 -070045 */
46public abstract class SoftRestrictedPermissionPolicy {
Philip P. Moltmann8625cdd2019-05-30 08:27:19 -070047 private static final int FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT =
48 FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT
49 | FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT
50 | FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT;
51
Philip P. Moltmannfaa788a2019-05-29 16:18:18 -070052 private static final SoftRestrictedPermissionPolicy DUMMY_POLICY =
53 new SoftRestrictedPermissionPolicy() {
54 @Override
Hai Zhangfa291702019-09-19 16:35:44 -070055 public boolean mayGrantPermission() {
Philip P. Moltmann8625cdd2019-05-30 08:27:19 -070056 return true;
57 }
Philip P. Moltmannfaa788a2019-05-29 16:18:18 -070058 };
59
60 /**
Philip P. Moltmannf75bb772019-07-17 08:35:47 -070061 * TargetSDK is per package. To make sure two apps int the same shared UID do not fight over
62 * what to set, always compute the combined targetSDK.
63 *
64 * @param context A context
65 * @param appInfo The app that is changed
66 * @param user The user the app belongs to
67 *
68 * @return The minimum targetSDK of all apps sharing the uid of the app
69 */
70 private static int getMinimumTargetSDK(@NonNull Context context,
71 @NonNull ApplicationInfo appInfo, @NonNull UserHandle user) {
72 PackageManager pm = context.getPackageManager();
73
74 int minimumTargetSDK = appInfo.targetSdkVersion;
75
76 String[] uidPkgs = pm.getPackagesForUid(appInfo.uid);
77 if (uidPkgs != null) {
78 for (String uidPkg : uidPkgs) {
79 if (!uidPkg.equals(appInfo.packageName)) {
80 ApplicationInfo uidPkgInfo;
81 try {
82 uidPkgInfo = pm.getApplicationInfoAsUser(uidPkg, 0, user);
83 } catch (PackageManager.NameNotFoundException e) {
84 continue;
85 }
86
87 minimumTargetSDK = min(minimumTargetSDK, uidPkgInfo.targetSdkVersion);
88 }
89 }
90 }
91
92 return minimumTargetSDK;
93 }
94
95 /**
Philip P. Moltmannfaa788a2019-05-29 16:18:18 -070096 * Get the policy for a soft restricted permission.
97 *
98 * @param context A context to use
Hai Zhangfa291702019-09-19 16:35:44 -070099 * @param appInfo The application the permission belongs to.
100 * @param user The user the app belongs to.
Philip P. Moltmannfaa788a2019-05-29 16:18:18 -0700101 * @param permission The name of the permission
102 *
103 * @return The policy for this permission
104 */
105 public static @NonNull SoftRestrictedPermissionPolicy forPermission(@NonNull Context context,
Philip P. Moltmannfad1a8f2019-06-14 09:02:24 -0700106 @Nullable ApplicationInfo appInfo, @Nullable UserHandle user,
Philip P. Moltmann69b645f2019-06-17 14:28:11 -0700107 @NonNull String permission) {
Philip P. Moltmannfaa788a2019-05-29 16:18:18 -0700108 switch (permission) {
109 // Storage uses a special app op to decide the mount state and supports soft restriction
110 // where the restricted state allows the permission but only for accessing the medial
111 // collections.
Philip P. Moltmann64f20dd2019-06-25 08:41:51 -0700112 case READ_EXTERNAL_STORAGE: {
Philip P. Moltmannfad1a8f2019-06-14 09:02:24 -0700113 final boolean isWhiteListed;
Hai Zhangfa291702019-09-19 16:35:44 -0700114 boolean shouldApplyRestriction;
Philip P. Moltmannfad1a8f2019-06-14 09:02:24 -0700115 final int targetSDK;
Hai Zhangfa291702019-09-19 16:35:44 -0700116 final boolean hasRequestedLegacyExternalStorage;
Philip P. Moltmannfad1a8f2019-06-14 09:02:24 -0700117
118 if (appInfo != null) {
Philip P. Moltmannf75bb772019-07-17 08:35:47 -0700119 PackageManager pm = context.getPackageManager();
Hai Zhangfa291702019-09-19 16:35:44 -0700120 int flags = pm.getPermissionFlags(permission, appInfo.packageName, user);
Philip P. Moltmannfad1a8f2019-06-14 09:02:24 -0700121 isWhiteListed = (flags & FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT) != 0;
Hai Zhangfa291702019-09-19 16:35:44 -0700122 shouldApplyRestriction = (flags & FLAG_PERMISSION_APPLY_RESTRICTION) != 0;
Philip P. Moltmannf75bb772019-07-17 08:35:47 -0700123 targetSDK = getMinimumTargetSDK(context, appInfo, user);
Hai Zhangfa291702019-09-19 16:35:44 -0700124 hasRequestedLegacyExternalStorage = hasUidRequestedLegacyExternalStorage(
125 appInfo.uid, context);
Philip P. Moltmannfad1a8f2019-06-14 09:02:24 -0700126 } else {
Philip P. Moltmannfad1a8f2019-06-14 09:02:24 -0700127 isWhiteListed = false;
Hai Zhangfa291702019-09-19 16:35:44 -0700128 shouldApplyRestriction = false;
Philip P. Moltmannfad1a8f2019-06-14 09:02:24 -0700129 targetSDK = 0;
Hai Zhangfa291702019-09-19 16:35:44 -0700130 hasRequestedLegacyExternalStorage = false;
Philip P. Moltmannfad1a8f2019-06-14 09:02:24 -0700131 }
Philip P. Moltmannfaa788a2019-05-29 16:18:18 -0700132
Hai Zhangfa291702019-09-19 16:35:44 -0700133 // We have a check in PermissionPolicyService.PermissionToOpSynchroniser.setUidMode
134 // to prevent apps losing files in legacy storage, because we are holding the
135 // package manager lock here. If we ever remove this policy that check should be
136 // removed as well.
Philip P. Moltmannfaa788a2019-05-29 16:18:18 -0700137 return new SoftRestrictedPermissionPolicy() {
138 @Override
Hai Zhangfa291702019-09-19 16:35:44 -0700139 public boolean mayGrantPermission() {
140 return isWhiteListed || targetSDK >= Build.VERSION_CODES.Q;
141 }
142 @Override
143 public int getExtraAppOpCode() {
Philip P. Moltmannfaa788a2019-05-29 16:18:18 -0700144 return OP_LEGACY_STORAGE;
145 }
Philip P. Moltmannfaa788a2019-05-29 16:18:18 -0700146 @Override
Hai Zhangfa291702019-09-19 16:35:44 -0700147 public boolean mayAllowExtraAppOp() {
Nandana Dutt13ecad42020-01-06 08:12:57 +0000148 return !shouldApplyRestriction && hasRequestedLegacyExternalStorage
149 && targetSDK <= Build.VERSION_CODES.Q;
Philip P. Moltmannfaa788a2019-05-29 16:18:18 -0700150 }
Philip P. Moltmannfaa788a2019-05-29 16:18:18 -0700151 @Override
Hai Zhangfa291702019-09-19 16:35:44 -0700152 public boolean mayDenyExtraAppOpIfGranted() {
153 return shouldApplyRestriction;
Philip P. Moltmann8625cdd2019-05-30 08:27:19 -0700154 }
Philip P. Moltmannfaa788a2019-05-29 16:18:18 -0700155 };
156 }
Philip P. Moltmann64f20dd2019-06-25 08:41:51 -0700157 case WRITE_EXTERNAL_STORAGE: {
158 final boolean isWhiteListed;
159 final int targetSDK;
160
161 if (appInfo != null) {
162 final int flags = context.getPackageManager().getPermissionFlags(permission,
163 appInfo.packageName, user);
164 isWhiteListed = (flags & FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT) != 0;
Philip P. Moltmannf75bb772019-07-17 08:35:47 -0700165 targetSDK = getMinimumTargetSDK(context, appInfo, user);
Philip P. Moltmann64f20dd2019-06-25 08:41:51 -0700166 } else {
167 isWhiteListed = false;
168 targetSDK = 0;
169 }
170
171 return new SoftRestrictedPermissionPolicy() {
172 @Override
Hai Zhangfa291702019-09-19 16:35:44 -0700173 public boolean mayGrantPermission() {
Philip P. Moltmann64f20dd2019-06-25 08:41:51 -0700174 return isWhiteListed || targetSDK >= Build.VERSION_CODES.Q;
175 }
176 };
177 }
Philip P. Moltmannfaa788a2019-05-29 16:18:18 -0700178 default:
179 return DUMMY_POLICY;
180 }
181 }
182
Hai Zhangfa291702019-09-19 16:35:44 -0700183 private static boolean hasUidRequestedLegacyExternalStorage(int uid, @NonNull Context context) {
184 PackageManager packageManager = context.getPackageManager();
185 String[] packageNames = packageManager.getPackagesForUid(uid);
186 if (packageNames == null) {
187 return false;
Winson243e7ea2019-08-07 10:51:25 -0700188 }
Hai Zhangfa291702019-09-19 16:35:44 -0700189 UserHandle user = UserHandle.getUserHandleForUid(uid);
190 for (String packageName : packageNames) {
191 ApplicationInfo applicationInfo;
192 try {
193 applicationInfo = packageManager.getApplicationInfoAsUser(packageName, 0, user);
194 } catch (PackageManager.NameNotFoundException e) {
195 continue;
196 }
197 if (applicationInfo.hasRequestedLegacyExternalStorage()) {
198 return true;
199 }
200 }
201 return false;
Winson243e7ea2019-08-07 10:51:25 -0700202 }
203
204 /**
Hai Zhangfa291702019-09-19 16:35:44 -0700205 * @return If the permission can be granted
206 */
207 public abstract boolean mayGrantPermission();
208
209 /**
Philip P. Moltmannfaa788a2019-05-29 16:18:18 -0700210 * @return An app op to be changed based on the state of the permission or
211 * {@link AppOpsManager#OP_NONE} if not app-op should be set.
212 */
Hai Zhangfa291702019-09-19 16:35:44 -0700213 public int getExtraAppOpCode() {
214 return OP_NONE;
215 }
Philip P. Moltmannfaa788a2019-05-29 16:18:18 -0700216
217 /**
Hai Zhangfa291702019-09-19 16:35:44 -0700218 * @return Whether the {@link #getExtraAppOpCode() app op} may be granted.
Philip P. Moltmannfaa788a2019-05-29 16:18:18 -0700219 */
Hai Zhangfa291702019-09-19 16:35:44 -0700220 public boolean mayAllowExtraAppOp() {
221 return false;
222 }
Philip P. Moltmannfaa788a2019-05-29 16:18:18 -0700223
224 /**
Hai Zhangfa291702019-09-19 16:35:44 -0700225 * @return Whether the {@link #getExtraAppOpCode() app op} may be denied if was granted.
Philip P. Moltmannfaa788a2019-05-29 16:18:18 -0700226 */
Hai Zhangfa291702019-09-19 16:35:44 -0700227 public boolean mayDenyExtraAppOpIfGranted() {
228 return false;
229 }
Philip P. Moltmannfaa788a2019-05-29 16:18:18 -0700230}