Skip to content

Commit 08c1ca1

Browse files
authored
Managed profile Android util method (#2561)
### Summary This PR creates an `AndroidPlatformUtil` method named `isInWorkProfile`, which returns a boolean based on if the host app is in a work profile. For Android level 30 (Android 11/R) and above, we are able to use the simple `isManagedProfile` method of `UserManager`. For Android level 21 (Android 5/Lollipop) and above, we get the `DevicePolicyManager` instance and get a list of the active admins. If any of these active admins are found to be the profile owner app, then we know that the calling app is in a work profile. (We do something similar in `InstallCertActivity` of the WPJ feature). If the device is below Android level 21 (should be very rare), we always return false. I tested this method out using testDPC and made sure both paths of the logic return the correct values.
1 parent 9954c92 commit 08c1ca1

File tree

3 files changed

+33
-1
lines changed

3 files changed

+33
-1
lines changed

changelog.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ vNext
77
- [MINOR] Add Child Spans for Interactive Span (#2516)
88
- [MINOR] For MSAL CPP flows, match exact claims when deleting AT with intersecting scopes (#2548)
99
- [MINOR] Replace Deprecated Keystore API for Android 28+ (#2558)
10+
- [MINOR] Managed profile Android util method (#2561)
1011
- [PATCH] Make userHandle response field optional (#2560)
1112

1213
Version 18.2.2

common/src/main/java/com/microsoft/identity/common/internal/fido/AuthFidoChallengeHandler.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ package com.microsoft.identity.common.internal.fido
2525
import android.webkit.WebView
2626
import androidx.lifecycle.LifecycleOwner
2727
import androidx.lifecycle.lifecycleScope
28+
import com.microsoft.identity.common.internal.platform.AndroidPlatformUtil
2829
import com.microsoft.identity.common.internal.ui.webview.challengehandlers.IChallengeHandler
2930
import com.microsoft.identity.common.java.constants.FidoConstants
3031
import com.microsoft.identity.common.java.constants.FidoConstants.Companion.PASSKEY_PROTOCOL_ERROR_PREFIX_STRING
@@ -66,7 +67,8 @@ class AuthFidoChallengeHandler (
6667
span.setAttribute(
6768
AttributeName.fido_challenge_handler.name,
6869
TAG
69-
);
70+
)
71+
Logger.info(methodTag, "Is app in work profile?: " + AndroidPlatformUtil.isInManagedProfile(webView.context))
7072
// First verify submitUrl and context. Without these two, we can't respond back to the server.
7173
// If either one of these are missing or malformed, throw an exception and let the main WebViewClient handle it.
7274
val submitUrl = fidoChallenge.submitUrl.getOrThrow()

common/src/main/java/com/microsoft/identity/common/internal/platform/AndroidPlatformUtil.java

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828

2929
import android.app.Activity;
3030
import android.app.ActivityManager;
31+
import android.app.admin.DevicePolicyManager;
3132
import android.content.ComponentName;
3233
import android.content.Context;
3334
import android.content.pm.ActivityInfo;
@@ -37,6 +38,7 @@
3738
import android.os.Handler;
3839
import android.os.Looper;
3940
import android.os.SystemClock;
41+
import android.os.UserManager;
4042

4143
import com.microsoft.identity.common.BuildConfig;
4244
import com.microsoft.identity.common.adal.internal.AuthenticationConstants;
@@ -261,6 +263,33 @@ public static ArrayList<Map.Entry<String, String>> updateWithOrDeleteWebAuthnPar
261263
return result;
262264
}
263265

266+
/**
267+
* Check if the host app is running within a managed profile.
268+
* @param appContext current application context.
269+
* @return true if app is in a managed profile, false if in personal profile or OS is below LOLLIPOP.
270+
*/
271+
public static boolean isInManagedProfile(@NonNull final Context appContext) {
272+
// If the device is running on Android R or above, we can use the UserManager method isManagedProfile.
273+
// Otherwise, if the device is running on Lollipop or above, we'll use DPM's isProfileOwnerApp. We return false for lower versions.
274+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
275+
final UserManager um = (UserManager) appContext.getSystemService(Context.USER_SERVICE);
276+
return um.isManagedProfile();
277+
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
278+
final DevicePolicyManager dpm = (DevicePolicyManager) appContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
279+
final List<ComponentName> activeAdmins = dpm.getActiveAdmins();
280+
if (activeAdmins != null) {
281+
// If any active admin apps are the profile owner, then the current calling app is in a managed profile.
282+
for (final ComponentName admin : activeAdmins) {
283+
final String packageName = admin.getPackageName();
284+
if (dpm.isProfileOwnerApp(packageName)) {
285+
return true;
286+
}
287+
}
288+
}
289+
}
290+
return false;
291+
}
292+
264293
/**
265294
* This method optionally re-orders tasks to bring the task that launched
266295
* the interactive activity to the foreground. This is useful when the activity provided

0 commit comments

Comments
 (0)