Skip to content

Commit

Permalink
Managed profile Android util method (#2561)
Browse files Browse the repository at this point in the history
### 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.
  • Loading branch information
melissaahn authored Jan 2, 2025
1 parent 9954c92 commit 08c1ca1
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 1 deletion.
1 change: 1 addition & 0 deletions changelog.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ vNext
- [MINOR] Add Child Spans for Interactive Span (#2516)
- [MINOR] For MSAL CPP flows, match exact claims when deleting AT with intersecting scopes (#2548)
- [MINOR] Replace Deprecated Keystore API for Android 28+ (#2558)
- [MINOR] Managed profile Android util method (#2561)
- [PATCH] Make userHandle response field optional (#2560)

Version 18.2.2
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ package com.microsoft.identity.common.internal.fido
import android.webkit.WebView
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.lifecycleScope
import com.microsoft.identity.common.internal.platform.AndroidPlatformUtil
import com.microsoft.identity.common.internal.ui.webview.challengehandlers.IChallengeHandler
import com.microsoft.identity.common.java.constants.FidoConstants
import com.microsoft.identity.common.java.constants.FidoConstants.Companion.PASSKEY_PROTOCOL_ERROR_PREFIX_STRING
Expand Down Expand Up @@ -66,7 +67,8 @@ class AuthFidoChallengeHandler (
span.setAttribute(
AttributeName.fido_challenge_handler.name,
TAG
);
)
Logger.info(methodTag, "Is app in work profile?: " + AndroidPlatformUtil.isInManagedProfile(webView.context))
// First verify submitUrl and context. Without these two, we can't respond back to the server.
// If either one of these are missing or malformed, throw an exception and let the main WebViewClient handle it.
val submitUrl = fidoChallenge.submitUrl.getOrThrow()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@

import android.app.Activity;
import android.app.ActivityManager;
import android.app.admin.DevicePolicyManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ActivityInfo;
Expand All @@ -37,6 +38,7 @@
import android.os.Handler;
import android.os.Looper;
import android.os.SystemClock;
import android.os.UserManager;

import com.microsoft.identity.common.BuildConfig;
import com.microsoft.identity.common.adal.internal.AuthenticationConstants;
Expand Down Expand Up @@ -261,6 +263,33 @@ public static ArrayList<Map.Entry<String, String>> updateWithOrDeleteWebAuthnPar
return result;
}

/**
* Check if the host app is running within a managed profile.
* @param appContext current application context.
* @return true if app is in a managed profile, false if in personal profile or OS is below LOLLIPOP.
*/
public static boolean isInManagedProfile(@NonNull final Context appContext) {
// If the device is running on Android R or above, we can use the UserManager method isManagedProfile.
// Otherwise, if the device is running on Lollipop or above, we'll use DPM's isProfileOwnerApp. We return false for lower versions.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
final UserManager um = (UserManager) appContext.getSystemService(Context.USER_SERVICE);
return um.isManagedProfile();
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
final DevicePolicyManager dpm = (DevicePolicyManager) appContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
final List<ComponentName> activeAdmins = dpm.getActiveAdmins();
if (activeAdmins != null) {
// If any active admin apps are the profile owner, then the current calling app is in a managed profile.
for (final ComponentName admin : activeAdmins) {
final String packageName = admin.getPackageName();
if (dpm.isProfileOwnerApp(packageName)) {
return true;
}
}
}
}
return false;
}

/**
* This method optionally re-orders tasks to bring the task that launched
* the interactive activity to the foreground. This is useful when the activity provided
Expand Down

0 comments on commit 08c1ca1

Please sign in to comment.