blob: c1a6dbd8ae147b343aa1457aa600293acb75de82 [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;
21import static android.app.AppOpsManager.MODE_ALLOWED;
22import static android.app.AppOpsManager.MODE_DEFAULT;
23import static android.app.AppOpsManager.MODE_IGNORED;
24import static android.app.AppOpsManager.OP_LEGACY_STORAGE;
25import static android.app.AppOpsManager.OP_NONE;
26import static android.content.pm.PackageManager.FLAG_PERMISSION_APPLY_RESTRICTION;
Philip P. Moltmann8625cdd2019-05-30 08:27:19 -070027import static android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT;
28import static android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT;
29import static android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT;
Philip P. Moltmannfaa788a2019-05-29 16:18:18 -070030
Philip P. Moltmann33ca78a2019-07-17 08:35:47 -070031import static java.lang.Integer.min;
32
Philip P. Moltmannfaa788a2019-05-29 16:18:18 -070033import android.annotation.NonNull;
Philip P. Moltmannfad1a8f2019-06-14 09:02:24 -070034import android.annotation.Nullable;
Philip P. Moltmannfaa788a2019-05-29 16:18:18 -070035import android.app.AppOpsManager;
36import android.content.Context;
37import android.content.pm.ApplicationInfo;
Philip P. Moltmann33ca78a2019-07-17 08:35:47 -070038import android.content.pm.PackageManager;
Philip P. Moltmann8625cdd2019-05-30 08:27:19 -070039import android.os.Build;
Philip P. Moltmann69b645f2019-06-17 14:28:11 -070040import android.os.UserHandle;
Philip P. Moltmannfaa788a2019-05-29 16:18:18 -070041
42/**
43 * The behavior of soft restricted permissions is different for each permission. This class collects
44 * the policies in one place.
Philip P. Moltmannf4de94f2019-05-30 11:14:33 -070045 *
46 * This is the twin of
47 * {@link com.android.packageinstaller.permission.utils.SoftRestrictedPermissionPolicy}
Philip P. Moltmannfaa788a2019-05-29 16:18:18 -070048 */
49public abstract class SoftRestrictedPermissionPolicy {
Philip P. Moltmann8625cdd2019-05-30 08:27:19 -070050 private static final int FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT =
51 FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT
52 | FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT
53 | FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT;
54
Philip P. Moltmannfaa788a2019-05-29 16:18:18 -070055 private static final SoftRestrictedPermissionPolicy DUMMY_POLICY =
56 new SoftRestrictedPermissionPolicy() {
57 @Override
Philip P. Moltmann404b51c2019-06-11 09:01:59 -070058 public int resolveAppOp() {
Philip P. Moltmannfaa788a2019-05-29 16:18:18 -070059 return OP_NONE;
60 }
61
62 @Override
Philip P. Moltmann404b51c2019-06-11 09:01:59 -070063 public int getDesiredOpMode() {
Philip P. Moltmannfaa788a2019-05-29 16:18:18 -070064 return MODE_DEFAULT;
65 }
66
67 @Override
68 public boolean shouldSetAppOpIfNotDefault() {
69 return false;
70 }
Philip P. Moltmann8625cdd2019-05-30 08:27:19 -070071
72 @Override
73 public boolean canBeGranted() {
74 return true;
75 }
Philip P. Moltmannfaa788a2019-05-29 16:18:18 -070076 };
77
78 /**
Philip P. Moltmann33ca78a2019-07-17 08:35:47 -070079 * TargetSDK is per package. To make sure two apps int the same shared UID do not fight over
80 * what to set, always compute the combined targetSDK.
81 *
82 * @param context A context
83 * @param appInfo The app that is changed
84 * @param user The user the app belongs to
85 *
86 * @return The minimum targetSDK of all apps sharing the uid of the app
87 */
88 private static int getMinimumTargetSDK(@NonNull Context context,
89 @NonNull ApplicationInfo appInfo, @NonNull UserHandle user) {
90 PackageManager pm = context.getPackageManager();
91
92 int minimumTargetSDK = appInfo.targetSdkVersion;
93
94 String[] uidPkgs = pm.getPackagesForUid(appInfo.uid);
95 if (uidPkgs != null) {
96 for (String uidPkg : uidPkgs) {
97 if (!uidPkg.equals(appInfo.packageName)) {
98 ApplicationInfo uidPkgInfo;
99 try {
100 uidPkgInfo = pm.getApplicationInfoAsUser(uidPkg, 0, user);
101 } catch (PackageManager.NameNotFoundException e) {
102 continue;
103 }
104
105 minimumTargetSDK = min(minimumTargetSDK, uidPkgInfo.targetSdkVersion);
106 }
107 }
108 }
109
110 return minimumTargetSDK;
111 }
112
113 /**
Philip P. Moltmannfaa788a2019-05-29 16:18:18 -0700114 * Get the policy for a soft restricted permission.
115 *
116 * @param context A context to use
Philip P. Moltmannfad1a8f2019-06-14 09:02:24 -0700117 * @param appInfo The application the permission belongs to. Can be {@code null}, but then
118 * only {@link #resolveAppOp} will work.
119 * @param user The user the app belongs to. Can be {@code null}, but then only
120 * {@link #resolveAppOp} will work.
Philip P. Moltmannfaa788a2019-05-29 16:18:18 -0700121 * @param permission The name of the permission
122 *
123 * @return The policy for this permission
124 */
125 public static @NonNull SoftRestrictedPermissionPolicy forPermission(@NonNull Context context,
Philip P. Moltmannfad1a8f2019-06-14 09:02:24 -0700126 @Nullable ApplicationInfo appInfo, @Nullable UserHandle user,
Philip P. Moltmann69b645f2019-06-17 14:28:11 -0700127 @NonNull String permission) {
Philip P. Moltmannfaa788a2019-05-29 16:18:18 -0700128 switch (permission) {
129 // Storage uses a special app op to decide the mount state and supports soft restriction
130 // where the restricted state allows the permission but only for accessing the medial
131 // collections.
Philip P. Moltmann64f20dd2019-06-25 08:41:51 -0700132 case READ_EXTERNAL_STORAGE: {
Philip P. Moltmannfad1a8f2019-06-14 09:02:24 -0700133 final int flags;
134 final boolean applyRestriction;
135 final boolean isWhiteListed;
136 final boolean hasRequestedLegacyExternalStorage;
137 final int targetSDK;
138
139 if (appInfo != null) {
Philip P. Moltmann33ca78a2019-07-17 08:35:47 -0700140 PackageManager pm = context.getPackageManager();
141 flags = pm.getPermissionFlags(permission, appInfo.packageName, user);
Philip P. Moltmannfad1a8f2019-06-14 09:02:24 -0700142 applyRestriction = (flags & FLAG_PERMISSION_APPLY_RESTRICTION) != 0;
143 isWhiteListed = (flags & FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT) != 0;
Philip P. Moltmann33ca78a2019-07-17 08:35:47 -0700144 targetSDK = getMinimumTargetSDK(context, appInfo, user);
145
146 boolean hasAnyRequestedLegacyExternalStorage =
147 appInfo.hasRequestedLegacyExternalStorage();
148
149 // hasRequestedLegacyExternalStorage is per package. To make sure two apps in
150 // the same shared UID do not fight over what to set, always compute the
151 // combined hasRequestedLegacyExternalStorage
152 String[] uidPkgs = pm.getPackagesForUid(appInfo.uid);
153 if (uidPkgs != null) {
154 for (String uidPkg : uidPkgs) {
155 if (!uidPkg.equals(appInfo.packageName)) {
156 ApplicationInfo uidPkgInfo;
157 try {
158 uidPkgInfo = pm.getApplicationInfoAsUser(uidPkg, 0, user);
159 } catch (PackageManager.NameNotFoundException e) {
160 continue;
161 }
162
163 hasAnyRequestedLegacyExternalStorage |=
164 uidPkgInfo.hasRequestedLegacyExternalStorage();
165 }
166 }
167 }
168
169 hasRequestedLegacyExternalStorage = hasAnyRequestedLegacyExternalStorage;
Philip P. Moltmannfad1a8f2019-06-14 09:02:24 -0700170 } else {
171 flags = 0;
172 applyRestriction = false;
173 isWhiteListed = false;
174 hasRequestedLegacyExternalStorage = false;
175 targetSDK = 0;
176 }
Philip P. Moltmannfaa788a2019-05-29 16:18:18 -0700177
178 return new SoftRestrictedPermissionPolicy() {
179 @Override
Philip P. Moltmann404b51c2019-06-11 09:01:59 -0700180 public int resolveAppOp() {
Philip P. Moltmannfaa788a2019-05-29 16:18:18 -0700181 return OP_LEGACY_STORAGE;
182 }
183
184 @Override
Philip P. Moltmann404b51c2019-06-11 09:01:59 -0700185 public int getDesiredOpMode() {
Philip P. Moltmannfaa788a2019-05-29 16:18:18 -0700186 if (applyRestriction) {
187 return MODE_DEFAULT;
188 } else if (hasRequestedLegacyExternalStorage) {
189 return MODE_ALLOWED;
190 } else {
191 return MODE_IGNORED;
192 }
193 }
194
195 @Override
196 public boolean shouldSetAppOpIfNotDefault() {
197 // Do not switch from allowed -> ignored as this would mean to retroactively
198 // turn on isolated storage. This will make the app loose all its files.
Philip P. Moltmann404b51c2019-06-11 09:01:59 -0700199 return getDesiredOpMode() != MODE_IGNORED;
Philip P. Moltmannfaa788a2019-05-29 16:18:18 -0700200 }
Philip P. Moltmann8625cdd2019-05-30 08:27:19 -0700201
202 @Override
203 public boolean canBeGranted() {
204 if (isWhiteListed || targetSDK >= Build.VERSION_CODES.Q) {
205 return true;
206 } else {
Philip P. Moltmann8625cdd2019-05-30 08:27:19 -0700207 return false;
208 }
209 }
Philip P. Moltmannfaa788a2019-05-29 16:18:18 -0700210 };
211 }
Philip P. Moltmann64f20dd2019-06-25 08:41:51 -0700212 case WRITE_EXTERNAL_STORAGE: {
213 final boolean isWhiteListed;
214 final int targetSDK;
215
216 if (appInfo != null) {
217 final int flags = context.getPackageManager().getPermissionFlags(permission,
218 appInfo.packageName, user);
219 isWhiteListed = (flags & FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT) != 0;
Philip P. Moltmann33ca78a2019-07-17 08:35:47 -0700220 targetSDK = getMinimumTargetSDK(context, appInfo, user);
Philip P. Moltmann64f20dd2019-06-25 08:41:51 -0700221 } else {
222 isWhiteListed = false;
223 targetSDK = 0;
224 }
225
226 return new SoftRestrictedPermissionPolicy() {
227 @Override
228 public int resolveAppOp() {
229 return OP_NONE;
230 }
231
232 @Override
233 public int getDesiredOpMode() {
234 return MODE_DEFAULT;
235 }
236
237 @Override
238 public boolean shouldSetAppOpIfNotDefault() {
239 return false;
240 }
241
242 @Override
243 public boolean canBeGranted() {
244 return isWhiteListed || targetSDK >= Build.VERSION_CODES.Q;
245 }
246 };
247 }
Philip P. Moltmannfaa788a2019-05-29 16:18:18 -0700248 default:
249 return DUMMY_POLICY;
250 }
251 }
252
253 /**
254 * @return An app op to be changed based on the state of the permission or
255 * {@link AppOpsManager#OP_NONE} if not app-op should be set.
256 */
Philip P. Moltmann404b51c2019-06-11 09:01:59 -0700257 public abstract int resolveAppOp();
Philip P. Moltmannfaa788a2019-05-29 16:18:18 -0700258
259 /**
Philip P. Moltmann404b51c2019-06-11 09:01:59 -0700260 * @return The mode the {@link #resolveAppOp() app op} should be in.
Philip P. Moltmannfaa788a2019-05-29 16:18:18 -0700261 */
Philip P. Moltmann404b51c2019-06-11 09:01:59 -0700262 public abstract @AppOpsManager.Mode int getDesiredOpMode();
Philip P. Moltmannfaa788a2019-05-29 16:18:18 -0700263
264 /**
Philip P. Moltmann404b51c2019-06-11 09:01:59 -0700265 * @return If the {@link #resolveAppOp() app op} should be set even if the app-op is currently
266 * not {@link AppOpsManager#MODE_DEFAULT}.
Philip P. Moltmannfaa788a2019-05-29 16:18:18 -0700267 */
268 public abstract boolean shouldSetAppOpIfNotDefault();
Philip P. Moltmann8625cdd2019-05-30 08:27:19 -0700269
270 /**
271 * @return If the permission can be granted
272 */
273 public abstract boolean canBeGranted();
Philip P. Moltmannfaa788a2019-05-29 16:18:18 -0700274}