2929import androidx .annotation .NonNull ;
3030import androidx .annotation .RequiresApi ;
3131
32+ import com .microsoft .identity .common .internal .BatteryOptimizationStatus ;
33+ import com .microsoft .identity .common .internal .DeviceDozeModeStatus ;
34+ import com .microsoft .identity .common .logging .Logger ;
35+
36+ import java .util .Map ;
37+ import java .util .concurrent .ConcurrentHashMap ;
38+
3239/**
3340 * Wrapper class for PowerManager.
3441 */
3542
3643public class PowerManagerWrapper {
3744
45+ private static final String TAG = PowerManagerWrapper .class .getSimpleName ();
46+
3847 private static PowerManagerWrapper sInstance ;
3948
4049 private static final String UNKNOWN_STATUS = "Unknown" ;
50+
51+ // In-memory cache for battery optimization status for each apps.
52+ private final Map <String , BatteryOptimizationStatus > batteryOptOutCache = new ConcurrentHashMap <>();
4153 /**
4254 * Set instance of PowerManagerWrapper.
4355 *
@@ -99,6 +111,40 @@ public String getDeviceIdleMode(@NonNull final Context context){
99111 return "" ;
100112 }
101113
114+ /**
115+ * Gets the Device Doze Mode Status.
116+ *
117+ * This is exposed to OneAuth.
118+ *
119+ * @param context The context to use for PowerManager.
120+ * @return a {@link DeviceDozeModeStatus}
121+ */
122+ @ NonNull
123+ public DeviceDozeModeStatus getDeviceDozeModeStatus (@ NonNull final Context context ){
124+ final String methodTag = TAG + ":getDeviceDozeModeStatus" ;
125+
126+ try {
127+ final PowerManager powerManager = ((PowerManager ) context .getSystemService (Context .POWER_SERVICE ));
128+ if (powerManager == null ) {
129+ Logger .error (methodTag , "PowerManager is null" , null );
130+ return DeviceDozeModeStatus .CannotRetrievePowerManager ;
131+ }
132+ if (powerManager .isDeviceIdleMode ()) {
133+ return DeviceDozeModeStatus .Idle ;
134+ }
135+
136+ if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .TIRAMISU &&
137+ powerManager .isDeviceLightIdleMode ()) {
138+ return DeviceDozeModeStatus .LightIdle ;
139+ }
140+
141+ return DeviceDozeModeStatus .NotInDozeMode ;
142+ } catch (Exception e ){
143+ Logger .error (methodTag , "Unknown Exception when checking doze mode status" , e );
144+ return DeviceDozeModeStatus .UnknownError ;
145+ }
146+ }
147+
102148 /**
103149 * Gets a string representing Power Optimization settings of the calling app
104150 * Will return an empty string if the app isn't opting out.
@@ -107,10 +153,6 @@ public String getDeviceIdleMode(@NonNull final Context context){
107153 @ NonNull
108154 public String getPowerOptimizationSettings (@ NonNull final Context context ){
109155 try {
110- if (Build .VERSION .SDK_INT < Build .VERSION_CODES .M ) {
111- return UNKNOWN_STATUS ;
112- }
113-
114156 final PowerManager powerManager = ((PowerManager ) context .getSystemService (Context .POWER_SERVICE ));
115157 if (powerManager .isIgnoringBatteryOptimizations (context .getPackageName ())){
116158 return "OptOut" ;
@@ -130,8 +172,41 @@ public String getPowerOptimizationSettings(@NonNull final Context context){
130172 * @param connectionContext Context used to query if app is ignoring battery optimizations.
131173 * @return true if the given application package name is on the device's power allow list.
132174 */
133- @ RequiresApi (Build .VERSION_CODES .M )
134175 public boolean isIgnoringBatteryOptimizations (final Context connectionContext ) {
135176 return ((PowerManager ) connectionContext .getSystemService (Context .POWER_SERVICE )).isIgnoringBatteryOptimizations (connectionContext .getPackageName ());
136177 }
178+
179+ /**
180+ * Checks if the app with the given package name is opted out from battery optimization.
181+ * Caches the result in memory using computeIfAbsent for thread safety.
182+ * Returns a string indicating the result or exception type.
183+ *
184+ * This is exposed to OneAuth.
185+ *
186+ * @param packageName The package name to check.
187+ * @param context The context to use for PowerManager.
188+ * @return a {@link BatteryOptimizationStatus}
189+ */
190+ public BatteryOptimizationStatus isAppOptedOutFromBatteryOptimization (@ NonNull final String packageName , @ NonNull final Context context ) {
191+ final String methodTag = TAG + ":isAppOptedOutFromBatteryOptimization" ;
192+
193+ return batteryOptOutCache .computeIfAbsent (packageName , key -> {
194+ try {
195+ final PowerManager powerManager = (PowerManager ) context .getSystemService (Context .POWER_SERVICE );
196+ if (powerManager == null ) {
197+ Logger .error (methodTag , "PowerManager is null" , null );
198+ return BatteryOptimizationStatus .CannotRetrievePowerManager ;
199+ }
200+
201+ if (powerManager .isIgnoringBatteryOptimizations (key )) {
202+ return BatteryOptimizationStatus .OptOut ;
203+ } else {
204+ return BatteryOptimizationStatus .NotOptOut ;
205+ }
206+ } catch (Exception e ) {
207+ Logger .error (methodTag , "Unknown Exception when checking battery optimization status for package: " + packageName , e );
208+ return BatteryOptimizationStatus .UnknownError ;
209+ }
210+ });
211+ }
137212}
0 commit comments