1 package org.robolectric.shadows; 2 3 import static android.os.Build.VERSION_CODES.KITKAT_WATCH; 4 import static android.os.Build.VERSION_CODES.LOLLIPOP; 5 import static android.os.Build.VERSION_CODES.M; 6 import static org.robolectric.shadows.ShadowApplication.getInstance; 7 8 import android.Manifest.permission; 9 import android.content.Context; 10 import android.content.pm.PackageManager; 11 import android.os.PowerManager; 12 import android.os.WorkSource; 13 import com.google.common.collect.ImmutableList; 14 import java.util.ArrayList; 15 import java.util.HashMap; 16 import java.util.List; 17 import java.util.Map; 18 import org.robolectric.RuntimeEnvironment; 19 import org.robolectric.annotation.HiddenApi; 20 import org.robolectric.annotation.Implementation; 21 import org.robolectric.annotation.Implements; 22 import org.robolectric.annotation.Resetter; 23 import org.robolectric.shadow.api.Shadow; 24 25 @Implements(PowerManager.class) 26 public class ShadowPowerManager { 27 private boolean isScreenOn = true; 28 private boolean isInteractive = true; 29 private boolean isPowerSaveMode = false; 30 private boolean isDeviceIdleMode = false; 31 private List<String> rebootReasons = new ArrayList<String>(); 32 private Map<String, Boolean> ignoringBatteryOptimizations = new HashMap<>(); 33 34 @Implementation newWakeLock(int flags, String tag)35 protected PowerManager.WakeLock newWakeLock(int flags, String tag) { 36 PowerManager.WakeLock wl = Shadow.newInstanceOf(PowerManager.WakeLock.class); 37 getInstance().addWakeLock(wl); 38 return wl; 39 } 40 41 @Implementation isScreenOn()42 protected boolean isScreenOn() { 43 return isScreenOn; 44 } 45 setIsScreenOn(boolean screenOn)46 public void setIsScreenOn(boolean screenOn) { 47 isScreenOn = screenOn; 48 } 49 50 @Implementation(minSdk = LOLLIPOP) isInteractive()51 protected boolean isInteractive() { 52 return isInteractive; 53 } 54 setIsInteractive(boolean interactive)55 public void setIsInteractive(boolean interactive) { 56 isInteractive = interactive; 57 } 58 59 @Implementation(minSdk = LOLLIPOP) isPowerSaveMode()60 protected boolean isPowerSaveMode() { 61 return isPowerSaveMode; 62 } 63 64 @HiddenApi 65 @Implementation(minSdk = KITKAT_WATCH) setPowerSaveMode(boolean powerSaveMode)66 protected boolean setPowerSaveMode(boolean powerSaveMode) { 67 final Context context = RuntimeEnvironment.application; 68 final int perm = context.getPackageManager() 69 .checkPermission(permission.DEVICE_POWER, context.getPackageName()); 70 if (perm != PackageManager.PERMISSION_GRANTED) { 71 throw new SecurityException( 72 "You need DEVICE_POWER permission to: set the device power-save mode"); 73 } 74 isPowerSaveMode = powerSaveMode; 75 return true; 76 } 77 78 /** 79 * Alters the power-save mode without verifying that the package under test has the required 80 * permission. 81 */ setIsPowerSaveMode(boolean powerSaveMode)82 public void setIsPowerSaveMode(boolean powerSaveMode) { 83 isPowerSaveMode = powerSaveMode; 84 } 85 86 private Map<Integer, Boolean> supportedWakeLockLevels = new HashMap<>(); 87 88 @Implementation(minSdk = LOLLIPOP) isWakeLockLevelSupported(int level)89 protected boolean isWakeLockLevelSupported(int level) { 90 return supportedWakeLockLevels.containsKey(level) ? supportedWakeLockLevels.get(level) : false; 91 } 92 setIsWakeLockLevelSupported(int level, boolean supported)93 public void setIsWakeLockLevelSupported(int level, boolean supported) { 94 supportedWakeLockLevels.put(level, supported); 95 } 96 97 /** 98 * @return `false` by default, or the value specified via {@link #setIsDeviceIdleMode(boolean)} 99 */ 100 @Implementation(minSdk = M) isDeviceIdleMode()101 protected boolean isDeviceIdleMode() { 102 return isDeviceIdleMode; 103 } 104 105 /** Sets the value returned by {@link #isDeviceIdleMode()}. */ setIsDeviceIdleMode(boolean isDeviceIdleMode)106 public void setIsDeviceIdleMode(boolean isDeviceIdleMode) { 107 this.isDeviceIdleMode = isDeviceIdleMode; 108 } 109 110 /** Discards the most recent {@code PowerManager.WakeLock}s */ 111 @Resetter reset()112 public static void reset() { 113 ShadowApplication shadowApplication = ShadowApplication.getInstance(); 114 if (shadowApplication != null) { 115 shadowApplication.clearWakeLocks(); 116 } 117 } 118 119 /** 120 * Retrieves the most recent wakelock registered by the application 121 * 122 * @return Most recent wake lock. 123 */ getLatestWakeLock()124 public static PowerManager.WakeLock getLatestWakeLock() { 125 ShadowApplication shadowApplication = Shadow.extract(RuntimeEnvironment.application); 126 return shadowApplication.getLatestWakeLock(); 127 } 128 129 @Implementation(minSdk = M) isIgnoringBatteryOptimizations(String packageName)130 protected boolean isIgnoringBatteryOptimizations(String packageName) { 131 Boolean result = ignoringBatteryOptimizations.get(packageName); 132 return result == null ? false : result; 133 } 134 setIgnoringBatteryOptimizations(String packageName, boolean value)135 public void setIgnoringBatteryOptimizations(String packageName, boolean value) { 136 ignoringBatteryOptimizations.put(packageName, Boolean.valueOf(value)); 137 } 138 139 @Implementation reboot(String reason)140 protected void reboot(String reason) { 141 rebootReasons.add(reason); 142 } 143 144 /** Returns the number of times {@link #reboot(String)} was called. */ getTimesRebooted()145 public int getTimesRebooted() { 146 return rebootReasons.size(); 147 } 148 149 /** Returns the list of reasons for each reboot, in chronological order. */ getRebootReasons()150 public ImmutableList<String> getRebootReasons() { 151 return ImmutableList.copyOf(rebootReasons); 152 } 153 154 @Implements(PowerManager.WakeLock.class) 155 public static class ShadowWakeLock { 156 private boolean refCounted = true; 157 private int refCount = 0; 158 private boolean locked = false; 159 private WorkSource workSource = null; 160 161 @Implementation acquire()162 protected void acquire() { 163 acquire(0); 164 } 165 166 @Implementation acquire(long timeout)167 protected synchronized void acquire(long timeout) { 168 if (refCounted) { 169 refCount++; 170 } else { 171 locked = true; 172 } 173 } 174 175 @Implementation release()176 protected synchronized void release() { 177 if (refCounted) { 178 if (--refCount < 0) throw new RuntimeException("WakeLock under-locked"); 179 } else { 180 locked = false; 181 } 182 } 183 184 @Implementation isHeld()185 protected synchronized boolean isHeld() { 186 return refCounted ? refCount > 0 : locked; 187 } 188 189 /** 190 * Retrieves if the wake lock is reference counted or not 191 * 192 * @return Is the wake lock reference counted? 193 */ isReferenceCounted()194 public boolean isReferenceCounted() { 195 return refCounted; 196 } 197 198 @Implementation setReferenceCounted(boolean value)199 protected void setReferenceCounted(boolean value) { 200 refCounted = value; 201 } 202 203 @Implementation setWorkSource(WorkSource ws)204 protected synchronized void setWorkSource(WorkSource ws) { 205 workSource = ws; 206 } 207 getWorkSource()208 public synchronized WorkSource getWorkSource() { 209 return workSource; 210 } 211 } 212 } 213