Androidアプリのベストプラクティス

この記事で説明するベストプラクティスは、Citrix Endpoint Management™とAndroidデバイス用モバイルアプリ間の互換性を向上させます。

MDXアプリSDKとラッピング

アプリがMDXアプリSDKを使用している場合、ラッピングには対応するMDX Toolkitバージョンを使用する必要があります。これら2つのコンポーネント間でバージョンが一致しないと、不適切な動作を引き起こす可能性があります。

  • このような不一致を防ぐには、アプリをPremiumまたはGeneralのアプリタイプでラッピングします。これにより、事前にラッピングされたアプリを配信できます。その結果、顧客はアプリをラッピングする必要がなくなり、MDX Toolkitのバージョン不一致を回避できます。アプリのラッピングの詳細については、「Androidモバイルアプリのラッピング」を参照してください。

メインスレッドのブロック回避

メインスレッドで実行中にブロッキングコードを使用しないでください。これはGoogleのガイドラインですが、Citrix Endpoint Managementではさらに重要です。管理対象アプリでは、一部のアクションに時間がかかったり、スレッドのさらなる実行をブロックしたりする場合があります。

ブロッキングコードには、以下が含まれますが、これらに限定されません。

  • ファイルまたはデータベース操作
  • ネットワーク操作

明確にするために、onCreateなどのすべてのアプリライフサイクルメソッドはメインスレッドで実行されます。

Googleは、ブロッキングコードの検出に役立つStrictMode APIを提供しています。詳細については、こちらのブログ投稿を参照してください: https://android-developers.blogspot.com/2010/12/new-gingerbread-api-strictmode.html

  • 堅牢なコードの記述

特に、フレームワークAPIからの戻り値をチェックするか、例外をキャッチする必要があります。これは一般的なプログラミングのベストプラクティスにすぎませんが、管理対象アプリにとっては特に重要です。

Citrix Endpoint Managementポリシーが基盤となる機能をブロックする場合、常に機能すると予想されるさまざまなAPIが失敗します。例としては、以前に説明した機能のいずれかが挙げられます。

  • ネットワークAPIは、ネットワークが利用できないかのように失敗します。
  • GPSやカメラなどのセンサーAPIは、nullを返すか、例外をスローします。
  • 非管理対象アプリに向けられたインテントは失敗します。
  • ファイルおよびデータベースアクセスは、メインスレッドから使用された場合に失敗する可能性があります。詳細については、この記事の後半にある「データ暗号化の互換性の確保」および「暗号化ユーザーエントロピー」のセクションを参照してください。

失敗に遭遇した場合、アプリはクラッシュするのではなく、問題を適切に処理する必要があります。

フッキングの制限

MDXは、APK内のDEXコードを変更することにより、バイナリAndroidアプリに機能を注入します。いくつかの制限があります。

  • Citrix Endpoint Managementは、Android SDKの4.0より前のバージョンからの非推奨のフレームワーククラスを管理しない場合があります。これらの非推奨クラスは必ず避けてください。
  • ほとんどの機能は、Java/AndroidフレームワークAPIに注入されます。ネイティブ(C/C++)コードは通常管理されません。唯一の例外は、ネイティブコードであってもファイル暗号化が行われることです。
  • JNIを使用してJava機能にアクセスするネイティブコードは、ユーザーアプリ内のコードのみをターゲットにする必要があります。つまり、JNIを使用してJavaまたはAndroidフレームワークメソッドを直接呼び出さないでください。代わりに、プロキシデザインパターンを使用して、目的のフレームワーククラスを独自のJavaクラスで「ラップ」します。次に、ネイティブコードからそのクラスを呼び出します。

データ暗号化の互換性の確保

-  MDXの主要な機能の1つは、永続化されたすべてのデータが透過的に暗号化されることです。この機能を利用するためにアプリを変更する必要はなく、実際、直接回避することはできません。管理者は暗号化を選択的または完全に無効にする機能を持っていますが、アプリにはその機能はありません。

-  これはMDXのより重い側面の1つであり、以下の点を理解する必要があります。
  • ファイル暗号化は、管理対象プロセスで実行されるすべてのJavaおよびネイティブコードに存在します。

  • メディアプレーヤーや印刷サポートなど、一部のフレームワークAPIは実際には個別のOSプロセスで実行されます。このようなAPIを使用すると、問題が発生する可能性があります。

    • 例: アプリがファイルをディスクに保存し(暗号化)、そのファイルへの参照をメディアAPIに渡します。メディアAPIはファイルを読み取ろうとしますが、暗号化されたコンテンツを理解できません。失敗するか、アプリがクラッシュすることもあります。
    • 例: ファイルハンドル(暗号化されたファイルを開始する)を作成し、カメラAPIに渡します。カメラプロセスは、暗号化されたファイルに暗号化されていないデータを直接書き込みます。アプリがそのデータを読み取ろうとすると、データは復号化され、ごみデータが生成されます。
  • 個別のプロセスを処理する1つの方法は、関連するAPIに渡す前にファイルを復号化することです。または、APIがデータを書き込む場合は、最初に書き込ませてから、APIが終了したときに暗号化します。いくつかの手順が必要です。

    1. 暗号化されない領域を指定します。Citrix Endpoint Management管理者が暗号化除外ポリシーを作成する必要があるため、これを顧客に文書化する必要があります。
    1. 復号化するには、ファイルを通常の(暗号化された)場所から復号化された場所にコピーするだけです。バイトコピーを実行する必要があり、ファイル移動操作ではないことに注意してください。
    1. 暗号化するには、方向を逆にします。暗号化されていない場所から暗号化された場所にコピーします。
    1. 不要になったら、暗号化されていないファイルを削除します。
  • 暗号化されたファイルではメモリマッピングはサポートされていません。メモリマッピングを実行するAPIを呼び出すと、失敗します。エラーを処理する必要があります。可能な限り、メモリマッピングの直接的および間接的な使用を避けてください。間接的な使用の注目すべきケースの1つは、サードパーティのSqlCipherライブラリです。

  • メモリマッピングを回避できない場合、管理者は関連ファイルを省略する暗号化除外ポリシーを指定する必要があります。このポリシーを顧客に文書化する必要があります。

  • 暗号化は測定可能なオーバーヘッドを追加します。パフォーマンスの低下を防ぐために、ファイルI/Oを最適化するようにしてください。たとえば、同じ情報を繰り返し読み書きしている場合は、アプリレベルのキャッシュを実装することをお勧めします。

  • データベースは単なるファイルであるため、暗号化されます。ここでもパフォーマンスが問題になる可能性があります。標準のデータベースキャッシュサイズは2000ページまたは8メガバイトです。データベースが大きい場合は、このサイズを増やすことができます。

    • SQLite WALモードは、メモリマッピングの制限によりサポートされていません。
  • 暗号化ユーザーエントロピー

暗号化のためのCitrix Endpoint Managementオプションの1つは、暗号化キーを生成する前にエンドユーザーがPINを入力することを要求します。このオプションはユーザーエントロピーと呼ばれます。これはアプリに特定の問題を引き起こす可能性があります。

具体的には、ユーザーがPINを入力するまで、ファイルまたはデータベースアクセスを実行できません。このようなI/O操作がPIN UIが表示される前に実行される場所にある場合、常に失敗します。いくつかの影響があります。

  • ファイルおよびデータベース操作をメインスレッドから外してください。たとえば、アプリオブジェクトのonCreate()メソッドからファイルを読み取ろうとすると、常に失敗します。
  • サービスやコンテンツプロバイダーなどのバックグラウンド操作は、アプリのアクティビティが存在しなくても実行される場合があります。これらのバックグラウンドコンポーネントはPIN UIを表示できないため、ファイルまたはデータベースアクセスを実行できません。アクティビティがアプリで実行されると、バックグラウンド操作はI/O操作を実行できるようになることに注意してください。

ユーザーエントロピーのために暗号化キーが利用できない場合、いくつかの失敗メカニズムがあります。

-  PINが利用可能になる前にメインスレッドがデータベースにアクセスすると、アプリは強制終了されます。
-  PINが利用可能になる前に非メインスレッドがデータベースにアクセスすると、PINが入力されるまでそのスレッドはブロックされます。
  • PINが利用可能になる前に開始された非データベースアクセスの場合、オープン操作は失敗します。CレベルではEACCESエラーが返されます。Javaでは例外がスローされます。

この問題がアプリに存在しないことを確認するには、ユーザーエントロピーを有効にしてテストしてください。Citrix Endpoint Managementクライアントプロパティ「Encrypt secrets using Passcode」はユーザーエントロピーを追加します。このクライアントプロパティはデフォルトで無効になっており、Citrix Endpoint Managementコンソールの[構成] > [設定] > [詳細] > [クライアントプロパティ]で構成します。

ネットワークとマイクロVPN

    -  Citrix Endpoint Managementの複数のポリシーオプションが、ネットワークに関して管理者が利用できます。ネットワークアクセス ポリシーは、アプリのネットワークアクティビティを防止、許可、またはリダイレクトします。

    -  >重要:
    -  > > MDX Toolkit バージョン 18.12.0 リリースには、以前のポリシーを統合または置き換える新しいポリシーが含まれていました。 ネットワークアクセス ポリシーは、ネットワークアクセス、優先VPNモード、およびVPNモード切り替えの許可を統合します。 除外リスト ポリシーは、スプリットトンネル除外リストを置き換えます。マイクロVPNセッション必須ポリシーは、オンラインセッション必須を置き換えます。詳細については、「[以前のリリースでの新機能](/ja-jp/mdx-toolkit/about-mdx-toolkit/whats-new.html#whats-new-in-earlier-releases)」を参照してください。

    -  オプションは以下のとおりです。

    -  **以前の設定を使用**: 以前のポリシーで設定した値がデフォルトになります。このオプションを変更した場合、**以前の設定を使用**に戻すべきではありません。また、新しいポリシーへの変更は、ユーザーがアプリをバージョン 18.12.0 以降にアップグレードするまで有効になりません。
    -  **ブロック済み**: アプリで使用されるネットワークAPIは失敗します。以前のガイドラインに従い、このような失敗は適切に処理する必要があります。
  • 無制限: すべてのネットワーク呼び出しは直接行われ、トンネル化されません。
  • トンネル化 - フルVPN: 管理対象アプリからのすべてのトラフィックはCitrix Gatewayを介してトンネル化されます。

制限事項: Citrix Endpoint Managementはソケットサーバーをサポートしていません。ラップされたアプリ内でソケットサーバーが実行されている場合、ソケットサーバーへのネットワークトラフィックはCitrix Gatewayを介してトンネル化されません。

モバイルアプリ開発フレームワークのサポート

  • 一部のアプリフレームワークには、Citrix Endpoint Managementとの互換性の問題があります。

  • PhoneGapでは、位置情報サービスはブロックされません。
  • SQLCipherはメモリマッピングを使用するため、暗号化では機能しません。1つの解決策はSQLCipherを使用しないことです。2つ目の解決策は、暗号化除外ポリシーを使用してデータベースファイルを暗号化から除外することです。Citrix Endpoint Management管理者は、Citrix Endpoint Managementコンソールでポリシーを構成する必要があります。

デバッグのヒント

ラップされたアプリをデバッグする際は、以下のヒントを考慮してください。

  • アプリのラップされていないバージョンで問題が発生するかどうかを判断します。ラップされていない状態で問題が発生する場合は、通常のデバッグ手法を使用します。
  • さまざまなCitrix Endpoint Managementポリシーを無効にしてみてください。
    • これにより、互換性の問題を特定できます。ポリシーを無効にすると、MDXは関連する制限を強制しなくなり、アプリがラップされていないかのようにそれらの機能をテストできるようになります。
    • ポリシーを無効にすることで問題が解決する場合、問題はアプリが関連するAPIのエラーをチェックしていないことにある可能性があります。
  • 変更されていないが再署名されたアプリが実行されない場合:
    1. JARを使用してAPKの内容を解凍します。

      jar xvf {some.apk}

    2. META-INFフォルダーを削除します。

      rm -rf META-INF

    3. JARを使用して内容を新しいAPKに再圧縮します。

      jar cvf {/tmp/new.apk} *

    4. JARSIGNERを使用して新しいAPKに署名します。

      jarsigner -keystore {some.keystore} -storepass {keystorepassword} -keypass {keypassword} {/tmp/new.apk} {keyalias}

    5. アプリがまだ実行されない場合、元のAPKで使用されたものとは異なる署名証明書を使用してアプリをラップすることはできません。

  • 逆コンパイルまたは再コンパイルされた.apkが実行されない場合:
    1. APKTOOLを使用して逆コンパイルおよび再コンパイルします。

      apktool d {some.apk} -o {some.directory}

      apktool b {some.directory} -o {new.apk}

    2. 上記のようにJARSIGNERを使用してAPKに署名します。

    3. アプリがまだ実行されない場合、これはサードパーティのAPKTOOLのバグです。

  • アプリのラッピングが機能しない場合:
    1. APKTOOLフレームワークを削除して、再度ラッピングしてみてください。
      • Mac/Linux: rm -rf ~/Library/apktool/framework
      • Windows: del /q /s C:\Users\{username}\apktool\framework
    2. ラッパーが使用しているAPKTOOLと、前の手順で正常に逆コンパイルおよび再コンパイルするために使用したAPKTOOLを比較します。
      • 同じAPKTOOLバージョンである場合、Wrapperにバグがあります。
      • 異なるAPKTOOLバージョンである場合、MDX Toolkitユーティリティに統合されているAPKTOOLにバグがある可能性があります。
        1. ManagedAppUtility.jarの内容を解凍します。
        2. 前の手順でアプリを正常にラップするために使用したAPKTOOL.jarの内容で上書きします。
        3. 内容を新しいManagedAppUtility.jarに再圧縮します。
        4. アプリをラップして、組み込みのAPKTOOLのバグを確認します。
  • ラップされたアプリを実行し、ログ情報を取得します。
    1. grepを使用して、アプリで何が起こっているかを調査します。

      アプリのアクティビティを追跡するには: grep “MDX-Activity”

      アプリのMDXロックを追跡するには: grep “MDX-Locked”

      両方のログをまとめて表示するには: egrep “MDX-Act MDX-Loc”
    2. アプリケーション応答なしエラーがある場合は、ADBを使用してANRトレースをプルします。

  • 「Open in」を使用する場合など、複数のアプリとやり取りするときに問題が発生する場合:
    1. アプリ間で暗号化ポリシーとセキュリティグループ設定が同じであることを確認します。
    2. 別のアプリを試してください。テスト中のいずれかのアプリにバグがある可能性があります。
    3. 関連するすべてのアプリからログを取得します。Secure Hubは、個々のアプリからログをバンドルし、ログをメールで送信できることに注意してください。「マイアプリ」画面から右にスワイプして「サポート」画面に移動します。次に、画面下部の「ヘルプが必要」ボタンをクリックします。

上記で述べたツールに加えて、以下も役立つ場合があります。

  • アプリに関する情報をダンプするためのAAPT。

    aapt dump badging {some.apk}

  • デバイスでのDUMPSYSコマンド。

    adb shell dumpsys 2>&1 | tee {dumpsys.out}

  • クラスを疑似Javaに再コンパイルするためのDEX2JAR。

    dex2jar {some.apk}

    Dual-Dexでラップされたアプリからクラスを変換します。

    apktool d {some.apk} -o {some.dir}

    dex2jar {some.dir}/assets/secondary-1.dex

  • 疑似Javaコードを表示するためのJD-GUI。

  • Dual-Dexでラップされたアプリからアプリクラスを逆コンパイルするためのBAKSMALI。

    • ラップされたAPKを逆コンパイルします。

      apktool d {some.apk} -o {some.dir}

    • 上記の呼び出しから逆コンパイルされないアプリのクラスを逆コンパイルします。

      baksmali {some.dir}/assets/secondary-1.dex -o {some.dir}/smali

Androidアプリのベストプラクティス