Это просто подбивка списка изменений с пояснениями, чтобы не скакать по статьям на официальном сайте. Хотя правильнее, конечно, читать оригинал.
Изменения, касающиеся всех приложений
Фото такого пирога с сайта рецептов |
Здесь собраны изменения, которые задевают приложения, даже если их таргет ниже
14
. Называется новая версия
Android Upside-down cake
— "перевёрнутый" пирог или "вверх
ногами" или "наизнанку" — как угодно. В общем, суть в том, что после выпекания
этот пирог переворачивают и то, что раньше было верхом пирога становится его
дном.
Core функциональность
Разрешение SCHEDULE_EXACT_ALARM
больше не выдаётся
автоматически
Exact alarms
— это когда нужно выполнить задачу в строго заданное
время, ни раньше, ни позже. К примеру — напомнить о встрече. Начиная с Android
14 для большинства устанавливаемых приложений (очевидно, не касается уже
установленных), таргет которых начинается с 13, пермишен
SCHEDULE_EXACT_ALARM
автоматически выдаваться больше не будет. В общем, теперь нужно спрашивать его
явно.
О чём речь
-
SCHEDULE_EXACT_ALARM
появилось вAPI level 31
(этоS
, он же12
) - без этого разрешения запланированные задачи будут выполняться не в строго заданное время, а когда ОС разрешит
-
его нужно (теперь, когда представили - не нужно было) запрашивать.
Сценарий будет не как с
runtime permission
— диалог — а как разрешение на установку приложений, когда пользователь на отдельном экране дёргает переключатель - этого экрана может не быть вовсе. Вообще все экраны "специальных разрешений" имеют пометку, что производитель прошивки может его убирать. Имейте в виду
- и пользователь, и ОС могут переключатель выставить в отключенное состояние, отозвав разрешение
- отзыв разрешения автоматически удаляет все запланированные задачи
-
в
API level 33
(этоTIRAMISU
, он же13
) у этого разрешения появилась альтернативаUSE_EXACT_ALARM
- это разрешение выдаётся автоматически
- пользователь не может его отозвать
-
вас выбросят из Google Play за использование этого разрешения, если
приложение не является будильником или чем-то подобным, чему реально
нужно срабатывать секунда в секунду.
Я смотрел весь сериал только из-за этой гифки
Контекстно-зарегистрированные бродкасты ставятся в очередь для кешированных приложений
Если приложение перешло в состояние "кешировано" (то есть оно не на переднем плане и не убито системой и становится кандидатом на отстрел), то контекстные бродкасты складываются в очередь. Если приложение не будет отстрелено системой и пользователь снова к нему обратиться, то лежащие в очереди бродкасты будут доставлены, если контекст позволяет.
О чём речь
Контекстно-зарегистрированные бродкасты, это те, которые были зареганы в контексте чего-то. Например, в контексте активити. То есть если активити приложения умерло, то и бродкаст больше не придёт. Если бродкаст был зареган в апликейшен контексте, то будет доставлен, пока жив хоть какой-то компонент приложенияПриложения могут убить только собственные фоновые процессы
Метод
killBackgroundProcesses()
может убить теперь только фоновые процессы того приложения, из которого был
вызван. Наконец сдохнет большинство "оптимизаторов батареи". К сожалению,
во-первых только сторонние, во-вторых не все. Вендорский шлак так и будет
существовать, а разработчики стороннего ПО то и дело будут ссылаться на
https://dontkillmyapp.com/
О чём речь
-
это древняя апишка, которая защищена разрешением
KILL_BACKGROUND_PROCESSES
. Само её существование для сторонних приложений — бред какой-то -
эта апишка не позволяла убивать системные приложения, только сторонние -
патч безопасности от 1 декабря 2022 уже ограничил работу пермишена
KILL_BACKGROUND_PROCESSES
— стало нельзя убивать сторонние процессы - теперь это ограничение, которое раньше было только фиксом безопасности, затащили в ОС. Другими словами, изменение произошло в конце прошлого года на всех актуальных версиях ОС
-
некоторые оптимизаторы использовали не эту апишку, а работали хитрее через
Accessibility Service
. Так что готовьтесь к тому, что шлак массово будет переходить на это, очень опасное, API
Безопасность
Поднят минимальный таргет для устанавливаемых приложений
Приложения с targetSdkVersion
ниже 23 (это
Android 6
) больше не могут быть установлены. То есть в
Android 14
теперь все приложения будут поддерживать
runtime permission
, либо не будут работать вовсе. Если приложение
с более низким таргетом было установлено, когда на ваше устройство прилетел
Android 14
, то для него будет сделано исключение и оно продолжит
работать. Но вот переустановить его уже нельзя будет.
Поле Media owner package name может быть изменено
К хранилищу медиа можно делать запросы поля
OWNER_PACKAGE_NAME
, в котором будет (или не будет — может быть NULL
) указано имя
пакета, который сохранил определённый медиа файл. Начиная с
Android 14
значение этого поля будет подменяться для всех
приложений кроме (должно быть выполнено хоть одно условие):
- приложение, которое сохранило этот файл, входит в список приложений, которые всегда видимы другим
-
приложение, которое запросило это поле, имеет разрешение
QUERY_ALL_PACKAGES
О чём речь
Ничего не понятно, правда?
-
запросы к медиа хранилищам делаются как запросы к БД. Пусть они и обёрнуты
в некое API, сути это не меняет - запросить можно в том числе "а кто
создал этот файл":
OWNER_PACKAGE_NAME
. В ответ придёт или имя пакета приложения, илиNULL
-
Google решили, что через такие запросы можно понять, какие приложения
установлены на устройстве. И это, надо сказать, разумно. Вполне себе тянет
на утечку по сторонним каналам. А ведь уже давно запрос списка
установленных приложений защищается пермишеном
QUERY_ALL_PACKAGES
-
сложив два и два, Google сделали так. Если приложение, которое пытается
узнать владельца файла через эту апишку, не владеет разрешением
QUERY_ALL_PACKAGES
, то проверяется, является ли владелец файла всегда видимым приложением - это всякие системные приложения — они ведь всегда есть, чего их скрывать
- приложения, которые установило ваше собственное приложение. Оно и так знает, что установило, чего скрывать
-
приложение, которое вызывало ваше через
startActivityForResult()
. Это ведь оно вас позвало, оно и так уже призналось в своём существовании - всякое другое, типа контент провайдеров, клавитур - Подробнее тут, если надо
-
в итоге, если владелец не входит в список всегда видимых
&&
запрашивающий не владеетQUERY_ALL_PACKAGES
, то он не сможет узнать владельца. Как именно подменяется владелец я не смотрел. Да и не важно, хоть там будет простоandroid
написано, хоть там будетNULL
. Главное, что нельзя таким образом составить список установленных приложений
User experience
Давайте уже согласимся, что говорить "юикс" нормально, а говорить "пользовательский опыт" — это ментальное заболевание. Впрочем, лично мне нравится говорить "привычки", хотя в реальной речи стараюсь не использовать это слово. Оно нравится мне, но другие как-то так не говорят и боюсь, что буду сбивать с толку других
Взаимодействие с несмахиваемыми уведомлениями
В Android 14
пользователь может смахивать уведомления, которые
ранее были не смахиваемыми, но в конкретных случаях. Если уведомление сделано
не смахиваемым установкой флага
FLAG_ONGOING_EVENT
, то пользователь теперь может всё-таки его смахнуть. Флаг выставляется,
например, через
NotificationCompat.Builder#setOngoing(boolean)
.
Такие уведомления всё равно не будут смахиваться, если:
- телефон заблокирован. Ну то есть на экране блокировки
- пользователь нажал кнопку очистки уведомлений. То есть случайно потерять не получится
Поведение не меняется, если:
-
уведомление создано с использованием
MediaStyle API
— то есть явно для плееров - политики запрещают это действие
О чём речь
Подавляющее большинство моих знакомых (и я сам) в первую очередь подумали —
касается ли это foreground service
? Если вы знаете ответ
заранее — снимаю шляпу
- мы привыкли, что уведомления фореграунд сервисов не смахивались, в общем-то
-
в
Android 13
поведение изменилось. Уведомления от фореграундов стало можно смахивать -
чтобы всё же закрепить уведомление, нужно ручками выставлять флаг
Notification#FLAG_ONGOING_EVENT
, о котором говорилось выше. По умолчанию билдер за вас его не выставит - вAndroid 14
уже пошли дальше и флаг тоже почикали
Предоставление частичного доступа к фото и видео
Если вам достаточно фото пикера, то можно пропускать.
В Android 13
появились пермишены доступа к изображениям
(READ_MEDIA_IMAGES
) и видео (READ_MEDIA_VIDEO
). В
Android 14
диалог запроса разрешения изменился:
- появилась кнопка для выбранных фото и видео. Пользователи могут указать вполне конкретные фото и видео, к которым приложению можно иметь доступ
- кнопка всегда разрешающая доступ к выбранному типу медиа
- кнопка, которая не даёт разрешения
Ну и видно, что разрешение READ_MEDIA_AUDIO
не задето.
О чём речь
-
в
Android 13
добавились новые разрешения на доступ к медиа, которые нужно использовать вместоREAD_EXTERNAL_STORAGE
:READ_MEDIA_IMAGES
,READ_MEDIA_VIDEO
иREAD_MEDIA_AUDIO
-
если приложение, которое работает с этими разрешениями, будет установлено
в
Android 14
, то диалог запроса разрешений изменится — добавится новая кнопка -
то есть мы понимаем, что минимально возможный таргет будет
Android 13
. Ведь до этого таких разрешений не было - теперь пользователь может нажать новую кнопку и дать доступ не ко всем картинкам, а к конкретным
-
если пользователь сделает выбор конкретных, то это разрешение сработает
примерно как
one time permission
— то есть будет выдано на текущую жизнь прилаги. После отстрела и нового запроса разрешения диалог появится вновь - разрешение всего и запрет всего работают как раньше
-
это поведение можно сделать немного прозрачнее в первую очередь для себя
самого, благодаря новому пермишену
READ_MEDIA_VISUAL_USER_SELECTED
, но оно доступно только дляtarget 14
- впрочем, в этом случае лучше использовать именно пикер, а не изобретать свой велосипед
Специальные возможности
Масштабирование шрифта до 200%
Система позволит задавать масштабирование шрифта до 200%. Будьте добры убедиться, что ваше приложение к этому готово.
Признаюсь, я ни разу не видел приложений, которые готовы хотя бы к небольшому увеличению размеров шрифтов. Всё сразу начинает ехать, перестаёт влезать. В общем, дизайнеры будут класть болт на людей со слабым зрением на 200% яростнее.
Изменения, стреляющие для target на 14 и выше
Некоторые из изменений ниже касаются приложений с таргетом и ниже
14
. Но поддержка 14
позволяет управлять поведением
более точно.
Core функциональность
Для foreground services требуется указание типа
Для приложений с targetSdkVersion
на Android 14
для
каждого foreground service
нужно указывать его тип из
заранее предопределённого списка. Если для приложения нельзя выбрать логичный
тип из списка, то нужно переехать на WorkManager
или специально
созданный user-initiated data transfer job
. Не уверен, что это
нужно переводить. Вроде понятно, что речь идёт о задачах, которые инициировал
сам пользователь.
О чём речь
-
для таргета на
Android 10
для сервисов в манифесте стало нужно указывать типandroid:foregroundServiceType
для некоторых задач: камеры, геолокации. Если сервис не использовался для этих целей, то и тип не нужен -
для таргета на
Android 14
тип указать обязан и никак иначе. На выбор даётся более десятка типов (звонки, камера, локация, здоровье, микрофон и другое). Фореграунд сервиса без типа быть теперь не может - если для какого-то сервиса не подходит ни один тип, то у вас два пути:
-
переход на
WorkManager
. С этим, думаю, всё понятно, ему лет сто уже. Если не знакомы — ознакомьтесь, сбросьте часть рутины. Но добавите новую зависимость. А мы знаем, чтоGoogle
делает со своими либами каждые несколько лет -
переход на "задачи передачи данных, инициированных пользователем":
user-initiated data transfer jobs
- пользовательские задачи — это всякие длинные задачи, которые пользователь инициировал явно, но которые не подходят по списку типов. К примеру, скачивание файла с удалённого ресурса или копирование файлов на внешний носитель
-
трансфер джобы обязывают объявить пермишен
RUN_LONG_JOBS
. Читайте так: в следующей версии или через пару тоже начнут ограничивать. Да иGoogle Play
может начать делать кислые щи - есть некоторая защита от того, что вы впишете тип хоть какой-нибудь, чтобы от вас отстали. Для этого почти каждому типу сопоставлено разрешение, которое нужно объявить в манифесте. А лепить разрешения "чтобы отстали" уже не хочется
-
есть отдельный тип
Short service
, который может быть полезен для реально коротких задач. Он не требует отдельного разрешения. Однако жизнь этого сервиса ограничена ~3 минутами, которые начинают тикать с вызоваstartForeground()
и заканчивают в момент остановки сервиса (можноselfStop()
, можноstopForeground()
. Если не уложились в 3 минуты и не остановили сервис, ловитеANR
. Кроме того одновременно может работать только один короткоживущий сервис. Попытка запустить ещё один приведёт к исключениюForegroundServiceStartNotAllowedException
Безопасность
Ограничения неявных и отложеных интентов
Мы все говорим "интент". А вот говорите вы "отложенный" или "пендинг"?
Приложения с таргетом на Android 14
получают изменённое поведение
взаимодействия со своими собственными компонентами:
- больше нельзя послать неявный интент не экспортированному компоненту — поймаешь исключение
- при попытке создать отложенный мутабельный интент, не указав компонент или хотя бы пакет явно, снова словите исключение
О чём речь
Вроде и понятно, а вроде не очень, да?
-
компонентам приложения (активити, сервисы, вот это всё) в манифесте нужно
выставлять флаг экспорта
android:exported
-
документация нам говорила, что экспортированные
(
android:exported="true"
) компоненты доступны всем приложениями на подёргать. Если хотите экспортированный, но защищённый, для вас есть специальный механизм разрешений -
если компонент не экспортированный, то дёргать его можно только изнутри
этого же приложения (более широко — от этого же
UID
). И привилегированные системные компоненты ещё могли войти в этот сарай (отсылка для поехавших). Если же левое приложение попытается дёрнуть такой компонент, то получит исключение, что нет такого
-
многие разработчики делали не экспортированные, например, активити и для
него создавали фильтр вида
my.mega.app.action.NAME
. Ну и далее из кодаcontext.startActivity(my.mega.app.action.NAME)
- однако (это частный пример) авторы вредоносных приложений стали создавать экраны, которые выглядят точно также, как те, на которые они нацелились. И создавали такие же фильтры. Теперь система не знает, кому послать интент, он же не явный. Выбор из настоящего экрана и поддельного перекладывается на пользователя и тот может сделать неправильный выбор. То есть внешние приложения по-прежнему не могут вызвать не экспортированные компоненты, но могли прикинуться теми приложениями
-
теперь, если таргет
Android 14
, для вызова своего собственного не экспортированного компонента нужно создавать явный интент. Что не позволит уже уйти этому интенту не в те ворота
Бродкаст ресиверы, зарегистрированные в рантайме, должны явно указать эспортированность
Контекстные ресиверы (о них было выше) в приложениях с таргетом на
Android 14
обязаны явно указать, экспортированы они
(RECEIVER_EXPORTED
) или нет (RECEIVER_NOT_EXPORTED
).
При регистрации слушателя системных бродкастов (к примеру переход в режим
полёта или установка приложения) это указание не требуется. Всё равно
системные послать кто угодно не может — считается, что защита уже есть.
Более безопасная динамическая загрузка кода
Если приложение имеет таргет Android 14
и использует динамическую
загрузку кода, то есть буквально передаёт внешнюю либу загрузчику, то теперь
нужно помечать файл с этим кодом доступным только для чтения:
File#setReadOnly()
Ну и отдельно Google
просит (но не проверяет) явно пересоздавать
эти файлы. Не проверять, что раз файл есть, то не перекачивать. А прям удалять
и качать его снова. Видимо идея в том, чтобы вам не подложили левый файл и вы
не приняли его за свой собственный. Ну и просят проверять подпись файла.
В общем-то суть сводится к старой истории, когда одно приложение качало недостающие компоненты со своего сайта в общедоступную папку, а троян на устройстве пользователя просто клал заранее подготовленный файл и приложение жрало его как родной.
Новые ограничения запуска активити из фона
Для приложений с таргетом на Android 14
, система накладывает
ограничения на запуск активити из фона:
-
отправитель, посылающий
PendingIntent
другому приложению, должен явно указать, позволяет ли он запускать себя из фона -
когда видимое приложение использует метод
bindService()
и, собственно, биндится к сервису другого приложения, которое сейчас в фоне, оно должно сообщить, можно ли теперь тому фоновому сервису запускать активити инцииатора. Для этого есть флагBIND_ALLOW_ACTIVITY_STARTS
О чём речь
Запутанно из-за всех этих "оно", "его". Кто, кому, чего? Признаюсь, перечитывал описание раз 10 и каждый раз понимал по-разному
Сначала PendingIntent
- это такой вид интента, который можно передать другому приложению так, будто ты его внутри себя перебрасываешь. И получатель теперь может дёргать твои компоненты от твоего имени. То есть создаёшь интент на запуск экрана. Оборачиваешь его в пендинг интент и передаёшь этот пендинг внешнему приложению. Теперь внешнее приложение может запустить твой экран от твоего же имени - при создании объекта отложенного интента нужно заранее решить, позволяем ли получателю поднимать наши экраны из фона. Можем разрешить, запретить или оставить на усмотрение системы
-
метод пришёл на смену
setPendingIntentBackgroundActivityLaunchAllowed
, который появился в13
и сдох в14
. Впрочем, его нигде и не афишировали. И судя по исходникам, он вообще скрыт. Видимо планировали, но не доделали
Теперь биндинг к сервису
- наше приложение видимо пользователю
- есть другое приложение, которое предоставляет сервис и сейчас в фоне где-то
- наше приложение биндится к сервису второго приложения
- раньше это второе приложение получало возможность запускать активити из фона. Типа, ко мне же подключилось видимое приложение
-
теперь поведение изменилось. Разрешение на запуск активити не
предоставляется. Приложение, которое биндится, должно явно передать
BIND_ALLOW_ACTIVITY_STARTS
Обновлены ограничения не SDK интерфейсов
Начиная с Android 9
урезают доступ к разным API. Часть держат в
белом списке, часть в сером (типа, готовьтесь, можем отрезать), к части доступ
заблокировали. Каждый релиз список пополняется. Вот, снова пополнили. Сейчас
он весит больше 50 Мегабайт в csv
формате.
Пример одной строки, как эти ограничения описаны:
Landroid/Manifest$permission
->BIND_COMPANION_DEVICE_SERVICE:Ljava/lang/String public-api sdk
system-api test-api
В этих ограничениях меня забавляет то, что куча либ самого
Google
, из джет пака, обращаются к серым API
.
Не знаю как вам, а мне изучение было полезным. А то я уже несколько релизов пропустил.
Ну и мой канал в Telegram: https://t.me/mydaybug
Комментариев нет:
Отправить комментарий