• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.packageinstaller.permission.service;
18 
19 import static com.android.packageinstaller.PermissionControllerStatsLog.RUNTIME_PERMISSIONS_UPGRADE_RESULT;
20 
21 import android.Manifest;
22 import android.content.Context;
23 import android.content.pm.PackageInfo;
24 import android.content.pm.PackageManager;
25 import android.content.pm.PermissionInfo;
26 import android.permission.PermissionManager;
27 import android.text.TextUtils;
28 import android.util.Log;
29 
30 import androidx.annotation.NonNull;
31 
32 import com.android.packageinstaller.PermissionControllerStatsLog;
33 import com.android.packageinstaller.permission.model.AppPermissionGroup;
34 import com.android.packageinstaller.permission.model.Permission;
35 import com.android.packageinstaller.permission.utils.Utils;
36 
37 import java.util.ArrayList;
38 import java.util.List;
39 
40 /**
41  * This class handles upgrading the runtime permissions database
42  */
43 class RuntimePermissionsUpgradeController {
44     private static final String LOG_TAG = RuntimePermissionsUpgradeController.class.getSimpleName();
45 
46     // The latest version of the runtime permissions database
47     private static final int LATEST_VERSION = 7;
48 
RuntimePermissionsUpgradeController()49     private RuntimePermissionsUpgradeController() {
50         /* do nothing - hide constructor */
51     }
52 
upgradeIfNeeded(@onNull Context context)53     static void upgradeIfNeeded(@NonNull Context context) {
54         final PermissionManager permissionManager = context.getSystemService(
55                 PermissionManager.class);
56         final int currentVersion = permissionManager.getRuntimePermissionsVersion();
57 
58         whitelistAllSystemAppPermissions(context);
59 
60         final int upgradedVersion = onUpgradeLocked(context, currentVersion);
61 
62         if (upgradedVersion != LATEST_VERSION) {
63             Log.wtf("PermissionControllerService", "warning: upgrading permission database"
64                             + " to version " + LATEST_VERSION + " left it at " + currentVersion
65                             + " instead; this is probably a bug. Did you update LATEST_VERSION?",
66                     new Throwable());
67             throw new RuntimeException("db upgrade error");
68         }
69 
70         if (currentVersion != upgradedVersion) {
71             permissionManager.setRuntimePermissionsVersion(LATEST_VERSION);
72         }
73     }
74 
75     /**
76      * Whitelist permissions of system-apps.
77      *
78      * <p>Apps that are updated via OTAs are never installed. Hence their permission are never
79      * whitelisted. This code replaces that by always whitelisting them.
80      *
81      * @param context A context to talk to the platform
82      */
whitelistAllSystemAppPermissions(@onNull Context context)83     private static void whitelistAllSystemAppPermissions(@NonNull Context context) {
84         // Only whitelist permissions that are in the OTA. For non-OTA updates the installer should
85         // do the white-listing
86         final List<PackageInfo> apps = context.getPackageManager()
87                 .getInstalledPackages(PackageManager.GET_PERMISSIONS
88                         | PackageManager.MATCH_UNINSTALLED_PACKAGES
89                         | PackageManager.MATCH_FACTORY_ONLY);
90 
91         final int appCount = apps.size();
92         for (int i = 0; i < appCount; i++) {
93             final PackageInfo app = apps.get(i);
94 
95             if (app.requestedPermissions == null) {
96                 continue;
97             }
98 
99             for (String requestedPermission : app.requestedPermissions) {
100                 final PermissionInfo permInfo;
101                 try {
102                     permInfo = context.getPackageManager().getPermissionInfo(
103                             requestedPermission, 0);
104                 } catch (PackageManager.NameNotFoundException e) {
105                     continue;
106                 }
107 
108                 if ((permInfo.flags & (PermissionInfo.FLAG_HARD_RESTRICTED
109                         | PermissionInfo.FLAG_SOFT_RESTRICTED)) == 0) {
110                     continue;
111                 }
112 
113                 context.getPackageManager().addWhitelistedRestrictedPermission(
114                         app.packageName, requestedPermission,
115                         PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE);
116             }
117         }
118     }
119 
120     /**
121      * You must perform all necessary mutations to bring the runtime permissions
122      * database from the old to the new version. When you add a new upgrade step
123      * you *must* update LATEST_VERSION.
124      *
125      * @param context Context to access APIs.
126      * @param currentVersion The current db version.
127      */
onUpgradeLocked(@onNull Context context, int currentVersion)128     private static int onUpgradeLocked(@NonNull Context context, int currentVersion) {
129         final List<PackageInfo> apps = context.getPackageManager()
130                 .getInstalledPackages(PackageManager.MATCH_ALL
131                         | PackageManager.GET_PERMISSIONS);
132         final int appCount = apps.size();
133 
134         final boolean sdkUpgradedFromP;
135         if (currentVersion <= -1) {
136             Log.i(LOG_TAG, "Upgrading from Android P");
137 
138             sdkUpgradedFromP = true;
139 
140             currentVersion = 0;
141         } else {
142             sdkUpgradedFromP = false;
143         }
144 
145         if (currentVersion == 0) {
146             Log.i(LOG_TAG, "Grandfathering SMS and CallLog permissions");
147 
148             final List<String> smsPermissions = Utils.getPlatformPermissionNamesOfGroup(
149                     android.Manifest.permission_group.SMS);
150             final List<String> callLogPermissions = Utils.getPlatformPermissionNamesOfGroup(
151                     Manifest.permission_group.CALL_LOG);
152 
153             for (int i = 0; i < appCount; i++) {
154                 final PackageInfo app = apps.get(i);
155                 if (app.requestedPermissions == null) {
156                     continue;
157                 }
158 
159                 for (String requestedPermission : app.requestedPermissions) {
160                     if (smsPermissions.contains(requestedPermission)
161                             || callLogPermissions.contains(requestedPermission)) {
162                         context.getPackageManager().addWhitelistedRestrictedPermission(
163                                 app.packageName, requestedPermission,
164                                 PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE);
165                     }
166                 }
167             }
168             currentVersion = 1;
169         }
170 
171         if (currentVersion == 1) {
172             // moved to step 4->5 as it has to be after the grandfathering of loc bg perms
173             currentVersion = 2;
174         }
175 
176         if (currentVersion == 2) {
177             // moved to step 5->6 to clean up broken permission state during dogfooding
178             currentVersion = 3;
179         }
180 
181         if (currentVersion == 3) {
182             Log.i(LOG_TAG, "Grandfathering location background permissions");
183 
184             for (int i = 0; i < appCount; i++) {
185                 final PackageInfo app = apps.get(i);
186                 if (app.requestedPermissions == null) {
187                     continue;
188                 }
189 
190                 for (String requestedPermission : app.requestedPermissions) {
191                     if (requestedPermission.equals(
192                             Manifest.permission.ACCESS_BACKGROUND_LOCATION)) {
193                         context.getPackageManager().addWhitelistedRestrictedPermission(
194                                 app.packageName, Manifest.permission.ACCESS_BACKGROUND_LOCATION,
195                                 PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE);
196                         break;
197                     }
198                 }
199             }
200             currentVersion = 4;
201         }
202 
203         if (currentVersion == 4) {
204             // moved to step 5->6 to clean up broken permission state during beta 4->5 upgrade
205             currentVersion = 5;
206         }
207 
208         if (currentVersion == 5) {
209             Log.i(LOG_TAG, "Grandfathering Storage permissions");
210 
211             final List<String> storagePermissions = Utils.getPlatformPermissionNamesOfGroup(
212                     Manifest.permission_group.STORAGE);
213 
214             for (int i = 0; i < appCount; i++) {
215                 final PackageInfo app = apps.get(i);
216                 if (app.requestedPermissions == null) {
217                     continue;
218                 }
219 
220                 // We don't want to allow modification of storage post install, so put it
221                 // on the internal system whitelist to prevent the installer changing it.
222                 for (String requestedPermission : app.requestedPermissions) {
223                     if (storagePermissions.contains(requestedPermission)) {
224                         context.getPackageManager().addWhitelistedRestrictedPermission(
225                                 app.packageName, requestedPermission,
226                                 PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE);
227                     }
228                 }
229             }
230             currentVersion = 6;
231         }
232 
233         if (currentVersion == 6) {
234             if (sdkUpgradedFromP) {
235                 Log.i(LOG_TAG, "Expanding location permissions");
236 
237                 for (int i = 0; i < appCount; i++) {
238                     final PackageInfo app = apps.get(i);
239                     if (app.requestedPermissions == null) {
240                         continue;
241                     }
242 
243                     for (String perm : app.requestedPermissions) {
244                         String groupName = Utils.getGroupOfPlatformPermission(perm);
245 
246                         if (!TextUtils.equals(groupName, Manifest.permission_group.LOCATION)) {
247                             continue;
248                         }
249 
250                         final AppPermissionGroup group = AppPermissionGroup.create(context, app,
251                                 perm, false);
252                         final AppPermissionGroup bgGroup = group.getBackgroundPermissions();
253 
254                         if (group.areRuntimePermissionsGranted()
255                                 && bgGroup != null
256                                 && !bgGroup.isUserSet() && !bgGroup.isSystemFixed()
257                                 && !bgGroup.isPolicyFixed()) {
258                             bgGroup.grantRuntimePermissions(group.isUserFixed());
259 
260                             logRuntimePermissionUpgradeResult(bgGroup,
261                                     app.applicationInfo.uid, app.packageName);
262                         }
263 
264                         break;
265                     }
266                 }
267             } else {
268                 Log.i(LOG_TAG, "Not expanding location permissions as this is not an upgrade "
269                         + "from Android P");
270             }
271 
272             currentVersion = 7;
273         }
274 
275         // XXX: Add new upgrade steps above this point.
276 
277         return currentVersion;
278     }
279 
logRuntimePermissionUpgradeResult(AppPermissionGroup permissionGroup, int uid, String packageName)280     private static void logRuntimePermissionUpgradeResult(AppPermissionGroup permissionGroup,
281             int uid, String packageName) {
282         ArrayList<Permission> permissions = permissionGroup.getPermissions();
283         int numPermissions = permissions.size();
284         for (int i = 0; i < numPermissions; i++) {
285             Permission permission = permissions.get(i);
286             PermissionControllerStatsLog.write(RUNTIME_PERMISSIONS_UPGRADE_RESULT,
287                     permission.getName(), uid, packageName);
288             Log.v(LOG_TAG, "Runtime permission upgrade logged for permissionName="
289                     + permission.getName() + " uid=" + uid + " packageName=" + packageName);
290         }
291     }
292 }
293