1 package org.robolectric.shadows; 2 3 import static android.content.Intent.ACTION_SCREEN_OFF; 4 import static android.content.Intent.ACTION_SCREEN_ON; 5 import static android.os.Build.VERSION_CODES.LOLLIPOP_MR1; 6 import static android.os.Build.VERSION_CODES.M; 7 import static android.os.Build.VERSION_CODES.N; 8 import static android.os.Build.VERSION_CODES.O; 9 import static android.os.Build.VERSION_CODES.P; 10 import static android.os.Build.VERSION_CODES.Q; 11 import static android.os.Build.VERSION_CODES.R; 12 import static android.os.Build.VERSION_CODES.S; 13 import static android.os.Build.VERSION_CODES.TIRAMISU; 14 import static android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE; 15 import static com.google.common.base.Preconditions.checkState; 16 import static java.util.Comparator.comparing; 17 import static java.util.stream.Collectors.toCollection; 18 import static org.robolectric.util.reflector.Reflector.reflector; 19 20 import android.annotation.NonNull; 21 import android.annotation.Nullable; 22 import android.annotation.RequiresPermission; 23 import android.annotation.SystemApi; 24 import android.annotation.TargetApi; 25 import android.content.Context; 26 import android.content.Intent; 27 import android.os.Binder; 28 import android.os.PowerManager; 29 import android.os.PowerManager.LowPowerStandbyPortDescription; 30 import android.os.PowerManager.LowPowerStandbyPortsLock; 31 import android.os.PowerManager.WakeLock; 32 import android.os.SystemClock; 33 import android.os.WorkSource; 34 import com.google.common.collect.ImmutableSet; 35 import java.time.Duration; 36 import java.util.ArrayList; 37 import java.util.Collections; 38 import java.util.HashMap; 39 import java.util.HashSet; 40 import java.util.List; 41 import java.util.Map; 42 import java.util.Optional; 43 import java.util.Set; 44 import org.robolectric.RuntimeEnvironment; 45 import org.robolectric.annotation.HiddenApi; 46 import org.robolectric.annotation.Implementation; 47 import org.robolectric.annotation.Implements; 48 import org.robolectric.annotation.RealObject; 49 import org.robolectric.annotation.Resetter; 50 import org.robolectric.shadow.api.Shadow; 51 import org.robolectric.util.reflector.Accessor; 52 import org.robolectric.util.reflector.ForType; 53 54 /** Shadow of PowerManager */ 55 @Implements(value = PowerManager.class, looseSignatures = true) 56 public class ShadowPowerManager { 57 58 @RealObject private PowerManager realPowerManager; 59 60 private boolean isInteractive = true; 61 private boolean isPowerSaveMode = false; 62 private boolean isDeviceIdleMode = false; 63 private boolean isLightDeviceIdleMode = false; 64 @Nullable private Duration batteryDischargePrediction = null; 65 private boolean isBatteryDischargePredictionPersonalized = false; 66 67 @PowerManager.LocationPowerSaveMode 68 private int locationMode = PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF; 69 70 private final List<String> rebootReasons = new ArrayList<>(); 71 private final Map<String, Boolean> ignoringBatteryOptimizations = new HashMap<>(); 72 73 private int thermalStatus = 0; 74 // Intentionally use Object instead of PowerManager.OnThermalStatusChangedListener to avoid 75 // ClassLoader exceptions on earlier SDKs that don't have this class. 76 private final Set<Object> thermalListeners = new HashSet<>(); 77 78 private final Set<String> ambientDisplaySuppressionTokens = 79 Collections.synchronizedSet(new HashSet<>()); 80 private volatile boolean isAmbientDisplayAvailable = true; 81 private volatile boolean isRebootingUserspaceSupported = false; 82 private volatile boolean adaptivePowerSaveEnabled = false; 83 84 private static PowerManager.WakeLock latestWakeLock; 85 86 private boolean lowPowerStandbyEnabled = false; 87 private boolean lowPowerStandbySupported = false; 88 private boolean exemptFromLowPowerStandby = false; 89 private final Set<String> allowedFeatures = new HashSet<String>(); 90 91 @Implementation newWakeLock(int flags, String tag)92 protected PowerManager.WakeLock newWakeLock(int flags, String tag) { 93 PowerManager.WakeLock wl = Shadow.newInstanceOf(PowerManager.WakeLock.class); 94 ((ShadowWakeLock) Shadow.extract(wl)).setTag(tag); 95 latestWakeLock = wl; 96 return wl; 97 } 98 99 @Implementation isScreenOn()100 protected boolean isScreenOn() { 101 return isInteractive; 102 } 103 104 /** 105 * @deprecated Use {@link #turnScreenOn(boolean)} instead. 106 */ 107 @Deprecated setIsScreenOn(boolean screenOn)108 public void setIsScreenOn(boolean screenOn) { 109 setIsInteractive(screenOn); 110 } 111 112 @Implementation isInteractive()113 protected boolean isInteractive() { 114 return isInteractive; 115 } 116 117 /** 118 * @deprecated Prefer {@link #turnScreenOn(boolean)} instead. 119 */ 120 @Deprecated setIsInteractive(boolean interactive)121 public void setIsInteractive(boolean interactive) { 122 isInteractive = interactive; 123 } 124 125 /** Emulates turning the screen on/off if the screen is not already on/off. */ turnScreenOn(boolean screenOn)126 public void turnScreenOn(boolean screenOn) { 127 if (isInteractive != screenOn) { 128 isInteractive = screenOn; 129 getContext().sendBroadcast(new Intent(screenOn ? ACTION_SCREEN_ON : ACTION_SCREEN_OFF)); 130 } 131 } 132 133 @Implementation isPowerSaveMode()134 protected boolean isPowerSaveMode() { 135 return isPowerSaveMode; 136 } 137 setIsPowerSaveMode(boolean powerSaveMode)138 public void setIsPowerSaveMode(boolean powerSaveMode) { 139 isPowerSaveMode = powerSaveMode; 140 } 141 142 private Map<Integer, Boolean> supportedWakeLockLevels = new HashMap<>(); 143 144 @Implementation isWakeLockLevelSupported(int level)145 protected boolean isWakeLockLevelSupported(int level) { 146 return supportedWakeLockLevels.containsKey(level) ? supportedWakeLockLevels.get(level) : false; 147 } 148 setIsWakeLockLevelSupported(int level, boolean supported)149 public void setIsWakeLockLevelSupported(int level, boolean supported) { 150 supportedWakeLockLevels.put(level, supported); 151 } 152 153 /** 154 * @return false by default, or the value specified via {@link #setIsDeviceIdleMode(boolean)} 155 */ 156 @Implementation(minSdk = M) isDeviceIdleMode()157 protected boolean isDeviceIdleMode() { 158 return isDeviceIdleMode; 159 } 160 161 /** Sets the value returned by {@link #isDeviceIdleMode()}. */ setIsDeviceIdleMode(boolean isDeviceIdleMode)162 public void setIsDeviceIdleMode(boolean isDeviceIdleMode) { 163 this.isDeviceIdleMode = isDeviceIdleMode; 164 } 165 166 /** 167 * @return false by default, or the value specified via {@link #setIsLightDeviceIdleMode(boolean)} 168 */ 169 @Implementation(minSdk = N) isLightDeviceIdleMode()170 protected boolean isLightDeviceIdleMode() { 171 return isLightDeviceIdleMode; 172 } 173 174 /** Sets the value returned by {@link #isLightDeviceIdleMode()}. */ setIsLightDeviceIdleMode(boolean lightDeviceIdleMode)175 public void setIsLightDeviceIdleMode(boolean lightDeviceIdleMode) { 176 isLightDeviceIdleMode = lightDeviceIdleMode; 177 } 178 179 @Implementation(minSdk = TIRAMISU) isDeviceLightIdleMode()180 protected boolean isDeviceLightIdleMode() { 181 return isLightDeviceIdleMode(); 182 } 183 184 /** Sets the value returned by {@link #isDeviceLightIdleMode()}. */ setIsDeviceLightIdleMode(boolean lightDeviceIdleMode)185 public void setIsDeviceLightIdleMode(boolean lightDeviceIdleMode) { 186 setIsLightDeviceIdleMode(lightDeviceIdleMode); 187 } 188 189 /** 190 * Returns how location features should behave when battery saver is on. When battery saver is 191 * off, this will always return {@link #LOCATION_MODE_NO_CHANGE}. 192 */ 193 @Implementation(minSdk = P) 194 @PowerManager.LocationPowerSaveMode getLocationPowerSaveMode()195 protected int getLocationPowerSaveMode() { 196 if (!isPowerSaveMode()) { 197 return PowerManager.LOCATION_MODE_NO_CHANGE; 198 } 199 return locationMode; 200 } 201 202 /** Sets the value returned by {@link #getLocationPowerSaveMode()} when battery saver is on. */ setLocationPowerSaveMode(@owerManager.LocationPowerSaveMode int locationMode)203 public void setLocationPowerSaveMode(@PowerManager.LocationPowerSaveMode int locationMode) { 204 checkState( 205 locationMode >= PowerManager.MIN_LOCATION_MODE, 206 "Location Power Save Mode must be at least " + PowerManager.MIN_LOCATION_MODE); 207 checkState( 208 locationMode <= PowerManager.MAX_LOCATION_MODE, 209 "Location Power Save Mode must be no more than " + PowerManager.MAX_LOCATION_MODE); 210 this.locationMode = locationMode; 211 } 212 213 /** This function returns the current thermal status of the device. */ 214 @Implementation(minSdk = Q) getCurrentThermalStatus()215 protected int getCurrentThermalStatus() { 216 return thermalStatus; 217 } 218 219 /** This function adds a listener for thermal status change. */ 220 @Implementation(minSdk = Q) addThermalStatusListener(Object listener)221 protected void addThermalStatusListener(Object listener) { 222 checkState( 223 listener instanceof PowerManager.OnThermalStatusChangedListener, 224 "Listener must implement PowerManager.OnThermalStatusChangedListener"); 225 this.thermalListeners.add(listener); 226 } 227 228 /** This function gets listeners for thermal status change. */ getThermalStatusListeners()229 public ImmutableSet<Object> getThermalStatusListeners() { 230 return ImmutableSet.copyOf(this.thermalListeners); 231 } 232 233 /** This function removes a listener for thermal status change. */ 234 @Implementation(minSdk = Q) removeThermalStatusListener(Object listener)235 protected void removeThermalStatusListener(Object listener) { 236 checkState( 237 listener instanceof PowerManager.OnThermalStatusChangedListener, 238 "Listener must implement PowerManager.OnThermalStatusChangedListener"); 239 this.thermalListeners.remove(listener); 240 } 241 242 /** Sets the value returned by {@link #getCurrentThermalStatus()}. */ setCurrentThermalStatus(int thermalStatus)243 public void setCurrentThermalStatus(int thermalStatus) { 244 checkState( 245 thermalStatus >= PowerManager.THERMAL_STATUS_NONE, 246 "Thermal status must be at least " + PowerManager.THERMAL_STATUS_NONE); 247 checkState( 248 thermalStatus <= PowerManager.THERMAL_STATUS_SHUTDOWN, 249 "Thermal status must be no more than " + PowerManager.THERMAL_STATUS_SHUTDOWN); 250 this.thermalStatus = thermalStatus; 251 for (Object listener : thermalListeners) { 252 ((PowerManager.OnThermalStatusChangedListener) listener) 253 .onThermalStatusChanged(thermalStatus); 254 } 255 } 256 257 /** Discards the most recent {@code PowerManager.WakeLock}s */ 258 @Resetter reset()259 public static void reset() { 260 clearWakeLocks(); 261 } 262 263 /** 264 * Retrieves the most recent wakelock registered by the application 265 * 266 * @return Most recent wake lock. 267 */ getLatestWakeLock()268 public static PowerManager.WakeLock getLatestWakeLock() { 269 return latestWakeLock; 270 } 271 272 /** Clears most recent recorded wakelock. */ clearWakeLocks()273 public static void clearWakeLocks() { 274 latestWakeLock = null; 275 } 276 277 /** 278 * Controls result from {@link #getLatestWakeLock()} 279 * 280 * @deprecated do not use 281 */ 282 @Deprecated addWakeLock(WakeLock wl)283 static void addWakeLock(WakeLock wl) { 284 latestWakeLock = wl; 285 } 286 287 @Implementation(minSdk = M) isIgnoringBatteryOptimizations(String packageName)288 protected boolean isIgnoringBatteryOptimizations(String packageName) { 289 Boolean result = ignoringBatteryOptimizations.get(packageName); 290 return result == null ? false : result; 291 } 292 setIgnoringBatteryOptimizations(String packageName, boolean value)293 public void setIgnoringBatteryOptimizations(String packageName, boolean value) { 294 ignoringBatteryOptimizations.put(packageName, Boolean.valueOf(value)); 295 } 296 297 /** 298 * Differs from real implementation as device charging state is not checked. 299 * 300 * @param timeRemaining The time remaining as a {@link Duration}. 301 * @param isPersonalized true if personalized based on device usage history, false otherwise. 302 */ 303 @SystemApi 304 @RequiresPermission(android.Manifest.permission.DEVICE_POWER) 305 @Implementation(minSdk = S) setBatteryDischargePrediction( @onNull Duration timeRemaining, boolean isPersonalized)306 protected void setBatteryDischargePrediction( 307 @NonNull Duration timeRemaining, boolean isPersonalized) { 308 this.batteryDischargePrediction = timeRemaining; 309 this.isBatteryDischargePredictionPersonalized = isPersonalized; 310 } 311 312 /** 313 * Returns the current battery life remaining estimate. 314 * 315 * <p>Differs from real implementation as the time that {@link #setBatteryDischargePrediction} was 316 * called is not taken into account. 317 * 318 * @return The estimated battery life remaining as a {@link Duration}. Will be {@code null} if the 319 * prediction has not been set. 320 */ 321 @Nullable 322 @Implementation(minSdk = S) getBatteryDischargePrediction()323 protected Duration getBatteryDischargePrediction() { 324 return this.batteryDischargePrediction; 325 } 326 327 /** 328 * Returns whether the current battery life remaining estimate is personalized based on device 329 * usage history or not. This value does not take a device's powered or charging state into 330 * account. 331 * 332 * @return A boolean indicating if the current discharge estimate is personalized based on 333 * historical device usage or not. 334 */ 335 @Implementation(minSdk = S) isBatteryDischargePredictionPersonalized()336 protected boolean isBatteryDischargePredictionPersonalized() { 337 return this.isBatteryDischargePredictionPersonalized; 338 } 339 340 @Implementation reboot(@ullable String reason)341 protected void reboot(@Nullable String reason) { 342 if (RuntimeEnvironment.getApiLevel() >= R 343 && "userspace".equals(reason) 344 && !isRebootingUserspaceSupported()) { 345 throw new UnsupportedOperationException( 346 "Attempted userspace reboot on a device that doesn't support it"); 347 } 348 rebootReasons.add(reason); 349 } 350 351 /** Returns the number of times {@link #reboot(String)} was called. */ getTimesRebooted()352 public int getTimesRebooted() { 353 return rebootReasons.size(); 354 } 355 356 /** 357 * Returns the list of reasons for each reboot, in chronological order. May contain {@code null}. 358 */ getRebootReasons()359 public List<String> getRebootReasons() { 360 return new ArrayList<>(rebootReasons); 361 } 362 363 /** Sets the value returned by {@link #isAmbientDisplayAvailable()}. */ setAmbientDisplayAvailable(boolean available)364 public void setAmbientDisplayAvailable(boolean available) { 365 this.isAmbientDisplayAvailable = available; 366 } 367 368 /** Sets the value returned by {@link #isRebootingUserspaceSupported()}. */ setIsRebootingUserspaceSupported(boolean supported)369 public void setIsRebootingUserspaceSupported(boolean supported) { 370 this.isRebootingUserspaceSupported = supported; 371 } 372 373 /** 374 * Returns true by default, or the value specified via {@link 375 * #setAmbientDisplayAvailable(boolean)}. 376 */ 377 @Implementation(minSdk = R) isAmbientDisplayAvailable()378 protected boolean isAmbientDisplayAvailable() { 379 return isAmbientDisplayAvailable; 380 } 381 382 /** 383 * If true, suppress the device's ambient display. Ambient display is defined as anything visible 384 * on the display when {@link PowerManager#isInteractive} is false. 385 * 386 * @param token An identifier for the ambient display suppression. 387 * @param suppress If {@code true}, suppresses the ambient display. Otherwise, unsuppresses the 388 * ambient display for the given token. 389 */ 390 @Implementation(minSdk = R) suppressAmbientDisplay(String token, boolean suppress)391 protected void suppressAmbientDisplay(String token, boolean suppress) { 392 String suppressionToken = Binder.getCallingUid() + "_" + token; 393 if (suppress) { 394 ambientDisplaySuppressionTokens.add(suppressionToken); 395 } else { 396 ambientDisplaySuppressionTokens.remove(suppressionToken); 397 } 398 } 399 400 /** 401 * Returns true if {@link #suppressAmbientDisplay(String, boolean)} has been called with any 402 * token. 403 */ 404 @Implementation(minSdk = R) isAmbientDisplaySuppressed()405 protected boolean isAmbientDisplaySuppressed() { 406 return !ambientDisplaySuppressionTokens.isEmpty(); 407 } 408 409 /** 410 * Returns last value specified in {@link #setIsRebootingUserspaceSupported(boolean)} or {@code 411 * false} by default. 412 */ 413 @Implementation(minSdk = R) isRebootingUserspaceSupported()414 protected boolean isRebootingUserspaceSupported() { 415 return isRebootingUserspaceSupported; 416 } 417 418 /** 419 * Sets whether Adaptive Power Saver is enabled. 420 * 421 * <p>This has no effect, other than the value of {@link #getAdaptivePowerSaveEnabled()} is 422 * changed, which can be used to ensure this method is called correctly. 423 * 424 * @return true if the value has changed. 425 */ 426 @Implementation(minSdk = Q) 427 @SystemApi setAdaptivePowerSaveEnabled(boolean enabled)428 protected boolean setAdaptivePowerSaveEnabled(boolean enabled) { 429 boolean changed = adaptivePowerSaveEnabled != enabled; 430 adaptivePowerSaveEnabled = enabled; 431 return changed; 432 } 433 434 /** Gets the value set by {@link #setAdaptivePowerSaveEnabled(boolean)}. */ getAdaptivePowerSaveEnabled()435 public boolean getAdaptivePowerSaveEnabled() { 436 return adaptivePowerSaveEnabled; 437 } 438 439 @Implements(PowerManager.WakeLock.class) 440 public static class ShadowWakeLock { 441 private boolean refCounted = true; 442 private WorkSource workSource = null; 443 private int timesHeld = 0; 444 private String tag = null; 445 private List<Optional<Long>> timeoutTimestampList = new ArrayList<>(); 446 acquireInternal(Optional<Long> timeoutOptional)447 private void acquireInternal(Optional<Long> timeoutOptional) { 448 ++timesHeld; 449 timeoutTimestampList.add(timeoutOptional); 450 } 451 452 /** Iterate all the wake lock and remove those timeouted ones. */ refreshTimeoutTimestampList()453 private void refreshTimeoutTimestampList() { 454 timeoutTimestampList = 455 timeoutTimestampList.stream() 456 .filter(o -> !o.isPresent() || o.get() >= SystemClock.elapsedRealtime()) 457 .collect(toCollection(ArrayList::new)); 458 } 459 460 @Implementation acquire()461 protected void acquire() { 462 acquireInternal(Optional.empty()); 463 } 464 465 @Implementation acquire(long timeout)466 protected synchronized void acquire(long timeout) { 467 Long timeoutMillis = timeout + SystemClock.elapsedRealtime(); 468 if (timeoutMillis > 0) { 469 acquireInternal(Optional.of(timeoutMillis)); 470 } else { 471 // This is because many existing tests use Long.MAX_VALUE as timeout, which will cause a 472 // long overflow. 473 acquireInternal(Optional.empty()); 474 } 475 } 476 477 /** Releases the wake lock. The {@code flags} are ignored. */ 478 @Implementation release(int flags)479 protected synchronized void release(int flags) { 480 refreshTimeoutTimestampList(); 481 482 // Dequeue the wake lock with smallest timeout. 483 // Map the subtracted value to 1 and -1 to avoid long->int cast overflow. 484 Optional<Optional<Long>> wakeLockOptional = 485 timeoutTimestampList.stream() 486 .min( 487 comparing( 488 (Optional<Long> arg) -> arg.orElse(Long.MAX_VALUE), 489 (Long leftProperty, Long rightProperty) -> 490 (leftProperty - rightProperty) > 0 ? 1 : -1)); 491 492 if (wakeLockOptional.isEmpty()) { 493 if (refCounted) { 494 throw new RuntimeException("WakeLock under-locked"); 495 } else { 496 return; 497 } 498 } 499 500 Optional<Long> wakeLock = wakeLockOptional.get(); 501 502 if (refCounted) { 503 timeoutTimestampList.remove(wakeLock); 504 } else { 505 // If a wake lock is not reference counted, then one call to release() is sufficient to undo 506 // the effect of all previous calls to acquire(). 507 timeoutTimestampList = new ArrayList<>(); 508 } 509 } 510 511 @Implementation isHeld()512 protected synchronized boolean isHeld() { 513 refreshTimeoutTimestampList(); 514 return !timeoutTimestampList.isEmpty(); 515 } 516 517 /** 518 * Retrieves if the wake lock is reference counted or not 519 * 520 * @return Is the wake lock reference counted? 521 */ isReferenceCounted()522 public boolean isReferenceCounted() { 523 return refCounted; 524 } 525 526 @Implementation setReferenceCounted(boolean value)527 protected void setReferenceCounted(boolean value) { 528 refCounted = value; 529 } 530 531 @Implementation setWorkSource(WorkSource ws)532 protected synchronized void setWorkSource(WorkSource ws) { 533 workSource = ws; 534 } 535 getWorkSource()536 public synchronized WorkSource getWorkSource() { 537 return workSource; 538 } 539 540 /** Returns how many times the wakelock was held. */ getTimesHeld()541 public int getTimesHeld() { 542 return timesHeld; 543 } 544 545 /** Returns the tag. */ 546 @HiddenApi 547 @Implementation(minSdk = O) getTag()548 public String getTag() { 549 return tag; 550 } 551 552 /** Sets the tag. */ 553 @Implementation(minSdk = LOLLIPOP_MR1) setTag(String tag)554 protected void setTag(String tag) { 555 this.tag = tag; 556 } 557 } 558 getContext()559 private Context getContext() { 560 return reflector(ReflectorPowerManager.class, realPowerManager).getContext(); 561 } 562 563 @Implementation(minSdk = TIRAMISU) isLowPowerStandbySupported()564 protected boolean isLowPowerStandbySupported() { 565 return lowPowerStandbySupported; 566 } 567 568 @TargetApi(TIRAMISU) setLowPowerStandbySupported(boolean lowPowerStandbySupported)569 public void setLowPowerStandbySupported(boolean lowPowerStandbySupported) { 570 this.lowPowerStandbySupported = lowPowerStandbySupported; 571 } 572 573 @Implementation(minSdk = TIRAMISU) isLowPowerStandbyEnabled()574 protected boolean isLowPowerStandbyEnabled() { 575 return lowPowerStandbySupported && lowPowerStandbyEnabled; 576 } 577 578 @Implementation(minSdk = TIRAMISU) setLowPowerStandbyEnabled(boolean lowPowerStandbyEnabled)579 protected void setLowPowerStandbyEnabled(boolean lowPowerStandbyEnabled) { 580 this.lowPowerStandbyEnabled = lowPowerStandbyEnabled; 581 } 582 583 @Implementation(minSdk = UPSIDE_DOWN_CAKE) isAllowedInLowPowerStandby(String feature)584 protected boolean isAllowedInLowPowerStandby(String feature) { 585 if (!lowPowerStandbySupported) { 586 return true; 587 } 588 return allowedFeatures.contains(feature); 589 } 590 591 @TargetApi(UPSIDE_DOWN_CAKE) addAllowedInLowPowerStandby(String feature)592 public void addAllowedInLowPowerStandby(String feature) { 593 allowedFeatures.add(feature); 594 } 595 596 @Implementation(minSdk = UPSIDE_DOWN_CAKE) isExemptFromLowPowerStandby()597 protected boolean isExemptFromLowPowerStandby() { 598 if (!lowPowerStandbySupported) { 599 return true; 600 } 601 return exemptFromLowPowerStandby; 602 } 603 604 @TargetApi(UPSIDE_DOWN_CAKE) setExemptFromLowPowerStandby(boolean exemptFromLowPowerStandby)605 public void setExemptFromLowPowerStandby(boolean exemptFromLowPowerStandby) { 606 this.exemptFromLowPowerStandby = exemptFromLowPowerStandby; 607 } 608 609 @Implementation(minSdk = UPSIDE_DOWN_CAKE) newLowPowerStandbyPortsLock( List<LowPowerStandbyPortDescription> ports)610 protected Object /* LowPowerStandbyPortsLock */ newLowPowerStandbyPortsLock( 611 List<LowPowerStandbyPortDescription> ports) { 612 PowerManager.LowPowerStandbyPortsLock lock = 613 Shadow.newInstanceOf(PowerManager.LowPowerStandbyPortsLock.class); 614 ((ShadowLowPowerStandbyPortsLock) Shadow.extract(lock)).setPorts(ports); 615 return (Object) lock; 616 } 617 618 /** Shadow of {@link LowPowerStandbyPortsLock} to allow testing state. */ 619 @Implements( 620 value = PowerManager.LowPowerStandbyPortsLock.class, 621 minSdk = UPSIDE_DOWN_CAKE, 622 isInAndroidSdk = false) 623 public static class ShadowLowPowerStandbyPortsLock { 624 private List<LowPowerStandbyPortDescription> ports; 625 private boolean isAcquired = false; 626 private int acquireCount = 0; 627 628 @Implementation(minSdk = UPSIDE_DOWN_CAKE) acquire()629 protected void acquire() { 630 isAcquired = true; 631 acquireCount++; 632 } 633 634 @Implementation(minSdk = UPSIDE_DOWN_CAKE) release()635 protected void release() { 636 isAcquired = false; 637 } 638 isAcquired()639 public boolean isAcquired() { 640 return isAcquired; 641 } 642 getAcquireCount()643 public int getAcquireCount() { 644 return acquireCount; 645 } 646 setPorts(List<LowPowerStandbyPortDescription> ports)647 public void setPorts(List<LowPowerStandbyPortDescription> ports) { 648 this.ports = ports; 649 } 650 getPorts()651 public List<LowPowerStandbyPortDescription> getPorts() { 652 return ports; 653 } 654 } 655 656 /** Reflector interface for {@link PowerManager}'s internals. */ 657 @ForType(PowerManager.class) 658 private interface ReflectorPowerManager { 659 660 @Accessor("mContext") getContext()661 Context getContext(); 662 } 663 } 664