В Android 17 появились новые функции и API для разработчиков. В следующих разделах кратко описаны эти функции, чтобы вы могли начать работу с соответствующими API.
Подробный список новых, измененных и удаленных API можно найти в отчете о различиях API. Подробную информацию о новых API можно найти в документации по Android API. Для удобства они выделены.
Также следует проверить, не повлияют ли изменения платформы на ваши приложения. Дополнительную информацию можно найти на следующих страницах:
- Изменения в поведении, которые затрагивают приложения, поддерживающие Android 17
- Изменения в поведении, которые затрагивают все приложения независимо от
targetSdkVersion.
Основные функции
В Android 17 добавлены следующие новые функции, связанные с основными возможностями Android.
Новые триггеры ProfilingManager
В Android 17 добавлено несколько новых системных триггеров для ProfilingManager, которые помогают собирать подробные данные для отладки проблем производительности.
Новые триггеры:
TRIGGER_TYPE_COLD_START– триггер срабатывает во время холодного запуска приложения. В ответе будет содержаться пример стека вызовов и трассировка системы.TRIGGER_TYPE_OOM– триггер срабатывает, когда приложение выдает ошибкуOutOfMemoryError, и в ответ предоставляет дамп кучи Java.TRIGGER_TYPE_KILL_EXCESSIVE_CPU_USAGE– триггер срабатывает, когда приложение закрывается из-за аномальной и чрезмерной загрузки ЦП, и в ответ предоставляет образец стека вызовов.TRIGGER_TYPE_ANOMALY– выявляет отклонения в производительности системы, например чрезмерное количество вызовов Binder и использование памяти.
Чтобы узнать, как настроить системный триггер, ознакомьтесь с документацией по профилированию на основе триггеров и получению и анализу данных профилирования.
Триггер профилирования для отклонений в работе приложений
В Android 17 появился сервис обнаружения отклонений на устройстве, который отслеживает ресурсоемкие действия и потенциальные регрессии совместимости. Этот сервис интегрирован с ProfilingManager и позволяет вашему приложению получать артефакты профилирования, созданные в результате определенных событий, обнаруженных системой.
Используйте триггер TRIGGER_TYPE_ANOMALY, чтобы выявлять проблемы производительности системы, например слишком большое количество вызовов Binder или чрезмерное использование памяти. Когда приложение превышает установленные ОС ограничения на использование памяти, триггер аномалий позволяет разработчикам получать дампы кучи для конкретного приложения, чтобы выявлять и устранять проблемы с памятью. Кроме того, при чрезмерном спаме в Binder триггер отклонений предоставляет профиль выборки стека для транзакций Binder.
Этот обратный вызов API происходит до применения каких-либо принудительных мер со стороны системы. Например, это может помочь разработчикам собрать данные отладки до того, как система закроет приложение из-за превышения лимита памяти.
val profilingManager =
applicationContext.getSystemService(ProfilingManager::class.java)
val triggers = ArrayList<ProfilingTrigger>()
triggers.add(ProfilingTrigger.Builder(ProfilingTrigger.TRIGGER_TYPE_ANOMALY))
val mainExecutor: Executor = Executors.newSingleThreadExecutor()
val resultCallback = Consumer<ProfilingResult> { profilingResult ->
if (profilingResult.errorCode != ProfilingResult.ERROR_NONE) {
// upload profile result to server for further analysis
setupProfileUploadWorker(profilingResult.resultFilePath)
}
profilingManager.registerForAllProfilingResults(mainExecutor,
resultCallback)
profilingManager.addProfilingTriggers(triggers)
}
API JobDebugInfo
В Android 17 появились новые API JobDebugInfo, которые помогают разработчикам отлаживать задания JobScheduler. С помощью этих API можно узнать, почему задания не выполняются, сколько времени они выполнялись, и получить другую агрегированную информацию.
Первый метод расширенного JobDebugInfo API – getPendingJobReasonStats(). Он возвращает карту причин, по которым задание находилось в состоянии ожидания выполнения, и соответствующую совокупную продолжительность ожидания. Этот метод объединяет методы getPendingJobReasonsHistory() и getPendingJobReasons(), чтобы вы могли получить статистику о том, почему запланированное задание не выполняется должным образом. При этом он упрощает поиск информации, предоставляя как продолжительность, так и причину задания в одном методе.
Например, для указанного jobId метод может вернуть PENDING_JOB_REASON_CONSTRAINT_CHARGING и длительность 60 000 мс, что означает, что задание ожидало 60 000 мс из-за невыполнения ограничения на зарядку.
Сократите количество запретов блокировки с помощью поддержки слушателей для будильников, разрешенных в режиме ожидания
В Android 17 представлен новый вариант AlarmManager.setExactAndAllowWhileIdle, который принимает OnAlarmListener вместо PendingIntent. Этот новый механизм на основе обратных вызовов идеально подходит для приложений, которые в настоящее время полагаются на непрерывные блокировки пробуждения для выполнения периодических задач, например для мессенджеров, поддерживающих сокет-соединения.
Конфиденциальность
В Android 17 добавлены перечисленные ниже функции для повышения конфиденциальности пользователей.
Поддержка платформы Encrypted Client Hello (ECH)
В Android 17 появилась поддержка технологии Encrypted Client Hello (ECH), которая значительно повышает конфиденциальность сетевых коммуникаций. ECH — это расширение TLS 1.3, которое шифрует Server Name Indication (SNI) во время первоначального установления TLS-соединения. Это шифрование помогает защитить конфиденциальность пользователей, затрудняя сетевым посредникам идентификацию конкретного домена, к которому подключается приложение.
Теперь платформа включает необходимые API для сетевых библиотек, позволяющие реализовать ECH. Это включает новые возможности в DnsResolver для запроса записей HTTPS DNS, содержащих конфигурации ECH, а также новые методы в SSLEngines и SSLSockets Conscrypt для включения ECH путем передачи этих конфигураций при подключении к домену. Разработчики могут настраивать параметры ECH, например, включать его по мере необходимости или предписывать его использование, с помощью нового элемента <domainEncryption> в файле конфигурации сетевой безопасности, применимого глобально или для каждого домена отдельно.
Ожидается, что популярные сетевые библиотеки, такие как HttpEngine, WebView и OkHttp, интегрируют эти API платформы в будущих обновлениях, что упростит внедрение ECH в приложения и повысит конфиденциальность пользователей.
Для получения более подробной информации см. документацию по протоколу Encrypted Client Hello .
Выбор контактов Android
Выбор контактов Android – это стандартизированный интерфейс, который позволяет пользователям делиться контактами с вашим приложением. Он доступен на устройствах с Android 17 (уровень API 37) или более поздней версией и обеспечивает конфиденциальность, в отличие от разрешения READ_CONTACTS. Вместо того чтобы запрашивать доступ ко всей адресной книге пользователя, приложение указывает нужные ему поля данных, например номера телефонов или адреса электронной почты, а пользователь выбирает контакты, которыми хочет поделиться. Это дает вашему приложению доступ на чтение только к выбранным данным, обеспечивая детальный контроль и предоставляя пользователям единообразный опыт работы со встроенным поиском, переключением профилей и возможностями множественного выбора без необходимости создавать или поддерживать пользовательский интерфейс.
Подробную информацию можно найти в документации по инструменту выбора контактов.
Безопасность
В Android 17 добавлены новые функции, которые повышают безопасность устройств и приложений.
Режим Дополнительной защиты Android (AAPM)
Расширенный режим защиты Android (AAPM) предлагает пользователям Android мощный новый набор функций безопасности, что является значительным шагом в защите пользователей — особенно тех, кто подвержен повышенному риску — от сложных атак. Разработанный как функция с возможностью выбора, AAPM активируется с помощью одной настройки конфигурации, которую пользователи могут включить в любое время, чтобы применить определенный набор мер защиты.
Эти основные настройки включают блокировку установки приложений из неизвестных источников (боковая загрузка), ограничение передачи данных по USB и обязательное сканирование с помощью Google Play Protect, что значительно уменьшает площадь поверхности атаки устройства. Разработчики могут интегрировать эту функцию с помощью API AdvancedProtectionManager для определения статуса режима, что позволяет приложениям автоматически переходить в режим повышенной безопасности или ограничивать работу с функциями высокого риска, если пользователь дал на это согласие.
Подписание APK-файлов с помощью постквантовой криптографии
Теперь Android поддерживает гибридную схему подписания APK, которая защищает идентификационные данные подписи приложения от потенциальных атак с использованием квантовых компьютеров. Эта функция представляет новую схему подписи APK, которая позволяет использовать классический ключ подписи (например, RSA или EC) с новым алгоритмом постквантовой криптографии (PQC) – ML-DSA.
Этот гибридный подход обеспечивает защиту приложения от будущих квантовых атак, сохраняя при этом полную обратную совместимость со старыми версиями Android и устройствами, которые используют классическую проверку подписи.
Что изменится для разработчиков
- Приложения, использующие функцию подписания приложений в Google Play. Если вы используете функцию подписания приложений в Google Play, то можете дождаться, когда Google Play предложит вам обновить гибридную подпись с помощью ключа PQC, сгенерированного Google Play. Это обеспечит защиту вашего приложения без необходимости вручную управлять ключами.
- Приложения, использующие ключи, управляемые разработчиком. Разработчики, которые управляют собственными ключами подписи, могут использовать обновленные инструменты сборки Android (например, apksigner), чтобы перейти на гибридную идентификацию, сочетающую PQC-ключ с новым классическим ключом. (Вам нужно создать новый классический ключ, а не использовать старый.)
Функции связи
В Android 17 добавлены следующие функции, улучшающие подключение устройств и приложений.
Ограниченные спутниковые сети
Внедряет оптимизации, позволяющие приложениям эффективно работать в сетях спутниковой связи с низкой пропускной способностью.
Удобство для пользователей и системный интерфейс
В Android 17 внесены следующие изменения, которые улучшают взаимодействие с пользователем.
Отдельный поток громкости для Ассистента
В Android 17 появился отдельный аудиопоток для приложений-помощников, который воспроизводится с помощью USAGE_ASSISTANT. Это изменение отделяет аудио Ассистента от стандартного медиапотока, позволяя пользователям отдельно управлять громкостью каждого из них. Это позволяет, например, отключить звук медиаконтента, но оставить его для ответов Ассистента, и наоборот.
Приложения Помощника с доступом к новому режиму аудио MODE_ASSISTANT_CONVERSATION могут ещё больше улучшить согласованность управления громкостью. Приложения Ассистента могут использовать этот режим, чтобы сообщить системе об активном сеансе Ассистента. Это позволяет управлять потоком Ассистента за пределами активного воспроизведения USAGE_ASSISTANT или с помощью подключенных периферийных устройств Bluetooth.
Handoff
Handoff – это новая функция и API, которые появятся в Android 17. Разработчики приложений смогут интегрировать их, чтобы обеспечить непрерывность работы пользователей на разных устройствах. Она позволяет пользователю начать работу в приложении на одном устройстве Android и продолжить на другом. Handoff работает в фоновом режиме на устройстве пользователя и показывает доступные действия с других устройств поблизости через различные точки входа, такие как панель запуска и панель задач, на принимающем устройстве.
Приложения могут использовать Handoff для запуска того же нативного приложения Android, если оно установлено и доступно на принимающем устройстве. В этом случае пользователь переходит по ссылке на контент к нужному действию. Кроме того, передача данных из приложения на сайт может предлагаться в качестве резервного варианта или реализовываться непосредственно с помощью передачи URL.
Поддержка Handoff реализована для каждого действия отдельно. Чтобы включить Handoff, вызовите метод setHandoffEnabled() для действия. Вместе с передачей может потребоваться передать дополнительные данные, чтобы воссозданное действие на принимающем устройстве могло восстановить соответствующее состояние. Реализуйте обратный вызов onHandoffActivityDataRequested(), чтобы возвращать объект HandoffActivityData, который содержит сведения о том, как Handoff должна обрабатывать и воссоздавать действие на принимающем устройстве.
Live Update – Semantic Color API
В Android 17 Live Update запускает Semantic Coloring API для поддержки цветов с универсальным значением.
Семантическую раскраску поддерживают следующие классы:
NotificationNotification.MetricNotification.ProgressStyle.PointNotification.ProgressStyle.Segment
Раскраски
- Зеленый – безопасность. Этот цвет следует использовать, чтобы сообщить людям, что вы находитесь в безопасной ситуации.
- Оранжевый – для обозначения опасности и физических угроз. Этот цвет следует использовать в ситуации, когда пользователям необходимо обратить внимание на то, чтобы установить более эффективные настройки защиты.
- Красный – опасность, остановка. Оно должно показываться в случаях, когда требуется срочно привлечь внимание пользователя.
- Синий – нейтральный цвет для информационного контента, который должен выделяться на фоне остального.
В следующем примере показано, как применить семантические стили к тексту в уведомлении:
val ssb = SpannableStringBuilder()
.append("Colors: ")
.append("NONE", Notification.createSemanticStyleAnnotation(SEMANTIC_STYLE_UNSPECIFIED), 0)
.append(", ")
.append("INFO", Notification.createSemanticStyleAnnotation(SEMANTIC_STYLE_INFO), 0)
.append(", ")
.append("SAFE", Notification.createSemanticStyleAnnotation(SEMANTIC_STYLE_SAFE), 0)
.append(", ")
.append("CAUTION", Notification.createSemanticStyleAnnotation(SEMANTIC_STYLE_CAUTION), 0)
.append(", ")
.append("DANGER", Notification.createSemanticStyleAnnotation(SEMANTIC_STYLE_DANGER), 0)
Notification.Builder(context, channelId)
.setSmallIcon(R.drawable.ic_icon)
.setContentTitle("Hello World!")
.setContentText(ssb)
.setOngoing(true)
.setRequestPromotedOngoing(true)
UWB Downlink-TDoA API для Android 17
Определение расстояния по разнице во времени прибытия сигнала по нисходящему каналу (DL-TDoA) позволяет устройству определить свое положение относительно нескольких якорей, измеряя относительное время прибытия сигналов.В следующем фрагменте кода показано, как инициализировать Ranging Manager, проверить возможности устройства и запустить сеанс DL-TDoA:
Kotlin
class RangingApp {
fun initDlTdoa(context: Context) {
// Initialize the Ranging Manager
val rangingManager = context.getSystemService(RangingManager::class.java)
// Register for device capabilities
val capabilitiesCallback = object : RangingManager.RangingCapabilitiesCallback {
override fun onRangingCapabilities(capabilities: RangingCapabilities) {
// Make sure Dl-TDoA is supported before starting the session
if (capabilities.uwbCapabilities != null && capabilities.uwbCapabilities!!.isDlTdoaSupported) {
startDlTDoASession(context)
}
}
}
rangingManager.registerCapabilitiesCallback(Executors.newSingleThreadExecutor(), capabilitiesCallback)
}
fun startDlTDoASession(context: Context) {
// Initialize the Ranging Manager
val rangingManager = context.getSystemService(RangingManager::class.java)
// Create session and configure parameters
val executor = Executors.newSingleThreadExecutor()
val rangingSession = rangingManager.createRangingSession(executor, RangingSessionCallback())
val rangingRoundIndexes = byteArrayOf(0)
val config: ByteArray = byteArrayOf() // OOB config data
val params = DlTdoaRangingParams.createFromFiraConfigPacket(config, rangingRoundIndexes)
val rangingDevice = RangingDevice.Builder().build()
val rawTagDevice = RawRangingDevice.Builder()
.setRangingDevice(rangingDevice)
.setDlTdoaRangingParams(params)
.build()
val dtTagConfig = RawDtTagRangingConfig.Builder(rawTagDevice).build()
val preference = RangingPreference.Builder(DEVICE_ROLE_DT_TAG, dtTagConfig)
.setSessionConfig(SessionConfig.Builder().build())
.build()
// Start the ranging session
rangingSession.start(preference)
}
}
private class RangingSessionCallback : RangingSession.Callback {
override fun onDlTdoaResults(peer: RangingDevice, measurement: DlTdoaMeasurement) {
// Process measurement results here
}
}
Java
public class RangingApp {
public void initDlTdoa(Context context) {
// Initialize the Ranging Manager
RangingManager rangingManager = context.getSystemService(RangingManager.class);
// Register for device capabilities
RangingManager.CapabilitiesCallback capabilitiesCallback = new RangingManager.RangingCapabilitiesCallback() {
@Override
public void onRangingCapabilities(RangingCapabilities capabilities) {
// Make sure Dl-TDoA is supported before starting the session
if (capabilities.getUwbCapabilities() != null && capabilities.getUwbCapabilities().isDlTdoaSupported()) {
startDlTDoASession(context);
}
}
};
rangingManager.registerCapabilitiesCallback(Executors.newSingleThreadExecutor(), capabilitiesCallback);
}
public void startDlTDoASession(Context context) {
RangingManager rangingManager = context.getSystemService(RangingManager.class);
// Create session and configure parameters
Executor executor = Executors.newSingleThreadExecutor();
RangingSession rangingSession = rangingManager.createRangingSession(executor, new RangingSessionCallback());
byte[] rangingRoundIndexes = new byte[] {0};
byte[] config = new byte[0]; // OOB config data
DlTdoaRangingParams params = DlTdoaRangingParams.createFromFiraConfigPacket(config, rangingRoundIndexes);
RangingDevice rangingDevice = new RangingDevice.Builder().build();
RawRangingDevice rawTagDevice = new RawRangingDevice.Builder()
.setRangingDevice(rangingDevice)
.setDlTdoaRangingParams(params)
.build();
RawDtTagRangingConfig dtTagConfig = new RawDtTagRangingConfig.Builder(rawTagDevice).build();
RangingPreference preference = new RangingPreference.Builder(DEVICE_ROLE_DT_TAG, dtTagConfig)
.setSessionConfig(new SessionConfig.Builder().build())
.build();
// Start the ranging session
rangingSession.start(preference);
}
private static class RangingSessionCallback implements RangingSession.Callback {
@Override
public void onDlTdoaResults(RangingDevice peer, DlTdoaMeasurement measurement) {
// Process measurement results here
}
}
}
Конфигурации Out-of-Band (OOB)
Ниже приведен пример данных конфигурации DL-TDoA OOB для Wi-Fi и BLE.
Java
// Wifi Configuration
byte[] wifiConfig = {
(byte) 0xDD, (byte) 0x2D, (byte) 0x5A, (byte) 0x18, (byte) 0xFF, // Header
(byte) 0x5F, (byte) 0x19, // FiRa Sub-Element
(byte) 0x02, (byte) 0x00, // Profile ID
(byte) 0x06, (byte) 0x02, (byte) 0x20, (byte) 0x08, // MAC Address
(byte) 0x14, (byte) 0x01, (byte) 0x0C, // Preamble Index
(byte) 0x27, (byte) 0x02, (byte) 0x08, (byte) 0x07, // Vendor ID
(byte) 0x28, (byte) 0x06, (byte) 0xCA, (byte) 0xC8, (byte) 0xA6, (byte) 0xF7, (byte) 0x6F, (byte) 0x08, // Static STS IV
(byte) 0x08, (byte) 0x02, (byte) 0x60, (byte) 0x09, // Slot Duration
(byte) 0x1B, (byte) 0x01, (byte) 0x0A, // Slots per RR
(byte) 0x09, (byte) 0x04, (byte) 0xE8, (byte) 0x03, (byte) 0x00, (byte) 0x00, // Duration
(byte) 0x9F, (byte) 0x04, (byte) 0x67, (byte) 0x45, (byte) 0x23, (byte) 0x01 // Session ID
};
// BLE Configuration
byte[] bleConfig = {
(byte) 0x2D, (byte) 0x16, (byte) 0xF4, (byte) 0xFF, // Header
(byte) 0x5F, (byte) 0x19, // FiRa Sub-Element
(byte) 0x02, (byte) 0x00, // Profile ID
(byte) 0x06, (byte) 0x02, (byte) 0x20, (byte) 0x08, // MAC Address
(byte) 0x14, (byte) 0x01, (byte) 0x0C, // Preamble Index
(byte) 0x27, (byte) 0x02, (byte) 0x08, (byte) 0x07, // Vendor ID
(byte) 0x28, (byte) 0x06, (byte) 0xCA, (byte) 0xC8, (byte) 0xA6, (byte) 0xF7, (byte) 0x6F, (byte) 0x08, // Static STS IV
(byte) 0x08, (byte) 0x02, (byte) 0x60, (byte) 0x09, // Slot Duration
(byte) 0x1B, (byte) 0x01, (byte) 0x0A, // Slots per RR
(byte) 0x09, (byte) 0x04, (byte) 0xE8, (byte) 0x03, (byte) 0x00, (byte) 0x00, // Duration
(byte) 0x9F, (byte) 0x04, (byte) 0x67, (byte) 0x45, (byte) 0x23, (byte) 0x01 // Session ID
};
Если вы не можете использовать готовую конфигурацию, потому что она отсутствует, или вам нужно изменить значения по умолчанию, которых нет в готовой конфигурации, вы можете создать параметры с помощью DlTdoaRangingParams.Builder, как показано в следующем фрагменте кода. Вместо DlTdoaRangingParams.createFromFiraConfigPacket() можно использовать следующие параметры:
Kotlin
val dlTdoaParams = DlTdoaRangingParams.Builder(1)
.setComplexChannel(UwbComplexChannel.Builder()
.setChannel(9).setPreambleIndex(10).build())
.setDeviceAddress(deviceAddress)
.setSessionKeyInfo(byteArrayOf(0x01, 0x02, 0x03, 0x04))
.setRangingIntervalMillis(240)
.setSlotDuration(UwbRangingParams.DURATION_2_MS)
.setSlotsPerRangingRound(20)
.setRangingRoundIndexes(byteArrayOf(0x01, 0x05))
.build()
Java
DlTdoaRangingParams dlTdoaParams = new DlTdoaRangingParams.Builder(1)
.setComplexChannel(new UwbComplexChannel.Builder()
.setChannel(9).setPreambleIndex(10).build())
.setDeviceAddress(deviceAddress)
.setSessionKeyInfo(new byte[]{0x01, 0x02, 0x03, 0x04})
.setRangingIntervalMillis(240)
.setSlotDuration(UwbRangingParams.DURATION_2_MS)
.setSlotsPerRangingRound(20)
.setRangingRoundIndexes(new byte[]{0x01, 0x05})
.build();