• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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         writePoliciesAndVersion(allUsers, allUsersData, ownersData, currentVersion);
108     }
109 
110     /**
111      * This upgrade step is for Device Owner scenario only: For devices upgrading to S, if there is
112      * a device owner, it retains the ability to control sensors-related permission grants.
113      */
upgradeSensorPermissionsAccess( int[] allUsers, OwnersData ownersData, SparseArray<DevicePolicyData> allUsersData)114     private void upgradeSensorPermissionsAccess(
115             int[] allUsers, OwnersData ownersData, SparseArray<DevicePolicyData> allUsersData) {
116         for (int userId : allUsers) {
117             DevicePolicyData userData = allUsersData.get(userId);
118             if (userData == null) {
119                 continue;
120             }
121             for (ActiveAdmin admin : userData.mAdminList) {
122                 if (ownersData.mDeviceOwnerUserId == userId
123                         && ownersData.mDeviceOwner != null
124                         && ownersData.mDeviceOwner.admin.equals(admin.info.getComponent())) {
125                     Slog.i(LOG_TAG, String.format(
126                             "Marking Device Owner in user %d for permission grant ", userId));
127                     admin.mAdminCanGrantSensorsPermissions = true;
128                 }
129             }
130         }
131     }
132 
133     /**
134      * This upgrade step moves device owner protected packages to ActiveAdmin.
135      * Initially these packages were stored in DevicePolicyData, then moved to Owners without
136      * employing PolicyVersionUpgrader. Here we check both places.
137      */
upgradeProtectedPackages( OwnersData ownersData, SparseArray<DevicePolicyData> allUsersData)138     private void upgradeProtectedPackages(
139             OwnersData ownersData, SparseArray<DevicePolicyData> allUsersData) {
140         if (ownersData.mDeviceOwner == null) {
141             return;
142         }
143         List<String> protectedPackages = null;
144         DevicePolicyData doUserData = allUsersData.get(ownersData.mDeviceOwnerUserId);
145         if (doUserData == null) {
146             Slog.e(LOG_TAG, "No policy data for do user");
147             return;
148         }
149         if (ownersData.mDeviceOwnerProtectedPackages != null) {
150             protectedPackages = ownersData.mDeviceOwnerProtectedPackages
151                     .get(ownersData.mDeviceOwner.packageName);
152             if (protectedPackages != null) {
153                 Slog.i(LOG_TAG, "Found protected packages in Owners");
154             }
155             ownersData.mDeviceOwnerProtectedPackages = null;
156         } else if (doUserData.mUserControlDisabledPackages != null) {
157             Slog.i(LOG_TAG, "Found protected packages in DevicePolicyData");
158             protectedPackages = doUserData.mUserControlDisabledPackages;
159             doUserData.mUserControlDisabledPackages = null;
160         }
161 
162         ActiveAdmin doAdmin = doUserData.mAdminMap.get(ownersData.mDeviceOwner.admin);
163         if (doAdmin == null) {
164             Slog.e(LOG_TAG, "DO admin not found in DO user");
165             return;
166         }
167 
168         if (protectedPackages != null) {
169             doAdmin.protectedPackages = new ArrayList<>(protectedPackages);
170         }
171     }
172 
loadOwners(int[] allUsers)173     private OwnersData loadOwners(int[] allUsers) {
174         OwnersData ownersData = new OwnersData(mPathProvider);
175         ownersData.load(allUsers);
176         return ownersData;
177     }
178 
writePoliciesAndVersion(int[] allUsers, SparseArray<DevicePolicyData> allUsersData, OwnersData ownersData, int currentVersion)179     private void writePoliciesAndVersion(int[] allUsers, SparseArray<DevicePolicyData> allUsersData,
180             OwnersData ownersData, int currentVersion) {
181         boolean allWritesSuccessful = true;
182         for (int user : allUsers) {
183             allWritesSuccessful =
184                     allWritesSuccessful && writeDataForUser(user, allUsersData.get(user));
185         }
186 
187         allWritesSuccessful = allWritesSuccessful && ownersData.writeDeviceOwner();
188         for (int user : allUsers) {
189             allWritesSuccessful = allWritesSuccessful && ownersData.writeProfileOwner(user);
190         }
191 
192         if (allWritesSuccessful) {
193             writeVersion(currentVersion);
194         } else {
195             Slog.e(LOG_TAG, String.format("Error: Failed upgrading policies to version %d",
196                     currentVersion));
197         }
198     }
199 
loadAllUsersData(int[] allUsers, int loadVersion, OwnersData ownersData)200     private SparseArray<DevicePolicyData> loadAllUsersData(int[] allUsers, int loadVersion,
201             OwnersData ownersData) {
202         final SparseArray<DevicePolicyData> allUsersData = new SparseArray<>();
203         for (int user: allUsers) {
204             ComponentName owner = getOwnerForUser(ownersData, user);
205             allUsersData.append(user, loadDataForUser(user, loadVersion, owner));
206         }
207         return allUsersData;
208     }
209 
210     @Nullable
getOwnerForUser(OwnersData ownersData, int user)211     private ComponentName getOwnerForUser(OwnersData ownersData, int user) {
212         ComponentName owner = null;
213         if (ownersData.mDeviceOwnerUserId == user && ownersData.mDeviceOwner != null) {
214             owner = ownersData.mDeviceOwner.admin;
215         } else if (ownersData.mProfileOwners.containsKey(user)) {
216             owner = ownersData.mProfileOwners.get(user).admin;
217         }
218         return owner;
219     }
220 
loadDataForUser( int userId, int loadVersion, ComponentName ownerComponent)221     private DevicePolicyData loadDataForUser(
222             int userId, int loadVersion, ComponentName ownerComponent) {
223         DevicePolicyData policy = new DevicePolicyData(userId);
224         DevicePolicyData.load(policy,
225                 !mProvider.storageManagerIsFileBasedEncryptionEnabled(),
226                 mProvider.makeDevicePoliciesJournaledFile(userId),
227                 mProvider.getAdminInfoSupplier(userId),
228                 ownerComponent);
229         return policy;
230     }
231 
writeDataForUser(int userId, DevicePolicyData policy)232     private boolean writeDataForUser(int userId, DevicePolicyData policy) {
233         return DevicePolicyData.store(
234                 policy,
235                 mProvider.makeDevicePoliciesJournaledFile(userId),
236                 !mProvider.storageManagerIsFileBasedEncryptionEnabled());
237     }
238 
getVersionFile()239     private JournaledFile getVersionFile() {
240         return mProvider.makePoliciesVersionJournaledFile(UserHandle.USER_SYSTEM);
241     }
242 
readVersion()243     private int readVersion() {
244         JournaledFile versionFile = getVersionFile();
245 
246         File file = versionFile.chooseForRead();
247         if (VERBOSE_LOG) {
248             Slog.v(LOG_TAG, "Loading version from " + file);
249         }
250         try {
251             String versionString = Files.readAllLines(
252                     file.toPath(), Charset.defaultCharset()).get(0);
253             return Integer.parseInt(versionString);
254         } catch (IOException | NumberFormatException | IndexOutOfBoundsException e) {
255             Slog.e(LOG_TAG, "Error reading version", e);
256             return 0;
257         }
258     }
259 
writeVersion(int version)260     private void writeVersion(int version) {
261         JournaledFile versionFile = getVersionFile();
262 
263         File file = versionFile.chooseForWrite();
264         if (VERBOSE_LOG) {
265             Slog.v(LOG_TAG, String.format("Writing new version to: %s", file));
266         }
267 
268         try {
269             byte[] versionBytes = String.format("%d", version).getBytes();
270             Files.write(file.toPath(), versionBytes);
271             versionFile.commit();
272         } catch (IOException e) {
273             Slog.e(LOG_TAG, String.format("Writing version %d failed", version), e);
274             versionFile.rollback();
275         }
276     }
277 }
278