Android Q の機能と API

Android Q では、ユーザーやデベロッパー向けの優れた新機能が導入されています。このドキュメントでは、デベロッパー向けの新機能について紹介します。

新しい API について詳しくは、API 比較レポートAndroid API リファレンスをご覧ください。ハイライト表示されているものが新しい API です。また、プラットフォームの変更によるアプリへの影響については、Android Q の動作変更(Android Q をターゲットとするアプリ向け、すべてのアプリ向け)やプライバシーに関する変更点に関する各記事をご覧ください。

セキュリティの機能強化

以下に示すように、Android Q には、さまざまなセキュリティ機能が導入されています。

生体認証ダイアログの改善

Android Q では、Android 9 で追加された統合生体認証ダイアログが次のように改善されています。

ユーザー確認要件を指定する

ユーザーが暗黙的な生体認証モダリティを使用して認証を行った場合は、その後にユーザー確認を要求しないようにシステムに通知できるようになりました。たとえば、ユーザーが顔認証を使用して認証した後は、それ以上の確認は不要であることをシステムに伝えることができます。

デフォルトでは、システムはユーザー確認を要求します。通常、ユーザーにとって確認操作が必要だと考えられるのは、機密性の高いアクションやリスクの高いアクション(購入など)を行う際です。ただし、アプリ内のアクションのリスクが低い場合は、setConfirmationRequired() メソッドに false を渡すことで、ユーザー確認を要求しないようにシステムに指示することができます。このフラグはシステムに対してヒントとして渡されるため、生体認証を要求するようにシステム設定をユーザーが変更した場合、システムは値を無視することができます。

ユーザー確認なしの顔認証の例。

図 1.ユーザー確認なしの顔認証

ユーザー確認が必要な顔認証の例。

図 2.ユーザー確認が必要な顔認証

代替的なデバイス認証情報のサポートの向上

ユーザーがなんらかの理由で生体認証入力を使用して認証を行うことができない場合に、デバイス PIN や、パターン、パスワードを使用して認証可能にするようにシステムに指示できるようになりました。この代替サポートを有効にするには、setDeviceCredentialAllowed() メソッドを使用します。

アプリが現在 createConfirmDeviceCredentialIntent() を使用してデバイス認証情報を代替手段にしている場合は、新しいメソッドを使用するように切り替えてください。

デバイスに生体認証機能があるかどうかを確認する

BiometricPrompt を呼び出す前に BiometricManager クラスの canAuthenticate() メソッドを使用して、デバイスが生体認証をサポートしているかどうかを確認できるようになりました。

APK から埋め込み DEX コードを直接実行する

アプリの APK ファイルから埋め込み DEX コードを直接実行するようにプラットフォームに指示できるようになりました。この機能は、攻撃者がデバイス上のローカル コンパイル コードを改ざんしていた場合に、攻撃を防ぐのに役立ちます。

この機能を有効にするには、アプリのマニフェスト ファイルの <application> 要素で、android:useEmbeddedDex 属性の値を true に設定します。また、ART が直接アクセスできる非圧縮 DEX コードを含むように APK をビルドする必要があります。Gradle または Bazel の設定ファイルに次の設定を追加して、非圧縮 DEX コードを含む APK をビルドしてください。

Gradle

aaptOptions {
       noCompress 'dex'
    }
    

Bazel

    android_binary(
       ...,
       nocompress_extensions = [“.dex”],
    )
    

TLS 1.3 のサポート

プラットフォームの TLS 実装が、TLS 1.3 をサポートするようになりました。TLS 1.3 は、TLS 規格のメジャー リビジョンで、パフォーマンス面やセキュリティ面が強化されています。Google のベンチマークでは、TLS 1.3 でセキュア接続を確立すると、TLS 1.2 に比べて 40% 速くなることが示されています。

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

TLS 1.3 実装に関する主な重要事項は以下のとおりです。

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

公開 Conscrypt API

Conscrypt セキュリティ プロバイダに、TLS 機能用の公開 API が追加されました。これまでは、この機能にアクセスするにはリフレクションを経由していました。ただし、非公開 API の呼び出しに関する制限が Android P で追加されているため、この機能は Android Q ではグレーリストに記載されており、今後のリリースでさらに制限される予定です。

今回のアップデートにより、汎用 javax.net.ssl API からでは利用できない機能にアクセスするための静的メソッドを含むクラス コレクションが android.net.ssl の下に追加されます。クラスの名前は、対応する javax.net.ssl クラスの複数形になります。たとえば、javax.net.ssl.SSLSocket インスタンス上で動作するコードは、新しい android.net.ssl.SSLSockets クラスのメソッドを使用できます。

接続機能

Android Q では、ネットワークや接続の機能に関していくつかの点で改善されています。

Wi-Fi ネットワーク接続 API

Android Q では、ピアツーピア接続がサポートされるようになりました。この機能により、アプリはリクエストされたネットワークのプロパティを WifiNetworkSpecifier を使用して記述することで、デバイスが接続されているアクセス ポイントの変更を求めるプロンプトをユーザーに表示できます。ピアツーピア接続は、Chromecast や Google Home ハードウェアなどのセカンダリ デバイスのブートストラップ設定など、ネットワーク提供以外の目的で使用されます。

この API を使用する場合のフローは次のとおりです。

  1. WifiNetworkSpecifier.Builder を使用して Wi-Fi ネットワーク指定子を作成します。

  2. 必要な認証情報とともに、接続するネットワークに一致するネットワーク フィルタを設定します。

  3. 以下の要件に沿って SSIDSSID patternBSSIDBSSID pattern の組み合わせを決定し、リクエストごとにネットワーク フィルタを設定します。

    • 各リクエストは、SSIDSSID patternBSSIDBSSID pattern の少なくとも 1 つを提供する必要がある。
    • 各リクエストで設定できるのは、SSID または SSID pattern のどちらか一方のみである。
    • 各リクエストで設定できるのは、BSSID または BSSID pattern のどちらか一方のみである。
  4. リクエストのステータスを追跡するために、ネットワーク リクエストに NetworkCallback インスタンスとともに指定子を追加します。

    ユーザーがリクエストを受け入れて、ネットワークへの接続が成功すると、コールバック オブジェクトで NetworkCallback.onAvailable() が呼び出されます。ユーザーがリクエストを拒否した場合、またはネットワークへの接続が失敗した場合は、コールバック オブジェクトで NetworkCallback.onUnavailable() が呼び出されます。

ピアツーピア接続には、位置情報または Wi-Fi の利用許可は必要ありません。ピアデバイスへの接続リクエストを開始すると、そのデバイスでダイアログ ボックスが表示されます。デバイスのユーザーは、そのダイアログ ボックスで接続リクエストを受け入れることができます。

ユーザー承認の回避

ユーザーが特定のアプリからのリクエストに応答して接続するネットワークを承認すると、デバイスは特定のアクセス ポイントに対する承認を保存します。アプリがそのアクセス ポイントへの接続を再度リクエストした場合、デバイスはユーザー承認フェーズを省略して自動的にネットワークに接続します。API によってリクエストされたネットワークに接続した状態で、ユーザーがネットワークの切断を選択した場合、このアプリとネットワークの組み合わせに対して保存された承認は削除され、アプリからの以降のリクエストではユーザーによる承認が再度必要になります。アプリから SSID や BSSID pattern などが指定されていないリクエストが行われた場合、ユーザーはリクエストを承認する必要があります。

コードサンプル

次のコードサンプルは、SSID プレフィックスが「test」で、BSSID OUI が「10:03:23」のオープン ネットワークに接続する方法を示しています。

Kotlin

    val specifier = WifiNetworkSpecifier.Builder()
        .setSsidPattern(PatternMatcher("test", PatternMatcher.PATTERN_PREFIX))
        .setBssidPattern(MacAddress.fromString("10:03:23:00:00:00"), MacAddress.fromString("ff:ff:ff:00:00:00"))
        .build()

    val request = NetworkRequest.Builder()
        .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
        .removeCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
        .setNetworkSpecifier(specifier)
        .build()

    val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager

    val networkCallback = object : ConnectivityManager.NetworkCallback() {
        ...
        override fun onAvailable(network: Network?) {
            // do success processing here..
        }

        override fun onUnavailable() {
            // do failure processing here..
        }
        ...
    }
    connectivityManager.requestNetwork(request, networkCallback)
    ...
    // Release the request when done.
    connectivityManager.unregisterNetworkCallback(networkCallback)
    

Java

    final NetworkSpecifier specifier =
      new WifiNetworkSpecifier.Builder()
      .setSsidPattern(new PatternMatcher("test", PatterMatcher.PATTERN_PREFIX))
      .setBssidPattern(MacAddress.fromString("10:03:23:00:00:00"), MacAddress.fromString("ff:ff:ff:00:00:00"))
      .build();

    final NetworkRequest request =
      new NetworkRequest.Builder()
      .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
      .removeCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
      .setNetworkSpecifier(specifier)
      .build();

    final ConnectivityManager connectivityManager = (ConnectivityManager)
      context.getSystemService(Context.CONNECTIVITY_SERVICE);

    final NetworkCallback networkCallback = new NetworkCallback() {
      ...
      @Override
      void onAvailable(...) {
          // do success processing here..
      }

      @Override
      void onUnavailable(...) {
          // do failure processing here..
      }
      ...
    };
    connectivityManager.requestNetwork(request, networkCallback);
    ...
    // Release the request when done.
    connectivityManager.unregisterNetworkCallback(networkCallback);
    

Wi-Fi ネットワーク候補 API

Android Q では、アプリがデバイスを Wi-Fi アクセス ポイントに自動接続させるためのネットワーク認証情報を追加できるようになりました。WifiNetworkSuggestion を使用して、接続するネットワークを提案できます。どのアクセス ポイントを受け入れるかは、アプリからの入力に基づいてプラットフォームが最終的に選択します。

次のコードサンプルは、オープン ネットワーク、WPA2 ネットワーク、WPA3 ネットワークのそれぞれの認証情報を提供する方法を示しています。

Kotlin

    val suggestion1 = WifiNetworkSuggestion.Builder()
            .setSsid("test111111")
            .setIsAppInteractionRequired() // Optional (Needs location permission)
            .build()

    val suggestion2 = WifiNetworkSuggestion.Builder()
            .setSsid("test222222")
            .setWpa2Passphrase("test123456")
            .setIsAppInteractionRequired() // Optional (Needs location permission)
            .build()

    val suggestion3 = WifiNetworkSuggestion.Builder()
            .setSsid("test333333")
            .setWpa3Passphrase("test6789")
            .setIsAppInteractionRequired() // Optional (Needs location permission)
            .build()

    val suggestionsList = listOf(suggestion1, suggestion2, suggestion3)

    val wifiManager = context.getSystemService(Context.WIFI_SERVICE) as WifiManager

    val status = wifiManager.addNetworkSuggestions(suggestionsList);
    if (status != WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS) {
        // do error handling here
    }

    // Optional (Wait for post connection broadcast to one of your suggestions)
    val intentFilter = IntentFilter(WifiManager.ACTION_WIFI_NETWORK_SUGGESTION_POST_CONNECTION);

    val broadcastReceiver = object : BroadcastReceiver() {
        override fun onReceive(context: Context, intent: Intent) {
            if (!intent.action.equals(WifiManager.ACTION_WIFI_NETWORK_SUGGESTION_POST_CONNECTION)) {
                return;
            }
            // do post connect processing here
        }
    };
    context.registerReceiver(broadcastReceiver, intentFilter);
    

Java

    final WifiNetworkSuggestion suggestion1 =
      new WifiNetworkSuggestion.Builder()
      .setSsid("test111111")
      .setIsAppInteractionRequired() // Optional (Needs location permission)
      .build()

    final WifiNetworkSuggestion suggestion2 =
      new WifiNetworkSuggestion.Builder()
      .setSsid("test222222")
      .setWpa2Passphrase("test123456")
      .setIsAppInteractionRequired() // Optional (Needs location permission)
      .build()

    final WifiNetworkSuggestion suggestion3 =
      new WifiNetworkSuggestion.Builder()
      .setSsid("test333333")
      .setWpa3Passphrase("test6789")
      .setIsAppInteractionRequired() // Optional (Needs location permission)
      .build()

    final List<WifiNetworkSuggestion> suggestionsList =
      new ArrayList<WifiNetworkSuggestion> {{
        add(suggestion1);
        add(suggestion2);
        add(suggestion3);
      }};

    final WifiManager wifiManager =
      (WifiManager) context.getSystemService(Context.WIFI_SERVICE);

    final int status = wifiManager.addNetworkSuggestions(suggestionsList);
    if (status != WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS) {
    // do error handling here…
    }

    // Optional (Wait for post connection broadcast to one of your suggestions)
    final IntentFilter intentFilter =
      new IntentFilter(WifiManager.ACTION_WIFI_NETWORK_SUGGESTION_POST_CONNECTION);

    final BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
      @Override
      public void onReceive(Context context, Intent intent) {
        if (!intent.getAction().equals(
          WifiManager.ACTION_WIFI_NETWORK_SUGGESTION_POST_CONNECTION)) {
          return;
        }
        // do post connect processing here..
      }
    };
    context.registerReceiver(broadcastReceiver, intentFilter);
    

プラットフォームが接続を開始する前に、アプリが提示した候補をユーザーが承認する必要があります。プラットフォームは、アプリが提示した候補の 1 つに合致するネットワークをスキャン結果内に最初に見つけたときに、ユーザーに通知を表示します。ユーザーは、この通知に応答することで承認を行います。プラットフォームがネットワーク候補のいずれかに接続すると、そのネットワーク接続と、候補を提示したアプリとを結び付けるテキストが設定に表示されます。

ユーザーが接続を解除した場合の処理

ネットワーク候補のいずれかに接続されている場合にユーザーが Wi-Fi 選択ツールを使用してその接続を明示的に解除すると、そのネットワークが 24 時間ブラックリストに登録されます。その間に、アプリで対応するネットワーク候補を削除してから再追加しても、そのネットワークは自動接続の対象とみなされません。

アプリの承認ステータスの変更

ユーザーがネットワーク候補の通知を拒否すると、CHANGE_WIFI_STATE 権限がアプリから削除されます。ユーザーは後から Wi-Fi の管理メニュー(設定アプリ > [アプリと通知] > [特別なアプリアクセス] > [Wi-Fi の管理] > アプリ名)で、通知の承認を行うことができます。

Wi-Fi 高性能モードと Wi-Fi 低遅延モードの改善

Android Q を使用すると、基盤モデムの遅延を最小限に抑えるヒントを提供することができます。

Android Q は、Wi-Fi Lock API を拡張して、高性能モードと低遅延モードを効果的にサポートします。Wi-Fi 省電力機能は、高性能モードと低遅延モードでは無効になります。モデムのサポートによっては、低遅延モードではさらに遅延最適化が有効になる場合があります。

低遅延モードは、ロックを取得するアプリがフォアグラウンドで実行されていて、画面がオンになっている場合に限り有効になります。低遅延モードは、リアルタイム モバイルゲーム アプリの場合に特に役立ちます。

DNS リゾルバの特殊ルックアップ

Android Q では、クリアテキスト ルックアップと DNS over TLS モードの両方を使用する特殊な DNS ルックアップがネイティブでサポートされるようになりました。これまで、プラットフォームの DNS リゾルバは A レコードと AAAA レコードのみサポートしていました。それによって、名前に関連付けられた IP アドレスのみルックアップが可能であり、それ以外のレコードタイプはサポートされていませんでした。DnsResolver API は汎用的な非同期解決を提供するので、SRVNAPTR などのレコードタイプもルックアップすることができます。応答の解析はアプリが行う必要があります。

NDK ベースのアプリについては、android_res_nsend の説明をご覧ください。

Wi-Fi Easy Connect

Android Q では、Easy Connect を使用してピアデバイスの Wi-Fi 認証情報をプロビジョニングできます。これはサポートが終了した WPS の代わりとなります。アプリは ACTION_PROCESS_WIFI_EASY_CONNECT_URI インテントを使用することで、セットアップとプロビジョニングのフローに Easy Connect を統合できます。このインテントには URI が必要です。呼び出し元のアプリはステッカーや表示から QR コードをスキャンしたり、Bluetooth LE や NFC の広告からスキャンしたりといったさまざまな方法で URI を取得できます。

URI を取得したら、ACTION_PROCESS_WIFI_EASY_CONNECT_URI インテントを指定してピアデバイスの Wi-Fi 認証情報をプロビジョニングできます。これにより、ユーザーは、認証情報を共有して安全に転送する Wi-Fi ネットワークを選択することができます。

Easy Connect には、位置情報または Wi-Fi の利用許可は必要ありません。

Wi-Fi Direct connection API

Android Q では、WifiP2pConfigWifiP2pManager の API クラスが更新され、事前に決定済みの情報を使用することで Wi-Fi Direct への接続を高速確立する機能がサポートされるようになりました。この事前決定済み情報は、Bluetooth や NFC などのサイドチャネル経由で共有されます。

事前決定済み情報を使用してグループを作成する方法を、次のサンプルコードに示します。

Kotlin

    val manager = getSystemService(Context.WIFI_P2P_SERVICE) as WifiP2pManager
    val channel = manager.initialize(this, mainLooper, null)

    // prefer 5G band for this group
    val config = WifiP2pConfig.Builder()
        .setNetworkName("networkName")
        .setPassphrase("passphrase")
        .enablePersistentMode(false)
        .setGroupOperatingBand(WifiP2pConfig.GROUP_OWNER_BAND_5GHZ)
        .build()

    // create a non-persistent group on 5GHz
    manager.createGroup(channel, config, null)
    

Java

    WifiP2pManager manager = (WifiP2pManager) getSystemService(Context.WIFI_P2P_SERVICE);
    Channel channel = manager.initialize(this, getMainLooper(), null);

    // prefer 5G band for this group
    WifiP2pConfig config = new WifiP2pConfig.Builder()
    .setNetworkName("networkName")
    .setPassphrase("passphrase")
    .enablePersistentMode(false)
    .setGroupOperatingBand(WifiP2pConfig.GROUP_OWNER_BAND_5GHZ)
    .build();

    // create a non-persistent group on 5GHz
    manager.createGroup(channel, config, null);
    

認証情報を使用してグループに参加するには、manager.createGroup() を次のように置き換えます。

Kotlin

    manager.connect(channel, config, null)
    

Java

    manager.connect(channel, config, null);
    

Bluetooth LE Connection Oriented Channels(CoC)

Android Q では、BLE CoC 接続を使用することで、2 つの BLE デバイス間で大きなデータ ストリームを転送できるようになりました。このインターフェースは、Bluetooth と接続の仕組みを抽象化することで、実装を簡素化します。

電話機能

Android Q では、電話機能に関して以下のように改善されています。

通話品質の向上

Android Q では、進行中の IP マルチメディア サブシステム(IMS)通話の品質(ネットワークとの間の品質など)に関する情報を収集することができるようになりました。ただし、対象デバイスがこの機能をサポートしている必要があります。

コール スクリーニングと発信者番号

Android Q では、ユーザーのアドレス帳にない通話を潜在的なスパム通話として識別し、ユーザーの代わりにスパム通話を無言で拒否する機能を利用できるようになりました。ブロックした通話に関する情報は、通話履歴のログに「ブロック済み通話」として記録されるため、不在着信時の透明性が向上します。この新しい API を使用すると、コール スクリーニング機能や発信者番号機能を実現するうえで、ユーザーから READ_CALL_LOG アクセス権限を取得する必要がなくなります。

通話転送サービス API

Android Q では、通話インテントの処理方法が変更されています。NEW_OUTGOING_CALL ブロードキャストはサポートが終了し、CallRedirectionService API に置き換えられました。CallRedirectionService API は、Android プラットフォームが行う発信通話を編集するためのインターフェースを提供します。たとえば、サードパーティ製アプリで通話をキャンセルし、VoIP 経由で転送することができます。

外部ストレージ上でのファイルの作成に関する改善点

Android Q では、対象範囲別ストレージのプライバシーに関する動作が変更されたことに加え、ファイルの書き込みに関する柔軟性が増し、外部ストレージ デバイス上のファイルの保存場所を管理するための機能が導入されています。

新しいメディア ファイルの保留中ステータス

Android Q には IS_PENDING フラグが導入されています。アプリはこのフラグを使用して、ディスクへの書き込み時にメディア ファイルに排他的にアクセスすることができます。

次のコード スニペットは、アプリ内で新しい画像を作成する際の IS_PENDING フラグの使用方法を示しています。

Kotlin

    val values = ContentValues().apply {
        put(MediaStore.Images.Media.DISPLAY_NAME, "IMG1024.JPG")
        put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg")
        put(MediaStore.Images.Media.IS_PENDING, 1)
    }

    val resolver = context.getContentResolver()
    val collection = MediaStore.Images.Media
            .getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
    val item = resolver.insert(collection, values)

    resolver.openFileDescriptor(item, "w", null).use { pfd ->
        // Write data into the pending image.
    }

    // Now that we're finished, release the "pending" status, and allow other apps
    // to view the image.
    values.clear()
    values.put(MediaStore.Images.Media.IS_PENDING, 0)
    resolver.update(item, values, null, null)
    

Java

    ContentValues values = new ContentValues();
    values.put(MediaStore.Images.Media.DISPLAY_NAME, "IMG1024.JPG");
    values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
    values.put(MediaStore.Images.Media.IS_PENDING, 1);

    ContentResolver resolver = context.getContentResolver();
    Uri collection = MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY);
    Uri item = resolver.insert(collection, values);

    try (ParcelFileDescriptor pfd = resolver.openFileDescriptor(item, "w", null)) {
        // Write data into the pending image.
    } catch (IOException e) {
        e.printStackTrace();
    }

    // Now that we're finished, release the "pending" status, and allow other apps
    // to view the image.
    values.clear();
    values.put(MediaStore.Images.Media.IS_PENDING, 0);
    resolver.update(item, values, null, null);
    

保存場所の管理

Android Q には、アプリが外部ストレージに保存するファイルを整理するのに役立つ機能がいくつか導入されています。

ディレクトリに関するヒント

Android Q を実行しているデバイスにアプリがメディアを保存する場合、デフォルトでは、メディアはその種類に基づいて整理されます。たとえば、新しい画像ファイルはデフォルトで「Pictures」ディレクトリに保存されます。

アプリがファイルの特定の保存場所(Pictures/MyVacationPictures など)を認識している場合、MediaColumns.RELATIVE_PATH を設定することで、新たに作成したファイルの保存場所に関するヒントをシステムに与えることができます。同様に、update() の呼び出し中にディスク上のファイルを移動する(MediaColumns.RELATIVE_PATH または MediaColumns.DISPLAY_NAME を変更する)ことができます。

デバイスの選択

Android 9(API レベル 28)以前の場合、外部ストレージ デバイスに保存されているすべてのファイルは external という単一ボリュームの下に表示されます。一方、Android Q では、各外部ストレージ デバイスに固有のボリューム名が付与されます。この命名システムにより、コンテンツを効率的に整理してインデックスに登録し、新しいコンテンツの保存場所を管理することができます。

メインの共有ストレージ デバイスには必ず VOLUME_EXTERNAL_PRIMARY という名前が付けられます。他のボリュームは MediaStore.getExternalVolumeNames() を呼び出すことによって検出できます。

特定のボリュームを照会、挿入、更新、削除するには、以下のコード スニペットに示すように、MediaStore API で利用できる getContentUri() メソッドのいずれかにボリューム名を渡します。

    // Publish an audio file onto a specific external storage device.
    val values = ContentValues().apply {
        put(MediaStore.Audio.Media.RELATIVE_PATH, "Music/My Album/My Song")
        put(MediaStore.Audio.Media.DISPLAY_NAME, "My Song.mp3")
    }

    // Assumes that the storage device of interest is the 2nd one
    // that your app recognizes.
    val volumeNames = MediaStore.getExternalVolumeNames(context)
    val selectedVolumeName = volumeNames[1]
    val collection = MediaStore.Audio.Media.getContentUri(selectedVolumeName)
    val item = resolver.insert(collection, values)
    

メディアとグラフィック

Android Q では、メディアとグラフィックに関して以下の機能や API が新たに導入されています。

音声再生キャプチャ

Android Q では、あるアプリで他のアプリによる音声の再生をキャプチャすることができます。詳しくは、再生キャプチャをご覧ください。

Native MIDI API

Android Native MIDI API(AMidi)を使用すると、アプリ デベロッパーは、C / C++ コードを使用して MIDI データを送受信できるようになります。これにより、C / C++ のオーディオ / 制御ロジックと密接に統合できるため、JNI の必要性を最小限に抑えることができます。

詳細については、Android Native MIDI API をご覧ください。

MediaCodecInfo の改善

MediaCodecInfo に新しいメソッドが追加され、コーデックに関する詳細情報を取得できるようになりました。

isSoftwareOnly()
コーデックがソフトウェア内でのみ実行されている場合は true を返します。ソフトウェア コーデックは、レンダリング パフォーマンスについて保証しません。
isHardwareAccelerated()
コーデックがハードウェア アクセラレーションを使用している場合は true を返します。
isVendor()
コーデックがデバイス ベンダーによって提供されている場合は true を返し、Android プラットフォームによって提供されている場合は false を返します。
isAlias()
代替コーデック名(エイリアス)を使用している場合、MediaCodecList には、1 つの基盤コーデックに対して複数のエントリが含まれている可能性があります。このエントリのコーデックが別のコーデックのエイリアスである場合、このメソッドは true を返します。

また、MediaCodec.getCanonicalName() はエイリアス経由で作成されたコーデックの基盤コーデック名を返します。

パフォーマンス ポイント

「パフォーマンス ポイント」とは、特定の高さ、幅、フレームレートで動画をレンダリングするコーデックの能力を示します。たとえば、UHD_60 パフォーマンス ポイントは、60 fps でレンダリングされた Ultra HD 動画(3840x2160 ピクセル)を示します。

MediaCodecInfo.VideoCapabilities.getSupportedPerformancePoints() メソッドは、そのコーデックによってレンダリングやキャプチャが可能な PerformancePoint エントリのリストを返します。

PerformancePoint.covers(PerformancePoint) を呼び出すと、指定した PerformancePoint が別のパフォーマンス ポイントをカバーしているかどうかを確認できます。たとえば、UHD_60.covers(UHD_50) は true を返します。

パフォーマンス ポイントのリストには、すべてのハードウェア アクセラレーション コーデックが含まれています。コーデックが最も低い標準パフォーマンス ポイントでさえ満たさない場合、空のリストになることがあります。

パフォーマンス ポイントに関するデータはベンダー HAL から提供されるため、ベンダー イメージを更新せずに Android Q にアップグレードしたデバイスの場合、パフォーマンス ポイントに関するデータがありません。この場合、getSupportedPerformancePoints() は null を返します。

ANGLE

Android Q のリリースにより、Android のデベロッパーやパートナーは、ベンダー提供の ES ドライバを使用する代わりに、ANGLE を使用して Chrome 組織内のプロジェクトを実行し、ES を Vulkan の上に重ねられるようになりました。

詳細については、ANGLE をご覧ください。

Thermal API

デバイスが熱くなると、CPU や GPU が正常に動作しなくなり、アプリやゲームも予想外の影響を受けることがあります。複雑なグラフィック、大量の計算、持続的なネットワーク アクティビティを使用するアプリでは、問題が発生する可能性が高くなります。こうした問題は、チップセットとコアの周波数、統合レベル、デバイスのパッケージやフォーム ファクタに基づき、デバイスごとに異なります。

Android Q では、デバイスの変化を監視し、消費電力を抑えて通常の温度に戻す対策を講じるための Thermal API をアプリとゲームで使用できるようになりました。アプリで PowerManagerリスナーを登録することで、温度ステータス(低、中、高、重大、緊急、シャットダウン)がシステムから常時報告されます。

デバイスから熱応力が報告されると、アプリとゲームは継続的なアクティビティを停止し、さまざまな方法で消費電力を抑えて熱応力を低減します。たとえば、ストリーミング アプリの場合は解像度やビットレートを下げる、またはネットワーク トラフィックを低減させる、カメラアプリの場合は負荷の高い画像加工やフラッシュを無効にする、ゲームの場合はフレームレートやポリゴン テッセレーションを制限する、メディアアプリの場合はスピーカーの音量を下げる、地図アプリの場合は GPS をオフにする、といった対応が可能です。

Thermal API には新しいデバイス HAL レイヤが必要です。デバイス HAL レイヤは現在、Android Q が実行されている Pixel デバイスでサポートされており、Google はパートナーのデバイス メーカーと共同で、できる限り早くエコシステムに幅広いサポートを提供できるよう取り組んでいます。

カメラと画像

Android Q では、カメラと画像に関連する次の新機能が導入されています。

モノクロカメラのサポート

Android 9(API レベル 28)で初めてモノクロカメラ機能が導入されました。Android Q では、モノクロカメラのサポートに関して機能が強化されています。

  • 新しい Y8 ストリーム形式がサポートされ、メモリ効率が改善されています。
  • モノクロ RAW DNG キャプチャがサポートされるようになりました。
  • MONO 列挙型と NIR CFA 列挙型が導入され、通常のモノクロカメラと近赤外線カメラが区別されるようになりました。

この機能を使用することで、ネイティブ モノクロ画像をキャプチャすることができます。論理マルチカメラ デバイスで、モノクロカメラを物理的サブカメラとして使用することで、優れた低照度画質を実現できます。

Dynamic Depth 形式

Android Q 以降、カメラは、Dynamic Depth 形式(DDF)と呼ばれる新しいスキーマを使用して、画像の深度データを別ファイルに保存できるようになりました。アプリは、JPG 画像とその深度メタデータの両方をリクエストし、その情報を使用することで、元の画像データを変更することなく、必要なぼかしを後処理で適用できるようになります。

新しい形式の仕様については、Dynamic Depth 形式をご覧ください。

High Efficiency Image File 形式

High Efficiency Image File(HEIF)形式は、他のファイル形式よりも高画質のエンコードと削減されたファイルサイズを実現する、画像と動画の標準形式です。

ファイル形式の詳細については、HEIC をご覧ください。

マルチカメラの改善

Android Q では、Android 9(API レベル 28)で導入された機能である単一の論理カメラとマルチカメラの融合が改善されています。以下が Camera2 API に追加されました。

  • isSessionConfigurationSupported(SessionConfiguration sessionConfig) - 渡されたセッション設定を使用してカメラ撮影セッションを作成できるかどうかを問い合わせることができます。

  • LOGICAL_MULTI_CAMERA_ACTIVE_PHYSICAL_ID - 論理カメラデバイスをサポートしているアクティブな物理カメラの ID を特定できます。電力効率を高めるために、返された ID を使用して、論理ストリームと物理サブカメラ ストリームをリクエストできます。

ユーザー補助サービス API

Android Q では、ユーザー補助サービスに関して以下の機能や API が新たに導入されています。

AccessibilityNodeInfo 入力キーフラグ

Android Q では、AccessibilityNodeInfo が拡張され、テキスト入力キーであるかどうかを示す新しいフラグが導入されました。このフラグにアクセスするには、AccessibilityNodeInfo.isTextEntryKey() メソッドを使用します。

ユーザー補助ダイアログ音声フィードバック

ユーザー補助サービスがサービスを開始する際、ユーザー補助ショートカットを繰り返すようユーザーに求める場合、そのリクエスト ダイアログと一緒にテキスト読み上げプロンプトを提示できるようになりました。

ジェスチャー ナビゲーションが有効であるときのユーザー補助ショートカット

Android Q でジェスチャー ナビゲーション機能が有効になると、ユーザー補助機能ボタンを押してもユーザー補助サービス メニューは表示されなくなります。代わりに、次のジェスチャーのどちらかを行います。

  • 2 本の指で上にスワイプ
  • 2 本の指で上にスワイプして長押し

物理キーボード用のユーザー補助ショートカット

Android Q では、物理キーボードで Ctrl+Alt+Z キーを押すことで、ユーザー補助ショートカットをトリガーできるようになりました。

ソフト キーボード コントローラの機能強化

Android Q では、デバイスがハード キーボードの接続を検出した場合でも、ユーザー補助サービスがソフト キーボードを表示するようリクエストできるようになりました。ユーザーはこの動作をオーバーライドできます。

ユーザー定義のユーザー補助機能タイムアウト

Android Q では、API メソッドの AccessibilityManager.getRecommendedTimeoutMillis() が導入され、インタラクティブおよび非インタラクティブのユーザー補助 UI 要素に対して、ユーザー定義のタイムアウトがサポートされるようになりました。戻り値は、ユーザー設定とユーザー補助サービス API の両方の影響を受けます。

自動入力の改善

Android Q では、自動入力サービスに関して以下のように改善されています。

FillRequest.FLAG_COMPATIBILITY_MODE_REQUEST フラグを使用することで、自動入力リクエストが互換モード経由で生成されたかどうかを判断できるようになりました。

ユーザー名とパスワードを同時に保存する

SaveInfo.FLAG_DELAY_SAVE フラグを使用することで、アプリが複数のアクティビティを使用してユーザー名やパスワードなどの項目を表示するケースをサポートできるようになりました。

保存 UI のユーザー操作

保存ダイアログにアクション リスナーを設定し、対応するパスワード リモートビューの表示 / 非表示を変更することで、保存ダイアログでパスワード フィールドの表示 / 非表示を切り替えることができるようになりました。

データセット更新のサポート

自動入力で、既存のパスワードを更新できるようになりました。たとえば、ユーザーがすでにパスワードを保存済みのときに、新しいパスワードを保存しようとした場合、自動入力は、新しいパスワードを保存するのではなく、既存のパスワードを更新するようユーザーに促すプロンプトを表示します。

フィールド分類の改善

Android Q では、Field Classification API に関して以下のように改善されています。

UserData.Builder コンストラクタ

UserData.Builder コンストラクタが Builder パターンに合致するように変更されました。

1 つの値を複数のカテゴリ ID タイプにマッピング可能にする

UserData.Builder を Android Q で使用する際に、1 つの値を複数のカテゴリ ID タイプにマッピングできるようになりました。以前のリリースでは、1 つの値が複数回追加されると、例外がスローされていました。

クレジット カード番号のサポート向上

フィールド分類で、クレジット カード番号の末尾 4 桁となる 4 桁の数字を検出できるようになりました。

アプリ固有のフィールド分類のサポート

Android Q では、FillResponse.setUserData() が追加され、セッションの継続中にアプリ固有のユーザーデータを設定できるようになりました。これにより、自動入力サービスが、アプリ固有のコンテンツを持つフィールドのタイプを検出できるようになります。

UI とシステム制御

Android Q では、ユーザー インターフェースに関して次のように改善されています。

JVMTI PopFrame キャップのサポート

Android Q では、Android JVMTI 実装で can_pop_frames 機能がサポートされるようになりました。デバッグ時にこの機能を使用すると、ブレークポイントで一時停止し、ローカルや、グローバル、関数の実装を調整してから、関数を再実行することができます。詳細については、Oracle の Pop Frame リファレンス ページをご覧ください。

Surface Control API

Android Q では SurfaceControl API が導入され、システム コンポジタ(SurfaceFlinger)に対するローレベル アクセスが可能になりました。コンポジタを活用するには SurfaceView を利用することをおすすめします。SurfaceControl API は、次のような特殊なケースで役に立ちます。

  • 複数のサーフェスの同期
  • クロスプロセス サーフェス埋め込み
  • ローレベル ライフタイム管理

SurfaceControl API は、SDK バインディングと NDK バインディングの両方で利用できます。NDK 実装には、コンポジタと手動でバッファを交換するための API が含まれています。これにより、BufferQueue の制限に直面したユーザーに代替手段を提供することができます。

WebView ハングレンダラの検出

Android Q では新しい WebViewRenderProcessClient 抽象クラスが導入されています。アプリでこの抽象クラスを使用すると、WebView が応答しなくなっているかどうかを検出できます。このクラスを使用する手順は次のとおりです。

  1. 独自のサブクラスを定義し、その onRenderProcessResponsive() メソッドと onRenderProcessUnresponsive() メソッドを実装します。
  2. WebViewRenderProcessClient のインスタンスを 1 つ以上の WebView オブジェクトに追加します。
  3. WebView が応答しなくなっている場合、システムはクライアントの onRenderProcessUnresponsive() メソッドを呼び出して、WebViewWebViewRenderProcess を渡します(WebView が単一プロセスの場合、WebViewRenderProcess パラメータは null です)。これにより、レンダリング プロセスを中止するかどうかを尋ねるダイアログ ボックスをユーザーに表示するなど、アプリが適切なアクションを実行できるようになります。

WebView が引き続き応答しない場合、システムは定期的に(多くても 5 秒に 1 回)onRenderProcessUnresponsive() を呼び出しますが、それ以外のアクションを実行することはありません。WebView が再び応答するようになると、システムは onRenderProcessResponsive() を一度だけ呼び出します。

設定パネル

Android Q では、「設定パネル」が導入されました。これは、アプリのコンテキスト内でユーザーに設定を表示できるようにする API です。これにより、ユーザーがアプリを使用する際、[NFC] や [モバイルデータ] などの設定を変更するために [設定] に移動する必要がなくなります。

図 1: デバイスがネットワークに接続していないときに、ユーザーがウェブページを開こうとしました。Chrome が [インターネット接続] 設定パネルをポップアップ表示します。

図 2: ユーザーは、Chrome アプリから離れることなく、Wi-Fi をオンにしてネットワークを選択することができます。

たとえば、デバイスが機内モードのときに、ユーザーがウェブブラウザを開いたとします。Android Q より前のリリースの場合、各アプリが表示できるのは、接続を再開するために [設定] を開くようユーザーに求める汎用メッセージだけに限られていました。Android Q の場合、ブラウザアプリは、インライン パネルを表示して、機内モード、Wi-Fi(周辺のネットワークを含む)、モバイルデータなどの主要な接続設定を示すことができます。このパネルを使用することで、ユーザーは、アプリを離れることなく接続を再開することができます。

設定パネルを表示するには、新しい Settings.Panel アクションの 1 つを指定したインテントを呼び出します。

Kotlin

    val panelIntent = Intent(Settings.Panel.settings_panel_type)
    startActivityForResult(panelIntent)
    

Java

    Intent panelIntent = new Intent(Settings.Panel.settings_panel_type);
    startActivityForResult(panelIntent);
    

settings_panel_type には、次のいずれかを指定します。

ACTION_INTERNET_CONNECTIVITY
機内モード、Wi-Fi、モバイルデータなど、インターネット接続に関する設定を表示します。
ACTION_WIFI
Wi-Fi の設定を表示しますが、その他の接続の設定は表示しません。これは、大量のアップロードやダウンロードを行うために Wi-Fi 接続が必要なアプリに有効です。
ACTION_NFC
近距離無線通信(NFC)に関する設定をすべて表示します。
ACTION_VOLUME
すべてのオーディオ ストリームの音量設定を表示します。

今後、この機能向けの AndroidX ラッパーを導入する予定です。Android 9(API レベル 28)以前を実行しているデバイスで呼び出されると、このラッパーは [設定] アプリ内で最も適切なページを開きます。

共有の改善

Android Q では、共有に関して次のように改善されています。詳細については、Android Q で導入された共有の改善点をご覧ください。

ダークテーマ

Android Q では、Android システムの UI とデバイス上で実行されているアプリの両方に適用可能な新しいダークテーマを使用できます。詳しくは、ダークテーマをご覧ください。

フォアグラウンド サービスのタイプ

Android Q では、新しい XML マニフェスト属性 foregroundServiceType が導入されています。特定のサービスを定義する際は、この属性を宣言に含めます。複数のフォアグラウンド サービスのタイプを特定のサービスに割り当てることも可能ですが、ほとんどの場合、これは適切ではありません。

次の表に、フォアグラウンド サービスの各種タイプと、そのタイプを宣言する必要があるサービスを示します。

フォアグラウンド サービスのタイプ 各タイプを宣言する必要があるサービスの例
connectedDevice ウェアラブル フィットネス トラッカーを監視する
dataSync ネットワークからファイルをダウンロードする
location ユーザーが開始したアクションを継続する
mediaPlayback オーディオブック、ポッドキャスト、音楽を再生する
mediaProjection デバイスのディスプレイに表示された動画を短時間録画する
phoneCall 通話を処理する

Kotlin

Android Q では、Kotlin 開発向けに以下のアップデートが導入されています。

libcore API 向けの null 可能性アノテーション

Android Q では、libcore API 用 SDK 内の null 可能性アノテーションの適用範囲が改善されています。Android Studio 内で Kotlin または Java の null 可能性分析を使用しているアプリ デベロッパーは、このアノテーションを使用することで、API とインタラクションを行う際に nullness 情報を取得できます。

通常、Kotlin 内で null 可能性規約違反が発生すると、コンパイル エラーになります。既存のコードとの互換性を維持するため、Android 9 の際に導入された新しいアノテーションは @RecentlyNullable@RecentlyNonNull だけに限定されていました。これにより、null 可能性違反があっても、エラーではなく警告が出力されるようになっていました。

今回、Android 9 のときに追加されていた @RecentlyNullable アノテーションと @RecentlyNonNull アノテーションは、それぞれ @Nullable@NonNull に変更されました。そのため、今後 null 可能性違反があった場合は、警告ではなくエラーが出力されるようになります。

アノテーション変更の詳細については、Android デベロッパー ブログの「Android Pie SDK が Kotlin への対応を強化」をご覧ください。

NDK

Android Q では、NDK に関して次のように変更されています。

ファイル記述子オーナー権限のデバッグの改善

Android Q で、fdsan が追加されました。これにより、ファイル記述子のオーナー権限に関する問題を簡単に見つけて修正することができます。

ファイル記述子オーナー権限の取り扱いミスに関連するバグは、use-after-close や double-close として現れることが多く、メモリ割り当ての use-after-free バグや double-free バグに似ていますが、診断と修正ははるかに難しくなる傾向があります。fdsan は、ファイル記述子のオーナー権限を適用することによって、ファイル記述子の管理ミスを検出、防止しようとします。

この問題に関連するクラッシュの詳細については、fdsan によって検出されるエラーをご覧ください。fdsan の詳細については、Googlesource の fdsan の記事をご覧ください。

ELF TLS

API レベル 29 以降の NDK を使用して構築されたアプリの場合、emutls を使用する必要がなくなりました。代わりに ELF TLS を使用できます。スレッド ローカル変数を処理する新しいメソッドをサポートするため、動的および静的リンカーのサポートが追加されています。

API レベル 28 以前向けに構築されたアプリに関しては、emutls に関するいくつかの問題に対処するため、libgcc/compiler-rt が改善されました。

詳細については、NDK デベロッパー向けの Android の変更点をご覧ください。

ランタイム

Android Q には、ランタイムに関して以下の変更があります。

Mallinfo ベースのガベージ コレクション トリガー

小さなプラットフォーム Java オブジェクトが C++ ヒープ内の大きなオブジェクトを参照している場合、C++ オブジェクトが再利用できるようになるのは多くの場合、Java オブジェクトを収集し、たとえばファイナライズした後に限られます。以前のリリースでは、Java オブジェクトに関連付けられた多数の C++ オブジェクトのサイズをプラットフォームが推定していました。このサイズの推定値は必ずしも正確ではなく、ガベージ コレクションが失敗するとメモリ使用量が大幅に増加することがありました。

Android Q では、システムの malloc() によって割り当てられたヒープサイズの合計がガベージ コレクタ(GC)によってトラッキングされ、malloc() の大きな割り当てが常に GC トリガー計算に含まれるようになります。多数の C++ 割り当てを Java 実行とインターリーブするアプリの場合は、結果的にガベージ コレクションの頻度が増加する可能性があります。他のアプリの場合は、少し減少する可能性があります。

テストとデバッグ

Android Q では、テストとデバッグに関して以下のように改善されています。

デバイス上のシステム トレースの改善

デバイス上でシステム トレースを行う際、トレースのサイズと期間の制限を指定できるようになりました。どちらかの値を指定すると、長時間トレースが記録される間、トレース バッファがファイルに定期的にコピーされます。指定したサイズまたは期間の制限に達するとトレースが完了します。

追加されたこのパラメータを使って、標準のトレースよりもさまざまなユースケースをテストできます。たとえば、アプリが長時間実行された後にのみ発生するパフォーマンスのバグを診断できます。この場合、1 日分の長時間のトレースを記録してから、CPU スケジューラ、ディスクのアクティビティ、アプリのスレッドなどのデータを分析してレポートを作成し、バグの原因の判断に利用することができます。

TextClassifier の改善

Android Q では、TextClassifier インターフェースにテキスト分類機能が追加されました。

言語の検出

TextClassifierdetectLanguage() メソッドが追加されました。このメソッドは既存の分類メソッドと同様に機能し、TextLanguage.Request オブジェクトを受け取って、TextLanguage オブジェクトを返します。

新しい TextLanguage オブジェクトは、順序が指定されたペアのリストで構成されています。各ペアには、リクエストされたテキスト サンプルの言語 / 地域と対応する信頼度スコアが含まれています。

会話アクションの候補

TextClassifiersuggestConversationActions() メソッドが追加されました。このメソッドは既存の分類メソッドと同様に機能し、ConversationActions.Request オブジェクトを受け取って、ConversationActions オブジェクトを返します。

新しい ConversationActions オブジェクトは、ConversationAction オブジェクトのリストで構成されています。各 ConversationAction オブジェクトには、候補になり得るアクションとその信頼度スコアが含まれています。

スマート リプライと通知でのアクション

Android 9 で、通知内に定型返信文を表示する機能が導入されました。Android Q 以降では、通知にインテント ベースのアクションの候補を含めることもできます。さらに、こうした候補を自動生成できるようにもなっています。アプリでは、これまでどおり独自の候補を提供することも、システムが生成した候補を除外することもできます。

定型返信文の生成に使用される API は TextClassifier に含まれており、Android Q ではデベロッパーに直接公開されています。詳しくは、TextClassifier の改善についての説明をご覧ください。

アプリで独自の候補を提供する場合、プラットフォームによる候補の自動生成は行われません。アプリの通知に返信文やアクションの候補を表示したくない場合は、setAllowGeneratedReplies()setAllowSystemGeneratedContextualActions() を使用することで、システムが生成した返信文やアクションを除外できます。