Android 17 では、デベロッパー向けに優れた新しい機能と API が導入されました。以下のセクションでは、これらの機能の概要を説明し、関連する API を使い始めるうえで役立つ情報を提供します。
新しい API、変更された API、削除された API の一覧については、API 差分レポートをご覧ください。新しい API について詳しくは、Android API リファレンスをご覧ください。新しい API は、見つけやすいようにハイライト表示されています。
また、プラットフォームの変更がアプリに影響する可能性がある領域も確認する必要があります。詳しくは次のページをご覧ください。
コア機能
Android 17 では、Android のコア機能に関連する次の新機能が追加されています。
新しい ProfilingManager トリガー
Android 17 adds several new system triggers to ProfilingManager to
help you collect in-depth data to debug performance issues.
The new triggers are:
TRIGGER_TYPE_COLD_START: Trigger occurs during app cold start. It provides both a call stack sample and a system trace in the response.TRIGGER_TYPE_OOM: Trigger occurs when an app throws anOutOfMemoryErrorand provides a Java Heap Dump in response.TRIGGER_TYPE_KILL_EXCESSIVE_CPU_USAGE: Trigger occurs when an app is killed due to abnormal and excessive CPU usage and provides a call stack sample in response.TRIGGER_TYPE_ANOMALY: Detect system performance anomalies such as excessive binder calls and excessive memory usage.
To understand how to set up the system trigger, see the documentation on trigger-based profiling and how to retrieve and analyze profiling data documentation.
Profiling trigger for app anomalies
Android 17
introduces an on-device anomaly detection service that monitors for
resource-intensive behaviors and potential compatibility regressions. Integrated
with ProfilingManager, this service allows your app to receive profiling
artifacts triggered by specific system-detected events.
Use the TRIGGER_TYPE_ANOMALY trigger to detect system performance issues
such as excessive binder calls and excessive memory usage. When an app breaches
OS-defined memory limits, the anomaly trigger allows developers to receive
app-specific heap dumps to help identify and fix memory issues. Additionally,
for excessive binder spam, the anomaly trigger provides a stack sampling profile
on binder transactions.
This API callback occurs prior to any system imposed enforcements. For example, it can help developers collect debug data before the app is terminated by the system for exceeding memory limits.
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)
}
JobDebugInfo API
Android 17 では、デベロッパーが JobScheduler ジョブのデバッグに役立つ新しい JobDebugInfo API が導入されました。ジョブが実行されない理由、実行時間、その他の集計情報などを確認できます。
拡張された JobDebugInfo API の最初のメソッドは getPendingJobReasonStats() です。これは、ジョブが実行保留状態になった理由と、それぞれの累積保留期間のマップを返します。このメソッドは、getPendingJobReasonsHistory() メソッドと getPendingJobReasons() メソッドを結合して、スケジュールされたジョブが想定どおりに実行されない理由を把握できるようにします。また、期間とジョブの理由の両方を 1 つのメソッドで使用できるようにすることで、情報取得を簡素化します。
たとえば、指定された jobId に対して、メソッドは PENDING_JOB_REASON_CONSTRAINT_CHARGING と 60000 ミリ秒の期間を返すことがあります。これは、充電の制約が満たされなかったため、ジョブが 60000 ミリ秒間保留されたことを示します。
allow-while-idle アラームのリスナー サポートでウェイクロックを削減
Android 17 では、PendingIntent の代わりに OnAlarmListener を受け入れる AlarmManager.setExactAndAllowWhileIdle の新しいバリアントが導入されています。この新しいコールバック ベースのメカニズムは、現在、連続的なウェイクロックに依存して定期的なタスク(ソケット接続を維持するメッセージ アプリなど)を実行しているアプリに最適です。
プライバシー
Android 17 には、ユーザーのプライバシーを強化するための次の新機能が含まれています。
Encrypted Client Hello(ECH)プラットフォームのサポート
Android 17 introduces platform support for Encrypted Client Hello (ECH), a significant privacy enhancement for network communications. ECH is a TLS 1.3 extension that encrypts the Server Name Indication (SNI) during the initial TLS handshake. This encryption helps protect user privacy by making it more difficult for network intermediaries to identify the specific domain an app is connecting to.
The platform now includes the necessary APIs for networking libraries to
implement ECH. This includes new capabilities in DnsResolver to query for
HTTPS DNS records containing ECH configurations, and new methods in Conscrypt's
SSLEngines and SSLSockets to enable ECH by passing in these configurations when
connecting to a domain. Developers can configure ECH preferences, such as
enabling it opportunistically or mandating its use, through the new
<domainEncryption> element within the Network Security Configuration file,
applicable globally or on a per-domain basis.
Popular networking libraries such as HttpEngine, WebView, and OkHttp are expected to integrate these platform APIs in future updates, making it easier for apps to adopt ECH and enhance user privacy.
For more information, see the Encrypted Client Hello documentation.
Android 連絡先選択ツール
Android 連絡先選択ツールは、ユーザーがアプリと連絡先を共有するための標準化された閲覧可能なインターフェースです。Android 17(API レベル 37)以降を搭載したデバイスで利用できるこの選択ツールは、広範な READ_CONTACTS 権限に代わるプライバシー保護型の選択肢を提供します。アプリはユーザーのアドレス帳全体へのアクセスをリクエストするのではなく、電話番号やメールアドレスなど、必要なデータ フィールドを指定します。ユーザーは共有する特定の連絡先を選択します。これにより、アプリには選択したデータに対する読み取りアクセス権のみが付与されます。UI を構築または維持することなく、組み込みの検索、プロファイルの切り替え、複数選択機能を使用して、一貫したユーザー エクスペリエンスを提供しながら、きめ細かい制御を確保できます。
詳しくは、連絡先選択ツールのドキュメントをご覧ください。
セキュリティ
Android 17 では、デバイスとアプリのセキュリティを強化するために、次の新機能が追加されています。
Android の高度な保護機能モード(AAPM)
Android の高度な保護モードは、Android ユーザーに強力な新しいセキュリティ機能を提供し、ユーザー(特にリスクの高いユーザー)を高度な攻撃から保護するうえで大きな進歩となります。オプトイン機能として設計された AAPM は、ユーザーがいつでもオンにできる単一の構成設定で有効になり、セキュリティ保護の意見の強いセットが適用されます。
これらのコア構成には、提供元不明のアプリのインストール(サイドローディング)のブロック、USB データ シグナリングの制限、Google Play プロテクトのスキャン義務付けが含まれており、デバイスの攻撃対象領域を大幅に削減します。デベロッパーは AdvancedProtectionManager API を使用してこの機能と統合し、モードのステータスを検出できます。これにより、ユーザーがオプトインしたときに、アプリケーションが自動的にセキュリティ強化された状態に移行したり、リスクの高い機能を制限したりできるようになります。
PQC APK 署名
Android now supports a hybrid APK signature scheme to future-proof your app's signing identity against the potential threat of attacks that make use of quantum computing. This feature introduces a new APK Signature Scheme, which lets you pair a classical signing key (such as RSA or EC) with a new post-quantum cryptography (PQC) algorithm (ML-DSA).
This hybrid approach ensures your app remains secure against future quantum attacks while maintaining full backward compatibility with older Android versions and devices that rely on classical signature verification.
Impact on developers
- Apps using Play App Signing: If you use Play App Signing, you can wait for Google Play to give you the option to upgrade a hybrid signature using a PQC key generated by Google Play, ensuring your app is protected without requiring manual key management.
- Apps using self-managed keys: Developers who manage their own signing keys can utilize updated Android build tools (like apksigner) to rotate to a hybrid identity, combining a PQC key with a new classical key. (You must create a new classical key, you cannot reuse the older one.)
接続
Android 17 では、デバイスとアプリの接続性を向上させるために次の機能が追加されています。
制約のある衛星ネットワーク
低帯域幅の衛星ネットワークでアプリが効果的に機能するように最適化を実装します。
ユーザー エクスペリエンスとシステム UI
Android 17 には、ユーザー エクスペリエンスを向上させるための以下の変更が含まれています。
アシスタント専用の音量ストリーム
Android 17 では、USAGE_ASSISTANT での再生用に、アシスタント アプリ専用のアシスタント音量ストリームが導入されています。この変更により、アシスタントの音声が標準のメディア ストリームから分離され、ユーザーは両方の音量を個別に制御できるようになります。これにより、アシスタントの回答の可聴性を維持しながらメディア再生をミュートするなどのシナリオが可能になります。
新しい MODE_ASSISTANT_CONVERSATION 音声モードにアクセスできるアシスタント アプリを使用すると、音量調節の一貫性をさらに高めることができます。アシスタント アプリはこのモードを使用して、アクティブなアシスタント セッションに関するヒントをシステムに提供し、アクティブな USAGE_ASSISTANT 再生の外側で、または接続された Bluetooth 周辺機器でアシスタント ストリームを制御できるようにします。
引き継ぎ
ハンドオフは、Android 17 で導入される新しい機能と API です。アプリ デベロッパーはこれを統合して、ユーザーにクロスデバイスの継続性を提供できます。これにより、ユーザーは 1 つの Android デバイスでアプリ アクティビティを開始し、別の Android デバイスに移行できます。ハンドオフはユーザーのデバイスのバックグラウンドで実行され、受信側のデバイスのランチャーやタスクバーなどのさまざまなエントリ ポイントを通じて、ユーザーの近くにある他のデバイスで利用可能なアクティビティを表示します。
アプリは、受信側のデバイスに同じネイティブ Android アプリがインストールされていて利用可能な場合、そのアプリを起動するようにハンドオフを指定できます。このアプリ間フローでは、ユーザーは指定されたアクティビティにディープリンクされます。また、アプリからウェブへのハンドオフをフォールバック オプションとして提供したり、URL ハンドオフで直接実装したりすることもできます。
ハンドオフのサポートはアクティビティ単位で実装されます。ハンドオフを有効にするには、アクティビティの setHandoffEnabled() メソッドを呼び出します。受け渡しとともに追加のデータを渡す必要がある場合もあります。これにより、受信側のデバイスで再作成されたアクティビティが適切な状態を復元できます。onHandoffActivityDataRequested() コールバックを実装して、ハンドオフが受信側デバイスでアクティビティを処理して再作成する方法を指定する詳細を含む HandoffActivityData オブジェクトを返します。
ライブ情報 - セマンティック カラー API
Android 17 では、ライブ アップデートを通じて、普遍的な意味を持つ色をサポートするセマンティック カラーリング 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)
Android 17 向け UWB ダウンリンク TDoA API
ダウンリンク到達時間差(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
}
}
}
アウトオブバンド(OOB)構成
次のスニペットは、Wi-Fi と BLE の DL-TDoA OOB 構成データの例を示しています。
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
};
OOB 構成がないために使用できない場合や、OOB 構成にないデフォルト値を変更する必要がある場合は、次のスニペットに示すように、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();