1 /* 2 * Copyright (C) 2008 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 android.os; 18 19 import static org.junit.Assert.assertEquals; 20 import static org.junit.Assert.assertNotNull; 21 import static org.junit.Assert.fail; 22 import static org.mockito.Mockito.reset; 23 import static org.mockito.Mockito.timeout; 24 import static org.mockito.Mockito.verify; 25 26 import android.content.Context; 27 import android.support.test.uiautomator.UiDevice; 28 import android.test.AndroidTestCase; 29 30 import androidx.test.InstrumentationRegistry; 31 import androidx.test.filters.SmallTest; 32 33 import org.junit.After; 34 import org.junit.Test; 35 import org.mockito.Mock; 36 import org.mockito.MockitoAnnotations; 37 38 import java.util.concurrent.Executor; 39 import java.util.concurrent.Executors; 40 41 public class PowerManagerTest extends AndroidTestCase { 42 43 private static final String TAG = "PowerManagerTest"; 44 private PowerManager mPm; 45 private UiDevice mUiDevice; 46 private Executor mExec = Executors.newSingleThreadExecutor(); 47 @Mock 48 private PowerManager.OnThermalStatusChangedListener mListener1; 49 @Mock 50 private PowerManager.OnThermalStatusChangedListener mListener2; 51 private static final long CALLBACK_TIMEOUT_MILLI_SEC = 5000; nativeObtainPowerSaveStateParcel(boolean batterySaverEnabled, boolean globalBatterySaverEnabled, int locationMode, int soundTriggerMode, float brightnessFactor)52 private native Parcel nativeObtainPowerSaveStateParcel(boolean batterySaverEnabled, 53 boolean globalBatterySaverEnabled, int locationMode, int soundTriggerMode, 54 float brightnessFactor); nativeUnparcelAndVerifyPowerSaveState(Parcel parcel, boolean batterySaverEnabled, boolean globalBatterySaverEnabled, int locationMode, int soundTriggerMode, float brightnessFactor)55 private native void nativeUnparcelAndVerifyPowerSaveState(Parcel parcel, 56 boolean batterySaverEnabled, boolean globalBatterySaverEnabled, 57 int locationMode, int soundTriggerMode, float brightnessFactor); nativeObtainBSPConfigParcel(BatterySaverPolicyConfig bs, String[] keys, String[] values)58 private native Parcel nativeObtainBSPConfigParcel(BatterySaverPolicyConfig bs, 59 String[] keys, String[] values); nativeUnparcelAndVerifyBSPConfig(Parcel parcel, BatterySaverPolicyConfig bs, String[] keys, String[] values)60 private native void nativeUnparcelAndVerifyBSPConfig(Parcel parcel, BatterySaverPolicyConfig bs, 61 String[] keys, String[] values); 62 63 static { 64 System.loadLibrary("powermanagertest_jni"); 65 } 66 67 /** 68 * Setup any common data for the upcoming tests. 69 */ 70 @Override setUp()71 public void setUp() throws Exception { 72 super.setUp(); 73 MockitoAnnotations.initMocks(this); 74 mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); 75 mPm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); 76 mUiDevice.executeShellCommand("cmd thermalservice override-status 0"); 77 } 78 79 /** 80 * Reset data for the upcoming tests. 81 */ 82 @After tearDown()83 public void tearDown() throws Exception { 84 mUiDevice.executeShellCommand("cmd thermalservice reset"); 85 } 86 87 /** 88 * Confirm that the setup is good. 89 * 90 * @throws Exception 91 */ 92 @SmallTest testPreconditions()93 public void testPreconditions() throws Exception { 94 assertNotNull(mPm); 95 } 96 97 /** 98 * Confirm that we can create functional wakelocks. 99 * 100 * @throws Exception 101 */ 102 @SmallTest testNewWakeLock()103 public void testNewWakeLock() throws Exception { 104 PowerManager.WakeLock wl = mPm.newWakeLock(PowerManager.FULL_WAKE_LOCK, "FULL_WAKE_LOCK"); 105 doTestWakeLock(wl); 106 107 wl = mPm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "SCREEN_BRIGHT_WAKE_LOCK"); 108 doTestWakeLock(wl); 109 110 wl = mPm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, "SCREEN_DIM_WAKE_LOCK"); 111 doTestWakeLock(wl); 112 113 wl = mPm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "PARTIAL_WAKE_LOCK"); 114 doTestWakeLock(wl); 115 116 // TODO: Some sort of functional test (maybe not in the unit test here?) 117 // that confirms that things are really happening e.g. screen power, keyboard power. 118 } 119 120 /** 121 * Confirm that we can't create dysfunctional wakelocks. 122 * 123 * @throws Exception 124 */ 125 @SmallTest testBadNewWakeLock()126 public void testBadNewWakeLock() throws Exception { 127 final int badFlags = PowerManager.SCREEN_BRIGHT_WAKE_LOCK 128 | PowerManager.SCREEN_DIM_WAKE_LOCK; 129 // wrap in try because we want the error here 130 try { 131 PowerManager.WakeLock wl = mPm.newWakeLock(badFlags, "foo"); 132 } catch (IllegalArgumentException e) { 133 return; 134 } 135 fail("Bad WakeLock flag was not caught."); 136 } 137 138 /** 139 * Ensure that we can have work sources with work chains when uid is not set directly on work 140 * source, and that this doesn't crash system server. 141 * 142 * @throws Exception 143 */ 144 @SmallTest testWakeLockWithWorkChains()145 public void testWakeLockWithWorkChains() throws Exception { 146 PowerManager.WakeLock wakeLock = mPm.newWakeLock( 147 PowerManager.SCREEN_DIM_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP, 148 "TEST_LOCK"); 149 WorkSource workSource = new WorkSource(); 150 WorkSource.WorkChain workChain = workSource.createWorkChain(); 151 workChain.addNode(1000, "test"); 152 wakeLock.setWorkSource(workSource); 153 154 doTestWakeLock(wakeLock); 155 } 156 157 /** 158 * Apply a few tests to a wakelock to make sure it's healthy. 159 * 160 * @param wl The wakelock to be tested. 161 */ doTestWakeLock(PowerManager.WakeLock wl)162 private void doTestWakeLock(PowerManager.WakeLock wl) { 163 // First try simple acquire/release 164 wl.acquire(); 165 assertTrue(wl.isHeld()); 166 wl.release(); 167 assertFalse(wl.isHeld()); 168 169 // Try ref-counted acquire/release 170 wl.setReferenceCounted(true); 171 wl.acquire(); 172 assertTrue(wl.isHeld()); 173 wl.acquire(); 174 assertTrue(wl.isHeld()); 175 wl.release(); 176 assertTrue(wl.isHeld()); 177 wl.release(); 178 assertFalse(wl.isHeld()); 179 180 // Try non-ref-counted 181 wl.setReferenceCounted(false); 182 wl.acquire(); 183 assertTrue(wl.isHeld()); 184 wl.acquire(); 185 assertTrue(wl.isHeld()); 186 wl.release(); 187 assertFalse(wl.isHeld()); 188 189 // TODO: Threaded test (needs handler) to make sure timed wakelocks work too 190 } 191 192 /** 193 * Confirm that we can get thermal status. 194 * 195 * @throws Exception 196 */ 197 @Test testGetThermalStatus()198 public void testGetThermalStatus() throws Exception { 199 int status = 0; 200 assertEquals(status, mPm.getCurrentThermalStatus()); 201 status = 3; 202 mUiDevice.executeShellCommand("cmd thermalservice override-status " 203 + Integer.toString(status)); 204 assertEquals(status, mPm.getCurrentThermalStatus()); 205 } 206 207 /** 208 * Confirm that we can add/remove thermal status listener. 209 * 210 * @throws Exception 211 */ 212 @Test testThermalStatusCallback()213 public void testThermalStatusCallback() throws Exception { 214 // Initial override status is THERMAL_STATUS_NONE 215 int status = PowerManager.THERMAL_STATUS_NONE; 216 // Add listener1 217 mPm.addThermalStatusListener(mExec, mListener1); 218 verify(mListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC) 219 .times(1)).onThermalStatusChanged(status); 220 reset(mListener1); 221 status = PowerManager.THERMAL_STATUS_SEVERE; 222 mUiDevice.executeShellCommand("cmd thermalservice override-status " 223 + Integer.toString(status)); 224 verify(mListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC) 225 .times(1)).onThermalStatusChanged(status); 226 reset(mListener1); 227 // Add listener1 again 228 try { 229 mPm.addThermalStatusListener(mListener1); 230 fail("Expected exception not thrown"); 231 } catch (IllegalArgumentException expectedException) { 232 } 233 // Add listener2 on main thread. 234 mPm.addThermalStatusListener(mListener2); 235 verify(mListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC) 236 .times(1)).onThermalStatusChanged(status); 237 reset(mListener2); 238 status = PowerManager.THERMAL_STATUS_MODERATE; 239 mUiDevice.executeShellCommand("cmd thermalservice override-status " 240 + Integer.toString(status)); 241 verify(mListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC) 242 .times(1)).onThermalStatusChanged(status); 243 verify(mListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC) 244 .times(1)).onThermalStatusChanged(status); 245 reset(mListener1); 246 reset(mListener2); 247 // Remove listener1 248 mPm.removeThermalStatusListener(mListener1); 249 // Remove listener1 again 250 try { 251 mPm.removeThermalStatusListener(mListener1); 252 fail("Expected exception not thrown"); 253 } catch (IllegalArgumentException expectedException) { 254 } 255 status = PowerManager.THERMAL_STATUS_LIGHT; 256 mUiDevice.executeShellCommand("cmd thermalservice override-status " 257 + Integer.toString(status)); 258 verify(mListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC) 259 .times(0)).onThermalStatusChanged(status); 260 verify(mListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC) 261 .times(1)).onThermalStatusChanged(status); 262 } 263 264 @Test testGetThermalHeadroom()265 public void testGetThermalHeadroom() throws Exception { 266 float headroom = mPm.getThermalHeadroom(0); 267 // If the device doesn't support thermal headroom, return early 268 if (Float.isNaN(headroom)) { 269 return; 270 } 271 assertTrue("Expected non-negative headroom", headroom >= 0.0f); 272 assertTrue("Expected reasonably small headroom", headroom < 10.0f); 273 274 // Call again immediately to ensure rate limiting works 275 headroom = mPm.getThermalHeadroom(0); 276 assertTrue("Expected NaN because of rate limiting", Float.isNaN(headroom)); 277 278 // Sleep for a second before attempting to call again so as to not get rate limited 279 Thread.sleep(1000); 280 headroom = mPm.getThermalHeadroom(5); 281 assertFalse("Expected data to still be available", Float.isNaN(headroom)); 282 assertTrue("Expected non-negative headroom", headroom >= 0.0f); 283 assertTrue("Expected reasonably small headroom", headroom < 10.0f); 284 } 285 286 @Test testUserspaceRebootNotSupported_throwsUnsupportedOperationException()287 public void testUserspaceRebootNotSupported_throwsUnsupportedOperationException() { 288 // Can't use assumption framework with AndroidTestCase :( 289 if (mPm.isRebootingUserspaceSupported()) { 290 return; 291 } 292 try { 293 mPm.reboot(PowerManager.REBOOT_USERSPACE); 294 fail("UnsupportedOperationException not thrown"); 295 } catch (UnsupportedOperationException expected) { 296 } 297 } 298 299 /** 300 * Helper function to obtain a PowerSaveState as parcel from native, with 301 * specified parameters, and verify the PowerSaveState object created from the parcel. 302 */ unparcelPowerSaveStateFromNativeAndVerify(boolean batterySaverEnabled, boolean globalBatterySaverEnabled, int locationMode, int soundTriggerMode, float brightnessFactor)303 private void unparcelPowerSaveStateFromNativeAndVerify(boolean batterySaverEnabled, 304 boolean globalBatterySaverEnabled, int locationMode, int soundTriggerMode, 305 float brightnessFactor) { 306 // Obtain PowerSaveState as parcel from native, with parameters. 307 Parcel psParcel = nativeObtainPowerSaveStateParcel(batterySaverEnabled, 308 globalBatterySaverEnabled, locationMode, soundTriggerMode, brightnessFactor); 309 // Verify the parcel. 310 PowerSaveState ps = PowerSaveState.CREATOR.createFromParcel(psParcel); 311 assertEquals(ps.batterySaverEnabled, batterySaverEnabled); 312 assertEquals(ps.globalBatterySaverEnabled, globalBatterySaverEnabled); 313 assertEquals(ps.locationMode, locationMode); 314 assertEquals(ps.soundTriggerMode, soundTriggerMode); 315 assertEquals(ps.brightnessFactor, brightnessFactor, 0.01f); 316 } 317 318 /** 319 * Helper function to send a PowerSaveState as parcel to native, with 320 * specified parameters. Native will verify the PowerSaveState in native is expected. 321 */ parcelPowerSaveStateToNativeAndVerify(boolean batterySaverEnabled, boolean globalBatterySaverEnabled, int locationMode, int soundTriggerMode, float brightnessFactor)322 private void parcelPowerSaveStateToNativeAndVerify(boolean batterySaverEnabled, 323 boolean globalBatterySaverEnabled, int locationMode, int soundTriggerMode, 324 float brightnessFactor) { 325 Parcel psParcel = Parcel.obtain(); 326 // PowerSaveState API blocks Builder.build(), generate a parcel instead of object. 327 PowerSaveState ps = new PowerSaveState.Builder() 328 .setBatterySaverEnabled(batterySaverEnabled) 329 .setGlobalBatterySaverEnabled(globalBatterySaverEnabled) 330 .setLocationMode(locationMode) 331 .setBrightnessFactor(brightnessFactor).build(); 332 ps.writeToParcel(psParcel, 0 /* flags */); 333 psParcel.setDataPosition(0); 334 //Set the PowerSaveState as parcel to native and verify in native space. 335 nativeUnparcelAndVerifyPowerSaveState(psParcel, batterySaverEnabled, 336 globalBatterySaverEnabled, locationMode, soundTriggerMode, brightnessFactor); 337 } 338 339 /** 340 * Helper function to obtain a BatterySaverPolicyConfig as parcel from native, with 341 * specified parameters, and verify the BatterySaverPolicyConfig object created from the parcel. 342 */ unparcelBatterySaverPolicyFromNativeAndVerify(BatterySaverPolicyConfig bsIn)343 private void unparcelBatterySaverPolicyFromNativeAndVerify(BatterySaverPolicyConfig bsIn) { 344 // Obtain BatterySaverPolicyConfig as parcel from native, with parameters. 345 String[] keys = bsIn.getDeviceSpecificSettings().keySet().toArray( 346 new String[bsIn.getDeviceSpecificSettings().keySet().size()]); 347 String[] values = bsIn.getDeviceSpecificSettings().values().toArray( 348 new String[bsIn.getDeviceSpecificSettings().values().size()]); 349 Parcel bsParcel = nativeObtainBSPConfigParcel(bsIn, keys, values); 350 BatterySaverPolicyConfig bsOut = 351 BatterySaverPolicyConfig.CREATOR.createFromParcel(bsParcel); 352 assertEquals(bsIn.toString(), bsOut.toString()); 353 } 354 355 /** 356 * Helper function to send a BatterySaverPolicyConfig as parcel to native, with 357 * specified parameters. 358 * Native will verify BatterySaverPolicyConfig from native is expected. 359 */ parcelBatterySaverPolicyConfigToNativeAndVerify(BatterySaverPolicyConfig bsIn)360 private void parcelBatterySaverPolicyConfigToNativeAndVerify(BatterySaverPolicyConfig bsIn) { 361 Parcel bsParcel = Parcel.obtain(); 362 bsIn.writeToParcel(bsParcel, 0 /* flags */); 363 bsParcel.setDataPosition(0); 364 // Set the BatterySaverPolicyConfig as parcel to native. 365 String[] keys = bsIn.getDeviceSpecificSettings().keySet().toArray( 366 new String[bsIn.getDeviceSpecificSettings().keySet().size()]); 367 String[] values = bsIn.getDeviceSpecificSettings().values().toArray( 368 new String[bsIn.getDeviceSpecificSettings().values().size()]); 369 // Set the BatterySaverPolicyConfig as parcel to native and verify in native space. 370 nativeUnparcelAndVerifyBSPConfig(bsParcel, bsIn, keys, values); 371 } 372 373 /** 374 * Confirm that we can pass PowerSaveState from native to Java. 375 * 376 * @throws Exception 377 */ 378 @Test testPowerSaveStateNativeToJava()379 public void testPowerSaveStateNativeToJava() { 380 unparcelPowerSaveStateFromNativeAndVerify(false /* batterySaverEnabled */, 381 false /* globalBatterySaverEnabled */, 382 PowerManager.LOCATION_MODE_FOREGROUND_ONLY, 383 PowerManager.SOUND_TRIGGER_MODE_CRITICAL_ONLY, 384 0.3f /* brightnessFactor */); 385 unparcelPowerSaveStateFromNativeAndVerify(true /* batterySaverEnabled */, 386 true /* globalBatterySaverEnabled */, 387 PowerManager.LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF, 388 PowerManager.SOUND_TRIGGER_MODE_ALL_DISABLED, 389 0.5f /* brightnessFactor */); 390 } 391 392 /** 393 * Confirm that we can pass PowerSaveState from Java to native. 394 * 395 * @throws Exception 396 */ 397 @Test testSetPowerSaveStateJavaToNative()398 public void testSetPowerSaveStateJavaToNative() { 399 parcelPowerSaveStateToNativeAndVerify(false /* batterySaverEnabled */, 400 false /* globalBatterySaverEnabled */, 401 PowerManager.LOCATION_MODE_FOREGROUND_ONLY, 402 PowerManager.SOUND_TRIGGER_MODE_CRITICAL_ONLY, 403 0.3f /* brightnessFactor */); 404 parcelPowerSaveStateToNativeAndVerify(true /* batterySaverEnabled */, 405 true /* globalBatterySaverEnabled */, 406 PowerManager.LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF, 407 PowerManager.SOUND_TRIGGER_MODE_ALL_DISABLED, 408 0.5f /* brightnessFactor */); 409 } 410 411 /** 412 * Confirm that we can pass BatterySaverPolicyConfig from native to Java. 413 * 414 * @throws Exception 415 */ 416 @Test testBatterySaverPolicyConfigNativeToJava()417 public void testBatterySaverPolicyConfigNativeToJava() { 418 BatterySaverPolicyConfig bs1 = new BatterySaverPolicyConfig.Builder() 419 .setAdjustBrightnessFactor(0.55f) 420 .setAdvertiseIsEnabled(true) 421 .setDeferFullBackup(true) 422 .setForceBackgroundCheck(true) 423 .setLocationMode(PowerManager.LOCATION_MODE_FOREGROUND_ONLY).build(); 424 BatterySaverPolicyConfig bs2 = new BatterySaverPolicyConfig.Builder() 425 .setAdjustBrightnessFactor(0.55f) 426 .setAdvertiseIsEnabled(true) 427 .setLocationMode(PowerManager.LOCATION_MODE_FOREGROUND_ONLY) 428 .addDeviceSpecificSetting("Key1" /* key */, "Value1" /* value */) 429 .addDeviceSpecificSetting("Key2" /* key */, "Value2" /* value */) 430 .setDeferFullBackup(true).build(); 431 432 unparcelBatterySaverPolicyFromNativeAndVerify(bs1); 433 unparcelBatterySaverPolicyFromNativeAndVerify(bs2); 434 } 435 436 /** 437 * Confirm that we can pass BatterySaverPolicyConfig from Java to native. 438 * 439 * @throws Exception 440 */ 441 @Test testBatterySaverPolicyConfigFromJavaToNative()442 public void testBatterySaverPolicyConfigFromJavaToNative() { 443 BatterySaverPolicyConfig bs1 = new BatterySaverPolicyConfig.Builder() 444 .setAdjustBrightnessFactor(0.55f) 445 .setAdvertiseIsEnabled(true) 446 .setDeferFullBackup(true).build(); 447 BatterySaverPolicyConfig bs2 = new BatterySaverPolicyConfig.Builder() 448 .setAdjustBrightnessFactor(0.55f) 449 .setAdvertiseIsEnabled(true) 450 .addDeviceSpecificSetting("Key1" /* key */, "Value1" /* value */) 451 .addDeviceSpecificSetting("Key2" /* key */, "Value2" /* value */) 452 .setDeferFullBackup(true).build(); 453 parcelBatterySaverPolicyConfigToNativeAndVerify(bs1); 454 parcelBatterySaverPolicyConfigToNativeAndVerify(bs2); 455 } 456 457 } 458