
Максим Денисов, разработчик в Лиге Цифровой Экономики, поделился опытом создания системы управления доступом на Android и рассказал, как менялся подход к контролю доступа.
В этой статье расскажу, как изменился подход к контролю доступа к корпоративному устройству внутри одной компании.
Нашей задачей была разработка приложения, которое заблокирует доступ к потерянному устройству на Android, будет отправлять действия пользователя на сервер и обновляться также с сервера.
Такой опыт, на мой взгляд, будет интересен для тех, кто работает над подобными задачами, например системами управления корпоративной техникой или родительского контроля.
Разработка началась в 2015 году, когда 5-я версия Android была последней.
1. Подход на основе блокирующей view
1.1. Блокировка
Суть подхода — работа блокирующего окна, которое отображается поверх всех остальных. После авторизации оно закрывается.
В манифесте приложения нужно добавить следующее разрешение:
<uses-permission android:name=»android.permission.SYSTEM_ALERT_WINDOW» />
С API 23 необходимо явно его указывать. Поэтому при настройке приложения администратор должен выбрать «Разрешать всегда».
У блокирующего экрана добавить свойства:
WindowManager.LayoutParams.TYPE_SYSTEM_ALERT
WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
Кроме того, следует создать сервис, в котором ресивер отлавливает выключение экрана и вызывает блокирующую view.
private BroadcastReceiver screenStatusReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (Intent.ACTION_SCREEN_OFF.equals(intent.getAction())) { startLockScreenActivity(context); } } public static void startLockScreenActivity(Context context) { Intent activityIntent = new Intent(context, LockActivity.class); activityIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); activityIntent.addFlags(WindowManager.LayoutParams.TYPE_SYSTEM_ERROR); context.startActivity(activityIntent); }
Чтобы пользователь не мог удалить приложение, последнему выдаются права администратора.
Intent activateDeviceAdmin = new Intent(DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN);
activateDeviceAdmin.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, policyManager.getAdminComponent());
activateDeviceAdmin.putExtra(DevicePolicyManager.EXTRA_ADD_EXPLANATION, «After activating admin, you will be able to block application uninstallation.»);
startActivityForResult(activateDeviceAdmin, PolicyManager.DPM_ACTIVATION_REQUEST_CODE);
При попытке удалить права администратора происходит сброс к заводским настройкам.
devicePolicyManager.wipeData(0);
1.2. События безопасности
Для хранения событий пользователя применялась локальная база данных.
@Entity(tableName = «events»)
data class Event(
@PrimaryKey(autoGenerate = true)
@ColumnInfo(name = «id»)
val id: Int,
@ColumnInfo(name = «login»)
val login: String? = null,
……
Синхронизация происходила по таймеру.
internal inner class SyncEventTimer : TimerTask() { override fun run() { Timber.tag(TAG).d("SyncEventTimer") GlobalScope.launch(Dispatchers.IO) { try { sendFromLocal() } catch (e: Exception) { Timber.tag(TAG).e(e) } } } }
1.3 Обновление
Обновление происходит через загрузку apk на устройство и вызов следующего интента:
Intent intent = new Intent( Intent.ACTION_INSTALL_PACKAGE
intent.setData( apkUri );
intent.setFlags( Intent.FLAG_GRANT_READ_URI_PERMISSION );
context.startActivity( intent );
Со временем появились серьезные ограничения. В 9-й и 10-й версиях Android появилась возможность убрать блокирующую view, а также не удается блокировать запуск других приложений. Когда происходило обновление, у пользователя появлялась возможность закрыть блокировщик — он «снимался» в момент обновления.
Изначально было решено устанавливать еще одно приложение, которое бы и выполняло обновление, и выдавать ему права администратора. Однако это не помогло — у пользователя по-прежнему появлялась возможность закрыть блокировщик: основной экран появлялся во время обновления, и можно было совершить какие-либо действия с устройством.
Поэтому для новых версий Android такой подход, к сожалению, не применим.
2. Подход на основе библиотеки knox от Samsung
Дальнейшее развитие приложения потребовало контроль над запущенными приложениями. Для этой задачи использовалась библиотека Knox от Samsung. Помимо необходимой функции, библиотека упрощает работу с разрешениями и блокировкой устройства.
Для ее работы в манифесте добавляются разрешения:
<uses-permission android:name="com.samsung.android.knox.permission.KNOX_KIOSK_MODE" /> <uses-permission android:name="com.samsung.android.knox.permission.KNOX_CUSTOM_SYSTEM " /> <uses-permission android:name="com.samsung.android.knox.permission.KNOX_APP_MGMT" /> <uses-permission android:name="com.samsung.android.knox.permission.KNOX_ADVANCED_SECURITY" /> Для работы библиотеки необходимо ввести ключ лицензии. val mSKL = KnoxEnterpriseLicenseManager.getInstance(context) mSKL.activateLicense(“key”, context.packageName)
Выдача разрешений без запроса пользователю делалась так:
runtimePermissions.add(«android.permission.WRITE_EXTERNAL_STORAGE»);
Установка блокирующего приложения — следующим образом:
mKioskMode?.enableKioskMode(pkgName)
Отключение приложений на устройстве:
EnterpriseDeviceManager edm = EnterpriseDeviceManager.getInstance(context);
ApplicationPolicy appPolicy = edm.getApplicationPolicy();
appPolicy.setDisableApplication(“com.test.app”)
Проблема в том, что такой вариант работает только с техникой Samsung, а также нужно покупать ключ для использования библиотеки.
В нашем случае дальнейшее развитие потребовало поддержку устройств любых производителей. Поэтому мы перешли к подходу на основе владельца устройства.
3. Подход на основе владельца устройства
3.1. Установка режима device-owner
Для выдачи приложению администратора устройства необходимо выполнить команду через adb:
adb shell dpm set-device-owner ru.company.screenlocker/.AdminReceiver
3.2. Выдача разрешений
В этом режиме приложению можно выдать любые необходимые разрешения:
val dpm = context.getSystemService(Context.DEVICE_POLICY_SERVICE) as DevicePolicyManager val cn = ComponentName(context, AdminReceiver::class.java) dpm.setPermissionGrantState( cn, context.packageName, Manifest.permission.REQUEST_INSTALL_PACKAGES, DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED )
3.3. Запрет на изменение приложения
Запретить пользователю менять приложение можно следующей командой:
val dpm = context.getSystemService(Context.DEVICE_POLICY_SERVICE) as DevicePolicyManager
val cn = ComponentName(context, AdminReceiver::class.java)
dpm.addUserRestriction(cn, UserManager.DISALLOW_APPS_CONTROL)
3.4. Включить режим блокировки задач
В этом режиме пользователи устройств не могут видеть уведомления, получать доступ к приложениям, не включенным в белый список, или возвращаться на главный экран.
activity.startLockTask()
3.5. Установить свой рабочий стол
Получить список всех приложений с иконками:
val allApps = packageManager.queryIntentActivities(i, 0) for (ri in allApps) { val app = DesktopAppInfo() app.label = ri.loadLabel(packageManager) app.packageName = ri.activityInfo.packageName app.icon = ri.activityInfo.loadIcon(packageManager) loadList.add(app) }
Соответственно, на своем рабочем столе можно разместить только нужные приложения.
На мой взгляд, это самый актуальный на сегодняшний день формат — работает на всех устройствах Android и на новых версиях ОС. Однако для установки приложения администратора необходимо форматировать устройство.
Приложение, которые мы разрабатывали, стало полноценным блокировщиком, который может останавливать другие аппы, отправлять события безопасности, обновляться и выводить список только разрешенных приложений на рабочий стол.
____________________________________________
В этом материале я описал изменение подхода к контролю корпоративной техники — от устаревшего к актуальному и на сегодняшний день. Имели ли вы дело с каким-либо их них?
ссылка на оригинал статьи https://habr.com/ru/company/digitalleague/blog/722232/
Добавить комментарий