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