1 /* 2 * Copyright (C) 2018 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.cts.deviceowner; 18 19 import android.app.admin.DevicePolicyManager.InstallSystemUpdateCallback; 20 import android.content.Intent; 21 import android.content.IntentFilter; 22 import android.net.Uri; 23 import android.os.BatteryManager; 24 import android.os.SystemClock; 25 import android.os.SystemProperties; 26 27 import com.android.compatibility.common.util.SystemUtil; 28 29 import java.io.File; 30 import java.util.concurrent.CountDownLatch; 31 import java.util.concurrent.TimeUnit; 32 33 /** 34 * Test {@link android.app.admin.DevicePolicyManager#installSystemUpdate} 35 */ 36 public class InstallUpdateTest extends BaseDeviceOwnerTest { 37 private static final int BATTERY_STATE_CHANGE_TIMEOUT_MS = 5000; 38 private static final int BATTERY_STATE_CHANGE_SLEEP_PER_CHECK_MS = 50; 39 private static final int TEST_BATTERY_THRESHOLD = 10; 40 private static final IntentFilter BATTERY_CHANGED_FILTER = 41 new IntentFilter(Intent.ACTION_BATTERY_CHANGED); 42 private static final String AB_DEVICE_KEY = "ro.build.ab_update"; 43 44 public static final String TEST_SYSTEM_UPDATES_DIR = 45 "/data/local/tmp/cts/deviceowner/"; 46 public static final int TIMEOUT = 5; 47 48 private int callbackErrorCode; 49 50 public void testInstallUpdate_failFileNotFound() throws InterruptedException { 51 assertUpdateError( 52 "random", 53 InstallSystemUpdateCallback.UPDATE_ERROR_FILE_NOT_FOUND); 54 } 55 56 public void testInstallUpdate_failNoZipOtaFile() throws InterruptedException { 57 assertUpdateError("notZip.zi", 58 isDeviceAB() 59 ? InstallSystemUpdateCallback.UPDATE_ERROR_UPDATE_FILE_INVALID 60 : InstallSystemUpdateCallback.UPDATE_ERROR_UNKNOWN); 61 } 62 63 public void testInstallUpdate_failWrongPayloadFile() throws InterruptedException { 64 assertUpdateError("wrongPayload.zip", 65 isDeviceAB() 66 ? InstallSystemUpdateCallback.UPDATE_ERROR_UPDATE_FILE_INVALID 67 : InstallSystemUpdateCallback.UPDATE_ERROR_UNKNOWN); 68 } 69 70 public void testInstallUpdate_failEmptyOtaFile() throws InterruptedException { 71 assertUpdateError("empty.zip", 72 isDeviceAB() 73 ? InstallSystemUpdateCallback.UPDATE_ERROR_UPDATE_FILE_INVALID 74 : InstallSystemUpdateCallback.UPDATE_ERROR_UNKNOWN); 75 } 76 77 public void testInstallUpdate_failWrongHash() throws InterruptedException { 78 assertUpdateError("wrongHash.zip", 79 isDeviceAB() 80 ? InstallSystemUpdateCallback.UPDATE_ERROR_UPDATE_FILE_INVALID 81 : InstallSystemUpdateCallback.UPDATE_ERROR_UNKNOWN); 82 } 83 84 public void testInstallUpdate_failWrongSize() throws InterruptedException { 85 assertUpdateError("wrongSize.zip", 86 isDeviceAB() 87 ? InstallSystemUpdateCallback.UPDATE_ERROR_UPDATE_FILE_INVALID 88 : InstallSystemUpdateCallback.UPDATE_ERROR_UNKNOWN); 89 } 90 91 public void testInstallUpdate_notCharging_belowThreshold_failsBatteryCheck() throws Exception { 92 try { 93 setNonChargingBatteryThreshold(TEST_BATTERY_THRESHOLD); 94 setNonChargingBatteryLevelAndWait(TEST_BATTERY_THRESHOLD - 1); 95 assertUpdateError("wrongSize.zip", 96 InstallSystemUpdateCallback.UPDATE_ERROR_BATTERY_LOW); 97 } finally { 98 resetBatteryState(); 99 resetDevicePolicyConstants(); 100 } 101 } 102 103 public void testInstallUpdate_notCharging_aboveThreshold_passesBatteryCheck() throws Exception { 104 try { 105 setNonChargingBatteryThreshold(TEST_BATTERY_THRESHOLD); 106 setNonChargingBatteryLevelAndWait(TEST_BATTERY_THRESHOLD); 107 // Positive CTS tests aren't possible, so we verify that we get the file-related error 108 // rather than the battery one. 109 assertUpdateError("wrongSize.zip", 110 isDeviceAB() 111 ? InstallSystemUpdateCallback.UPDATE_ERROR_UPDATE_FILE_INVALID 112 : InstallSystemUpdateCallback.UPDATE_ERROR_UNKNOWN); 113 } finally { 114 resetBatteryState(); 115 resetDevicePolicyConstants(); 116 } 117 } 118 119 public void testInstallUpdate_charging_belowThreshold_failsBatteryCheck() throws Exception { 120 try { 121 setChargingBatteryThreshold(TEST_BATTERY_THRESHOLD); 122 setChargingBatteryLevelAndWait(TEST_BATTERY_THRESHOLD - 1); 123 assertUpdateError("wrongSize.zip", 124 InstallSystemUpdateCallback.UPDATE_ERROR_BATTERY_LOW); 125 } finally { 126 resetBatteryState(); 127 resetDevicePolicyConstants(); 128 } 129 } 130 131 public void testInstallUpdate_charging_aboveThreshold_passesBatteryCheck() throws Exception { 132 try { 133 setChargingBatteryThreshold(TEST_BATTERY_THRESHOLD); 134 setChargingBatteryLevelAndWait(TEST_BATTERY_THRESHOLD); 135 // Positive CTS tests aren't possible, so we verify that we get the file-related error 136 // rather than the battery one. 137 assertUpdateError("wrongSize.zip", 138 isDeviceAB() 139 ? InstallSystemUpdateCallback.UPDATE_ERROR_UPDATE_FILE_INVALID 140 : InstallSystemUpdateCallback.UPDATE_ERROR_UNKNOWN); 141 } finally { 142 resetBatteryState(); 143 resetDevicePolicyConstants(); 144 } 145 } 146 147 private void assertUpdateError(String fileName, int expectedErrorCode) 148 throws InterruptedException { 149 CountDownLatch latch = new CountDownLatch(1); 150 Uri uri = Uri.fromFile(new File(TEST_SYSTEM_UPDATES_DIR, fileName)); 151 mDevicePolicyManager.installSystemUpdate(getWho(), uri, 152 Runnable::run, new InstallSystemUpdateCallback() { 153 @Override 154 public void onInstallUpdateError(int errorCode, String errorMessage) { 155 callbackErrorCode = errorCode; 156 latch.countDown(); 157 } 158 }); 159 assertTrue(latch.await(TIMEOUT, TimeUnit.MINUTES)); 160 assertEquals(expectedErrorCode, callbackErrorCode); 161 } 162 163 private void setNonChargingBatteryThreshold(int threshold) { 164 SystemUtil.runShellCommand( 165 "settings put global device_policy_constants battery_threshold_not_charging=" 166 + threshold); 167 } 168 169 private void setNonChargingBatteryLevelAndWait(int level) throws Exception { 170 setBatteryStateAndWait(/* plugged= */ false, level); 171 } 172 173 private void setChargingBatteryThreshold(int threshold) { 174 SystemUtil.runShellCommand( 175 "settings put global device_policy_constants battery_threshold_charging=" 176 + threshold); 177 } 178 179 private void setChargingBatteryLevelAndWait(int level) throws Exception { 180 setBatteryStateAndWait(/* plugged= */ true, level); 181 } 182 183 /** Should be paired with {@link #resetBatteryState()} in a {@code finally} block. */ 184 private void setBatteryStateAndWait(boolean plugged, int level) throws Exception { 185 SystemUtil.runShellCommand(plugged ? "cmd battery set ac 1" : "cmd battery unplug"); 186 SystemUtil.runShellCommand("cmd battery set -f level " + level); 187 long startTime = SystemClock.elapsedRealtime(); 188 while (!isBatteryState(plugged, level) 189 && SystemClock.elapsedRealtime() <= startTime + BATTERY_STATE_CHANGE_TIMEOUT_MS) { 190 Thread.sleep(BATTERY_STATE_CHANGE_SLEEP_PER_CHECK_MS); 191 } 192 } 193 194 private boolean isBatteryState(boolean plugged, int level) { 195 final Intent batteryStatus = 196 mContext.registerReceiver(/* receiver= */ null, BATTERY_CHANGED_FILTER); 197 return isPluggedIn(batteryStatus) == plugged 198 && calculateBatteryPercentage(batteryStatus) == level; 199 } 200 201 private boolean isPluggedIn(Intent batteryStatus) { 202 return batteryStatus.getIntExtra(BatteryManager.EXTRA_PLUGGED, /* defaultValue= */ -1) > 0; 203 } 204 205 private float calculateBatteryPercentage(Intent batteryStatus) { 206 int level = batteryStatus.getIntExtra(BatteryManager.EXTRA_LEVEL, /* defaultValue= */ -1); 207 int scale = batteryStatus.getIntExtra(BatteryManager.EXTRA_SCALE, /* defaultValue= */ -1); 208 return 100 * level / (float) scale; 209 } 210 211 private void resetBatteryState() { 212 SystemUtil.runShellCommand("dumpsys battery reset"); 213 } 214 215 private void resetDevicePolicyConstants() { 216 SystemUtil.runShellCommand("settings delete global device_policy_constants"); 217 } 218 219 private boolean isDeviceAB() { 220 return "true".equalsIgnoreCase(SystemProperties.get(AB_DEVICE_KEY, "")); 221 } 222 } 223