Google анонсировал релиз Android 12 и уже выпустил две документации Developer preview. Изменений довольно много, поэтому мы решили рассказать вам о самых главных и интересных из них, которые затронут большую часть мобильных приложений. Но сперва расскажем о сложностях, с которыми вы можете столкнуться при миграции, и поделимся рекомендациями Google, как сделать переход на Android 12 приятным и безболезненным.


Оригинал: https://developer.android.com/about/versions/12/images/android-12-desktop-timeline-en-landpg-mar.svg

Начиная с мая 2021 года планируются релизы нескольких Beta-версий нового Android 12, которые завершатся финальным релизом с новой функциональностью и улучшенным API. Последовательные этапы релиза содержат период Platform stability. Предполагается, что с наступлением этого этапа не будет изменений, способных повлиять на приложения. У разработчиков появится время для адаптации приложений, пока пользователи не получили новую платформу на устройства. Однако уже сейчас можно сделать первые выводы о новом API и о том, как эти обновления отразятся на наших приложениях.

Миграция на новую compileSdkVersion


1. Задержка показа уведомлений от Foreground service


Компонент Foreground service довольно часто фигурирует в новых обновлениях. Так, показ уведомления от компонента Foreground service будет отложен до 10 секунд. Это позволит компоненту foreground service с коротким сроком выполнения завершиться до показа уведомлений и уменьшить общее количество уведомлений у пользователей. Однако такая задержка может стать нежелательным поведением для многих приложений (например, если уведомление важное, и вы явно хотите его отобразить). В последнем случае придется вызвать метод setShowForegroundImmediately().
Уведомления будут показаны сразу, если ваш Foreground service попадает хотя бы под одно из исключений ниже:

  • уведомление, связанное с компонентом Foreground service, содержит кнопку действия;
  • Service связан с проигрыванием медиа, телефонными звонками или навигацией (задается в категории уведомлений);
  • параметр foregroundServiceType имеет значение connectedDevice, mediaPlayback, mediaProjection или phoneCall.

2. Запрет использования низкоуровневого API при работе с MAC-адресами


В Android 12 при запросе MAC-адреса будет возвращаться null или заглушка:

  • если значение параметра targetSdkVersion больше 30, то вернется null;
  • если значение параметра targetSdkVersion меньше или равно 30, то вернется заглушка 02: 00: 00: 00: 00: 00.

При выполнении операций сетевого подключения следует использовать ConnectivityManager вместо низкоуровневого API (NetworkInterface, getifaddrs() или сокеты Netlink).

3. Сквозные касания через overlay блокируются


В Android сохранялась уязвимость с нажатием на системные окна с помощью прозрачного слоя. В Android 12 система блокирует обработку касания, которые проходят через определенные окна. Это ограничение включает следующие случаи:

  • приложения, которые позволяют пропускать касания дальше через свои окна с помощью флага FLAG_NOT_TOUCHABLE;
  • наложения, требующие разрешения SYSTEM_ALERT_WINDOW (окна, использующие флаг TYPE_APPLICATION_OVERLAY);
  • сообщения типа Toast.


Исключения:

  • взаимодействие внутри приложения (окна показываются только тогда, когда пользователь взаимодействует с одним приложением);
  • взаимодействие с доверенными окнами Accessibility, IME, Assistant (окна с типом TYPE_APPLICATION_OVERLAY не являются доверенными);
  • взаимодействие с окнами, чей корневой визуальный элемент имеет значение видимости INVISIBLE или GONE;
  • взаимодействие с полностью прозрачными окнами (параметр alpha имеет значение 0.0);
  • достаточно прозрачные окна системных предупреждений (если общая непрозрачность не превосходит определенного порогового значения).

4. Запрет закрытия системных диалоговых окон


Это ограничение также напрямую связано с безопасностью Android и является продолжением предыдущего ограничения. В Android 12 нельзя будет закрывать системные диалоги из приложений. При разных значениях параметра targetSdkVersion поведение будет отличаться:

  • если приложение, ориентированное на Android 12, попытается закрыть системный диалог, то возникнет исключение SecurityException;
  • если приложение со значением параметра targetSdkVersion до 30 включительно попытается закрыть системный диалог, то исключение SecurityException не возникнет. Неудачная попытка отразится записью в LogCat.


Закрывать системные диалоги в Android 12 можно будет только в трех исключительных случаях:

  • при работе инструментальных тестов;
  • при отображении приложением окна, находящегося в верхней части панели уведомлений (верно для приложений со значением параметра targetSdkVersion до 30 включительно);
  • при взаимодействии пользователя с уведомлениями (посредством нажатия на кнопки действий в уведомлениях) и запущенными компонентами Service или Broadcast receiver, обрабатывающими эти взаимодействия с приложением (верно для приложений со значением параметра targetSdkVersion до 30 включительно).


Миграция на новую targetSdkVersion


1. Ограничение доступной области для уведомлений


До появления Android 12 приложения могли использовать всю область уведомлений для отображения информации пользователю. Отсутствие ограничений привело к неконсистентности внешнего вида уведомлений. В Android 12 появятся единые шаблоны уведомлений для свернутого и развернутого состояний.
Вид стандартного шаблона с Customizable Area:
image

Оригинал: https://developer.android.com/images/about/versions/12/customizable-area.png

Новый шаблон уведомлений:

image

Оригинал: https://developer.android.com/images/about/versions/12/custom-collapsed-view.png

image

Оригинал: https://developer.android.com/images/about/versions/12/custom-expanded-view.png

Таким образом, уведомления не смогут использовать области произвольных размеров. Мы считаем это положительным обновлением, потому что системный UI должен быть унифицированным для всех приложений на разных устройствах, и нельзя разрешать каждому приложению показывать контент произвольных размеров.

2. Запрет использования трамплинов уведомлений


Когда пользователь взаимодействует с уведомлением, некоторые приложения в ответ на действия пользователя запускают компоненты, которые запускают Activity. Такие компоненты называют «трамплины уведомлений»(Notification trampoline).
В ответ на взаимодействие пользователя с уведомлением приложение не сможет запускать Activity из компонентов Service и BroadcastReceiver. Рекомендуется использовать уведомления с PendingIntent и запускать напрямую Activity.

3. Небезопасная работа с вложенным Intent


Это обновление повышает уровень безопасности мобильных приложений. Рассмотрим следующий сценарий с вложенным объектом Intent. Есть два приложения: ClientApp и ProviderApp. Приложение ClientApp запускает компонент из ProviderApp. Желаемым результатом является запуск из ProviderApp ClientCallbackActivity, принадлежащего ClientApp.


Оригинал: https://miro.medium.com/max/700/0*IEWopqfWI0gFMsnk

Для этого ClientApp создает Intent (ClientCallbackIntent), который передастся сперва в ProviderApp через extra другого объекта Intent, а потом будет использован для запуска ClientCallbackActivity.
Такой паттерн поведения в Android является уязвимостью. У приведенного выше сценария есть две проблемы:

  • чтобы приложение ProviderApp смогло запустить Activity ClientCallbackActivity, последнее должно быть помечено как exported. Но это позволит другим приложениям работать с ClientCallbackActivity;
  • вложенный Intent может быть использован для запуска любого Activity из приложения ProviderApp, в том числе приватных, не экспортируемых и чувствительных к персональным данным Activity. Например, вместо ClientCallbackActivity Intent может ссылаться на ApiSensitiveActivity из приложения ProviderApp.



Оригинал: https://miro.medium.com/max/700/0*iof2-14vTyoDX-h5

Решением этих проблем может служить PendingIntent: приложение ProviderApp будет принимать не просто Intent, а PendingIntent.


Оригинал: https://miro.medium.com/max/700/0*J1fPWozrWb-__b7u

Проверка и предупреждение небезопасного запуска вложенных объектов Intent станут доступны в Android 12. В StrictMode добавлена специальная проверка. Если приложение сперва достает вложенный Intent, переданный в параметре extras, а затем осуществляет запуск другого компонента приложения с этим вложенным объектом Intent, то возникнет нарушение StrictMode. Чтобы включить эту проверку, необходимо вызвать метод detectUnsafeIntentLaunch() при конфигурировании VmPolicy. Рекомендуется использовать PendingIntent вместо вложенных объектов Intent.

4. Изменяемый PendingIntent


В приложениях с targetSdkVersion = 31 при создании PendingIntent без указания PendingIntent.FLAG_MUTABLE или PendingIntent.FLAG_IMMUTABLE флагов возникнет исключение IllegalArgumentException. Эти флаги определяют возможность модификации существующего объекта PendingIntent, например чтоб добавить в него что-то.
Это обновление нацелено на повышение безопасности Рекомендуется использовать флаг PendingIntent.FLAG_IMMUTABLE там, где это возможно.

5. Более безопасный экспорт компонентов


Переходим к возможно самому ожидаемому обновлению Android 12. Если у приложения с targetSdkVersion = 31 есть компоненты с непустыми блоками intent filter, то для каждого такого компонента должно быть явно указано значение атрибута android:exported.
Сделаем два шага назад и вспомним, что по умолчанию у всех компонентов без блоков intent filter android:exported = false, а у компонентов с блоками android:exported = true. Это сделано для того, чтобы к нему можно было обратиться извне приложения. Однако нужно ограничить приложения, которые могут получить доступ к компоненту. Если же этого сделано не будет, то это является уязвимостью CWE-926: Improper Export of Android Application Components.
Важно, что сама среда разработки Android Studio будет сообщать о компонентах без атрибута android:exported:

  • в случае Android Studio 2020.3.1 Canary 11 в файле Manifest приложения будет сообщение о проблеме от анализатора Lint, а при попытке сборки будет появляться ошибка сборки;
  • в случае более ранних версий Android Studio ошибка возникнет при попытке установить приложение на устройство с Android 12.


6. Современные SameSite cookie в WebView


Атрибут SameSite определяет, можно ли отправлять cookie со всеми запросами или только к тому же сайту. В приложениях, адаптированных к Android 12:

  • cookie без атрибута SameSite обрабатываются как SameSite = Lax;
  • cookie с SameSite = None должны иметь атрибут Secure (будут передаваться только по HTTPS);
  • ссылки между HTTP и HTTPS обрабатываются как межсайтовые запросы, и cookie будут отправлять только в случае SameSite=None; Secure.

Рекомендуется убедиться, что атрибут SameSite явно задан с соответствующими значениями там, где это необходимо, и указать cookie, которым разрешено работать на веб-сайтах или при переходе с HTTP на HTTPS.

7. Ограничение запуска компонента Foreground service


Google с каждым релизом все настойчивее предлагает уходить от компонента Service к WorkManager. Так, приложения со значением параметра targetSdkVersion, равным 31, не смогут запускать Foreground service, если приложение в момент запуска работает в фоне.
Все еще есть ряд ограничений, которые позволят продолжить запускать из фона Foreground service. Например, после перезапуска устройства и получения приложением в компоненте Broadcast Reciever объекта Intent со значением поля action ACTION_BOOT_COMPLETED. Помимо этого, есть возможность попросить пользователя отключить оптимизацию заряда батареи, что позволит запускать Foreground service из фона.
Для задач, которые не попали под исключения, рекомендуется использовать новый тип задач Expedited jobs. Эти задачи могут запускаться мгновенно по мере возможности и не подвергаются таким ограничениям, как Battery Saver и Doze.

Дополнительные возможности новой платформы


Дополнительные возможности Android 12 не могут негативно повлиять на имеющиеся приложения. Во многом поэтому их интеграция не будет носить срочный характер, а зачастую потребует проведения анализа полезности тому или иному проекту.
Однако там тоже есть на что обратить внимание большинству мобильных команд:

  • Rounded corner API позволяет упростить работу по UI с экранами со скругленными краями;
  • при формировании действия в уведомлении появилась возможность указать флаг запроса аутентификации с помощью метода setAuthenticationRequired(). Переданное значение true обязывает Android в случае заблокированного устройства запросить у пользователя аутентификацию. Таким образом, действие, заданное для уведомления, будет выполнено только в случае, если пользователь корректно аутентифицировался;
  • Compatible media transcoding: если ваше приложение не поддерживает новые форматы медиа, то Android сам перекодирует их в более старые форматы. В файле res/xml/media_capabilities нужно указать, какие форматы кодеков поддерживает ваше приложение, а какие нет. Это обновление поможет сделать ваше приложение стабильным в случае получения медиа в новом формате, к которому оно еще не готово;
  • возвращаемые методами getLinkDownstreamBandwidthKbps() и getLinkUpstreamBandwidthKbps() оценки пропускной способности улучшены как для Wi-Fi, так и для сотовой связи. Методы будут возвращать средневзвешенные пропускные способности за все время для каждого оператора связи или SSID WiFi, типа сети и уровня сигнала среди всех приложений на устройстве. Ожидается повышение точности оценки при холодном старте приложения;
  • 3rd-party-приложения могут скрывать перекрывающие их наложения типа TYPE_APPLICATION_OVERLAY с помощью метода setHideOverlayWindows();
  • режим погружения упрощен: у пользователя появится возможность осуществлять навигацию одним жестом в полноэкранном режиме вместо двух (первое действие для возврата элементов навигации, а второе действие для выхода).

Большое количество улучшений касается также безопасности, UX, графики и изображений.

В комментариях интересно было бы услышать, какие изменения Вам кажутся важными и полезными, а что Вы ожидали получить, но все еще не получили?