Skip to content

Commit 62f8de0

Browse files
authored
Determine whether broker app opts out from battery optimization, Fixes AB#3428706 (#2819)
1 parent 59bad0a commit 62f8de0

File tree

4 files changed

+165
-5
lines changed

4 files changed

+165
-5
lines changed

changelog.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
vNext
22
----------
3+
- [MINOR] Determine whether broker app opts out from battery optimization (#2819)
34
- [MINOR] Cache Active Broker In Memory (BrokerDiscoveryClient) (#2817)
45
- [MINOR] Enable Broker Discovery by default in MSAL/Broker API (#2818)
56
- [MINOR] Share SharedPreferencesInMemoryCache across instances of BrokerOAuth2TokenCache

common/src/main/java/com/microsoft/identity/common/adal/internal/PowerManagerWrapper.java

Lines changed: 80 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,27 @@
2929
import androidx.annotation.NonNull;
3030
import 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

3643
public 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
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// All rights reserved.
3+
//
4+
// This code is licensed under the MIT License.
5+
//
6+
// Permission is hereby granted, free of charge, to any person obtaining a copy
7+
// of this software and associated documentation files(the "Software"), to deal
8+
// in the Software without restriction, including without limitation the rights
9+
// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
10+
// copies of the Software, and to permit persons to whom the Software is
11+
// furnished to do so, subject to the following conditions :
12+
//
13+
// The above copyright notice and this permission notice shall be included in
14+
// all copies or substantial portions of the Software.
15+
//
16+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22+
// THE SOFTWARE.
23+
package com.microsoft.identity.common.internal
24+
25+
/**
26+
* Enum representing the battery optimization status of the application.
27+
*/
28+
enum class BatteryOptimizationStatus {
29+
/** The application has opted out of battery optimization. */
30+
OptOut,
31+
32+
/** The application has not opted out of battery optimization. */
33+
NotOptOut,
34+
35+
/** Unable to retrieve the PowerManager service to determine battery optimization status. */
36+
CannotRetrievePowerManager,
37+
38+
/** An unknown error occurred while checking battery optimization status. */
39+
UnknownError
40+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// All rights reserved.
3+
//
4+
// This code is licensed under the MIT License.
5+
//
6+
// Permission is hereby granted, free of charge, to any person obtaining a copy
7+
// of this software and associated documentation files(the "Software"), to deal
8+
// in the Software without restriction, including without limitation the rights
9+
// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
10+
// copies of the Software, and to permit persons to whom the Software is
11+
// furnished to do so, subject to the following conditions :
12+
//
13+
// The above copyright notice and this permission notice shall be included in
14+
// all copies or substantial portions of the Software.
15+
//
16+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22+
// THE SOFTWARE.
23+
package com.microsoft.identity.common.internal
24+
25+
/**
26+
* Represents the current doze mode status of an Android device.
27+
* Doze mode is a battery-saving feature that restricts app behavior when the device is idle.
28+
*/
29+
enum class DeviceDozeModeStatus {
30+
/** The device is not in doze mode and operating normally. */
31+
NotInDozeMode,
32+
33+
/** The device is in idle state. */
34+
Idle,
35+
36+
/** The device is in light idle state. */
37+
LightIdle,
38+
39+
/** Unable to retrieve the PowerManager service to determine doze mode status. */
40+
CannotRetrievePowerManager,
41+
42+
/** An unknown error occurred while checking doze mode status. */
43+
UnknownError
44+
}

0 commit comments

Comments
 (0)