Android 17 では、デベロッパー向けに優れた新しい機能と API が導入されました。以下のセクションでは、これらの機能の概要を説明し、関連する API を試すための情報を提供します。
新しい API、変更された API、削除された API の一覧については、API 差分 レポートをご覧ください。新しい API について詳しくは、Android API リファレンスをご覧ください。新しい API は、見つけやすいようにハイライト表示されています。
プラットフォームの変更がアプリに影響する可能性がある領域も確認してください。詳しくは次のページをご覧ください。
コア機能
Android 17 では、Android のコア機能に関連する次の新機能が追加されています。
新しい ProfilingManager トリガー
Android 17 向 ProfilingManager 添加了多个新的系统触发器,以
帮助您收集深入数据来调试性能问题。
新触发器包括:
TRIGGER_TYPE_COLD_START:在应用冷启动期间触发。它在响应中同时提供调用堆栈样本和系统跟踪记录。TRIGGER_TYPE_OOM:当应用抛出OutOfMemoryError时触发,并在响应中提供 Java 堆转储。TRIGGER_TYPE_KILL_EXCESSIVE_CPU_USAGE:当应用因 CPU 使用异常且过高而被终止时触发,并在响应中提供调用堆栈样本。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)
}
JobDebugInfo API
Android 17 引入了新的 JobDebugInfo API,可帮助开发者调试其 JobScheduler 作业,了解作业未运行的原因、运行时长以及其他汇总信息。
扩展后的 JobDebugInfo API 的第一个方法是 getPendingJobReasonStats(),该方法会返回一个映射,其中包含作业处于待执行状态的原因及其各自的累计待执行时长。此方法将 getPendingJobReasonsHistory() 和 getPendingJobReasons() 方法联接在一起,可让您了解预定作业未按预期运行的原因,但通过在单个方法中同时提供时长和作业原因,简化了信息检索。
例如,对于指定的 jobId,该方法可能会返回 PENDING_JOB_REASON_CONSTRAINT_CHARGING 和 60000 毫秒的时长,表示作业因未满足充电约束而处于等待状态 60000 毫秒。
allow-while-idle アラームのリスナー サポートによりウェイクロックを削減
Android 17
引入了 AlarmManager.setExactAndAllowWhileIdle 的新变体,该变体
接受 OnAlarmListener 而不是 PendingIntent。这种基于回调的新机制非常适合目前依赖于连续唤醒锁来执行定期任务的应用,例如维护套接字连接的消息传递应用。
プライバシー
Android 17 には、ユーザーのプライバシーを強化するための次の新機能が搭載されています。
Encrypted Client Hello(ECH)プラットフォームのサポート
Android 17 では、ネットワーク通信のプライバシーを大幅に強化する Encrypted Client Hello(ECH)のプラットフォーム サポートが導入されています。ECH は、最初の TLS handshake 中に Server Name Indication(SNI)を暗号化する TLS 1.3 拡張機能です。この暗号化により、ネットワーク仲介者がアプリの接続先ドメインを特定することが難しくなり、ユーザーのプライバシーを保護できます。
プラットフォームに、ネットワーク ライブラリが ECH を実装するために必要な API が含まれるようになりました。これには、ECH 構成を含む HTTPS DNS レコードをクエリする DnsResolver の新機能と、ドメインに接続するときにこれらの構成を渡して ECH を有効にする Conscrypt の SSLEngines と SSLSockets の新しいメソッドが含まれます。デベロッパーは、ネットワーク セキュリティ構成ファイル内の新しい <domainEncryption> 要素を使用して、ECH の設定(機会的有効化や使用の義務付けなど)を構成できます。この設定は、グローバルに適用することも、ドメインごとに適用することもできます。
HttpEngine、WebView、OkHttp などの一般的なネットワーキング ライブラリは、今後のアップデートでこれらのプラットフォーム API を統合することが見込まれており、アプリが ECH を採用してユーザーのプライバシーを強化することが容易になります。
詳細については、暗号化されたクライアント Hello のドキュメントをご覧ください。
Android 連絡先選択ツール
Android 連絡先選択ツールは、ユーザーがアプリと連絡先を共有するための標準化されたブラウジング可能なインターフェースです。Android 17(API レベル 37)以降を搭載したデバイスで利用できるこの選択ツールは、広範な READ_CONTACTS 権限に代わるプライバシーの保護に配慮した選択肢となります。アプリは、ユーザーのアドレス帳全体へのアクセスをリクエストするのではなく、電話番号やメールアドレスなど、必要なデータフィールドを指定し、ユーザーが共有する特定の連絡先を選択します。これにより、アプリは選択したデータへの読み取りアクセスのみを許可され、UI を構築または維持することなく、組み込みの検索、プロファイルの切り替え、複数選択機能により、一貫したユーザー エクスペリエンスを提供しながら、きめ細かい制御を実現できます。
詳しくは、連絡先選択ツールのドキュメントをご覧ください。
セキュリティ
Android 17 では、デバイスとアプリのセキュリティを強化するための次の新機能が追加されています。
Android の高度な保護機能モード(AAPM)
Android の高度な保護モードは、Android ユーザーに強力な新しいセキュリティ機能を提供し、ユーザー(特にリスクの高いユーザー)を高度な攻撃から保護するうえで大きな進歩となります。オプトイン機能として設計された AAPM は、ユーザーがいつでもオンにできる単一の構成設定で有効になり、セキュリティ保護の意見の強いセットが適用されます。
これらのコア構成には、提供元不明のアプリのインストール(サイドローディング)のブロック、USB データ シグナリングの制限、Google Play プロテクトのスキャン義務付けが含まれており、デバイスの攻撃対象領域を大幅に削減します。デベロッパーは AdvancedProtectionManager API を使用してこの機能と統合し、モードのステータスを検出できます。これにより、ユーザーがオプトインしたときに、アプリケーションが自動的にセキュリティ強化された状態に移行したり、リスクの高い機能を制限したりできるようになります。
PQC APK 署名
Android は、量子コンピューティングを利用した攻撃の潜在的な脅威からアプリの署名 ID を保護するために、ハイブリッド APK 署名スキームをサポートするようになりました。この機能では、新しい APK 署名スキームが導入され、従来の署名鍵(RSA や EC など)と新しいポスト量子暗号(PQC)アルゴリズム(ML-DSA)をペアにすることができます。
このハイブリッド アプローチにより、従来の署名検証に依存する古い Android バージョンやデバイスとの完全な下位互換性を維持しながら、将来の量子攻撃からアプリを保護できます。
デベロッパーへの影響
- Google Play アプリ署名を使用するアプリ: Google Play アプリ署名を使用している場合は、Google Play で生成された PQC 鍵を使用してハイブリッド署名をアップグレードするオプションが Google Play に表示されるまで待つことができます。これにより、鍵を手動で管理しなくてもアプリを保護できます。
- 自己管理鍵を使用するアプリ: 署名鍵を自分で管理しているデベロッパーは、更新された Android ビルドツール(apksigner など)を使用して、PQC 鍵と新しい従来の鍵を組み合わせたハイブリッド ID にローテーションできます (新しい従来の鍵を作成する必要があります。古い鍵を再利用することはできません)。
接続
Android 17 では、デバイスとアプリの接続を強化するための次の機能が追加されています。
制約付き衛星ネットワーク
实现优化,使应用能够在低带宽卫星网络上有效运行。
ユーザー エクスペリエンスとシステム UI
Android 17 には、ユーザー エクスペリエンスを向上させるための次の変更が含まれています。
専用のアシスタント音量ストリーム
Android 17 では、アシスタント アプリ専用のアシスタント音量ストリームが導入され、
USAGE_ASSISTANTで再生できるようになりました。この変更により、アシスタントの音声が標準のメディア
ストリームから分離され、ユーザーは両方の音量を個別に制御できるようになります。これにより、アシスタントの応答の可聴性を維持しながらメディアの再生をミュートしたり、その逆の操作を行ったりできます。
新しい MODE_ASSISTANT_CONVERSATION 音声
モードにアクセスできるアシスタントアプリは、音量制御の一貫性をさらに向上させることができます。アシスタント アプリはこのモードを使用して、アクティブなアシスタント
セッションに関するヒントをシステムに提供できます。これにより、アクティブな USAGE_ASSISTANT
再生の外部または接続された Bluetooth 周辺機器を使用して、アシスタント ストリームを制御できます。
引き継ぎ
切换是 Android 17 中新增的一项功能和 API,应用开发者可以将其集成到应用中,以便为用户提供跨设备连续性。它允许用户在一个 Android 设备上启动应用 activity,然后将其转移到另一个 Android 设备。Handoff 在用户设备的后台运行,并通过各种入口点(例如接收设备上的启动器和任务栏)显示用户附近其他设备上的可用活动。
应用可以指定 Handoff 来启动相同的原生 Android 应用(如果该应用已安装在接收设备上且可供使用)。在此应用到应用流程中,用户通过深层链接跳转到指定 activity。或者,应用到网站切换功能可以作为后备选项提供,也可以通过网址切换功能直接实现。
切换支持是按 activity 实现的。如需启用 Handoff,请针对 activity 调用 setHandoffEnabled() 方法。可能需要随切换传递其他数据,以便接收设备上重新创建的 activity 可以恢复适当的状态。实现 onHandoffActivityDataRequested() 回调以返回 HandoffActivityData 对象,该对象包含用于指定 Handoff 应如何处理并在接收设备上重新创建 activity 的详细信息。
ライブアップデート - セマンティック カラー 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) 测距功能,设备可以通过测量信号的相对到达时间来确定其相对于多个锚点的相对位置。以下代码段演示了如何初始化 测距管理器、验证设备功能以及启动 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();