1 /*
2  * Copyright (C) 2021 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.devicepolicy;
18 
19 import android.annotation.Nullable;
20 import android.content.ComponentName;
21 import android.os.UserHandle;
22 import android.util.Slog;
23 import android.util.SparseArray;
24 
25 import com.android.internal.annotations.VisibleForTesting;
26 import com.android.internal.util.JournaledFile;
27 
28 import java.io.File;
29 import java.io.IOException;
30 import java.nio.charset.Charset;
31 import java.nio.file.Files;
32 import java.util.ArrayList;
33 import java.util.List;
34 
35 /**
36  * Class for dealing with Device Policy Manager Service version upgrades.
37  * Initially, this class is responsible for upgrading the "device_policies.xml" file upon
38  * platform version upgrade.
39  *
40  * It is useful for policies which have a different default for an upgrading device than a
41  * newly-configured device (for example, the admin can grant sensors-related permissions by
42  * default on existing fully-managed devices that upgrade to Android S, but on devices set up
43  * with Android S the value of the policy is set explicitly during set-up).
44  *
45  * Practically, it's useful for changes to the data model of the {@code DevicePolicyData} and
46  * {@code ActiveAdmin} classes.
47  *
48  * To add a new upgrade step:
49  * (1) Increase the {@code DPMS_VERSION} constant in {@code DevicePolicyManagerService} by one.
50  * (2) Add an if statement in {@code upgradePolicy} comparing the version, performing the upgrade
51  *     step and setting the value of {@code currentVersion} to the newly-incremented version.
52  * (3) Add a test in {@code PolicyVersionUpgraderTest}.
53  */
54 public class PolicyVersionUpgrader {
55     private static final String LOG_TAG = "DevicePolicyManager";
56     private static final boolean VERBOSE_LOG = DevicePolicyManagerService.VERBOSE_LOG;
57     private final PolicyUpgraderDataProvider mProvider;
58     private final PolicyPathProvider mPathProvider;
59 
60     @VisibleForTesting
PolicyVersionUpgrader(PolicyUpgraderDataProvider provider, PolicyPathProvider pathProvider)61     PolicyVersionUpgrader(PolicyUpgraderDataProvider provider, PolicyPathProvider pathProvider) {
62         mProvider = provider;
63         mPathProvider = pathProvider;
64     }
65     /**
66      * Performs the upgrade steps for all users on the system.
67      *
68      * @param dpmsVersion The version to upgrade to.
69      */
upgradePolicy(int dpmsVersion)70     public void upgradePolicy(int dpmsVersion) {
71         int oldVersion = readVersion();
72         if (oldVersion >= dpmsVersion) {
73             Slog.i(LOG_TAG, String.format("Current version %d, latest version %d, not upgrading.",
74                     oldVersion, dpmsVersion));
75             return;
76         }
77 
78         final int[] allUsers = mProvider.getUsersForUpgrade();
79         final OwnersData ownersData = loadOwners(allUsers);
80 
81         // NOTE: The current version is provided in case the XML file format changes in a
82         // non-backwards-compatible way, so that DeviceAdminData could load it with
83         // old tags, for example.
84         final SparseArray<DevicePolicyData> allUsersData =
85                 loadAllUsersData(allUsers, oldVersion, ownersData);
86 
87         int currentVersion = oldVersion;
88         if (currentVersion == 0) {
89             Slog.i(LOG_TAG, String.format("Upgrading from version %d", currentVersion));
90             // The first upgrade (from no version to version 1) is to overwrite
91             // the "active-password" tag in case it was left around.
92             currentVersion = 1;
93         }
94 
95         if (currentVersion == 1) {
96             Slog.i(LOG_TAG, String.format("Upgrading from version %d", currentVersion));
97             upgradeSensorPermissionsAccess(allUsers, ownersData, allUsersData);
98             currentVersion = 2;
99         }
100 
101         if (currentVersion == 2) {
102             Slog.i(LOG_TAG, String.format("Upgrading from version %d", currentVersion));
103             upgradeProtectedPackages(ownersData, allUsersData);
104             currentVersion = 3;
105         }
106 
107         if (currentVersion == 3) {
108             Slog.i(LOG_TAG, String.format("Upgrading from version %d", currentVersion));
109             upgradePackageSuspension(allUsers, ownersData, allUsersData);
110             currentVersion = 4;
111         }
112 
113         if (currentVersion == 4) {
114             Slog.i(LOG_TAG, String.format("Upgrading from version %d", currentVersion));
115             initializeEffectiveKeepProfilesRunning(allUsersData);
116             currentVersion = 5;
117         }
118 
119         if (currentVersion == 5) {
120             Slog.i(LOG_TAG, String.format("Upgrading from version %d", currentVersion));
121             // No-op upgrade here:
122             // DevicePolicyData.mEffectiveKeepProfilesRunning is only stored in XML file when it is
123             // different from its default value, otherwise the tag is not written. When loading, if
124             // the tag is missing, the field retains the value previously assigned in the
125             // constructor, which is the default value.
126             // In version 5 the default value was 'true', in version 6 it is 'false', so when
127             // loading XML version 5 we need to initialize the field to 'true' for it to be restored
128             // correctly in case the tag is missing. This is done in loadDataForUser().
129             currentVersion = 6;
130         }
131 
132         writePoliciesAndVersion(allUsers, allUsersData, ownersData, currentVersion);
133     }
134 
135     /**
136      * This upgrade step is for Device Owner scenario only: For devices upgrading to S, if there is
137      * a device owner, it retains the ability to control sensors-related permission grants.
138      */
upgradeSensorPermissionsAccess( int[] allUsers, OwnersData ownersData, SparseArray<DevicePolicyData> allUsersData)139     private void upgradeSensorPermissionsAccess(
140             int[] allUsers, OwnersData ownersData, SparseArray<DevicePolicyData> allUsersData) {
141         for (int userId : allUsers) {
142             DevicePolicyData userData = allUsersData.get(userId);
143             if (userData == null) {
144                 continue;
145             }
146             for (ActiveAdmin admin : userData.mAdminList) {
147                 if (ownersData.mDeviceOwnerUserId == userId
148                         && ownersData.mDeviceOwner != null
149                         && ownersData.mDeviceOwner.admin.equals(admin.info.getComponent())) {
150                     Slog.i(LOG_TAG, String.format(
151                             "Marking Device Owner in user %d for permission grant ", userId));
152                     admin.mAdminCanGrantSensorsPermissions = true;
153                 }
154             }
155         }
156     }
157 
158     /**
159      * This upgrade step moves device owner protected packages to ActiveAdmin.
160      * Initially these packages were stored in DevicePolicyData, then moved to Owners without
161      * employing PolicyVersionUpgrader. Here we check both places.
162      */
upgradeProtectedPackages( OwnersData ownersData, SparseArray<DevicePolicyData> allUsersData)163     private void upgradeProtectedPackages(
164             OwnersData ownersData, SparseArray<DevicePolicyData> allUsersData) {
165         if (ownersData.mDeviceOwner == null) {
166             return;
167         }
168         List<String> protectedPackages = null;
169         DevicePolicyData doUserData = allUsersData.get(ownersData.mDeviceOwnerUserId);
170         if (doUserData == null) {
171             Slog.e(LOG_TAG, "No policy data for do user");
172             return;
173         }
174         if (ownersData.mDeviceOwnerProtectedPackages != null) {
175             protectedPackages = ownersData.mDeviceOwnerProtectedPackages
176                     .get(ownersData.mDeviceOwner.packageName);
177             if (protectedPackages != null) {
178                 Slog.i(LOG_TAG, "Found protected packages in Owners");
179             }
180             ownersData.mDeviceOwnerProtectedPackages = null;
181         } else if (doUserData.mUserControlDisabledPackages != null) {
182             Slog.i(LOG_TAG, "Found protected packages in DevicePolicyData");
183             protectedPackages = doUserData.mUserControlDisabledPackages;
184             doUserData.mUserControlDisabledPackages = null;
185         }
186 
187         ActiveAdmin doAdmin = doUserData.mAdminMap.get(ownersData.mDeviceOwner.admin);
188         if (doAdmin == null) {
189             Slog.e(LOG_TAG, "DO admin not found in DO user");
190             return;
191         }
192 
193         if (protectedPackages != null) {
194             doAdmin.protectedPackages = new ArrayList<>(protectedPackages);
195         }
196     }
197 
198     /**
199      * This upgrade step stores packages suspended via DPM.setPackagesSuspended() into ActiveAdmin
200      * data structure. Prior to this it was only persisted in PackageManager which doesn't have any
201      * way of knowing which admin suspended it.
202      */
upgradePackageSuspension( int[] allUsers, OwnersData ownersData, SparseArray<DevicePolicyData> allUsersData)203     private void upgradePackageSuspension(
204             int[] allUsers, OwnersData ownersData, SparseArray<DevicePolicyData> allUsersData) {
205         if (ownersData.mDeviceOwner != null) {
206             saveSuspendedPackages(allUsersData, ownersData.mDeviceOwnerUserId,
207                     ownersData.mDeviceOwner.admin);
208         }
209 
210         for (int i = 0; i < ownersData.mProfileOwners.size(); i++) {
211             int ownerUserId = ownersData.mProfileOwners.keyAt(i);
212             OwnersData.OwnerInfo ownerInfo = ownersData.mProfileOwners.valueAt(i);
213             saveSuspendedPackages(allUsersData, ownerUserId, ownerInfo.admin);
214         }
215     }
216 
saveSuspendedPackages(SparseArray<DevicePolicyData> allUsersData, int ownerUserId, ComponentName ownerPackage)217     private void saveSuspendedPackages(SparseArray<DevicePolicyData> allUsersData, int ownerUserId,
218             ComponentName ownerPackage) {
219         DevicePolicyData ownerUserData = allUsersData.get(ownerUserId);
220         if (ownerUserData == null) {
221             Slog.e(LOG_TAG, "No policy data for owner user, cannot migrate suspended packages");
222             return;
223         }
224 
225         ActiveAdmin ownerAdmin = ownerUserData.mAdminMap.get(ownerPackage);
226         if (ownerAdmin == null) {
227             Slog.e(LOG_TAG, "No admin for owner, cannot migrate suspended packages");
228             return;
229         }
230 
231         ownerAdmin.suspendedPackages = mProvider.getPlatformSuspendedPackages(ownerUserId);
232         Slog.i(LOG_TAG, String.format("Saved %d packages suspended by %s in user %d",
233                 ownerAdmin.suspendedPackages.size(), ownerPackage, ownerUserId));
234     }
235 
initializeEffectiveKeepProfilesRunning( SparseArray<DevicePolicyData> allUsersData)236     private void initializeEffectiveKeepProfilesRunning(
237             SparseArray<DevicePolicyData> allUsersData) {
238         DevicePolicyData systemUserData = allUsersData.get(UserHandle.USER_SYSTEM);
239         if (systemUserData == null) {
240             return;
241         }
242         systemUserData.mEffectiveKeepProfilesRunning = false;
243         Slog.i(LOG_TAG, "Keep profile running effective state set to false");
244     }
245 
loadOwners(int[] allUsers)246     private OwnersData loadOwners(int[] allUsers) {
247         OwnersData ownersData = new OwnersData(mPathProvider);
248         ownersData.load(allUsers);
249         return ownersData;
250     }
251 
writePoliciesAndVersion(int[] allUsers, SparseArray<DevicePolicyData> allUsersData, OwnersData ownersData, int currentVersion)252     private void writePoliciesAndVersion(int[] allUsers, SparseArray<DevicePolicyData> allUsersData,
253             OwnersData ownersData, int currentVersion) {
254         boolean allWritesSuccessful = true;
255         for (int user : allUsers) {
256             allWritesSuccessful =
257                     allWritesSuccessful && writeDataForUser(user, allUsersData.get(user));
258         }
259 
260         allWritesSuccessful = allWritesSuccessful && ownersData.writeDeviceOwner();
261         for (int user : allUsers) {
262             allWritesSuccessful = allWritesSuccessful && ownersData.writeProfileOwner(user);
263         }
264 
265         if (allWritesSuccessful) {
266             writeVersion(currentVersion);
267         } else {
268             Slog.e(LOG_TAG, String.format("Error: Failed upgrading policies to version %d",
269                     currentVersion));
270         }
271     }
272 
loadAllUsersData(int[] allUsers, int loadVersion, OwnersData ownersData)273     private SparseArray<DevicePolicyData> loadAllUsersData(int[] allUsers, int loadVersion,
274             OwnersData ownersData) {
275         final SparseArray<DevicePolicyData> allUsersData = new SparseArray<>();
276         for (int user: allUsers) {
277             ComponentName owner = getOwnerForUser(ownersData, user);
278             allUsersData.append(user, loadDataForUser(user, loadVersion, owner));
279         }
280         return allUsersData;
281     }
282 
283     @Nullable
getOwnerForUser(OwnersData ownersData, int user)284     private ComponentName getOwnerForUser(OwnersData ownersData, int user) {
285         ComponentName owner = null;
286         if (ownersData.mDeviceOwnerUserId == user && ownersData.mDeviceOwner != null) {
287             owner = ownersData.mDeviceOwner.admin;
288         } else if (ownersData.mProfileOwners.containsKey(user)) {
289             owner = ownersData.mProfileOwners.get(user).admin;
290         }
291         return owner;
292     }
293 
loadDataForUser( int userId, int loadVersion, ComponentName ownerComponent)294     private DevicePolicyData loadDataForUser(
295             int userId, int loadVersion, ComponentName ownerComponent) {
296         DevicePolicyData policy = new DevicePolicyData(userId);
297         // See version 5 -> 6 step in upgradePolicy()
298         if (loadVersion == 5 && userId == UserHandle.USER_SYSTEM) {
299             policy.mEffectiveKeepProfilesRunning = true;
300         }
301         DevicePolicyData.load(policy,
302                 mProvider.makeDevicePoliciesJournaledFile(userId),
303                 mProvider.getAdminInfoSupplier(userId),
304                 ownerComponent);
305         return policy;
306     }
307 
writeDataForUser(int userId, DevicePolicyData policy)308     private boolean writeDataForUser(int userId, DevicePolicyData policy) {
309         return DevicePolicyData.store(policy, mProvider.makeDevicePoliciesJournaledFile(userId));
310     }
311 
getVersionFile()312     private JournaledFile getVersionFile() {
313         return mProvider.makePoliciesVersionJournaledFile(UserHandle.USER_SYSTEM);
314     }
315 
readVersion()316     private int readVersion() {
317         JournaledFile versionFile = getVersionFile();
318 
319         File file = versionFile.chooseForRead();
320         if (VERBOSE_LOG) {
321             Slog.v(LOG_TAG, "Loading version from " + file);
322         }
323         try {
324             String versionString = Files.readAllLines(
325                     file.toPath(), Charset.defaultCharset()).get(0);
326             return Integer.parseInt(versionString);
327         } catch (IOException | NumberFormatException | IndexOutOfBoundsException e) {
328             Slog.e(LOG_TAG, "Error reading version", e);
329             return 0;
330         }
331     }
332 
writeVersion(int version)333     private void writeVersion(int version) {
334         JournaledFile versionFile = getVersionFile();
335 
336         File file = versionFile.chooseForWrite();
337         if (VERBOSE_LOG) {
338             Slog.v(LOG_TAG, String.format("Writing new version to: %s", file));
339         }
340 
341         try {
342             byte[] versionBytes = String.format("%d", version).getBytes();
343             Files.write(file.toPath(), versionBytes);
344             versionFile.commit();
345         } catch (IOException e) {
346             Slog.e(LOG_TAG, String.format("Writing version %d failed", version), e);
347             versionFile.rollback();
348         }
349     }
350 }
351