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