Как проверить результаты Android DevicePolicyManager#isAdminActive и сбой при активации диспетчера устройств

Столкнулся с интересной ситуацией, не знаю как решить. Когда пользователь впервые входит в мое приложение в качестве пользователя Android for Work, я обязан убедиться, что приложение зарегистрировано в качестве диспетчера устройств. Я проверяю, так ли это, вызывая DevicePolicyManager#isAdminActive, и если это возвращает false, то я запускаю Intent с action=DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN, чтобы запустить элемент управления Android, который позволит управлять устройством. Как только это произошло, когда мое приложение снова запускается (или когда оно возвращается из потока управления устройством), значение DevicePolicyManager#isAdminActive проверяется снова. Если пользователь включил управление устройством, то все в порядке, и приложение продолжает свой путь.

Интересно, что это работает именно так, как и ожидалось, когда пользователь впервые проходит через наш поток. К сожалению, после перезагрузки, когда пользователь запускает мое приложение, оно проверяет, включено ли управление устройством через DevicePolicyManager#isAdminActive, и здесь становится интересно. DevicePolicyManager#isAdminActive сообщит false, что подтверждается просмотром настроек безопасности устройства. Однако, что еще хуже, попытка включить управление устройствами приведет к следующему исключению:

W/DeviceAdminAdd: исключение, пытающееся активировать администратора 1550) в android.os.Parcel.readException(Parcel.java:1499) в android.app.enterprise.IEnterpriseDeviceManager$Stub$Proxy.setActiveAdmin(IEnterpriseDeviceManager.java:867) в android.app.enterprise.EnterpriseDeviceManager.setActiveAdmin(EnterpriseDeviceManager .java:720) в com.android.settings.DeviceAdminAdd.addAndFinish(DeviceAdminAdd.java:346) в com.android.settings.DeviceAdminAdd$3.onClick(DeviceAdminAdd.java:313) в android.view.View.performClick(View .java:5242) в android.widget.TextView.performClick(TextView.java:10571) в android.view.View$PerformClick.run(View.java:21196) в android.os.Handler.handleCallback(Handler.java: 739) на android.os.Handler.dispatchMessage(Handler.java:95) на android.os.Looper.loop(Looper.java:145) на android.app.ActivityThread.main(ActivityThread.java:6938) на java.lang. Reflect.Method.invoke(собственный метод) в java.lang.reflect.Method.invoke(Method.java:372) в...

Это точное исключение появляется в моей консоли logcat, даже когда я пытаюсь вручную включить управление устройством через страницу настроек безопасности системы, поэтому я не думаю, что исходный Intent искажен.

Итак, вот моя проблема: один вызов API управления устройствами Android говорит мне, что мой администратор устройства не активирован, но другой вызов того же API говорит мне, что это так. Я считаю, что второй на самом деле ошибочен, но без возможности активировать администрацию мой пользователь застрял в цикле и не может использовать мое приложение.

Кто-нибудь еще сталкивался с этой ошибкой, и если да, то как вы ее кодируете?


person Patrick Brennan    schedule 01.02.2018    source источник
comment
Я не могу воспроизвести вашу проблему на Google Pixel под управлением Android 8.1, используя этот образец приложения. После перезагрузки мой активный администратор устройства по-прежнему сообщает об активности через isAdminActive(). На каком устройстве вы тестируете?   -  person CommonsWare    schedule 02.02.2018
comment
Galaxy S5 под управлением Android 5.1.1 (уровень API 22). Мне также не удалось воспроизвести это на любом устройстве Google под управлением Android 6, 7 или 8.   -  person Patrick Brennan    schedule 02.02.2018
comment
Вы упомянули, что ваше приложение открывается как пользователь Android for Work. Содержится ли ваше приложение в рабочем профиле (на значке есть маленький оранжевый портфель), в основном профиле пользователя с рабочим профилем (без оранжевого портфеля на значке) или у устройства есть владелец устройства? Какое приложение является контроллером политики устройства?   -  person Steve Miskovetz    schedule 02.02.2018
comment
Приложение содержится в рабочем профиле, а приложение для управления моей компанией (Корпоративный портал InTune) является контроллером политики устройства.   -  person Patrick Brennan    schedule 02.02.2018


Ответы (1)


Я проанализировал исходный код AOSP DevicePolicyManagerService.java. Функция, которая в этом случае дает сбой, называется setActiveAdmin(), и она выдает исключение, потому что утверждает, что наше приложение уже добавлено в качестве администратора устройства. Это исключение генерируется из setActiveAdmin после того, как вызов getActiveAdminUncheckedLocked() возвращает допустимый объект, указывающий, что запрошенный администратор устройства активен:

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");
}

Однако перед этим мы уже вызывали DevicePolicyManager.isAdminActive, в котором содержится такая логика:

return getActiveAdminUncheckedLocked(adminReceiver, userHandle) != null;

Это хорошо; это именно то, что мы хотим. Однако это код AOSP. Я предположил, что на затронутых устройствах может быть другая реализация, в которой есть ошибка в логике DevicePolicyManager#isAdminActive(), так что вызов возвращает false, когда он должен возвращать true.

После развертывания инструментов для обнаружения этого состояния в рабочей среде я убедился, что эта гипотеза верна. Судя по моим показателям, в популяции устройств Android существуют такие реализации, что значение, возвращаемое isAdminActive(X), будет возвращать false, даже если X будет найдено в списке перечисленных активных администраторов, возвращенном getActiveAdmins(). Это, к счастью, не частый случай, но он существует. Я бы посоветовал всем программистам, которые полагаются на эти вызовы, усилить защиту своего кода от такого сценария.

person Patrick Brennan    schedule 28.02.2018