Содержание статьи
- Почитать
- Android Q: privacy
- Извлекаем SSL-сертификат приложения из KeyStore
- Разработчику
- Более удобный if-else для Kotlin
- Kotlin и параметры вещественного типа
- Kotlin и польза null
- Ускорение эмулятора Android
- Вред (некоторых) выражений Kotlin
- Ускоряем Android Studio
- Инструменты
- Библиотеки
Сегодня в выпуске: privacy-новшества Android Q Beta 1, извлечение SSL-сертификатов подопытного приложения из KeyStore, Kotlin и его параметры вещественного типа, польза значения null, вред языковых конструкций Kotlin и более удобный оператор if-else. А также: метод ускорения Android Studio, дампер, позволяющий снять память любого приложения, и свежая подборка библиотек.
Почитать
Android Q: privacy
Privacy in Android Q — документация Google об изменениях в механизмах доступа к информации в недавно выпущенном Android Q Beta 1. Основные нововведения:
- Запрет фонового доступа к местоположению. Раньше, если пользователь разрешал приложению использовать местоположение, оно могло сделать это в любой момент, даже если находилось в фоне. В новой версии у пользователя есть возможность выбрать, в каких ситуациях отдавать приложению свои координаты: в любое время или только пока приложение находится на экране.
- Запрет фонового доступа к буферу обмена. В Android нет отдельного разрешения на доступ к буферу обмена, любое приложение может прочитать или записать его содержимое (так получилось из-за технических особенностей этого механизма). Теперь доступ к буферу обмена могут получить только приложения, которые в данный момент находятся на экране либо являются клавиатурами и другими системами ввода. Нововведение убивает целый класс приложений — менеджеры буфера обмена.
- Запрет фонового запуска активностей. Текущие версии Android позволяют приложениям запускать другие приложения (а точнее, их активности) в любое время, независимо от того, находятся они на экране или нет. Это может запутать пользователя и открывает возможности для фишинга и других зловредных действий. Android Q запрещает фоновым приложениям запускать активности. В качестве альтернативы предлагается использовать уведомления, которые, в свою очередь, запустят нужную активность с помощью PendingIntent.
- Запрет на прямой доступ к карте памяти. С Android Q приложения больше не смогут получить прямой доступ к карте памяти (внутренней или внешней) с помощью разрешений
READ_EXTERNAL_STORAGEиWRITE_EXTERNAL_STORAGE. Вместо этого следует использовать либо личный каталог приложения внутри/sdcard/Android(он создается автоматически и не требует разрешений), либо одно из разрешений, допускающих доступ к каталогам с фотографиями, видео и загрузками. - Возможность отзыва разрешений у старых приложений. Система подтверждения разрешений приложений пользователем появилась еще в Android 6.0. Но работала она только в отношении софта, собранного для Android 6.0 и выше. Весь старый софт продолжал получать все нужные ему разрешения автоматически. В Android Q ситуация не изменилась, но теперь при запуске старого приложения пользователь будет видеть экран, с помощью которого сможет отозвать уже выданные системой разрешения.
- Запрет на включение/выключение Wi-Fi. В Android Q приложения больше не смогут включать и выключать Wi-Fi, вместо этого они должны использовать новую функцию settings-panels, которая показывает всплывающий диалог с переключателем выбранной настройки.
- Ограничение на доступ к IMEI и серийному номеру устройства. Чтобы прочитать эту информацию, теперь требуется разрешение
READ_PRIVILEGED_PHONE_STATE. - Запрет на доступ к информации о частоте использования контактов. Приложения, получающие доступ к базе контактов телефона, больше не смогут также получить информацию о том, как часто пользователь контактировал с теми или иными людьми.
- Рандомизация MAC-адреса. При скане сетей Android Q будет использовать рандомизированный MAC-адрес вместо настоящего. Это изменение должно защитить от отслеживания пользователя: некоторые магазины используют MAC-адрес для отслеживания посетителей, а торговые центры — для трекинга их перемещения.
- Другие изменения: запрет на доступ к файловой системе
/proc/net, запрет на чтение серийных номеров подключенных USB-устройств до получения соответствующего разрешения, запрет на чтение параметров камеры без получения разрешения на использование камеры, необходимость иметь разрешениеACCESS_FINE_LOCATION, чтобы получить доступ ко многим функциям телефонии, Wi-Fi и Bluetooth, запрет на скрытое получение содержимого экрана устройства обходными путями.
Извлекаем SSL-сертификат приложения из KeyStore
Extracting Android KeyStores from apps — статья об извлечении SSL-сертификатов из приложения с помощью Frida.
Многие приложения хранят приватные данные в KeyStore — специальном хранилище, позволяющем зашифровать и надежно защитить данные с помощью хардварного TEE-модуля смартфона (если такой присутствует). Напрямую извлечь эти данные в большинстве случаев не удастся. Но вместо извлечения данные можно перехватить.
KeyStore имеет методы load(KeyStore.LoadStoreParameter param) и load(InputStream stream, char[] password) для извлечения данных из хранилища. Мы можем переписать код этих функций с помощью Frida и сохранить данные на своей машине.
Код скрипта для Frida выглядит так:
setTimeout(function() { Java.perform(function () { keyStoreLoadStream = Java.use('java.security.KeyStore')['load'].overload('java.io.InputStream', '[C'); /* Переписываем функцию Keystore.load */ keyStoreLoadStream.implementation = function(stream, charArray) { /* Если первый параметр null — запускаем оригинал */ if (stream == null) { this.load(stream, charArray); return; } /* Отправляем сообщение, что функция найдена */ send({event: '+found'}); /* Читаем InputStream в буфер */ var hexString = readStreamToHex (stream); /* Отправляем тип KeyStore */ send({event: '+type', certType: this.getType()}); /* Отправляем пароль */ send({event: '+pass', password: charArray}); /* Отправляем сертификат в текстовой форме */ send({event: '+write', cert: hexString}); /* Запускаем оригинальную функцию */ this.load(stream, charArray); } }); },0); /* Функция для чтения InputStream и его конвертации в ASCII */ function readStreamToHex (stream) { var data = []; var byteRead = stream.read(); while (byteRead != -1) { data.push( ('0' + (byteRead & 0xFF).toString(16)).slice(-2) ); /* <------------ binary to hex -----------> */ byteRead = stream.read(); } stream.close(); return data.join(''); }
Кроме этого скрипта, также понадобится скрипт, работающий на компе (именно ему приведенный выше скрипт отправляет данные с помощью функции send). Как работать с Frida и запустить скрипт, мы уже рассказывали в статье «Инъекция для андроида».

Разработчику
Более удобный if-else для Kotlin
Kotlin: when if-else is too mainstream — краткая заметка о том, как создать более удобный аналог оператора if-else, который можно использовать так:
val condition = true val output = condition then { 1 + 1 } elze { 2 + 2 }
Реализация такого «оператора» умещается в десять строк:
infix fun <T>Boolean.then(action : () -> T): T? { return if (this) action.invoke() else null } infix fun <T>T?.elze(action: () -> T): T { return if (this == null) action.invoke() else this }
Ключевое слово infix используется, чтобы функции-расширения then и elze можно было вызывать без необходимости ставить точку перед ними.
Kotlin и параметры вещественного типа
How Reified Type makes Kotlin so much better — краткая статья о том, как параметры вещественного типа могут облегчить твою жизнь. В официальной документации приводится только один пример использования такого типа параметров — для облегчения передачи классов внутрь функции. Например, вместо того чтобы писать так:
private fun <T : Activity> Activity.startActivity( context: Context, clazz: Class<T>) { startActivity(Intent(context, clazz)) } startActivity(context, NewActivity::class.java)
можно написать так:
inline fun <reified T : Activity> Activity.startActivity( context: Context) { startActivity(Intent(context, T::class.java)) } startActivity<NewActivity>(context)
Но на этом полезные качества вещественных типов не заканчиваются. Еще один пример — функция для получения данных из бандла:
fun <T> Bundle.getDataOrNull(): T? { return getSerializable(DATA_KEY) as? T } val bundle: Bundle? = Bundle() bundle?.putSerializable(DATA_KEY, "Testing") val strData: String? = bundle?.getDataOrNull() val intData: Int? = bundle?.getDataOrNull() // Crash
Последняя строка приведет к падению приложения, так как не совпадают ожидаемый тип данных и тип, возвращаемый функцией. Исправить это можно так:
private inline fun <reified T> Bundle.getDataOrNull(): T? { return getSerializable(DATA_KEY) as? T } val bundle: Bundle? = Bundle() bundle?.putSerializable(DATA_KEY, "Testing") val strData: String? = bundle?.getDataOrNull() val intData: Int? = bundle?.getDataOrNull() // Null
Также вещественные типы можно использовать для эмуляции перегрузки методов на основе возвращаемого значения (Kotlin и Java по умолчанию разрешают выполнять перегрузки только на основе аргументов):
inline fun <reified T> Resources.dpToPx(value: Int): T { val result = TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_DIP, value.toFloat(), displayMetrics) return when (T::class) { Float::class -> result as T Int::class -> result.toInt() as T else -> throw IllegalStateException("Type not supported") } } val intValue: Int = resource.dpToPx(64) val floatValue: Float = resource.dpToPx(64)
Kotlin и польза null
When You Should Use Null in Kotlin — небольшая заметка о пользе значения null.
В среде программистов на Kotlin использование nullable-типов данных считается дурным тоном. Но автор объясняет, что благодаря особенностям Kotlin (null safety) использование null становится скорее преимуществом, чем недостатком.
Значение null можно использовать для индикации отсутствия какого-либо значения или недоступности данных. И если в Java в этом случае ты легко мог совершить «ошибку на миллион долларов» (например, обратиться к методу null-объекта и уронить приложения), то Kotlin просто не позволит тебе этого сделать. Несколько примеров:
- Если объект != null, то выполняем код:
iuser?.let { handleNonNullUser(user) }
- Не выполняем функцию, если аргумент = null:
fun handleUser(user : User?) { user ?: return // Твой код }
- Если объект = null, то:
val userName = user?.getName() ?: "Unknown"
Ускорение эмулятора Android
Android Emulator: Project Marble Improvements — статья разработчиков эмулятора Android об улучшениях в производительности, сделанных в версии 28.1 (доступен в canary-канале). В целом все достаточно просто:
- Режим «на батарейке» по дефолту. Раньше эмулятор сообщал работающему в нем Android, что устройство питается от сети, а это приводило к излишней фоновой активности (в частности, система начинала обновлять приложения). Теперь эмулятор «работает» на батарейке.
-
Возможность остановить эмулятор. Появились две ADB-команды, позволяющие полностью приостановить/возобновить работу эмулятора:
$ adb emu avd pause $ adb emu avd resumПри сборке и установке приложения эмулятор будет разбужен автоматически.
- Оптимизация механизма отрисовки. Новый механизм позволил на 8% улучшить производительность стрессового приложения.
- Оптимизация I/O-кода на macOS. Версия для macOS теперь использует системный вызов kqueue вместо select для мониторинга дисковых операций. Это позволило сократить оверхед с 10 до 3%.
- Headless-режим. Теперь эмулятор можно запустить без интерфейса и вывода картинки на экран. Эту функцию можно использовать для запуска автоматизированных UI-тестов.

Вред (некоторых) выражений Kotlin
Advocating against (some) Kotlin expressions — статья о вреде использования некоторых языковых конструкций Kotlin.
Когда ты программируешь на Kotlin, плагин Android Studio настоятельно предлагает конвертировать все подряд в выражение. Особенно раздражающим это бывает, например, когда плагин предлагает вынести return из условного оператора или оператора выбора. Простой пример:

Проблема этого кода в том, что он отрывает контекст от значения. Конкретно этот пример не раскрывает проблему целиком, но представь, что оператор выбора получился действительно длинным и не умещается целиком на экран. Ты видишь кусок кода и задаешься вопросом: а что это вообще?

Тебе приходится проматывать код вверх, чтобы просто узнать, что происходит со значением rawPizza дальше. А теперь представь, что ты читаешь diff на GitHub, который, кроме измененной строки, показывает только три строки кода сверху и снизу.
Вывод: использование подобных выражений имеет смысл только для однострочных ветвлений, которые просто возвращают значение.

Ускоряем Android Studio
Is Your Android Studio Always Slow? Here’s How to Speed Up Immediately — краткая статья об ускорении Android Studio, интересная только наличием списка плагинов, которые можно безболезненно отключить в 90% случаев:
- Android APK Support;
- Android Games;
- Android NDK;
- App Links Assistant;
- Copyright;
- Coverage;
- CVS Integration;
- Editor Config;
- Fabric for Android Studio;
- Firebase (App Indexing, Services, Testing);
- GitHub;
- Google (Cloud Tools Core, Cloud Tools for Android, Developer Samples, Login, Services);
- Markdown Support;
- Mercurial integration;
- hg4idea;
- Settings repository;
- Subversion integration;
- Task management;
- Test recorder;
- TestNG-J;
- YAML.
Инструменты
- fridump3 — универсальный дампер памяти на основе Frida, совместим с iOS, Android и Windows;
- Stringer — инструмент для генерации локализованной базы строк из CSV-файла.
Библиотеки
- ExpandableBottomBar — кастомизируемая анимированная панель навигации для нижней части экрана приложения;
- bubble-navigation — еще одна панель навигации;
- android_dbinspector — библиотека для просмотра содержимого базы данных приложения прямо на устройстве;
- Android-BackgroundChart — фоновое изображение с графиком;
- ShowMoreText — кастомный TextView, позволяющий показывать больше текста по клику на ссылку (например, «Подробнее»);
- headless-wifi-manager — библиотека для управления конфигурацией Wi-Fi других устройств через Nearby API;
- crashx — очередная библиотека краш-репортинга, показывает окно с информацией об ошибке при падении приложения;
- MultiProgressBar — множественный прогресс-бар в стиле Instagram Stories;
- mr-clean — библиотека для очистки логов от важной персональной информации;
- KBarcode — библиотека для реализации сканера штрих-кодов;
- ProfileBar — эффектная реализация страницы профиля пользователя/автора/героя;
- Kaskade — однонаправленный state-контейнер для хранения состояния объектов;
- audio-visualizer-android — библиотека визуализации аудио;
- glimpse-android — библиотека для обрезки изображений, учитывающая положение объектов.