動作の変更点: すべてのアプリ

Android 10 には、アプリに影響する可能性がある動作変更が含まれています。このページに記載されている変更は、アプリの targetSdkVersion に関係なく、Android 10 上で稼働するアプリに適用されます。アプリをテストし、必要に応じて修正して、これらの変更に適切に対応してください。

アプリの targetSdkVersion が 29 以降の場合は、追加の変更にも対応する必要があります。詳細については、29 をターゲットとするアプリの動作変更をご覧ください。

注: このページに記載されている変更に加えて、Android 10 では、プライバシーに基づく変更や制限が多数導入されています。たとえば、次のようなものがあります。

  • デバイスの位置情報へのバックグラウンドでのアクセス
  • バックグラウンドからのアクティビティの起動
  • 連絡先のアフィニティ情報
  • MAC アドレスのランダム化
  • カメラのメタデータ
  • 権限モデル

これらの変更はすべてのアプリに影響し、ユーザーのプライバシーを強化します。これらの変更をサポートする方法について詳しくは、プライバシーの変更のページをご覧ください。

非 SDK インターフェースの制限

アプリの安定性と互換性を確保するため、Android 9(API レベル 28)以降、アプリで使用できる非 SDK インターフェースが制限されています。Android 10 では、Android デベロッパーの協力と直近の内部テストに基づいて、制限を受ける非 SDK インターフェースのリストが更新されています。Google は、非 SDK インターフェースを制限する前に、その代替となる公開インターフェースを利用可能にすることを目指しています。

Android 10(API レベル 29)をターゲットとしないアプリでは、この変更の一部はすぐには影響しない可能性があります。ただし、現時点で(アプリのターゲット API レベルに応じて)一部の非 SDK インターフェースを利用できていても、非 SDK のメソッドまたはフィールドをそのまま使用し続けると、将来的にアプリが機能しなくなるリスクが高くなります。

アプリが非 SDK インターフェースを使用しているかどうか不明な場合は、アプリをテストして確認できます。アプリが非 SDK インターフェースに依存している場合は、SDK の代替手段への移行を計画してください。ただし Google も、一部のアプリには非 SDK インターフェースを使用する正当なユースケースがあると承知しています。アプリの機能に使用している非 SDK インターフェースの代わりが見つからない場合は、新しい公開 API をリクエストしてください。

詳しくは、非 SDK インターフェースの制限に関する Android 10 での変更点と非 SDK インターフェースの制限をご覧ください。

ジェスチャー ナビゲーション

Android 10 以降では、ユーザーはデバイス全体でジェスチャー ナビゲーションを有効にできます。ユーザーがジェスチャー ナビゲーションを有効にすると、アプリが対象 API レベル 29 を対象としているかどうかにかかわらず、デバイス上のすべてのアプリに影響します。たとえば、ユーザーが画面の端からスワイプすると、システムはそのジェスチャーを「戻る」ナビゲーションとして解釈します。ただし、アプリが画面の一部でそのジェスチャーを明示的にオーバーライドしている場合は、その限りではありません。

アプリをジェスチャー ナビゲーションに対応させるには、アプリのコンテンツをエッジ ツー エッジに拡張し、競合するジェスチャーを適切に処理する必要があります。詳細については、ジェスチャー ナビゲーションのドキュメントをご覧ください。

NDK

Android 10 では、NDK が次のように変更されています。

共有オブジェクトにテキストの再配置を含めることはできません

Android 6.0(API レベル 23)では、共有オブジェクトでのテキスト再配置の使用が禁止されました。コードはそのまま読み込む必要があり、変更してはなりません。この制限により、アプリの読み込み時間とセキュリティが改善されます。

SELinux は、Android 10 以降をターゲットとするアプリにこの制限を適用します。これらのアプリがテキスト再配置を含む共有オブジェクトを引き続き使用すると、破損するリスクが高くなります。

Bionic ライブラリと動的リンカーのパスの変更

Android 10 以降では、いくつかのパスが通常のファイルではなくシンボリック リンクになっています。パスが通常のファイルであることを前提としているアプリは、動作しなくなる可能性があります。

  • /system/lib/libc.so -> /apex/com.android.runtime/lib/bionic/libc.so
  • /system/lib/libm.so -> /apex/com.android.runtime/lib/bionic/libm.so
  • /system/lib/libdl.so -> /apex/com.android.runtime/lib/bionic/libdl.so
  • /system/bin/linker -> /apex/com.android.runtime/bin/linker

これらの変更は、ファイルの 64 ビット バリアントにも適用されます。lib/ は lib64/ に置き換えられます。

互換性を維持するために、シンボリック リンクは古いパスで提供されます。たとえば、/system/lib/libc.so は /apex/com.android.runtime/lib/bionic/libc.so へのシンボリック リンクです。そのため、dlopen(“/system/lib/libc.so”) は引き続き機能しますが、アプリが /proc/self/maps などを読み取って、読み込まれたライブラリを実際に調べようとすると、違いが検出されます。これは通常のことではありませんが、一部のアプリでは、ハッキング対策プロセスの一環としてこれが行われます。その場合は、/apex/… パスを Bionic ファイルの有効なパスとして追加する必要があります。

システムのバイナリやライブラリを実行専用メモリにマッピング

Android 10 以降、システム バイナリとライブラリの実行可能セグメントは、コード再利用攻撃に対するセキュリティ強化対策として、実行専用(読み取り不可)メモリにマッピングされます。アプリが実行専用としてマークされたメモリ セグメントへの読み取りオペレーションを実行すると、バグ、脆弱性、意図的な Memory Inspector のいずれであっても、システムはアプリに SIGSEGV シグナルを送信します。

この動作がクラッシュの原因かどうかを特定するには、/data/tombstones/ にある関連する tombstone ファイルを調べます。実行専用に関連するクラッシュには、次の中止メッセージが含まれます。

Cause: execute-only (no-read) memory access error; likely due to data in .text.

この問題を回避して Memory Inspector などのオペレーションを実行するには、mprotect() を呼び出して、実行専用セグメントを読み取り + 実行としてマークします。ただし、このアクセス権限設定はアプリとユーザーをより適切に保護するため、その後は実行専用に戻すことを強くおすすめします。

セキュリティ

Android 10 では、セキュリティが次のように変更されています。

TLS 1.3 はデフォルトで有効

Android 10 以降、すべての TLS 接続に対して TLS 1.3 がデフォルトで有効になっています。TLS 1.3 実装に関する重要点は以下のとおりです。

  • TLS 1.3 暗号スイートはカスタマイズできません。TLS 1.3 が有効な場合、サポート対象の TLS 1.3 暗号スイートは常に有効です。setEnabledCipherSuites() を呼び出して無効にしようとしても無視されます。
  • TLS 1.3 がネゴシエートされると、セッションがセッション キャッシュに追加される前に、HandshakeCompletedListener オブジェクトが呼び出されます。(TLS 1.2 以前のバージョンでは、このオブジェクトはセッションがセッション キャッシュに追加されてから呼び出されます)。
  • 以前のバージョンの Android では SSLEngine インスタンスにより SSLHandshakeException がスローされる状況において、Android 10 以降では代わりに SSLProtocolException がスローされます。
  • 0-RTT モードはサポートされていません。

必要に応じて、TLS 1.3 が無効になっている SSLContext を取得するには、SSLContext.getInstance("TLSv1.2") を呼び出します。また、対象オブジェクトに対して setEnabledProtocols() を呼び出すことで、接続ごとにプロトコル バージョンの有効と無効を切り替えることもできます。

SHA-1 で署名された証明書は TLS では信頼されない

Android 10 では、SHA-1 ハッシュ アルゴリズムを使用している証明書は、TLS 接続では信頼されません。2016 年以降、このような証明書はルート CA が発行していないため、Chrome などの主要ブラウザでは信頼されなくなりました。

SHA-1 を使用している証明書を提示するサイトへの接続はすべて失敗します。

KeyChain の動作変更と機能強化

Google Chrome など一部のブラウザでは、TLS サーバーが TLS handshake の一環として証明書リクエスト メッセージを送信するときに、ユーザーが証明書を選択できます。Android 10 では、KeyChain.choosePrivateKeyAlias() を呼び出してユーザーに証明書の選択を促すプロンプトを表示するときに、KeyChain オブジェクトによって発行者と鍵の仕様パラメータが考慮されます。このプロンプトにはサーバー仕様に準拠しない選択肢は含まれません。

サーバーの仕様に一致する証明書がない場合や、デバイスに証明書がインストールされていない場合など、ユーザーが選択できる証明書がない場合は、証明書の選択を促すプロンプトは表示されません。

さらに、Android 10 以降では、鍵または CA 証明書を KeyChain オブジェクトにインポートするために、デバイスが画面ロックされている必要はありません。

TLS と暗号化に関するその他の変更

Android 10 では、TLS と暗号化ライブラリに対する軽微な変更が反映されています。

  • AES/GCM/NoPadding および ChaCha20/Poly1305/NoPadding 暗号は、getOutputSize() からより正確なバッファサイズを返します。
  • TLS 1.2 以降の最大プロトコルを使用した接続試行では、TLS_FALLBACK_SCSV 暗号スイートが省略されます。TLS サーバーの実装が強化されているため、TLS 外部のフォールバックを試みることはおすすめしません。代わりに、TLS バージョン ネゴシエーションの使用をおすすめします。
  • ChaCha20-Poly1305 は、ChaCha20/Poly1305/NoPadding のエイリアスとして機能します。
  • 末尾にピリオドが付いたホスト名は、有効な SNI ホスト名とは見なされません。
  • 証明書応答用の署名鍵を選択するとき、CertificateRequest の supported_signature_algorithms 拡張機能が考慮されます。
  • Android Keystore の署名鍵など、不透明な署名鍵は TLS の RSA-PSS 署名で使用できます。

Wi-Fi Direct ブロードキャスト

Android 10 では、Wi-Fi Direct に関連する次のブロードキャストはスティッキーではありません。

アプリがスティッキーであるために登録時にこれらのブロードキャストを受信することに依存している場合は、初期化時に適切な get() メソッドを使用して情報を取得してください。

Wi-Fi Aware 機能

Android 10 では、Wi-Fi Aware データパスを使用した TCP/UDP ソケットの作成を容易にするサポートが追加されています。ServerSocket に接続する TCP/UDP ソケットを作成するには、クライアント デバイスがサーバーの IPv6 アドレスとポートを認識している必要があります。以前は、BT や Wi-Fi Aware レイヤ 2 メッセージングなどを使用して帯域外で通信するか、mDNS などの他のプロトコルを使用して帯域内で検出する必要がありました。Android 10 では、この情報をネットワーク設定の一部として伝達できます。

サーバーは次のいずれかを実行します。

  • ServerSocket を初期化し、使用するポートを設定または取得します。
  • Wi-Fi Aware ネットワーク リクエストの一部としてポート情報を指定する。

次のコードサンプルは、ネットワーク リクエストの一部としてポート情報を指定する方法を示しています。

Kotlin

val ss = ServerSocket()
val ns = WifiAwareNetworkSpecifier.Builder(discoverySession, peerHandle)
  .setPskPassphrase("some-password")
  .setPort(ss.localPort)
  .build()

val myNetworkRequest = NetworkRequest.Builder()
  .addTransportType(NetworkCapabilities.TRANSPORT_WIFI_AWARE)
  .setNetworkSpecifier(ns)
  .build()

Java

ServerSocket ss = new ServerSocket();
WifiAwareNetworkSpecifier ns = new WifiAwareNetworkSpecifier
  .Builder(discoverySession, peerHandle)
  .setPskPassphrase(“some-password”)
  .setPort(ss.getLocalPort())
  .build();

NetworkRequest myNetworkRequest = new NetworkRequest.Builder()
  .addTransportType(NetworkCapabilities.TRANSPORT_WIFI_AWARE)
  .setNetworkSpecifier(ns)
  .build();

次に、クライアントは Wi-Fi Aware ネットワーク リクエストを実行して、サーバーから提供された IPv6 とポートを取得します。

Kotlin

val callback = object : ConnectivityManager.NetworkCallback() {
  override fun onAvailable(network: Network) {
    ...
  }
  
  override fun onLinkPropertiesChanged(network: Network,
      linkProperties: LinkProperties) {
    ...
  }

  override fun onCapabilitiesChanged(network: Network,
      networkCapabilities: NetworkCapabilities) {
    ...
    val ti = networkCapabilities.transportInfo
    if (ti is WifiAwareNetworkInfo) {
       val peerAddress = ti.peerIpv6Addr
       val peerPort = ti.port
    }
  }
  override fun onLost(network: Network) {
    ...
  }
};

connMgr.requestNetwork(networkRequest, callback)

Java

callback = new ConnectivityManager.NetworkCallback() {
  @Override
  public void onAvailable(Network network) {
    ...
  }
  @Override
  public void onLinkPropertiesChanged(Network network,
      LinkProperties linkProperties) {
    ...
  }
  @Override
  public void onCapabilitiesChanged(Network network,
      NetworkCapabilities networkCapabilities) {
    ...
    TransportInfo ti = networkCapabilities.getTransportInfo();
    if (ti instanceof WifiAwareNetworkInfo) {
       WifiAwareNetworkInfo info = (WifiAwareNetworkInfo) ti;
       Inet6Address peerAddress = info.getPeerIpv6Addr();
       int peerPort = info.getPort();
    }
  }
  @Override
  public void onLost(Network network) {
    ...
  }
};

connMgr.requestNetwork(networkRequest, callback);

Go デバイス上の SYSTEM_ALERT_WINDOW

Android 10(Go エディション)デバイスで動作するアプリは、SYSTEM_ALERT_WINDOW 権限を受け取ることができません。これは、オーバーレイ ウィンドウの描画でメモリが過剰に使用されるためです。これは、メモリの少ない Android デバイスのパフォーマンスに特に悪影響を及ぼします。

Android 9 以前を搭載した Go エディション デバイスで実行されているアプリが SYSTEM_ALERT_WINDOW 権限を受け取った場合、デバイスが Android 10 にアップグレードされても、アプリはその権限を保持します。ただし、デバイスのアップグレード後に、その権限をまだ持っていないアプリに権限を付与することはできません。

Go デバイス上のアプリがアクション ACTION_MANAGE_OVERLAY_PERMISSION を含むインテントを送信すると、システムは自動的にリクエストを拒否し、ユーザーを [設定] 画面に誘導します。この画面には、デバイスの速度が低下するため権限が許可されないことが表示されます。Go デバイス上のアプリが Settings.canDrawOverlays() を呼び出すと、このメソッドは常に false を返します。なお、デバイスが Android 10 にアップグレードされる前に SYSTEM_ALERT_WINDOW 権限が付与されたアプリには、これらの制限は適用されません。

古いバージョンの Android をターゲットにしているアプリに関する警告

Android 10 以降を搭載したデバイスでは、Android 5.1(API レベル 22)以下を対象とするアプリを初めて実行する際に、ユーザーに警告が表示されます。アプリがユーザーに権限の付与を要求する場合、ユーザーはアプリを初めて実行する前に、アプリの権限を調整することもできます。

Google Play の対象 API の要件により、ユーザーにこれらの警告が表示されるのは、最近更新されていないアプリを実行した場合のみです。他のストアで配信されているアプリについては、2019 年中に同様の対象 API 要件が適用されます。これらの要件について詳しくは、2019 年の対象 API レベル要件の拡大をご覧ください。

SHA-2 CBC 暗号スイートの削除

以下の SHA-2 CBC 暗号スイートがプラットフォームから削除されました。

  • TLS_RSA_WITH_AES_128_CBC_SHA256
  • TLS_RSA_WITH_AES_256_CBC_SHA256
  • TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256
  • TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384
  • TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
  • TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384

これらの暗号スイートは、GCM を使用する同様の暗号スイートよりも安全性が低く、ほとんどのサーバーは、これらの暗号スイートの GCM バリアントと CBC バリアントの両方をサポートしているか、どちらもサポートしていません。

アプリの使用状況

Android 10 では、アプリの利用に関して次のように動作が変更されました。

  • UsageStats アプリの使用状況の改善 - Android 10 では、アプリが分割画面モードまたはピクチャー イン ピクチャー モードで使用されている場合、UsageStats でアプリの使用状況を正確に追跡します。また、Android 10 では Instant App の使用状況が正しく追跡されます。

  • アプリ別のグレースケール - Android 10 では、アプリごとにグレースケール表示モードを設定できます。

  • アプリごとの注意散漫状態 - Android 10 では、アプリを「注意散漫状態」に選択的に設定できます。この状態では、アプリの通知が抑制され、おすすめのアプリとして表示されなくなります。

  • 停止と再生 - Android 10 では、停止されたアプリは音声を再生できません。

HTTPS 接続の変更

Android 10 で実行しているアプリが null を setSSLSocketFactory() に渡すと、IllegalArgumentException が発生します。以前のバージョンでは、setSSLSocketFactory() に null を渡した場合、現在のデフォルト ファクトリを渡す場合と同じ効果がありました。

android.preference ライブラリのサポート終了

android.preference ライブラリは、Android 10 でサポートが終了しました。 デベロッパーは、代わりに Android Jetpack の一部である AndroidX Preference ライブラリを使用する必要があります。移行と開発に役立つその他のリソースについては、更新された設定ガイド、公開サンプルアプリ、リファレンス ドキュメントをご覧ください。

ZIP ファイルのユーティリティ ライブラリの変更

Android 10 では、ZIP ファイルを処理する java.util.zip パッケージのクラスに次の変更が導入されています。これらの変更により、Android と java.util.zip を使用する他のプラットフォーム間でライブラリの動作の一貫性が向上します。

Inflater

以前のバージョンでは、end() の呼び出し後に Inflater クラスの一部のメソッドが呼び出されると、IllegalStateException がスローされていました。Android 10 では、これらのメソッドは代わりに NullPointerException をスローします。

ZipFile

Android 10 以降では、File、int、Charset 型の引数を取る ZipFile のコンストラクタは、指定された ZIP ファイルにファイルが含まれていない場合、ZipException をスローしません。

ZipOutputStream

Android 10 以降では、ZipOutputStream の finish() メソッドは、ファイルを含まない ZIP ファイルの出力ストリームを書き込もうとしても、ZipException をスローしません。

カメラに関する変更点

カメラを使用する多くのアプリは、デバイスが縦向きに設定されている場合、カメラの向きで説明されているように、実機も縦向きであると想定しています。以前は、この前提で問題ありませんでしたが、折りたたみ式デバイスなど、利用可能なフォーム ファクタの拡大に伴い、状況は変化しています。これらのデバイスでこの前提を適用すると、カメラのビューファインダーの表示が誤って回転したり、拡大縮小されたり(またはその両方)する可能性があります。

対象 API レベル 24 以降をターゲットとするアプリは、android:resizeableActivity を明示的に設定し、マルチウィンドウ操作を処理するために必要な機能を提供する必要があります。

電池使用量のトラッキング

Android 10 以降、SystemHealthManager は、メジャー充電イベント後にデバイスのプラグが抜かれるたびに、バッテリー使用統計情報をリセットします。大まかに言うと、主な充電イベントは、デバイスがフル充電されたか、デバイスがほぼ消耗した状態からほぼ充電された状態になったかのいずれかです。

Android 10 より前は、バッテリー残量の変化がどれほど小さくても、デバイスのプラグを抜くたびにバッテリー使用統計情報がリセットされていました。

Android ビームを削除

Android 10 では、近距離無線通信(NFC)を介してデバイス間でデータ共有を開始するための古い機能である Android ビームが正式に非推奨になりました。また、関連するいくつかの NFC API も非推奨になります。Android ビームは、引き続き、使用を希望するデバイス メーカー パートナーがオプションとして利用できますが、積極的な開発は行われなくなりました。ただし、Android は引き続き他の NFC 機能と API をサポートし、タグからの読み取りや支払いなどのユースケースは引き続き想定どおりに機能します。

java.math.BigDecimal.stripTrailingZeros() の動作変更

BigDecimal.stripTrailingZeros() は、入力値がゼロの場合に、末尾のゼロを特殊なケースとして保持しなくなりました。

java.util.regex.Matcher と Pattern の動作の変更

入力の先頭にゼロ幅の一致がある場合に、split() の結果が空の String("")で始まらないように変更されました。これは String.split() にも影響します。たとえば、"x".split("") は {"x"} を返すようになりました。以前のバージョンの Android では {"", "x"} を返していました。"aardvark".split("(?=a)" が {"", "a", "ardv", "ark"} ではなく {"a", "ardv", "ark"} を返すようになりました。

無効な引数に対する例外処理も改善されました。

  • 置換 String が単独のバックスラッシュで終わる場合(これは不正です)、appendReplacement(StringBuffer, String) が IndexOutOfBoundsException ではなく IllegalArgumentException をスローするようになりました。置換後の String が $ で終わる場合も、同じ例外がスローされるようになりました。以前は、このシナリオでは例外はスローされませんでした。
  • replaceFirst(null) が NullPointerException をスローした場合、Matcher で reset() を呼び出さなくなりました。一致するものがない場合にも NullPointerException がスローされるようになりました。以前は、一致があった場合にのみスローされていました。
  • start(int group)、end(int group)、group(int group) は、グループ インデックスが範囲外の場合、より一般的な IndexOutOfBoundsException をスローするようになりました。以前は、これらのメソッドは ArrayIndexOutOfBoundsException をスローしていました。

GradientDrawable のデフォルトの角度が TOP_BOTTOM になりました

Android 10 では、XML で GradientDrawable を定義し、角度の測定値を指定しない場合、グラデーションの方向はデフォルトで TOP_BOTTOM になります。 これは、以前のバージョンの Android(デフォルトは LEFT_RIGHT)からの変更です。

回避策として、最新バージョンの AAPT2 に更新すると、角度測定が指定されていない場合、ツールはレガシーアプリの角度測定を 0 に設定します。

デフォルトの SUID を使用したシリアル化されたオブジェクトのロギング

Android 7.0(API レベル 24)以降、プラットフォームではシリアル化可能なオブジェクトのデフォルトの serialVersionUID が修正されました。この修正は、API レベル 23 以前をターゲットとするアプリには影響しませんでした。

Android 10 以降では、アプリが API レベル 23 以前をターゲットとし、古い、誤った、デフォルトの serialVersionUID に依存している場合、システムは警告をログに記録し、コードの修正を提案します。

具体的には、次のすべての条件が満たされると、システムは警告をログに記録します。

  • アプリが対象 API レベル 23 以前をターゲットとしている。
  • クラスがシリアル化される。
  • シリアル化されたクラスは、serialVersionUID を明示的に設定するのではなく、デフォルトの serialVersionUID を使用します。
  • デフォルトの serialVersionUID は、アプリが対象 API レベル 24 以降をターゲットとする場合の serialVersionUID とは異なります。

この警告は、影響を受けるクラスごとに 1 回ログに記録されます。警告メッセージには、推奨される修正方法が含まれています。これは、アプリが対象 API レベル 24 以降をターゲットとしている場合に計算されるデフォルト値に serialVersionUID を明示的に設定することです。この修正を使用すると、API レベル 23 以下を対象とするアプリでそのクラスのオブジェクトがシリアル化された場合、24 以上を対象とするアプリでそのオブジェクトが正しく読み取られるようになります。逆の場合も同様です。

java.io.FileChannel.map() の変更

Android 10 以降、FileChannel.map() は、truncate() を使用してサイズを変更できない /dev/zero などの非標準ファイルではサポートされていません。以前のバージョンの Android では、truncate() から返された errno が無視されていましたが、Android 10 では IOException がスローされます。以前の動作が必要な場合は、ネイティブ コードを使用する必要があります。