2929import androidx .annotation .NonNull ;
3030import androidx .annotation .RequiresApi ;
3131
32+ import com .microsoft .identity .common .logging .Logger ;
33+
34+ import java .util .Map ;
35+ import java .util .concurrent .ConcurrentHashMap ;
36+
3237/**
3338 * Wrapper class for PowerManager.
3439 */
3540
3641public class PowerManagerWrapper {
3742
43+ private static final String TAG = PowerManagerWrapper .class .getSimpleName ();
44+
3845 private static PowerManagerWrapper sInstance ;
3946
4047 private static final String UNKNOWN_STATUS = "Unknown" ;
48+
49+ // In-memory cache for battery optimization status (now stores String results)
50+ private final Map <String , String > batteryOptOutCache = new ConcurrentHashMap <>();
4151 /**
4252 * Set instance of PowerManagerWrapper.
4353 *
@@ -107,10 +117,6 @@ public String getDeviceIdleMode(@NonNull final Context context){
107117 @ NonNull
108118 public String getPowerOptimizationSettings (@ NonNull final Context context ){
109119 try {
110- if (Build .VERSION .SDK_INT < Build .VERSION_CODES .M ) {
111- return UNKNOWN_STATUS ;
112- }
113-
114120 final PowerManager powerManager = ((PowerManager ) context .getSystemService (Context .POWER_SERVICE ));
115121 if (powerManager .isIgnoringBatteryOptimizations (context .getPackageName ())){
116122 return "OptOut" ;
@@ -130,8 +136,43 @@ public String getPowerOptimizationSettings(@NonNull final Context context){
130136 * @param connectionContext Context used to query if app is ignoring battery optimizations.
131137 * @return true if the given application package name is on the device's power allow list.
132138 */
133- @ RequiresApi (Build .VERSION_CODES .M )
134139 public boolean isIgnoringBatteryOptimizations (final Context connectionContext ) {
135140 return ((PowerManager ) connectionContext .getSystemService (Context .POWER_SERVICE )).isIgnoringBatteryOptimizations (connectionContext .getPackageName ());
136141 }
142+
143+ /**
144+ * Checks if the app with the given package name is opted out from battery optimization.
145+ * Caches the result in memory using computeIfAbsent for thread safety.
146+ * Returns a string indicating the result or exception type.
147+ *
148+ * @param packageName The package name to check.
149+ * @param context The context to use for PowerManager.
150+ * @return "OptedOut" if the app is opted out, "NotOptedOut" if not, or exception type string.
151+ */
152+ public String isAppOptedOutFromBatteryOptimization (@ NonNull final String packageName , @ NonNull final Context context ) {
153+ final String methodTag = TAG + ":isAppOptedOutFromBatteryOptimization" ;
154+
155+ return batteryOptOutCache .computeIfAbsent (packageName , key -> {
156+ try {
157+ final PowerManager powerManager = (PowerManager ) context .getSystemService (Context .POWER_SERVICE );
158+ if (powerManager .isIgnoringBatteryOptimizations (key )) {
159+ return "OptOut" ;
160+ } else {
161+ return "NotOptOut" ;
162+ }
163+ } catch (NullPointerException e ) {
164+ Logger .error (methodTag , "NullPointerException when checking battery optimization status for package: " + packageName , e );
165+ return "NullPointerException" ;
166+ } catch (SecurityException e ) {
167+ Logger .error (methodTag , "SecurityException when checking battery optimization status for package: " + packageName , e );
168+ return "SecurityException" ;
169+ } catch (IllegalArgumentException e ) {
170+ Logger .error (methodTag , "IllegalArgumentException when checking battery optimization status for package: " + packageName , e );
171+ return "IllegalArgumentException" ;
172+ } catch (Exception e ) {
173+ Logger .error (methodTag , "Unknown Exception when checking battery optimization status for package: " + packageName , e );
174+ return "UnknownException" ;
175+ }
176+ });
177+ }
137178}
0 commit comments