- PVSM.RU - https://www.pvsm.ru -
Для защиты от несанкционированного доступа к своему Nexus 4 я использую стандартную блокировку экрана с паролем. Но пароль приходится постоянно вводить вручную, а это немного напрягает. Блокировка с помощью графического ключа меня не впечатлила [1], так же как и PIN-код (слишком мало возможных комбинаций). Хотелось сильную защиту, с сохранением скорости разблокировки. Именно по этой причине я решил присмотреться к технологии NFC.
Единственным готовым решением, которое я нашёл, для реализации разблокировки девайса с помощью метки, является программа NFCSecure [2]. Она платная, а бесплатной версии нет. Посмотрев видео на его странице в Google Play, я сразу для себя решил, что ЭТО не стоит своих денег. Всё реализовано простейшим способом:
Даже, если предположить, что это окно блокирует любые попытки физически добраться до стандартного лаунчера (сенсорные кнопки, физические кнопки, жест по статусной строке), оно ни в коем случае не реализует всю ту защиту, которую предоставляет стандартный экран блокировки. Стандартная блокировка гораздо более функциональна и блокирует даже доступ к девайсу как к накопителю, т.е. пока не разблокируете экран — доступа к файлам не получите.
В общем это приложение достаточно дырявое и защиту сравнимую со стандартным экраном блокировки оно не предоставляет.
Самым идеальным вариантом для меня была интеграция со стандартным экраном блокировки. Хотелось, чтобы можно было прямо из него сканировать метки и авторизоваться, а если метки нет, то можно было бы использовать пароль как резервный вариант.
Перед тем как начать делать свою реализацию я определил две причины, по которым автор NFCSecure сделал своё приложение именно так, как оно работает сейчас, а не как задумал я. Опишем эти проблемы и пути их решения в разработанном мною приложении.
А точнее он не работает как минимум в двух случаях, которые нас интересуют:
Я предполагаю, что в этих случаях сканер не работает по соображениям безопасности. После сканирования метки запускается наиболее подходящее приложение, и не известно что это приложение захочет сделать. Для примера, Google Wallet (судя по видео) требует пин-код и выбор карты перед тем как приложить устройство к терминалу.
Правила работы сканера находятся в системном приложении NfcNci.apk. Там то и находится константа которая определяет в каком состоянии должен быть девайс, чтобы работало сканирование (можно почитать здесь [3]). Естественно эту константу нужно изменить. Многие разработчики выкладывают на xda-developers уже готовые модифицированные файлы NfcNci.apk под нужный вам девайс, поэтому я просто взял готовый файл и заменил им приложение на моём устройстве.
Константе можно установить как минимум одно из двух значений, которые позволят сканеру считывать метки при заблокированном экране:
Первое решение по словам форумчан [4] жрёт около 35% батареи за 3 часа при том, что телефон просто лежал без дела. Поэтому лучше выбирать второй вариант, так как он вообще не тратит заряд + предотвратит случайную разблокировку и включение экрана, если метка будет находиться очень близко.
Конечно данное решение резко уменьшает порог вхождения обычных пользователей, так как теперь потребуется рутованный девайс и некоторые телодвижения, чтобы заменить оригинальное приложение. Но так как приложение не ориентированно на большие массы, то с этим можно смириться.
Опять же, по соображениям безопасности приложение на устройстве не может просто взять и разблокировать экран защищённый каким-либо методом (пароль, пин, паттерн и др.). На данный момент существует 2 стандартных решения, которые позволяют разблокировать экран:
Этот класс содержит 2 метода: disableKeyguard() и reenableKeyguard(). Первый метод разблокирует только не защищёный экран. Если экран защищён каким-либо методом, то вызов будет проигнорирован. Метод reenableKeyguard() необходимо вызвать для повторной блокировки экрана, иначе блокировщик не будет запускаться после его выключения.
Минусы данного решения: нельзя разблокировать экран защищённый, допустим, паролем да ещё и надо по какому-то событию вызывать перезапуск экрана блокировки. Вдобавок данный класс является устаревшим начиная с API 13, так что на него не стоит надеяться.
Флаги нужно устанавливать на родительское окно Activity. FLAG_DISMISS_KEYGUARD разблокирует экран только в том случае, если он не защищён. После закрытия окна блокировщик не будет восстанавливаться пока не выключится экран. FLAG_SHOW_WHEN_LOCKED лишь спрячет блокировщик (даже защищённый) и после закрытия окна он сразу же перейдёт на передний план.
То есть, опять же, эти флаги не смогут разблокировать защищённый экран. Максимум что можно сделать — показать своё окно поверх него.
В данном случае, чем меньше давать возможностей разработчику, тем больше повышается безопасность самой ОС. Возможности флагов, особенно FLAG_SHOW_WHEN_LOCKED, позволяют отображать своё приложение поверх блокировщика не нарушая безопасности системы. Но, к сожалению, нас это не устраивает.
Так как Android API не предоставляет нам красивых решений для разблокировки, то придётся писать костыли обходные пути.
В итоге было написано базовое приложение реализующее 3 метода разблокировки. Решил назвать его NFC Unlocker (ссылки на Google Play и исходники в конце поста). Реализованные обходные пути могут быть не стабильны, но это и так понятно исходя из их названия. Все эти методы требуют, чтобы пользователь вводил пароль в настройках приложения, а не в системе. Это сделано для того, чтобы мы могли восстанавливать/вводить (в зависимости от метода) пароль вместо пользователя.
После чтения метки ОС должна запустить наиболее подходящее Activity. Поэтому воспользоваться BroadcastReceiver’ом не получится. Далее я опишу эти методы.
И всё-таки флаг нам может помочь в реализации задуманного. Данное решение наиболее чистое, так как не требует рута и использует флаги окна, которые вполне себе разрешены. Для того, чтобы флаги сработали придётся очистить пароль методом DevicePolicyManager.resetPassword(). Для этого нам потребуются права администратора, которые приложение запросит у пользователя на странице настроек.
Алгоритм разблокировки таков:
((DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE)).resetPassword("", 0);
getActivity().getWindow().addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
Следующий метод использует устаревшее API, но тем не менее успешно выполняет свою задачу на Android 4.3. Так же как и в предыдущем методе нам придётся очистить пароль, чтобы разблокировка сработала.
Здесь алгоритм сложнее и с первого взгляда достаточно не стабилен (на практике всё гораздо лучше):
((DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE)).resetPassword("", 0).KeyguardManager keyguardManager = (KeyguardManager) context.getSystemService(Activity.KEYGUARD_SERVICE);KeyguardLock keyguardLock = keyguardManager.newKeyguardLock("nfcunlocker");keyguardLock.disableKeyguard();ScreenReceiver screenReceiver = new ScreenReceiver();IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_OFF);registerReceiver(mReceiver, filter);Данный метод работает достаточно стабильно, но пользуется устаревшим API.
Этот способ я придумал первым так как он достаточно прост, но требует root. В Андроиде с shell-а можно вызвать команду «input», которая позволяет вводить текст и эмулировать нажатия клавиш. Для ввода текста используется такой синтаксис:
input text "type your text here"
Эмуляция нажатий клавиш производится с помощью такой команды:
input keyevent KEYCODE
Список кодов клавиш можно найти здесь [8].
Алгоритм разблокировки у данного метода очень прост:
Про алгоритм выбора Activity при сканировании метки можно почитать здесь [9]. Вкратце можно сказать, что ОС выбирает Activity на основании содержимого метки. Для гарантированного запуска нашего Activity нужно воспользоваться AAR [10] (Android Application Records), что обозначает просто запись названия пакета приложения на метку. С помощью данного способа можно было бы гарантированно запускать разблокировку, но у меня не было под рукой меток, которые поддерживают стандарт NDEF. Поэтому я идентифицирую их по уникальному идентификатору [11].
А теперь подытожим плюсы и минусы интеграции с родным блокировщиком.
Плюсы:
Минусы:
По ссылке на Github есть ссылки на модифицированные файлы NfcNci.apk для некоторых популярных смартфонов.
Для демонстрации настройки и работы приложения я записал видео:
Ссылки:
Google Play [12]
Github [13]
P.S. За качество кода прошу не пинать. Я не Java-разработчик.
Автор: TheSteelRat
Источник [14]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/android-development/45619
Ссылки в тексте:
[1] не впечатлила: http://habrahabr.ru/post/174773/
[2] NFCSecure: https://play.google.com/store/apps/details?id=com.t3hh4xx0r.nfcsecure
[3] здесь: http://forum.xda-developers.com/showpost.php?p=45078500&postcount=162
[4] по словам форумчан: http://forum.xda-developers.com/showpost.php?p=34974004&postcount=10
[5] KeyguardManager.KeyguardLock: http://developer.android.com/reference/android/app/KeyguardManager.KeyguardLock.html
[6] FLAG_DISMISS_KEYGUARD: http://developer.android.com/reference/android/view/WindowManager.LayoutParams.html#FLAG_DISMISS_KEYGUARD
[7] FLAG_SHOW_WHEN_LOCKED: http://developer.android.com/reference/android/view/WindowManager.LayoutParams.html#FLAG_SHOW_WHEN_LOCKED
[8] здесь: http://thecodeartist.blogspot.com/2011/03/simulating-keyevents-on-android-device.html
[9] здесь: http://developer.android.com/intl/ru/guide/topics/connectivity/nfc/nfc.html
[10] AAR: http://developer.android.com/intl/ru/guide/topics/connectivity/nfc/nfc.html#aar
[11] идентификатору: http://developer.android.com/intl/ru/reference/android/nfc/NfcAdapter.html#EXTRA_ID
[12] Google Play: https://play.google.com/store/apps/details?id=com.steelrat.nfcunlocker
[13] Github: https://github.com/PaulAnnekov/nfcunlocker
[14] Источник: http://habrahabr.ru/post/196708/
Нажмите здесь для печати.