I'm facing an interesting situation which I don't know how to resolve. When a user is signing into my app for the first time as an Android for Work user, I am obliged to make sure that the app is registered as a device manager. I check whether this is the case by calling DevicePolicyManager#isAdminActive
, and if this returns false
, then I launch an Intent
with action=DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN
, in order to launch the Android control which will enable device management. Once this has happened, when my app is launched again (or when it returns from the device management flow), the value of DevicePolicyManager#isAdminActive
is checked again. If the user enabled device management, then everything is fine, and the app continues on its way.
Interestingly, this works exactly as expected the first time the user goes through our flow. Unfortunately, after a reboot, when the user starts up my app, it checks to make sure device management is still on, via DevicePolicyManager#isAdminActive
, and here it gets interesting. DevicePolicyManager#isAdminActive
will report false
, which is verified by looking at the device security settings. Even worse, however, attempting to enable device management will result in the following exception:
W/DeviceAdminAdd: Exception trying to activate admin ComponentInfo{com.mysoft.myapp/com.mysoft.core.receivers.MyAppAdminReceiver} java.lang.IllegalArgumentException: Admin is already added at android.os.Parcel.readException(Parcel.java:1550) at android.os.Parcel.readException(Parcel.java:1499) at android.app.enterprise.IEnterpriseDeviceManager$Stub$Proxy.setActiveAdmin(IEnterpriseDeviceManager.java:867) at android.app.enterprise.EnterpriseDeviceManager.setActiveAdmin(EnterpriseDeviceManager.java:720) at com.android.settings.DeviceAdminAdd.addAndFinish(DeviceAdminAdd.java:346) at com.android.settings.DeviceAdminAdd$3.onClick(DeviceAdminAdd.java:313) at android.view.View.performClick(View.java:5242) at android.widget.TextView.performClick(TextView.java:10571) at android.view.View$PerformClick.run(View.java:21196) at android.os.Handler.handleCallback(Handler.java:739) at android.os.Handler.dispatchMessage(Handler.java:95) at android.os.Looper.loop(Looper.java:145) at android.app.ActivityThread.main(ActivityThread.java:6938) at java.lang.reflect.Method.invoke(Native Method) at java.lang.reflect.Method.invoke(Method.java:372) at ...
This exact exception appears on my logcat console even when I attempt to manually enable device management via the system security settings page, so I don't think the original Intent
is malformed.
So, here's my problem: One call to the Android device management API tells me that my device administrator is not activated, but another call to the same API tells me that it is. I believe that the 2nd one is, in fact, in error, but without being able to activate the administration, my user is stuck in a loop and unable to use my app.
Has anyone else encountered this error, and if so, how do you code around it?
I have analyzed AOSP source of DevicePolicyManagerService.java
. The function
which is failing in this case is called setActiveAdmin()
, and it is throwing an
exception because it claims that our application has already been added as a device
administrator. This exception is being thrown from setActiveAdmin
after a call
to getActiveAdminUncheckedLocked()
returns a valid object, indicating that the
requested device administrator is active:
final ActiveAdmin existingAdmin
= getActiveAdminUncheckedLocked(adminReceiver, userHandle);
if (!refreshing && existingAdmin != null) {
throw new IllegalArgumentException("Admin is already added");
}
if (policy.mRemovingAdmins.contains(adminReceiver)) {
throw new IllegalArgumentException(
"Trying to set an admin which is being removed");
}
However, before this, we have already called DevicePolicyManager.isAdminActive
,
which contains this logic:
return getActiveAdminUncheckedLocked(adminReceiver, userHandle) != null;
This is fine; this is exactly what we want. However, this is AOSP code. I theorized that there might be a different implementation on affected
devices, in which there is an error in the logic of
DevicePolicyManager#isAdminActive()
, so that the call is returning false when it
should be returning true.
After deploying instrumentation to catch this condition in production, I have verified that this hypothesis is correct. Inferring from my metrics, implementations exist in the Android device population such that the value returned by isAdminActive(X)
will return false, even though X
will be found in the list of enumerated active administrators returned by getActiveAdmins()
. This is, fortunately, not a common case, but it does exist. I would advise any coders who rely upon these calls to harden their code against this scenario.