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