開発者コンソール

Fireタブレット向けFire OS 7

Fireタブレット向けFire OS 7

Fire OS 7はAndroid 9 Pieをベースにしています。以下のガイドラインを読んで、アプリとFire OS 7の互換性を確認してください。

Fire OS 7搭載デバイスと旧Fire OS搭載デバイス

Android 9 Pie(APIレベル28)をベースにしたFire OS 7は、2019年に一部のFireタブレットデバイス向けに初めてリリースされました。

ほとんどのFireタブレットデバイスはFire OS 5(Android 5.1、APIレベル22)を採用していて、Fire 7(2019年)タブレットデバイスは、Android Nougat(Android 7.1.2、APIレベル25)をベースにしたFire OS 6を搭載しています。古いバージョンのFireタブレットデバイスの中には、まだFire OS 4以前のリリース版で動作するものもあります。

Fireタブレットデバイスおよびバージョンの詳細については、Fireタブレットのデバイス仕様:概要を参照してください。

Fire OS 7におけるAndroidの変更点

Fire OS 6からFire OS 7にアップグレードするには、Nougat(Android 7.1.2)からOreo(Android 8.0)またはPie(Android 9)への移行が必要です。

Android 8.0とAndroid 9で導入された変更点のため、アプリをFire OS 7デバイスで正常に動作させるにはコードを変更する必要があります。

Android 8.0の変更点

Android 8.0における主な変更点は以下のとおりです。

  • 通知チャネル: すべての通知(「おすすめ」パートナーが管理する「おすすめ」を含む)をチャネルに割り当てる必要があります。チャネルについて詳しくは、Androidドキュメントの通知チャネルを作成して管理するを参照してください。サンプルコードと追加情報については、後述のFire OS 7における通知セクションを参照してください。

  • バックグラウンドサービス: Android 8.0では、バックグラウンドサービスの実行が制限されています。そのため、Fire OS 7ではアプリがバックグラウンドサービスを使用しておすすめを更新することができません。このバックグラウンドサービスの制限を回避する方法として、AndroidではJobSchedulersの使用が推奨されています。詳細については、Androidドキュメントのバックグラウンド実行制限を参照してください。

  • ネイティブライブラリ: 書き込みと実行の両方が可能なロードセグメントが含まれているネイティブライブラリはロードされません。

  • パーミッション: 同じグループに属しているパーミッションでも、各パーミッションをアプリから明示的に要求する必要があります。

  • MediaSessionイベント: アプリでオーディオを処理するには、MediaSessionを適切に使用する必要があります。詳細については、Android 8.0ドキュメントのメディアセッションの検索を参照してください。Androidがイベントを処理する際、イベントを処理するためのアクティブなメディアセッションがフォアグラウンドアクティビティになければ、Androidはほかのメディアセッションを探します。マルチメディアアプリの要件も参照してください。

上記の変更点については、Android 8.0での動作変更点を参照してください。

Android 9の変更点

Android 9における主な変更点は以下のとおりです。

  • プライバシーに関する変更点: アプリからWi-Fiへのアクセスが制限されます。バックグラウンドで実行中のアプリからユーザー入力やセンサーデータにアクセスする機能が制限されています。

上記の変更点については、動作の変更点:すべてのアプリを参照してください。

Fire OS 7とAndroid 9の互換性

Fire OS 7で実装されたすべての機能は、Android 9と互換性があります。Android 9のすべての機能がFire OS 7で利用可能なわけではありませんが、Fire OS 7で実装された機能についてはAndroid 9でも同等に使用可能です。Android 9の一部の機能(分割画面、通知ドット、アダプティブアイコンなど)は、Fire OS 7ではサポートされていません。

また、Fire OS 7の機能とAndroid 9は互換性がありますが、Amazon FireデバイスでGoogleのサービスを使用することはできません。代わりに、必要なサービス(アプリ内課金など)に対応するAmazon SDKを使用する必要があります。

Fire OS 7デバイスをアプリの対象に指定

ユーザーがアプリを実行するデバイスとして、Fire OS 5、Fire OS 6、Fire OS 7が考えられます。デバイスのFire OSバージョンとアプリの互換性を最大限に高めるため、SDKレベルに基づいた対象デバイスの指定をお勧めします。

Fire OS 7デバイスを対象に指定するには、コードでBuild.VERSION.SDK_INT28(Android 9のAPIレベル)以上であることを確認してください。

詳細については、Androidドキュメントの各種のプラットフォームバージョンをサポートするも参照してください。

Fire OS 7デバイスにおけるアプリの互換性テスト

アプリとFire OS 7の互換性は、実際のデバイスに接続することでテストが可能です。

minSdkVersionとtargetSdkVersionの設定

minSdkVersionは、該当するFire OSバージョンの最小APIレベルに設定します。

Fire OSのバージョン minSdkVersion
Fire OS 5 22
Fire OS 6 25
Fire OS 7 28

targetSdkVersionは、アプリのテストに使用した最も高いAPIレベルに設定します。

最小APIレベル要件の詳細については、デバイスフィルタリングと互換性に記載の「Fire OSとAndroidのAPIレベル」を参照してください。

minSdkVersionがサポート対象デバイスに与える影響

アプリが正常に動作するために必要な最小SDKレベルは、アプリのマニフェスト(またはbuild.gradleファイル)内のminSdkVersion属性で設定します(そのAPIレベルをサポートしていないデバイスでは、該当アプリのインストールはできません。詳細については、デバイスフィルタリングと互換性を参照してください)。

Fire OS 5デバイスはAPIレベル22(Lollipop 5.1)、Fire OS 7デバイスはAPIレベル28(Android 9)をベースにしています。minSdkVersionを22に設定した場合、アプリが正常に動作するにはデバイスのAPIレベルが22以上であることが必要になります。

Androidではレベルに下位互換性があるため、minSdkVersionを22に設定すると、より高いAPIレベル(28など)のデバイスにもアプリがインストール可能になります。APIレベル28には、通常、レベル1~28のすべてのAPIが含まれます(リリースのたびに前回のレベルに追加)。

しかし、Android 9(APIレベル28)のAPIを利用する場合、minSdkVersionを22に設定すると、APIレベルが28ではないFire OS 5デバイスにアプリがインストールされてしまいます。そのため、防御的なコーディングを行う必要があります。デバイスのレベルを確認して、そのAPIレベルがサポートされていない場合は別のレベルにフォールバックするようなコード作成が必須となります。たとえば、以下のようなコードが考えられます。

targetSdkVersion

if (Build.VERSION.SDK_INT >= 28) {
 Log.v(TAG, "はい、これはAPIレベル28以上のデバイスです");
} else {
 Log.v(TAG, "いいえ、これはAPIレベル28以上のデバイスではありません");
}

このコードは、デバイスのAPIレベルが28以上であるかどうかを確認します。APIレベルが28以上の場合、コードが実行されます。そうでない場合、elseロジックでフォールバックします。

デフォルトでは、targetSdkVersionが指定されていない場合、minSdkVersionと同じ値が使用されます。targetSdkVersionには、アプリのテストに使用した最も高いAPIレベルを設定することができます。Androidは、アプリがこの値に基づいてデバイスで適切に動作するようにします。

たとえば、targetSdkVersionを23以上(Marshmallowのリリース)に設定すると、AndroidはMarshmallowに含まれる実行時パーミッション確認機能を適用します。しかし、targetSdkVersionが23未満(Marshmallowに実行時パーミッション確認機能が導入される前)の場合、Androidはこの動作をアプリに適用しません。

推奨はしませんが、Fire OS 7デバイスに古いアプリが表示されないようにする必要がある場合は、maxSdkVersionをFire OS 5(APIレベル22)に設定することもできます。

詳細については、以下を参照してください。

Fireタブレットの自動バックアップ

2023年5月31日以降、Amazon Fireタブレットでは、バックアップおよび復元を選択しているユーザーを対象に自動バックアップが有効になります。API 23以降をターゲットとするアプリでは、マニフェストで自動バックアップを明示的に無効にしていない限り、自動バックアップが自動的に適用されます。自動バックアップのデフォルト値はtrueですが、マニフェストで属性を明示的に設定することをお勧めします。

自動バックアップの詳細

  • Key-Valueバックアップ用のカスタムバックアップおよび復元ロジックの記述は不要になりましたが、引き続きサポートされています。
  • この機能は、API 23以降をターゲットとしている(targetSdkVersion)アプリに対してFire OS 7以降で実行されます。
  • アプリデータは、無料で自動的にAWS S3にバックアップされます。
  • 特に指定がない限り、アプリのプライベートディレクトリにあるデータベース、共有の環境設定、そのほかのコンテンツを含め、アプリキャッシュを除くすべてのデータファイルが自動的にバックアップされます。
  • 各ユーザーのバックアップ容量は、1デバイス、1アプリあたり25MBに制限されています。この制限を超えると、アプリのデータはバックアップされなくなりますが、制限に達するまでのバックアップは引き続き復元できます。
  • バックアップされるデータには、医療保険の携行性と責任に関する法律(HIPAA)で定義されている「保護対象医療情報」や、連邦取引委員会の健康侵害通知規則で定義されている「PHRを識別可能な医療情報」など、エンドユーザーの機密データが含まれないようにしてください。また、セキュリティ上の理由から、パスワードトークンや認証情報もバックアップから除外してください。

詳細については、Androidの自動バックアップに関するドキュメントを参照してください。

アプリのバックアップ

自動バックアップでは、API 23以上をターゲットとしてFire OS 7以降で実行されているアプリが自動的にバックアップされます。BackupAgentを実装する必要はありません。自動バックアップ(android:allowbackup)のデフォルト値はtrueですが、次に示すようにマニフェストでブール値を明示的に設定することをお勧めします。

<manifest ... >
...

	<application android:allowBackup="true" ... >
...
</application>
</manifest>

デフォルトでは、アプリのデータディレクトリにあるすべてのファイル(キャッシュファイルを除く)がバックアップされます。

データのサブセットのバックアップ

Androidでは、アプリ開発者はXMLファイルスキーマを変更すれば、バックアップするファイルをカスタマイズできます。このファイルのパターンを使用して、ファイルを含めるか除外するかを選択します。バックアップされるデータには、医療保険の携行性と責任に関する法律(HIPAA)で定義されている「保護対象医療情報」や、連邦取引委員会の健康侵害通知規則で定義されている「PHRを識別可能な医療情報」など、エンドユーザーの機密データが含まれないようにしてください。また、セキュリティ上の理由から、パスワードトークンや認証情報もバックアップから除外してください。共有の環境設定など(英語のみ)、暗号化されたデータファイルがある場合、新しいデバイスに移行したときに機能しないことがあります。これらも除外することをお勧めします。

バックアップするデータをより細かく制御する必要がある場合、またはonRestoreFinished()onQuotaExceeded()などのバックアップイベントをリッスンする必要がある場合は、BackupAgentの実装を検討してください。

自動バックアップの無効化

自動バックアップを無効にする場合は、アプリのマニフェストファイルでandroid:allowBackupfalseに設定します。詳細については、バックアップを有効または無効にするを参照してください。

Key-Valueバックアップから自動バックアップへの移行

以前にBackupAgentをサブクラス化してマニフェスト(android:backupAgent)で設定することでKey-Valueバックアップを実装済みの場合は、android:fullBackupOnly="true"属性を<application/>に追加します。

自動バックアップに切り替えるときは、既存のBackupAgentを再処理するか、完全に削除してください。バックアップオプションの切り替えにより、アプリとの下位互換性が損なわれる可能性があるため、事前にそのような状況への対処法を計画しておいてください。

自動バックアップのテスト

Fireタブレットの自動バックアップ機能をテストするための大まかな手順は以下のとおりです。自動バックアップのテストに関するAOSPのドキュメントの詳細については、Androidのバックアップテストガイドを参照してください。

オプション1: Amazonのトランスポートを使用する

  1. トランスポートが正しく設定されていることを確認します。

     adb shell bmgr list transports
    

    ログの結果は次のようになります。

     * com.amazon.device.backup/.transport.BackupTransportService
     com.android.localtransport/.LocalTransport
    

    Amazonのバックアップトランスポートに設定されていない場合は、コマンドラインで設定できます。

     adb shell bmgr transport com.amazon.device.backup/.transport.BackupTransportService
    
  2. グローバル設定force_auto_backupを設定します。

    デバイスで自動バックアップをサポートするには、force_auto_backupフラグを設定します。

     adb shell settings put global force_auto_backup 1
    
  3. 自動バックアップを実行するための前提条件として、Key-Valueバックアップを実行します。これは最初に実行する必要があります。

     adb shell bmgr backup @pm@ && adb shell bmgr run
    
  4. パッケージをバックアップします。

     adb shell bmgr backupnow <PACKAGE>
    

    例:

     adb shell bmgr backupnow com.example.app
    

    完全バックアップを実行すると、次のようなログが表示されます。

     I PFTBT   : Full data backup pass finished.
    
  5. 復元トークンを問い合わせます。

    復元トークンは、バックアップの実行を識別する一意のIDです。

    例:

     adb shell dumpsys backup | grep "Current:"
    

    出力:

     Current:   3
    

    この例では、3が「復元トークン」です。

  6. 復元トークンをテストします。

     adb shell bmgr restore <token> <PACKAGE>
    

    例:

     adb shell bmgr restore 3 com.example.app
    

    復元が正常に完了した場合は、次のログが出力されます。

     I BackupManagerService: Restore complete.
    

データを削除してみて、復元が機能するかどうかを確認することもできます。アプリをアンインストールした後に再インストールしてから、バックアップデータが復元されるかどうかを確認します。

すべての手順を適切に実行すると、アプリに変更を加えることができ、以前にバックアップしたデータと共に変更が復元されます。問題が発生した場合は、下記のトラブルシューティングの手順を参照してください。

オプション2: ローカルトランスポートを使用する

データをローカルでテストするには、AOSPのローカルトランスポートを使用します。この方法ではデータはサーバーにバックアップされませんが、どのデータがバックアップされ、アプリに復元されるかをテストできます。Androidのバックアップのテストに関するガイドには、ローカルトランスポートを使用するための情報が含まれています。大まかな手順は上記と同じです。唯一の違いは、トランスポートをAmazonのトランスポートではなく、AOSPのローカルトランスポートに設定することです。

adb shell bmgr transport com.android.localtransport/.LocalTransport
バックアップトランスポートについて

バックアップトランスポートは、デバイスバックアップデータをサーバーからアップロードおよびダウンロードするロジックを処理します。Amazonの場合、バックアップトランスポートは、データをAWS S3にバックアップするロジックを処理します。

すべてのトランスポートのリストを取得する方法

すべてのトランスポートのリストは次のコマンドで取得できます。

adb shell bmgr list transports

トラブルシューティング

バックアップが有効にならない

ログに次の結果が表示される場合:

Backup finished with result: Backup is not allowed
Unable to run backup

これは、デバイスでバックアップが有効になっていないことを意味します。この問題を修正するには、[設定] > [デバイスオプション] > [バックアップと復元] の順にクリックし、[バックアップと復元]オンにします。これでバックアップの再実行を試みます。

トランスポートが却下される

まず、Fireタブレットが最新バージョンであることを確認してください。ログに次のように表示される場合:

Package <PACKAGE NAME> with result: Transport rejected package because it wasn't able to process it at the time

これには複数の原因が考えられますが、ほとんどの場合、スロットリングが原因です。パッケージは1日に1回のみバックアップできます。スロットリング制限を下げるには、次のコマンドを実行します。

adb shell settings put secure overridden_backup_throttle_delay_ms <delay in milliseconds>

例:

adb shell settings put secure overridden_backup_throttle_delay_ms 1000

タブレットでスロットリング制限を既に下げている場合、このエラーは、バックアップするデータがないことを意味している可能性があります。バックアップするデータがアプリに設定されているかどうかを確認してください。任意のバックアップデータを取得するには、次のコマンドを実行します。

adb shell
cd /storage/emulated/0/Android/data/<your package name>/files

次に、テストファイルを作成します。

touch test.txt
echo "some test data" >> test.txt
パッケージが見つからない

ログに次の結果が表示される場合:

Package <PACKAGE NAME> with result: Package not found

パッケージがデバイスにインストールされているかどうかを確認します。

Fire OS 7における通知

Android 8.0(APIレベル26)では、アプリが送信する通知をチャネルに割り当てる必要があります(「おすすめ」は通知の一種です)。 アプリが通知またはおすすめを送信する場合、チャネルを作成し、そのチャネルを通知に関連付ける必要があります。チャネルIDがない通知は破棄されます。

通知にチャネルを追加する手順の概要は、以下のとおりです。

  1. 手順1: 通知チャネルを作成し、通知マネージャーに渡して登録する
  2. 手順2: 通知のチャネルIDを設定する(次のいずれかの方法を使用します)

以下のセクションでは、上記の内容について詳しく説明すると共に、コードサンプルを示します。

手順1: 通知チャネルを作成し、通知マネージャーに渡して登録する

以下は、Androidドキュメントの通知チャネルを作成するからの抜粋です。

通知チャネルを作成する

通知チャネルを作成する手順は次のとおりです。

  1. 固有のチャネルID、ユーザーに表示される名前、重要度を指定して、NotificationChannelオブジェクトを作成します。
  2. 必要であれば、システム設定でユーザーに表示される説明をsetDescriptionで指定します。
  3. createNotificationChannelに通知チャネルを渡して登録します。

    private void createNotificationChannel() {
        //NotificationChannelを作成しますが、API 26以降でのみ使用します。
        //これは、NotificationChannelクラスが新しく、サポートライブラリにないためです
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            CharSequence name = getString(R.string.channel_name);
            String description = getString(R.string.channel_description);
            int importance = NotificationManager.IMPORTANCE_DEFAULT;
            NotificationChannel channel = new NotificationChannel(CHANNEL_ID, name, importance);
            channel.setDescription(description);
            //チャネルをシステムに登録します。この後で重要度や
            //通知動作を変更することはできません
            NotificationManager notificationManager = getSystemService(NotificationManager.class);
            notificationManager.createNotificationChannel(channel);
        }
    }
    

手順2: 通知のチャネルIDを設定する

通知のチャネルIDを設定する方法には、2つのオプションがあります。ContentRecommendationとリフレクションを使用するオプションと、Notification.Builderを使用するオプションです。

オプション1: createContentRecommendationとリフレクションを使用してチャネルIDを設定する

Notification notification = createContentRecommendation(largeIcon, notificationId);

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    Log.d(TAG, "SDKバージョンはAndroid O以上です");

    try {
        Field channel = notification.getClass().getDeclaredField("mChannelId");
        channel.setAccessible(true);
        channel.set(notification, StringTerms.CHANNEL_ID);
    }
    catch (Exception e) {
        Log.d(TAG, "チャネルIDを設定できません", e);
    }
}

オプション2: Notification.BuilderとチャネルIDを使用する

以下のコードは、Google Gitに記載されたAndroidオープンソースプロジェクトのコードを適宜変更したものです。

public Notification getNotificationObject(Context context) {
    Notification.Builder builder = new Notification.Builder(context, "channelId");
    RecommendationExtender recExtender = new RecommendationExtender();

    //通知オブジェクト内にあるすべてのおすすめコンテンツデータをエンコードします

    builder.setCategory(Notification.CATEGORY_RECOMMENDATION);
    builder.setContentTitle(mTitle);
    builder.setContentText(mText);
    builder.setContentInfo(mSourceName);
    builder.setLargeIcon(mContentImage);
    builder.setSmallIcon(mBadgeIconId);
    if (mBackgroundImageUri != null) {
        builder.getExtras().putString(Notification.EXTRA_BACKGROUND_IMAGE_URI, mBackgroundImageUri);
    }
    builder.setColor(mColor);
    builder.setGroup(mGroup);
    builder.setSortKey(mSortKey);
    builder.setProgress(mProgressMax, mProgressAmount, false);
    builder.setAutoCancel(mAutoDismiss);

    if (mContentIntentData != null) {
        PendingIntent contentPending;
        if (mContentIntentData.mType == INTENT_TYPE_ACTIVITY) {
            contentPending = PendingIntent.getActivity(context, mContentIntentData.mRequestCode,
            mContentIntentData.mIntent, PendingIntent.FLAG_UPDATE_CURRENT,
            mContentIntentData.mOptions);
        }
        else if (mContentIntentData.mType == INTENT_TYPE_SERVICE) {
            contentPending = PendingIntent.getService(context, mContentIntentData.mRequestCode,
            mContentIntentData.mIntent, PendingIntent.FLAG_UPDATE_CURRENT);
            }
            else { // Default:INTENT_TYPE_BROADCAST{
            contentPending = PendingIntent.getBroadcast(context,
            mContentIntentData.mRequestCode,
            mContentIntentData.mIntent, PendingIntent.FLAG_UPDATE_CURRENT);
            }
        builder.setContentIntent(contentPending);
    }

    if (mDismissIntentData != null) {
        PendingIntent dismissPending;
        if (mDismissIntentData.mType == INTENT_TYPE_ACTIVITY) {
            dismissPending = PendingIntent.getActivity(context, mDismissIntentData.mRequestCode,
            mDismissIntentData.mIntent, PendingIntent.FLAG_UPDATE_CURRENT,
            mDismissIntentData.mOptions);
        }
        else if (mDismissIntentData.mType == INTENT_TYPE_SERVICE) {
            dismissPending = PendingIntent.getService(context, mDismissIntentData.mRequestCode,
            mDismissIntentData.mIntent, PendingIntent.FLAG_UPDATE_CURRENT);
            }
            else { // Default:INTENT_TYPE_BROADCAST{
                dismissPending = PendingIntent.getBroadcast(context,
                mDismissIntentData.mRequestCode,
                mDismissIntentData.mIntent, PendingIntent.FLAG_UPDATE_CURRENT);
            }
        builder.setDeleteIntent(dismissPending);
    }

    recExtender.setContentTypes(mContentTypes);
    recExtender.setGenres(mContentGenres);
    recExtender.setPricingInformation(mPriceType, mPriceValue);
    recExtender.setStatus(mStatus);
    recExtender.setMaturityRating(mMaturityRating);
    recExtender.setRunningTime(mRunningTime);

    builder.extend(recExtender);
    Notification notif = builder.build();
    return notif;
}

詳細については、Androidドキュメントの通知チャネルを作成して管理するを参照してください。

Amazon Device Messaging(ADM)の互換性

アプリでADMを使用している場合は、Fire OS 7でクラッシュが発生しないように、SDKを更新する必要があります。以前のリリースでは、ADMはIntentServiceを使用して、バックグラウンドで実行中のクライアントアプリにメッセージを送信していました。Fire OS 7では、IntentServiceはAndroid 8.0(APIレベル26)で課されるすべてのバックグラウンド実行制限の対象となります。たとえば、Androidドキュメントのバックグラウンドサービスの制限事項によれば、以下のような制限があります。

IntentServiceはサービスであるため、バックグラウンドサービスに対する新しい制限事項の対象になります。そのため、IntentServiceに依存する多くのアプリは、Android 8.0以降を対象にすると正常に動作しません。こうした理由から、Androidサポートライブラリ26.0.0に新しいJobIntentServiceクラスが導入されました。このクラスはIntentServiceと同じ機能を提供しますが、Android 8.0以降で実行されるときにサービスではなくジョブを使用します。

アプリがバックグラウンドで実行中にADMメッセージを受信できるようにするには、JobIntentServiceを使用する必要があります。この新しいクラスは、Android 8.0以降で実行される場合、サービスの代わりにジョブを使用します。

ADMMessageHandlerJobBaseは、JobIntentServiceを使用してバックグラウンドでメッセージを配信する新しいクラスです。これまでのADMMessageHandlerBaseに置き換えて、このクラスを使用してください。ADMとやり取りを行うアプリを作成する場合は、ハンドラーでADMMessageHandlerJobBaseを拡張する必要があります。実装例については、登録とメッセージの処理を実装するを参照してください。また、リリースの一環として更新されたAmazon Device Messaging(ADM)のセットアップ方法およびアプリのマニフェストを更新するも参照してください。

更新されたADM SDKは、こちらからダウンロードできます。

サポート

Fire OS 7でアプリに問題が見つかった場合は、Fireタブレットに関するフォーラムでその問題を報告してください。


Last updated: 2023年3月1日