[Информационная безопасность, Разработка мобильных приложений, Разработка под Android, Google API] Обновляемся на новую версию API Android по наставлению Google
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
Скоро выходит Android 12, но в этом августе уже с 11-й версии разработчикам придётся использовать новые стандарты доступа приложений к внешним файлам. Если раньше можно было просто поставить флаг, что ваше приложение не поддерживает нововведения, то скоро они станут обязательными для всех. Главный фокус — повышение безопасности.Переход на новую версию API — довольно трудоёмкая операция, требующая больших затрат на её поддержку при введении крупных апдейтов. Далее расскажу немного про наш переход и возникшие при этом трудности. Что происходитЕсли вы уже знакомы с теорией, то этот раздел можно пропустить — тут я хочу поверхностно сравнить подходы к предмету в разных версиях операционной системы.В Android есть внутреннее Internal Storage (IS) и внешнее хранилище External Storage (ES). Исторически это были встроенная память в телефоне и внешняя SD-карта, поэтому ES был больше, но медленнее и дешевле. Отсюда и разделение — настройки и критически важное записывали в IS, а в ES хранили данные и большие файлы, например, медиа. Потом ES тоже стал встраиваться в телефон, но разделение, по крайней мере логическое, осталось.У приложения всегда есть доступ к IS, и там оно может делать что угодно. Но эта папка только для конкретного приложения и она ограничена в памяти. К ES нужно было получать доступ и, кроме манипуляции со своими данными, можно было получить доступ к данным других приложений и производить с ними любые действия (редактировать, удалять или украсть).Но после разделения на внутреннее и внешнее хранилища все равно оставались проблемы. Многие приложения могли хранить чувствительную информацию не только в IS, но и в ES — то есть ответственность лежала целиком на разработчиках и на том, кто хочет завладеть файлами.В Android решили всё это переделать ещё в 10-й версии, а в 11-й это стало обязательным.Чтобы минимизировать риски для пользователя в Google решили внедрить Scoped Storage (SS) в ES. Возможность проникнуть в папки других приложений убрали, а доступ есть только к своим данным — теперь это сугубо личная папка. А IS с 10-й версии ещё и зашифрована по умолчанию.В Android 11 Google зафорсировала использование SS — когда таргет-версия SDK повышается до 30-й версии API, то нужно использовать SS, иначе будут ошибки, связанные с доступом к файлам. Фишка Android в том, что можно заявить совместимость с определённой версией ОС. Те, кто не переходили на 11, просто говорили, что пока не совместимы с этой версий, но теперь нужно начать поддерживать нововведения всем. С осени не получится заливать апдейты, если не поддерживаешь Android 11, а с августа нельзя будет заливать новые приложения. Если SS не поддерживается (обычно это для девайсов ниже 10-й версии), то для доступа к данным других приложений требуется получить доступ к чтению и записи в память. Иначе придётся получать доступ к файлам через Media Content, Storage Access Framework или новый, появившийся в 11-м Android, фреймворк Datasets в зависимости от типа данных. Здесь тоже придётся получать разрешение доступа к файлу, но по более интересной схеме. Когда расшариваемый файл создаёшь сам, то доступ к нему не нужен. Но если переустановить приложение — доступ к нему опять потребуется. К каждому файлу система привязывает приложение, поэтому когда запрашиваешь доступ, его может не оказаться. Особо беспокоиться не нужно, это сложно отследить, поэтому лучше просто сразу запрашивать пермишен.Media Content, SAF и Datasets относятся к Shared Storage (ShS). При удалении приложения расшаренные данные не удаляются. Это полезно, если не хочется потерять нужный контент.Хотя даже при наличии SS можно дать доступ к своим файлам по определённой технологии — через FileProvider можно указать возможность получения доступа к своим файлам из другого приложения. Это нормально, потому что файлы расшаривает сам разработчик.Также добавилась фича — если приложение не использовалось несколько месяцев, то снимаются все пермишены и доступы к системным элементам. По best practice разрешение запрашивается по требованию, поэтому мы просто перед выполнением какого-либо действия проверяем, есть ли у нас пермишены. Если нет, то запрашиваем. В то же время перекрыли доступы к приложениям внутри девайса. Если раньше можно было отследить, что установлены определённые приложения и отправлять к ним соответствующие интенты, то сейчас мы должны прямо в манифесте прописать, что работаем именно с этими приложениями, и только после этого получить доступ.В качестве примера можем взять шаринг — мы шарим множество приложений, и их всех нужно указывать в манифесте, иначе они не обнаружатся. Начнём перебирать пакет установленных приложений — будет информация, что не указанного в манифесте приложения нет и при шаринге всё отвалится. Перейдём к практике.Переход на новую версиюОсновная функциональность по работе с файлами в приложении iFunny представлена в виде сохранения мемов в память и расшаривания их между приложениями. Это было первое, что требовалось починить.Для этого выделили в общий интерфейс работу с файлами, реализация которого зависела от версии API.
interface FilesManipulator {
fun createVideoFile(fileName: String, copy: Copier): Uri
fun createImageFile(fileName: String, copy: Copier): Uri
fun createFile(fileName: String, copy: Copier): Uri
fun getPath(uri: Uri): String
fun deleteFile(uri: Uri)
}
FilesManipulator представляет собой интерфейс, который знает, как работать с файлами и предоставляет разработчику API для записи информации в файл. Copier — это интерфейс, который разработчик должен реализовать, и в который передаётся поток вывода. Грубо говоря, мы не заботимся о том, как создаются файлы, мы работаем только с потоком вывода. Под капотом до 10-й версии Android в FilesManipulator происходит работа с File API, после 10-й (и включая её) — MediaStore API.Рассмотрим на примере сохранения картинки.
fun getContentValuesForImageCreating(fileName: String): ContentValues {
return ContentValues().apply {
put(MediaStore.Images.Media.DISPLAY_NAME, fileName)
put(MediaStore.Images.Media.IS_PENDING, FILE_WRITING_IN_PENDING)
put(MediaStore.Images.Media.RELATIVE_PATH, Environment.DIRECTORY_PICTURES + File.separator + appFolderName)
}
}
fun createImageFile(fileName: String, copy: Copier): Uri {
val contentUri = MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
val contentValues = getContentValuesForImageCreating(fileName)
val uri = contentResolver.insert(contentUri, contentValues)
?: throw IllegalStateException("New image file insert error")
downloadContent(uri, copy)
return uri
}
fun downloadContent(uri: Uri, copy: Copier) {
try {
contentResolver.openFileDescriptor(uri, FILE_WRITE_MODE)
.use { pfd ->
if (pfd == null) {
throw IllegalStateException("Got nullable file descriptor")
}
copy.copyTo(FileOutputStream(pfd.fileDescriptor))
}
contentResolver.update(uri, getWriteDoneContentValues(), null, null)
} catch (e: Throwable) {
deleteFile(uri)
throw e
}
}
fun getWriteDoneContentValues(): ContentValues {
return ContentValues().apply {
put(MediaStore.Images.Media.IS_PENDING, FILE_WRITING_DONE)
}
}
Так как операция сохранения медиафайлов достаточно длительная, то целесообразно использовать MediaStore.Images.Media.IS_PENDING, которая при установлении значения 0 не дает видеть файл приложениям, отличного от текущего. По сути, вся работа с файлами реализована через эти классы. Шаринг в другие приложения автоматически сохраняют медиа в память устройства и последующая работа с URI уже происходит по новому пути. Но есть такие SDK, которые ещё не успели перестроиться под новые реалии и до сих пор используют File API для проверки медиа. В этом случае используем кеш из External Storage и при необходимости провайдим доступ к файлу через FileProvider API.Помимо ограничений с памятью в приложениях, таргетированных на 30-ю версию API, появилось ограничение на видимость приложения. Так как iFunny использует шаринг во множество приложений, то данная функциональность была сломана полностью. К счастью, достаточно добавить в манифест query, открывающую область видимости к приложению, и можно будет также полноценно использовать SDK.Для неявных интентов тоже приходится добавлять код в манифест, чтобы задекларировать то, с чем будет работать приложение. В качестве примера выложу часть кода, добавленного в манифест.
<manifest … >
<queries>
<intent>
<action android:name="android.intent.action.SENDTO" />
<data android:scheme="smsto, mailto" />
</intent>
…
<package android:name="com.twitter.android" />
<package android:name="com.snapchat.android" />
<package android:name="com.whatsapp" />
<package android:name="com.facebook.katana" />
<package android:name="com.instagram.android" />
<package android:name="com.facebook.orca" />
<package android:name="com.discord" />
<package android:name="com.linkedin.android" />
</queries>
</manifest>
После проверок запуска UI-тестов на девайсах с версиями API 29-30 было выявлено, что они также перестали корректно отрабатываться.Первоначально в LogCat обнаружил, что приложение не может приконнектиться к процессу Orchestrator и выдает ошибку java.lang.RuntimeException: Cannot connect to androidx.test.orchestrator.OrchestratorService.Эта проблема из разряда видимости других приложений, поэтому достаточно было добавить строку <package android:name="androidx.test.orchestrator" /> .Тест удачно запустился, но возникла другая ошибка — Allure не может сохранить отчёт в память устройства, падает с ошибкой.Очевидно из-за Scoped Storage стало невозможно сохранять файлы в другие папки, поэтому снова почитав документацию по управлению файлами в памяти на девайсе, обнаружил интересный раздел. Там рассказано, как для нужд тестов открыть доступ к папкам девайса, но с существенными ограничениями, которые можно почитать тут.Так как нам нужно использовать этот пермишен только для тестов, то нам условия подходят. Поэтому я быстренько написал свой ShellCommandExecutor, который выполняет команду adb shell appops set --uid PACKAGE_NAME MANAGE_EXTERNAL_STORAGE allow на создании раннера тестов.На Android 11 тесты удачно запустились и стали проходить без ошибок. После попытки запуска на 10-й версии Android обнаружил, что отчет Allure также перестал сохраняться в память девайса. Посмотрев issue Allure, обнаружил, что проблема известная, как и с 11-й версией. Достаточно выполнить команду adb shell appops set --uid PACKAGE_NAME LEGACY_STORAGE allow. Сказано, сделано. Запустил тесты — всё еще не происходит сохранения в память отчёта. Тогда я обнаружил, что в манифесте WRITE_EXTERNAL_STORAGE ограничен верхней планкой до 28 версии API, то есть запрашивая работу памятью мы не предоставили все разрешения. После изменения верхней планки (конечно, для варианта debug) и запроса пермишена на запись тесты удачно запустились и отчёт Allure сохранился в память устройства. Добавлены следующие определения пермишенов для debug-сборки.
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="29"
tools:node="replace" />
После всех вышеописанных манипуляций с приложением, можно спокойно устанавливать targetSdkVersion 30, загружать в Google Play и не беспокоиться про дедлайн, после которого загружать приложения версией ниже станет невозможно.
===========
Источник:
habr.com
===========
Похожие новости:
- [Законодательство в IT, Социальные сети и сообщества, IT-компании] Московский суд оштрафовал Google на 3,5 млн руб. и TikTok на 1,5 млн руб. за неудаление запрещенного контента
- [JavaScript, Google API] Google документы станут полновесными с 1 июня. Пишем скрипт для обхода этого ограничения
- [Разработка под iOS, Разработка мобильных приложений, Usability, Accessibility] Доступность на iOS началась с «36 секунд» (перевод)
- [Производство и разработка электроники, Компьютерное железо] iFixit оценил ремонтопригодность iMac с чипом M1 на 2 из 10
- [Машинное обучение, Искусственный интеллект, Звук, Голосовые интерфейсы] Ультимативное сравнение систем распознавания речи: Ashmanov, Google, Sber, Silero, Tinkoff, Yandex
- [Информационная безопасность, Интервью, IT-компании] Высокотехнологичные аферисты
- [Программирование, Разработка игр, Unity, Игры и игровые приставки, Интервью] Подкаст «Хочу в геймдев» #25 — текстовая версия
- [Настройка Linux, Информационная безопасность] Найти и не обезвредить: пишем пентесты с Kali Linux (перевод)
- [API] Бесплатный API диалоговой модели на ruGPT-3
- [Производство и разработка электроники, Компьютерное железо, UEFI] PCI-SIG пообещала выпустить стандарт PCIe 6.0 к концу года
Теги для поиска: #_informatsionnaja_bezopasnost (Информационная безопасность), #_razrabotka_mobilnyh_prilozhenij (Разработка мобильных приложений), #_razrabotka_pod_android (Разработка под Android), #_google_api, #_funcorp, #_android (андроид), #_android, #_api, #_google, #_mobilnye_prilozhenija (мобильные приложения), #_razrabotka (разработка), #_bezopasnost (безопасность), #_hranilische (хранилище), #_dannye (данные), #_blog_kompanii_funcorp (
Блог компании FunCorp
), #_informatsionnaja_bezopasnost (
Информационная безопасность
), #_razrabotka_mobilnyh_prilozhenij (
Разработка мобильных приложений
), #_razrabotka_pod_android (
Разработка под Android
), #_google_api
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 12:34
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
Скоро выходит Android 12, но в этом августе уже с 11-й версии разработчикам придётся использовать новые стандарты доступа приложений к внешним файлам. Если раньше можно было просто поставить флаг, что ваше приложение не поддерживает нововведения, то скоро они станут обязательными для всех. Главный фокус — повышение безопасности.Переход на новую версию API — довольно трудоёмкая операция, требующая больших затрат на её поддержку при введении крупных апдейтов. Далее расскажу немного про наш переход и возникшие при этом трудности. Что происходитЕсли вы уже знакомы с теорией, то этот раздел можно пропустить — тут я хочу поверхностно сравнить подходы к предмету в разных версиях операционной системы.В Android есть внутреннее Internal Storage (IS) и внешнее хранилище External Storage (ES). Исторически это были встроенная память в телефоне и внешняя SD-карта, поэтому ES был больше, но медленнее и дешевле. Отсюда и разделение — настройки и критически важное записывали в IS, а в ES хранили данные и большие файлы, например, медиа. Потом ES тоже стал встраиваться в телефон, но разделение, по крайней мере логическое, осталось.У приложения всегда есть доступ к IS, и там оно может делать что угодно. Но эта папка только для конкретного приложения и она ограничена в памяти. К ES нужно было получать доступ и, кроме манипуляции со своими данными, можно было получить доступ к данным других приложений и производить с ними любые действия (редактировать, удалять или украсть).Но после разделения на внутреннее и внешнее хранилища все равно оставались проблемы. Многие приложения могли хранить чувствительную информацию не только в IS, но и в ES — то есть ответственность лежала целиком на разработчиках и на том, кто хочет завладеть файлами.В Android решили всё это переделать ещё в 10-й версии, а в 11-й это стало обязательным.Чтобы минимизировать риски для пользователя в Google решили внедрить Scoped Storage (SS) в ES. Возможность проникнуть в папки других приложений убрали, а доступ есть только к своим данным — теперь это сугубо личная папка. А IS с 10-й версии ещё и зашифрована по умолчанию.В Android 11 Google зафорсировала использование SS — когда таргет-версия SDK повышается до 30-й версии API, то нужно использовать SS, иначе будут ошибки, связанные с доступом к файлам. Фишка Android в том, что можно заявить совместимость с определённой версией ОС. Те, кто не переходили на 11, просто говорили, что пока не совместимы с этой версий, но теперь нужно начать поддерживать нововведения всем. С осени не получится заливать апдейты, если не поддерживаешь Android 11, а с августа нельзя будет заливать новые приложения. Если SS не поддерживается (обычно это для девайсов ниже 10-й версии), то для доступа к данным других приложений требуется получить доступ к чтению и записи в память. Иначе придётся получать доступ к файлам через Media Content, Storage Access Framework или новый, появившийся в 11-м Android, фреймворк Datasets в зависимости от типа данных. Здесь тоже придётся получать разрешение доступа к файлу, но по более интересной схеме. Когда расшариваемый файл создаёшь сам, то доступ к нему не нужен. Но если переустановить приложение — доступ к нему опять потребуется. К каждому файлу система привязывает приложение, поэтому когда запрашиваешь доступ, его может не оказаться. Особо беспокоиться не нужно, это сложно отследить, поэтому лучше просто сразу запрашивать пермишен.Media Content, SAF и Datasets относятся к Shared Storage (ShS). При удалении приложения расшаренные данные не удаляются. Это полезно, если не хочется потерять нужный контент.Хотя даже при наличии SS можно дать доступ к своим файлам по определённой технологии — через FileProvider можно указать возможность получения доступа к своим файлам из другого приложения. Это нормально, потому что файлы расшаривает сам разработчик.Также добавилась фича — если приложение не использовалось несколько месяцев, то снимаются все пермишены и доступы к системным элементам. По best practice разрешение запрашивается по требованию, поэтому мы просто перед выполнением какого-либо действия проверяем, есть ли у нас пермишены. Если нет, то запрашиваем. В то же время перекрыли доступы к приложениям внутри девайса. Если раньше можно было отследить, что установлены определённые приложения и отправлять к ним соответствующие интенты, то сейчас мы должны прямо в манифесте прописать, что работаем именно с этими приложениями, и только после этого получить доступ.В качестве примера можем взять шаринг — мы шарим множество приложений, и их всех нужно указывать в манифесте, иначе они не обнаружатся. Начнём перебирать пакет установленных приложений — будет информация, что не указанного в манифесте приложения нет и при шаринге всё отвалится. Перейдём к практике.Переход на новую версиюОсновная функциональность по работе с файлами в приложении iFunny представлена в виде сохранения мемов в память и расшаривания их между приложениями. Это было первое, что требовалось починить.Для этого выделили в общий интерфейс работу с файлами, реализация которого зависела от версии API. interface FilesManipulator {
fun createVideoFile(fileName: String, copy: Copier): Uri fun createImageFile(fileName: String, copy: Copier): Uri fun createFile(fileName: String, copy: Copier): Uri fun getPath(uri: Uri): String fun deleteFile(uri: Uri) } fun getContentValuesForImageCreating(fileName: String): ContentValues {
return ContentValues().apply { put(MediaStore.Images.Media.DISPLAY_NAME, fileName) put(MediaStore.Images.Media.IS_PENDING, FILE_WRITING_IN_PENDING) put(MediaStore.Images.Media.RELATIVE_PATH, Environment.DIRECTORY_PICTURES + File.separator + appFolderName) } } fun createImageFile(fileName: String, copy: Copier): Uri { val contentUri = MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY) val contentValues = getContentValuesForImageCreating(fileName) val uri = contentResolver.insert(contentUri, contentValues) ?: throw IllegalStateException("New image file insert error") downloadContent(uri, copy) return uri } fun downloadContent(uri: Uri, copy: Copier) { try { contentResolver.openFileDescriptor(uri, FILE_WRITE_MODE) .use { pfd -> if (pfd == null) { throw IllegalStateException("Got nullable file descriptor") } copy.copyTo(FileOutputStream(pfd.fileDescriptor)) } contentResolver.update(uri, getWriteDoneContentValues(), null, null) } catch (e: Throwable) { deleteFile(uri) throw e } } fun getWriteDoneContentValues(): ContentValues { return ContentValues().apply { put(MediaStore.Images.Media.IS_PENDING, FILE_WRITING_DONE) } } <manifest … >
<queries> <intent> <action android:name="android.intent.action.SENDTO" /> <data android:scheme="smsto, mailto" /> </intent> … <package android:name="com.twitter.android" /> <package android:name="com.snapchat.android" /> <package android:name="com.whatsapp" /> <package android:name="com.facebook.katana" /> <package android:name="com.instagram.android" /> <package android:name="com.facebook.orca" /> <package android:name="com.discord" /> <package android:name="com.linkedin.android" /> </queries> </manifest> <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="29" tools:node="replace" /> =========== Источник: habr.com =========== Похожие новости:
Блог компании FunCorp ), #_informatsionnaja_bezopasnost ( Информационная безопасность ), #_razrabotka_mobilnyh_prilozhenij ( Разработка мобильных приложений ), #_razrabotka_pod_android ( Разработка под Android ), #_google_api |
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 12:34
Часовой пояс: UTC + 5