blob: 81ec46634e8ad30b4606986926f08974472e2283 [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;
Zim87eacab2020-01-24 01:03:09 +000021import static android.Manifest.permission.WRITE_MEDIA_STORAGE;
Philip P. Moltmannfaa788a2019-05-29 16:18:18 -070022import static android.app.AppOpsManager.OP_LEGACY_STORAGE;
23import static android.app.AppOpsManager.OP_NONE;
24import static android.content.pm.PackageManager.FLAG_PERMISSION_APPLY_RESTRICTION;
Philip P. Moltmann8625cdd2019-05-30 08:27:19 -070025import static android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT;
26import static android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT;
27import static android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT;
Zim87eacab2020-01-24 01:03:09 +000028import static android.content.pm.PackageManager.PERMISSION_GRANTED;
Philip P. Moltmannfaa788a2019-05-29 16:18:18 -070029
Philip P. Moltmannf75bb772019-07-17 08:35:47 -070030import static java.lang.Integer.min;
31
Philip P. Moltmannfaa788a2019-05-29 16:18:18 -070032import android.annotation.NonNull;
Philip P. Moltmannfad1a8f2019-06-14 09:02:24 -070033import android.annotation.Nullable;
Philip P. Moltmannfaa788a2019-05-29 16:18:18 -070034import android.app.AppOpsManager;
35import android.content.Context;
36import android.content.pm.ApplicationInfo;
Philip P. Moltmannf75bb772019-07-17 08:35:47 -070037import android.content.pm.PackageManager;
Philip P. Moltmann8625cdd2019-05-30 08:27:19 -070038import android.os.Build;
Philip P. Moltmann69b645f2019-06-17 14:28:11 -070039import android.os.UserHandle;
Philip P. Moltmannfaa788a2019-05-29 16:18:18 -070040
41/**
42 * The behavior of soft restricted permissions is different for each permission. This class collects
43 * the policies in one place.
Philip P. Moltmannf4de94f2019-05-30 11:14:33 -070044 *
45 * This is the twin of
46 * {@link com.android.packageinstaller.permission.utils.SoftRestrictedPermissionPolicy}
Philip P. Moltmannfaa788a2019-05-29 16:18:18 -070047 */
48public abstract class SoftRestrictedPermissionPolicy {
Philip P. Moltmann8625cdd2019-05-30 08:27:19 -070049 private static final int FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT =
50 FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT
51 | FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT
52 | FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT;
53
Philip P. Moltmannfaa788a2019-05-29 16:18:18 -070054 private static final SoftRestrictedPermissionPolicy DUMMY_POLICY =
55 new SoftRestrictedPermissionPolicy() {
56 @Override
Hai Zhangfa291702019-09-19 16:35:44 -070057 public boolean mayGrantPermission() {
Philip P. Moltmann8625cdd2019-05-30 08:27:19 -070058 return true;
59 }
Philip P. Moltmannfaa788a2019-05-29 16:18:18 -070060 };
61
62 /**
Philip P. Moltmannf75bb772019-07-17 08:35:47 -070063 * TargetSDK is per package. To make sure two apps int the same shared UID do not fight over
64 * what to set, always compute the combined targetSDK.
65 *
66 * @param context A context
67 * @param appInfo The app that is changed
68 * @param user The user the app belongs to
69 *
70 * @return The minimum targetSDK of all apps sharing the uid of the app
71 */
72 private static int getMinimumTargetSDK(@NonNull Context context,
73 @NonNull ApplicationInfo appInfo, @NonNull UserHandle user) {
74 PackageManager pm = context.getPackageManager();
75
76 int minimumTargetSDK = appInfo.targetSdkVersion;
77
78 String[] uidPkgs = pm.getPackagesForUid(appInfo.uid);
79 if (uidPkgs != null) {
80 for (String uidPkg : uidPkgs) {
81 if (!uidPkg.equals(appInfo.packageName)) {
82 ApplicationInfo uidPkgInfo;
83 try {
84 uidPkgInfo = pm.getApplicationInfoAsUser(uidPkg, 0, user);
85 } catch (PackageManager.NameNotFoundException e) {
86 continue;
87 }
88
89 minimumTargetSDK = min(minimumTargetSDK, uidPkgInfo.targetSdkVersion);
90 }
91 }
92 }
93
94 return minimumTargetSDK;
95 }
96
97 /**
Philip P. Moltmannfaa788a2019-05-29 16:18:18 -070098 * Get the policy for a soft restricted permission.
99 *
100 * @param context A context to use
Hai Zhangfa291702019-09-19 16:35:44 -0700101 * @param appInfo The application the permission belongs to.
102 * @param user The user the app belongs to.
Philip P. Moltmannfaa788a2019-05-29 16:18:18 -0700103 * @param permission The name of the permission
104 *
105 * @return The policy for this permission
106 */
107 public static @NonNull SoftRestrictedPermissionPolicy forPermission(@NonNull Context context,
Philip P. Moltmannfad1a8f2019-06-14 09:02:24 -0700108 @Nullable ApplicationInfo appInfo, @Nullable UserHandle user,
Philip P. Moltmann69b645f2019-06-17 14:28:11 -0700109 @NonNull String permission) {
Philip P. Moltmannfaa788a2019-05-29 16:18:18 -0700110 switch (permission) {
111 // Storage uses a special app op to decide the mount state and supports soft restriction
112 // where the restricted state allows the permission but only for accessing the medial
113 // collections.
Philip P. Moltmann64f20dd2019-06-25 08:41:51 -0700114 case READ_EXTERNAL_STORAGE: {
Philip P. Moltmannfad1a8f2019-06-14 09:02:24 -0700115 final boolean isWhiteListed;
Hai Zhangfa291702019-09-19 16:35:44 -0700116 boolean shouldApplyRestriction;
Philip P. Moltmannfad1a8f2019-06-14 09:02:24 -0700117 final int targetSDK;
Hai Zhangfa291702019-09-19 16:35:44 -0700118 final boolean hasRequestedLegacyExternalStorage;
Zim87eacab2020-01-24 01:03:09 +0000119 final boolean hasWriteMediaStorageGrantedForUid;
Philip P. Moltmannfad1a8f2019-06-14 09:02:24 -0700120
121 if (appInfo != null) {
Philip P. Moltmannf75bb772019-07-17 08:35:47 -0700122 PackageManager pm = context.getPackageManager();
Hai Zhangfa291702019-09-19 16:35:44 -0700123 int flags = pm.getPermissionFlags(permission, appInfo.packageName, user);
Philip P. Moltmannfad1a8f2019-06-14 09:02:24 -0700124 isWhiteListed = (flags & FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT) != 0;
Hai Zhangfa291702019-09-19 16:35:44 -0700125 shouldApplyRestriction = (flags & FLAG_PERMISSION_APPLY_RESTRICTION) != 0;
Philip P. Moltmannf75bb772019-07-17 08:35:47 -0700126 targetSDK = getMinimumTargetSDK(context, appInfo, user);
Hai Zhangfa291702019-09-19 16:35:44 -0700127 hasRequestedLegacyExternalStorage = hasUidRequestedLegacyExternalStorage(
128 appInfo.uid, context);
Zim87eacab2020-01-24 01:03:09 +0000129 hasWriteMediaStorageGrantedForUid = hasWriteMediaStorageGrantedForUid(
130 appInfo.uid, context);
Philip P. Moltmannfad1a8f2019-06-14 09:02:24 -0700131 } else {
Philip P. Moltmannfad1a8f2019-06-14 09:02:24 -0700132 isWhiteListed = false;
Hai Zhangfa291702019-09-19 16:35:44 -0700133 shouldApplyRestriction = false;
Philip P. Moltmannfad1a8f2019-06-14 09:02:24 -0700134 targetSDK = 0;
Hai Zhangfa291702019-09-19 16:35:44 -0700135 hasRequestedLegacyExternalStorage = false;
Zim87eacab2020-01-24 01:03:09 +0000136 hasWriteMediaStorageGrantedForUid = false;
Philip P. Moltmannfad1a8f2019-06-14 09:02:24 -0700137 }
Philip P. Moltmannfaa788a2019-05-29 16:18:18 -0700138
Hai Zhangfa291702019-09-19 16:35:44 -0700139 // We have a check in PermissionPolicyService.PermissionToOpSynchroniser.setUidMode
140 // to prevent apps losing files in legacy storage, because we are holding the
141 // package manager lock here. If we ever remove this policy that check should be
142 // removed as well.
Philip P. Moltmannfaa788a2019-05-29 16:18:18 -0700143 return new SoftRestrictedPermissionPolicy() {
144 @Override
Hai Zhangfa291702019-09-19 16:35:44 -0700145 public boolean mayGrantPermission() {
146 return isWhiteListed || targetSDK >= Build.VERSION_CODES.Q;
147 }
148 @Override
149 public int getExtraAppOpCode() {
Philip P. Moltmannfaa788a2019-05-29 16:18:18 -0700150 return OP_LEGACY_STORAGE;
151 }
Philip P. Moltmannfaa788a2019-05-29 16:18:18 -0700152 @Override
Hai Zhangfa291702019-09-19 16:35:44 -0700153 public boolean mayAllowExtraAppOp() {
Zim87eacab2020-01-24 01:03:09 +0000154 return !shouldApplyRestriction && targetSDK <= Build.VERSION_CODES.Q
155 && (hasRequestedLegacyExternalStorage
156 || hasWriteMediaStorageGrantedForUid);
Philip P. Moltmannfaa788a2019-05-29 16:18:18 -0700157 }
Philip P. Moltmannfaa788a2019-05-29 16:18:18 -0700158 @Override
Hai Zhangfa291702019-09-19 16:35:44 -0700159 public boolean mayDenyExtraAppOpIfGranted() {
160 return shouldApplyRestriction;
Philip P. Moltmann8625cdd2019-05-30 08:27:19 -0700161 }
Philip P. Moltmannfaa788a2019-05-29 16:18:18 -0700162 };
163 }
Philip P. Moltmann64f20dd2019-06-25 08:41:51 -0700164 case WRITE_EXTERNAL_STORAGE: {
165 final boolean isWhiteListed;
166 final int targetSDK;
167
168 if (appInfo != null) {
169 final int flags = context.getPackageManager().getPermissionFlags(permission,
170 appInfo.packageName, user);
171 isWhiteListed = (flags & FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT) != 0;
Philip P. Moltmannf75bb772019-07-17 08:35:47 -0700172 targetSDK = getMinimumTargetSDK(context, appInfo, user);
Philip P. Moltmann64f20dd2019-06-25 08:41:51 -0700173 } else {
174 isWhiteListed = false;
175 targetSDK = 0;
176 }
177
178 return new SoftRestrictedPermissionPolicy() {
179 @Override
Hai Zhangfa291702019-09-19 16:35:44 -0700180 public boolean mayGrantPermission() {
Philip P. Moltmann64f20dd2019-06-25 08:41:51 -0700181 return isWhiteListed || targetSDK >= Build.VERSION_CODES.Q;
182 }
183 };
184 }
Philip P. Moltmannfaa788a2019-05-29 16:18:18 -0700185 default:
186 return DUMMY_POLICY;
187 }
188 }
189
Hai Zhangfa291702019-09-19 16:35:44 -0700190 private static boolean hasUidRequestedLegacyExternalStorage(int uid, @NonNull Context context) {
191 PackageManager packageManager = context.getPackageManager();
192 String[] packageNames = packageManager.getPackagesForUid(uid);
193 if (packageNames == null) {
194 return false;
Winson243e7ea2019-08-07 10:51:25 -0700195 }
Hai Zhangfa291702019-09-19 16:35:44 -0700196 UserHandle user = UserHandle.getUserHandleForUid(uid);
197 for (String packageName : packageNames) {
198 ApplicationInfo applicationInfo;
199 try {
200 applicationInfo = packageManager.getApplicationInfoAsUser(packageName, 0, user);
201 } catch (PackageManager.NameNotFoundException e) {
202 continue;
203 }
204 if (applicationInfo.hasRequestedLegacyExternalStorage()) {
205 return true;
206 }
207 }
208 return false;
Winson243e7ea2019-08-07 10:51:25 -0700209 }
210
Zim87eacab2020-01-24 01:03:09 +0000211 private static boolean hasWriteMediaStorageGrantedForUid(int uid, @NonNull Context context) {
212 PackageManager packageManager = context.getPackageManager();
213 String[] packageNames = packageManager.getPackagesForUid(uid);
214 if (packageNames == null) {
215 return false;
216 }
217
218 for (String packageName : packageNames) {
219 if (packageManager.checkPermission(WRITE_MEDIA_STORAGE, packageName)
220 == PERMISSION_GRANTED) {
221 return true;
222 }
223 }
224 return false;
225 }
226
Winson243e7ea2019-08-07 10:51:25 -0700227 /**
Hai Zhangfa291702019-09-19 16:35:44 -0700228 * @return If the permission can be granted
229 */
230 public abstract boolean mayGrantPermission();
231
232 /**
Philip P. Moltmannfaa788a2019-05-29 16:18:18 -0700233 * @return An app op to be changed based on the state of the permission or
234 * {@link AppOpsManager#OP_NONE} if not app-op should be set.
235 */
Hai Zhangfa291702019-09-19 16:35:44 -0700236 public int getExtraAppOpCode() {
237 return OP_NONE;
238 }
Philip P. Moltmannfaa788a2019-05-29 16:18:18 -0700239
240 /**
Hai Zhangfa291702019-09-19 16:35:44 -0700241 * @return Whether the {@link #getExtraAppOpCode() app op} may be granted.
Philip P. Moltmannfaa788a2019-05-29 16:18:18 -0700242 */
Hai Zhangfa291702019-09-19 16:35:44 -0700243 public boolean mayAllowExtraAppOp() {
244 return false;
245 }
Philip P. Moltmannfaa788a2019-05-29 16:18:18 -0700246
247 /**
Hai Zhangfa291702019-09-19 16:35:44 -0700248 * @return Whether the {@link #getExtraAppOpCode() app op} may be denied if was granted.
Philip P. Moltmannfaa788a2019-05-29 16:18:18 -0700249 */
Hai Zhangfa291702019-09-19 16:35:44 -0700250 public boolean mayDenyExtraAppOpIfGranted() {
251 return false;
252 }
Philip P. Moltmannfaa788a2019-05-29 16:18:18 -0700253}