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