2011年3月1日火曜日

Android Application Licensing

■ まとめ

・ Android SDK and AVD を起動し、"Market Licensing" コンポーネントをダウンロードする
・ LVL は library project として使うのがおすすめ
・ 公開者ページの「プロフィールの編集」でライセンス用公開鍵を取得
・ ライセンスの許可・不許可の基準は Policy で設定
・ デフォルトでは ServerManagedPolicy と StrictPolicy が用意されている
・ ServerManagedPolicy はライセンスチェックのレスポンスを SharedPreferences に保存
・ 保存時には難読化ツール (Obfuscator が必要)
・ デフォルトでは AES暗号化を使った AESObfuscator が用意されている
・ AESObfuscator を使うには、20 個のランダムな SALT, パッケージ名, デバイスID が必要
・ ライセンスチェックを行うのが LicenseChecker
・ ライセンスチェック結果を受け取る callback が LicenseCheckerCallback

・ ともかく、Market Licensing コンポーネントに含まれる LVL サンプルアプリケーションをみるのがわかりやすい

----------

Application Licensing

・Android Market を通して公開する有料アプリケーション用のライセンスサービス
・アプリケーションの実行時にユーザーのライセンス状態を取得して許可・非許可を判定する

・アプリケーションごとにフレキシブルなライセンスポリシーを適用できる
・Android Market から得られるライセンス状態に応じてカスタマイズした制限をアプリに持たせられる
・“特定の回数だけ有効”や“特定の期間だけ有効”などが実現できる
・特定のデバイスに対してアプリケーションを制限するなど、他の制限を加えることができる

・アプリケーションへのアクセスをコントロールするという意味で、ライセンスサービスはセキュア
・アプリケーションがライセンス状態をチェックするとき、Market サーバーは公開者アカウントに関連した一意のキーペアを使ってライセンス状態を応答する
・アプリケーションはコンパイルした .apk ファイルに公開鍵を保存

・Android Market を通して公開しているアプリケーションであれば Android Market ライセンスサービスを利用可能
・特別なアカウントや登録は必要ない
・API level 3以上のアプリケーションであればライセンスを追加できる

・アプリケーションへのライセンスの追加を助けるために、Android SDK ではアプリケーションプロジェクトに含むことができるライブラリ集である License Verification Library (LVL) を提供している
・License Verification Library (LVL) は Android Market クライアントとライセンスサービス間のライセンスに関連したやりとり全てをハンドルする
・LVL によって、アプリケーションはライブラリをチェックするメソッドを呼び、ステータスを受け取るコールバックを実装するだけで、現在のユーザーに対する自身のライセンス状態を決定できる



Overview



・Android Market Licensing はネットワークベースのサービス
・Android-powered デバイス上のアプリケーションは、現在のデバイスユーザーがライセンスされているかどうか決定するために、信頼されたライセンスサーバーにクエリーを投げることができる
・サーバーのレンスポンスを受信した後、アプリケーションはアプリケーションの必要な機能を許可・不許可にできる
・ライセンスサーバーは現在のユーザーのライセンス状態を提供する
・アプリケーション自身はサーバーへのクエリーとアプリケーションへの条件付きアクセスに責任をもつ

Application, Android Market client, and server

・ライセンスサービスは、与えられたユーザーが与えられたアプリケーションに対してライセンスされているかどうか決定する Android Market サーバーの機能に基づいている
・ユーザーがアプリケーションを購入したことが記録されている、もしくはアプリケーションが無料で提供されている場合、サーバーは”ユーザーはライセンスされている”と判断する
・適切にユーザーを識別しライセンス状態を決定するために、サーバーはアプリケーションとユーザーの情報を要求する
・アプリケーションと Android Market クライアントが一緒に働いて、サーバーに渡す情報を組み立てる

・ライセンスサービスでは、アプリケーションはライセンスサーバーに直接クエリーを投げることはない
・アプリケーションは Android Market クライアントにリモート IPC を通してライセンスリクエストを投げることでライセンスチェックを開始する


ライセンスリクエストでは:

 ・アプリケーションはパッケージ名、サーバーからのレスポンスを認証するのに使用するワンパス、非同期に返されるレスポンス用のコールバックを提供する
 ・Android Market クライアントはアプリケーションよりも多くのパーミッションが許可されているため、デバイスの優先的 Google アカウントユーザー名、IMSI などのユーザーおよびデバイスの必要な情報を収集でき、アプリケーションにとってかわりサーバーへライセンスチェックリクエストを送る
 ・サーバーは全ての利用可能な情報を使ってリクエストを評価し、ユーザーの識別子を十分なコンフィデンスレベルに確立しようとする。そして、サーバーはユーザーの識別子をアプリケーションの購入記録とチェックし、Android Market クライアントが IPC コールバックを使ってアプリケーションに返すためのライセンスレスポンスを返す

ライセンスチェックの間、アプリケーションはいかなるネットワーク接続の管理およびライセンスに関連した Android platfrom のAPIを使用することはない


License responses secured through public key cryptography

・各ライセンスクエリーの整合性を確保するために、サーバーはライセンスレスポンスデータを、サーバーとアプリケーションの公開者間で独占的に共有される RSA キーペアを使って書き込む

・ライセンスサービスは各公開者アカウントに対して単一のライセンスキーペアを生成し、その公開鍵はアカウントのプロフィールページで見ることができる
・公開者はその公開鍵をコピーし、アプリケーションのソースコードに組み込み、コンパイルし、.apk をマーケットを通して公開する
・サーバーは秘密鍵を内部に保持し、あるアカウントの公開されたアプリケーションに対してライセンスレスポンスを署名するのに使う

・アプリケーションが署名されたレスポンスを受け取る際、組み込まれた公開鍵を使ってデータを認証する
・ライセンスサービスで公開鍵暗号化を使うことで、レスポンスの改ざんや偽装をアプリケーションで検出することができる


Use of licensing in your application

・ライセンスをアプリケーションで使うには、以下をアプリケーションに追加する
  ・ライセンスチェックリクエストを開始するコード
  ・レスポンス受け取り時に処理するコード

・いつ、どの程度の頻度でアプリケーションのライセンスをチェックするか選択できる
・レンスポンスの処理、署名されたレスポンスデータの認証、アクセスコントロールの強制を行うことができる

・ライセンスのサポートを追加するプロセスを簡単化するために、Licensing Verification Library をダウンロードし統合する
・統合は簡単

・LVLの統合が完了した際には、公開者サイトで提供されているテスト環境を使い、サーバーのレスポンスに対するアプリケーションの処理をテストできる

・最後に、アプリケーションの .apk を通常の方法でマーケットに公開する
・Android Market が提供している copy-protection をすでに使っている場合、ライセンスを使用するアプリケーションからそれを外すことができる


Licensing Verification Library simplifies implementation

・Android SDK には License Verification Library (LVL) が含まれる
・LVL をダウンロードし、アプリケーションのライセンス実装の基礎として使うことができる
・LVL はアプリケーションにライセンスを追加するプロセスを劇的に簡素化する
・LVL はよりセキュアで堅牢な実装を確認する助けになる
・LVL はライセンスクエリーの標準的操作(ライセンスリクエストの開始やレスポンスの認証など)のほとんどを処理する内部クラスを提供する
・ライセンスポリシーの定義と、アプリケーションが必要とするアクセス管理のカスタムコードを簡単にプラグインするキーインタフェースを提供する


キー LVL インタフェースは:

 ・Policy - サーバーから受け取ったレスポンスや、他の利用可能なデータ(例えば、自分のアプリケーションに関連するバックエンドサーバーなどからのデータ)に基づいて、アプリケーションへのアクセスを許可するかどうかを実装する。実装では、ライセンスレスポンスのいくつかのフィールドを評価し、必要であれば他の制限を適用することができる。実装ではライセンスチェックの処理を管理することで、ネットワークエラーなどのエラーを確認することができる。

 ・LicenseCheckerCallback - 実装では、Policy によるライセンスレスポンスの処理結果に基づいて、アプリケーションへのアクセスを管理する。また、実装では必要なすべての方法でアクセスを管理できる。ライセンス結果をUIで表示したり、ライセンスされていない場合にアプリケーションの購入へユーザーを誘導したりすることが可能。

Policy を始める手助けとして、LVL は 2つの完全な Policy 実装を提供する。変更しないで使ったり、必要に応じて適用することができる。

 ・ServerManagedPolicy - ライセンスサーバーによって提供される設定を使う柔軟性のある Policy。レスポンスのキャッシュを管理し、デバイスがオフラインの間(例えば、ユーザーが飛行機のなかにいるときなど)もアプリケーションにアクセスするための設定

 ・StrictPolicy - 制限された Policy。レスポンスデータはキャッシュせず、サーバーがライセンスレスポンスを返したとき”のみ”アプリケーションへのアクセスを許可する

・LVL は Android SDK のコンポーネントとしてダウンロードできる
・コンポーネントには、LVL 自身とサンプルアプリケーションの両方が含まれる
・サンプルアプリケーションでは、どのようにライブラリをアプリケーションに統合し、どのようにレスポンスデータ、UI、エラー状態を管理するかを示している

・LVL ソースは Android library project として提供されている。つまり、1つのライブラリソースをメンテナンスし、それを複数のアプリケーションで共有することができる
・完全なテスト環境は SDK を通して利用できる
・実際のデバイスを持っていなくても、アプリケーションを公開する前にライセンス実装をテストできる


Requirements and limitations

・Android Market Licensing は Android Market を通して公開されたアプリケーションにライセンスコントロールを適用するようにデザインされている
・Android Market 以外から公開されたアプリケーションや、Android Market クライアントが提供されていないデバイス上でのアプリケーションでは使用できない

・アプリケーションにライセンスを実装する際に注意すること:

 ・Android Market を通して公開された有料のアプリケーションのみ利用できる
 ・Android Market クライアントがインストールされていて、Android 1.5 (API Level 3)以上のデバイス上のアプリケーションでのみ利用できる
 ・完全なライセンスチェックのために、ライセンスサーバーはネットワークを通してアクセス可能でなければならない。ライセンスのキャッシュ化を実装すれば、ネットワーク接続が無い場合のアクセスを管理できる
 ・アプリケーションのライセンスコントロールのセキュリティは、最終的には実装自身のデザインに依存する。このサービスはあなたが確実にライセンスをチェックできるようにする building blocks を提供するが、実際の施行とライセンスの処理はあなたのコントロールの要因である
 ・ライセンスの追加は、Android Market が提供されていないデバイスで実行されているアプリケーションにはなんの影響も与えない
 ・無料アプリは全てのユーザーにライセンスを与えていると解釈できるので、ライセンスは現状では有料アプリのみ使用できる
 ・アプリケーションがすでに無料で公開されている場合、ライセンスを使用した新しいバージョンをアップロードすることはできない


Replace for copy protection

・Android Market Licensing はアプリケーションへのアクセスをコントロールする柔軟でセキュアなメカニズムである
・これは Android Market で提供されている copy-protection メカニズムを効果的に置き換える

 ・これまでの copy-protection メカニズムでの制限は、セキュアな内部ストレージ環境が提供されている互換性のあるデバイスにインストールされた場合のみ使えることである。例えば、copy-protected application は root アクセスが提供されるデバイスへは Market からダウンロードできないし、デバイスの SD カードにインストールすることもできない
 ・Android Market ライセンスを使って、ホストデバイスの特徴によってアクセスが制限されない license-based モデルに移行できる。代わりに Android Market の公開者アカウントと、定義したライセンスポリシーによって制限される。アプリケーションは互換性のあるデバイスの SD カードを含む全てのストレージにインストールし、コントロールできる。

・完全に全ての不正使用を防止するライセンスメカニズムは無いが、ライセンスサービスは、lock もしくは unlock で Android 1.5 以上の互換性のある全ての端末に対して、通常利用のほとんどのタイプへのアクセスコントロールを提供する

--------------

# ここからが実装について

Setting Up a Publisher Account



・Android Market licensing を使うには、Android Market の公開者アカウントが必要

・公開者アカウントを持っていない場合、あなたの Google account を使って新しく開発者登録をし、term of service に同意する必要がある
・一度登録すると、好きなときにアプリケーションをアップロードし、ライセンス実装のデバッグとテストを開始できる
・Android Market でのアプリケーション公開のより詳しい情報は Publishing Your Applications にある

・Android Market developer として登録し、公開者アカウントを設定するには、Android Market publisher site へ行く:

 http://market.android.com/publish

・すでに Android Market の開発者アカウントを持っている場合、ライセンスの設定に既存のアカウントを使う
・ライセンスをサポートするのに新しいアカウントを登録する”必要はない”(特に、既に公開しているアプリケーションにライセンスサポートを追加する場合、新しいアカウントで登録することは推奨しない)
・全てのケースにおいて、公開されているアプリケーションがある場合、そのアプリケーションを公開したアカウントを通してアプリケーションのライセンスを管理する

・一度公開者アカウントを設定すれば、次のことにアカウントを使用できる

 ・ライセンスの公開鍵を取得する
 ・アプリケーションの公開前にライセンス実装のデバッグとテストを行う
 ・ライセンスサポートを追加したアプリケーションを公開する


administrative settings for licensing

・一度公開者アカウントに署名すると、いくつかの管理者コントロールを設定することができる
・コントロールは Edit Profile ページで利用可能

・"Licensing(ライセンス)" パネルでは次のコントロールが可能

 ・メールアドレスによって識別される複数の "test accounts" を設定する。ライセンスサーバーはデバイスもしくはエミュレータでテストアカウントにサインインしているユーザーに対して、ライセンスチェックの送信と静的なテスト応答の受信を許可する
 ・アカウントのライセンス用公開鍵を取得する。アプリケーションにライセンスを実装する際、アプケーション内に公開鍵の文字列をコピーしなければならない
 ・サーバーが送信する静的なテスト応答を設定する。公開者アカウントもしくはテストアカウントからアプリケーションに対するライセンスチェックをサーバーが受信さい、このテスト応答が公開者アカウントへ送られる。



テストアカウントと静的テスト応答がどのように働くかのより詳しい情報は Setting Up a Testing Environment を参照する


Setting Up the Development Environment



Android Market の公開者アカウントの設定が完了したら、次のステップではライセンスの開発環境を設定する

ライセンスの環境を設定するには、次のタスクを行う:

1. 最新版の SDK をダウンロードする(まだ行なっていない場合)
2. 開発用のランタイム環境を設定する
3. SDK に Market Licensing コンポーネントをダウンロードする
4. Licensing Verification Library を設定する
5. LVL ライブラリプロジェクトを自分のアプリケーションに含める

これらのタスクが完了したら、「LVL を自分のアプリケーションに統合する」 を始めることができる


Downloading the latest SDK

・インストール手順など、全ての情報については Android SDK を参照する
・既にインストールしている場合、SDK tools と ADT Plugin が最新版かどうかアップデートを確認する


Setting up the runtime environment

・ライセンスのチェックにおいて、アプリケーションは直接ライセンスサーバーにコンタクトせず、Android Market によって提供されるサービスにバインドし、ライセンスチェックリクエストを開始する
・Android Market サービスがライセンスサーバーと直接やりとりし、最後にアプリケーションにレスポンスを返す
・アプリケーションのライセンスのデバッグとテストを行うには、アプリケーションがライセンスチェックリクエストをライセンスサーバーに送れるように、必要に応じて Android Market サービスを含むランタイム環境を設定する必要がある

2つのタイプのランタイム環境を使うことができる:

 ・Android Market アプリケーションを含む Android-powered デバイス
 ・Google APIs Add-on, API level 8 (release 2) 以上が走っている Android エミュレータ


Running on a device

Android-powered デバイスをライセンスのデバッグとテストのランタイム環境として利用できる

以下のデバイスを使用しなければならない:

 ・Android 1.5 (API level 3) 以上の標準バージョンプラットフォームが走っている
 ・Android Market クライアントアプリケーションがプリインストールされているシステムイメージが走っている

Android Market がプリインストールされていないシステムイメージでは、アプリケーションは Android Market ライセンスサーバーとやりとりすることができない

Android アプリケーション開発用にデバイスを設定するための一般的な情報は Developing on a Device を参照する


Running on an Android emulator

Android エミュレータをライセンスのデバッグとテストのためのランタイム環境として使うことができる

Android SDK として提供される標準の Android プラットフォームには Android Market は”含まれていない”ため、SDK repository から Google APIs Add-On platform, API Level 8 をダウンロードする必要がある。
add-on のダウンロード後、そのシステムイメージを使った AVD 設定を作成する必要がある

Google APIs Add-On は Android Market client の全てを含むわけではないが、以下を提供する :

 ・ILicensingService リモートインタフェースを実装した Android Market バックグラウンドサービスを提供する。そのため、アプリケーションはネットワークを通してライセンスサーバーにライセンスチェックを送信できる

 ・AVD 上に Google アカウントを追加し、公開者アカウントもしくはテストアカウントの資格を使ってサインインするための基本的なアカウントサービスのセット。公開者アカウントもしくはテストアカウントでサインインすることで、アプリケーションを公開せずにデバッグとテストを行うことができる。より詳しい情報は Signing in to an authorized account を参照する

SDK repository ではいくつかの add-on のバージョンが利用可能になっているが、Google APIs Add-On, API 8 (release 2) 以上のバージョンだけが必要な Android Market サービスを含む
つまり、Google APIs Add-On API 7 以下は、エミュレータ上での開発用ランタイム環境として使うことができない



アプリケーションにライセンスを追加するためのエミュレータの設定として、以下のステップを行う

 1. Android SDK and AVD Manager を起動する
 2. Available Packages パネルで、SDK repository から "Google APIs (Google Inc.) - API Level 8"(or higher) の SDK コンポーネントを選択しダウンロードする
 3. Android SDK and AVD Manager の Virtual Devices パネルで、New をクリックし、新しい AVD の詳細設定ダイアログを開く
 4. ダイアログが表示されたら、Name: に名前を設定し、Target: に "Google APIs (Google Inc.) - API Level 8"(or higher) を選択する。必要に応じてその他の項目を設定し、Create AVD をクリックする

AVD に不慣れだったり、使い方がわからない場合は Managing Virtual Devices を参照する


Updating your project configuration

ランタイム環境の設定後(実際のデバイスでもエミュレータでも)アプリケーションプロジェクトのアップデートもしくはスクリプトのビルド(必要に応じて)を行なって、ライセンスを使った .apk ファイルがその環境にデプロイされているか確認する
特に、Eclipse で開発している場合、Run/Debug 設定が適切なデバイスもしくは AVD をターゲットにしているか確認する

すでに標準の Android 1.5 (API level 3) 以上に対してコンパイルするよう設定されているプロジェクトでは、アプリケーションのビルド設定の変更はない


Downloading the LVL

・The License Verification Library (LVL) は、アプリケーションへのライセンス追加に必要な仕事を劇的に簡単化するヘルパークラスのコレクションである
・全てのケースで、LVL をダウンロードしライセンス実装の基礎として使用することを推奨する

・LVL は Android SDK のコンポーネントとしてダウンロード可能
・コンポーネントには以下が含まれる:
  ・Android library project 内に保存される LVL sources
  ・"sample" と呼ばれるサンプルアプリケーション。LVL library project に依存する。アプリケーションがライセンスのチェックと強制にどのようにライブラリのヘルパークラスを使用しているかを説明する

・開発環境に LVL コンポーネントをダウンロードするには、Android SDK and AVD Manager を使う
・Android SDK and AVD を起動し、"Market Licensing" コンポーネントを選択し、ダウンロードする



・ダウンロードが完了すると、Android SDK and AVD Manager は LVL library project と アプリケーションサンプルを以下のディレクトリにインストールする

<sdk>/market_licensing/library/ (the LVL library project)
<sdk>/market_licensing/sample/ (the example application)

SDK にコンポーネントをダウンロードする方法がよくわからない場合は Adding SDK Components ドキュメントを参照する


Setting Up the Licensing Verfication Library

・LVL のダウンロード後、Android library project として、もしくは library sources を直接既存のアプリケーションパッケージにコピー(もしくはインポート)のいずれかの方法で、LVL を開発環境にセットアップする必要がある
・一般的には、LVL を library project として使用することを推奨する。なぜなら、ライセンスコードを複数のアプリケーションで再利用でき、時間とともにより簡単にメンテナンスできるため
・LVL は個別にコンパイルして、.jar ファイルとしてアプリケーションに追加するようにはデザインされていない


Moving the library sources to a new location

・LVL sources を拡張するためにカスタマイズするので、library sources (<sdk>/market_licensing/library/ にある) を SDK の外のワーキングディレクトリへ "移動もしくはコピーする" 必要がある
・新しい場所に配置された sources をワーキングセットとして使用すべき
・source-code management system を使っている場合、SDK のデフォルトロケーションではなく、ワーキングロケーション内の sources を track に追加する

・library sources の移動は重要。なぜなら、後で Market licensing package がアップデートされた際に、SDK は新しいファイルを古いファイルと同じ場所にインストールするため
・working library files を安全な場所に移動することで、不注意で新しいバージョンの LVL をダウンロードして、sources に対して行なった作業が上書きされるのを防ぐことができる


Creating the LVL as a library project

・LVL の推奨される使用方法は、LVL を新しい Android library project として設定すること
・library project は開発プロジェクトのタイプの1つで、共有するための Android ソースコードとリソースを保持する
・他の Android application projects は library project を参照でき、ビルド時にそれらのコンパイルソースを .apk ファイルに含める
・ライセンスの場合、これはライセンス開発のほとんどを1度 library project に対して行うことで、library sources を含む複数のアプリケーションプロジェクトに反映されることを意味する
・この方法によって、全てのプロジェクトに対して、ライセンスの単一実装を簡単にメンテナンスできる

・LVL は library project として設定された状態で提供されている - つまり、ダウンロードするだけで、直ぐに使える状態になっている
・Eclipse で ADT を使って開発している場合、新しいアプリケーションプロジェクトを作成するのと同じ方法で、LVL を開発プロジェクトとしてワークスペースに追加する必要がある

 1. New Project Wizard を使って新しいプロジェクトを既存のソースから作成する。LVL の library ディレクトリ (library の Android Manifest.xml ファイルが含まれているディレクトリ) をプロジェクトの root として選択する
 2. プロジェクト名は自由に変更できる
 3. library のビルドターゲットとして Android 1.5(API level 3) 以上を選択する

・作成時、プロジェクトは default.properties ファイルで、library project として事前に定義されているので、さらなる設定は必要ない
・アプリケーションプロジェクトの作成や、Eclipse での library project の使用方法についてのより詳しい情報は Managing Projects from Eclipse with ADT を参照する


Copying the LVL sources to your application

・library project を LVL として追加する代替として、library sources を直接アプリケーションにコピーする方法がある
・LVL の library/src/com ディレクトリをアプリケーションの src/ ディレクトリにコピー(もしくはインポート)する

・LVL sources を直接アプリケーションに追加した場合、次のセクションをスキップし、ライブラリを使用するところから開始できる


Including the LVL library project sources in your application

・LVL sources を library project として使用したい場合、アプリケーションプロジェクトプロパティに LVL library project への参照を追加する必要がある
・これによって、ビルドツールがコンパイル時にアプリケーション内に LVL library project sources を含むようになる
・library project へのリファレンスを追加するプロセスは開発環境に依存する

・Eclipse で ADT を使って開発している場合、すでに library project がワークスペースに追加されている必要がある
・アプリケーションのプロジェクトプロパティウィンドウを開いて、"Android" プロパティを選択し、Add をクリックして LVL library project (com_android_vending_licensing) を選択して OK をクリックする。より詳しい情報は Managing Projects from Eclipse with ADT を参照する。



SDK command-line tools を使って開発している場合、アプリケーションプロジェクトを含むディレクトリまで移動し、default.properties ファイルを開き、android.library.reference.<n&gt と library へのパスを指定する次の1行を追加する


android.library.reference.1=path/to/library_project


もしくは、次のコマンドを使って library project への参照を含むようにプロジェクトプロパティをアップデートすることもできる

android update lib-project
--target \
--path path/to/my/app_project \
--library path/to/my/library_project


library project を使用するためのより詳しい情報は Managing Projects from the Command Line を参照する


Integrating the LVL with Your Application



公開者アカウントと開発環境の設定が完了したら、LVL をアプリケーションに統合する作業を始められる

アプリケーションコードに LVL を統合するには次のタスクを行う:

 1. アプリケーションのマニフェストにライセンス用のパーミッションを追加する
 2. Policy を実装する - LVL が提供する全実装か、自身で作成するか、のどちらか1つを選択できる
 3. Policy がライセンスレスポンスデータをキャッシュする場合、難読化ツールを実装する
 4. アプリケーションのメイン Activity にライセンスをチェックするコードを追加する
 5. DeviceLimiter を実装する (オプション、ほとんどのアプリケーションに対しては推奨しない)

統合が完了し、アプリケーションのコンパイルに成功したら、テストを開始することができる
テストについては Setting Up the Test Environment を参照する

LVL に含まれる source files のフルセットの概要は Summary of LVL Classes and Interfaces を参照する


Adding the licensing permission to your AndroidManifest.xml

・サーバーにライセンスチェックを送信するために Android Market アプリケーションを利用するには、適切なパーミッション com.android.vending.CHECK_LICENSE をリクエストしなければならない
・もしアプリケーションがライセンスパーミッションを宣言していないのにライセンスチェックを開始しようとした場合、LVL はセキュリティ例外を投げる

・アプリケーションにライセンスパーミッションをリクエストするには <manifest> の子要素として <uses-permission> タグを次のように宣言する

<uses-permission android:name="com.android.vending.CHECK_LICENSE">

例えば、LVL サンプルアプリケーションでは次のように宣言されている:


<?xml version="1.0" encoding="utf-8"?>

<manifest xmlns:android="http://schemas.android.com/apk/res/android" ...">
<!-- Devices >= 3 have version of Android Market that supports licensing. -->
<uses-sdk android:minSdkVersion="3" />
<!-- Required permission to check licensing. -->
<uses-permission android:name="com.android.vending.CHECK_LICENSE" />
...
</manifest>


注意点: 現状では、CHECK_LICENSE パーミッションを LVL library project のマニフェストに宣言することができない。なぜなら、SDK Tools は依存するアプリケーションのマニフェストをマージすることができないから。代わりに、依存するアプリケーションのマニフェストそれぞれでパーミッションを宣言しなければならない


Implementing a Policy

・Android Market ライセンスサービス自身は、与えられたユーザーに対してアプリケーションへのアクセスを付与するライセンスを与えるかどうか決定しない
・むしろ、その責任はアプリケーションで提供する Policy の実装に任されている

・Policy は LVL によって宣言されるインターフェース
・ライセンスチェックの結果に基づいてユーザーのアクセスを許可・不許可するためのアプリケーションのロジックを保持する
・アプリケーションは Policy の実装を提供”しなければならない”

・Policy インターフェースは2つのメソッド allowAccess()processServerResponse() を宣言する
・これらはライセンスサーバーからのレスポンスを処理する際に LicenseChekcer インスタンスによって呼ばる
LicenseResponse と呼ばれる enum も宣言する
・これは processServerResponse() の呼び出しに渡されるライセンスレスポンス値を指定する

  ・processServerResponse() によって、ライセンスサーバーから受け取った生のレスポンスデータを処理し、アクセスを付加するかどうか決定できる
   典型的な実装では、ライセンスレスポンスからのいくつか、もしくは全てのフィールドを取り出し、SharedPreference ストレージなどを通して、永続的ストアにローカルにデータを保存する。これは、アプリケーションの呼び出しとデバイスの電源サイクルに渡ってデータにアクセス可能か確かめるため。例えば、Policy は最後に成功したライセンスチェックのタイムスタンプ、リトライ回数、ライセンス検証期間、その他の似たような情報を、アプリケーションの起動ごとに毎回再設定するよりも、永続的ストアでメンテナンスする
   レスポンスデータをローカル保存する際、Policy はデータが難読化されていることを確認しなければならない (Implementing an Obfuscator を参照)

  ・allowAccess() は(ライセンスサーバーやキャッシュからの)利用可能なライセンスレスポンスデータや、他のアプリケーション固有の情報に基づいて、アプリケーションへのユーザーアクセスを付与するかどうか決定する
   例えば、allowAccess() の実装は、使用状態や、バックエンドサーバーから取得した他のデータなどの付加的な基準を取ることができる
   全てのケースにおいて、allowAccess() の実装は、ライセンスサーバーによる決定としてユーザーがアプリケーションの使用に対してライセンスられている場合、もしくは一時的にネットワークやシステムに問題があってライセンスチェックを完了できない場合のみ true を返すべきである
   このようなケースでは、リトライレスポンスの回数と前回の許可アクセスを、次のライセンスチェックが完了するまでメンテナンスする

アプリケーションにライセンスを追加する処理を簡素化し、Policy がどのようにデザインされるべきかを説明するために、LVL には2つの完全な Policy 実装が含まれている
これは、変更なしで使うことも、必要に応じて適応することもできる

 ・ServerManagedPolicy - サーバーが提供する設定を使った柔軟な Policy。さまざまなネットワーク状態に対してアクセスを管理するためにレスポンスをキャッシュする
 ・StrictPolicy - いかなるレスポンスデータもキャッシュしない。ライセンスされたレスポンスをサーバーが返したとき”のみ”許可する

・ほとんどのアプリケーションに対しては、ServerManagedPolicy を使うことを強く推奨する
・ServerManagedPolicy は LVL のデフォルトで、 LVL サンプルアプリケーションでも使われている


Guidelines for custom policies

# ちょっとパス


ServerManagedPolicy

・LVL には ServerManagedPolicy と呼ばれる、完全な、推奨される Policy インタフェースの実装が含まれている
・実装は LVL クラスと統合され、ライブラリでデフォルト Policy を提供する

・ServerManagedPolicy はライセンスとレスポンスのリトライに関する全ての処理を提供する
・全てのレスポンスデータをローカルの SharedPreference ファイルにキャッシュし、アプリケーションの難読化実装を使って難読化する
・ライセンスレスポンスデータの確認はセキュアで、デバイスの電源サイクルに渡って永続化される。
・ServerManagedPolicy はインタフェースメソッド processServerResponse()allowAccess() の具体的な実装を提供する。さらに、ライセンスレスポンスを管理するためのサポートメソッドとタイプのセットも提供する

・ServerManagedPolicy の重要な機能は、アプリケーションの払い戻し期間やさまざまなネットワークエラー状態に渡ってライセンスを管理するための基本として、サーバーが提供する設定を使用すること
・ライセンスチェックのためにアプリケーションが Android Market server にコンタクトする際、サーバーはライセンスレスポンスタイプを特定するための付加フィールドにいくつかの設定を key-value ペアとして追加する
・例えば、サーバーはアプリケーションのライセンス検証期間、再試行の猶予期間、許可される最大リトライ回数、その他の値として推奨される値を提供する
・ServerManagedPolicy は自身の processServerResponse() メソッドでライセンスレスポンスから値を取り出し、allowAccess() メソッドでそれらをチェックする
・ServerManagedPolicy が使う server-provided 設定のリストについては Server Response Extras を参照する

・利便性、ベストパフォーマンス、Android Market サーバーのライセンス設定を利用する利益から、ライセンスポリシーとして ServerManagedPolicy を使うことを強く推奨する

・ライセンスレスポンスデータが SharedPreference にローカルに保存されることを憂慮するならば、強力な難読化アルゴリズムを使うか、ライセンスデータを保存しない厳しい Policy をデザインすることができる
・LVL はこのような Policy の例を含んでいる。詳しくは StrictPolicy を参照する

・ServerManagedPolicy を使うには、単に Activity にインポートし、インスタンスを作成し、インスタンスへの参照を LicenseChecker のコンストラクト時に渡せばいい。詳しくは Instantiate LicenseChecker and LicenseCheckerCallback を参照する


StrictPolicy

# ここもパス


Implementing an Obfuscator

・典型的な Policy の実装では、アプリケーションの呼び出しとデバイスの電源サイクルに渡ってアクセスできるように、ライセンスレスポンスデータを永続的ストアに保存することが必要
・例えば、Policy は最後にライセンスチェックに成功したタイムスタンプ、リトライ回数、ライセンス継承期間、その他の似た情報を、アプリケーションの起動時に毎回設定するのではなく、永続的ストアでメンテナンスする
・LVL に含まれるデフォルトの Policy である ServerManagedPolicy は、永続化のためにライセンスレスポンスデータを SharedPreferences インスタンスに保存する

・Policy は保存されたライセンスレスポンスデータを使ってアプリケーションへの許可・不許可を決定するので、保存されたデータがセキュアで、デバイスの root ユーザーによって再利用や操作ができないことを確認する必要がある
・特に、Policy はアプリケーションとデバイスに対してユニークなキーを使って、保存前に常にデータを難読化しなければならない。
難読化に使うキーはアプリケーションとデバイス両方で固有であることが重要、なぜなら、アプリケーションとデバイスの間で難読化されたデータが共有されるのを防止するため

・LVL はセキュリティで保護された永続化マナーでライセンスレスポンスデータを保存することでアプリケーションを支援する
・まず、アプリケーションが保存するデータに難読化アルゴリズムを供給できるようにするための難読化インタフェースを提供する
・それを構築したら、LVL はヘルパークラスの PreferenceObfuscator を提供する
・これは、アプリケーションの難読化クラスの呼び出し、難読化したデータの SharedPreferences インスタンスへの読み込み、書き込みというほとんどの仕事を処理する

・LVL はデータの難読化に AES 暗号を使った AESObfuscator という名前の完全な難読化実装を提供する
・アプリケーションに AESObfuscator を使うことができる。これは、変更なしで使うこともできるし、必要に応じて適用することも可能


AESObfuscator

・LVL は AESObfuscator と呼ばれる難読化インタフェースの完全で、推奨される実装をふくむ
・実装は LVL ライブラリのデフォルト難読化ツールとしてサンプルアプリケーションに組み込まれている


・AESObfuscator は暗号化・復号化に AES を使ってストレージに読み書きするデータのセキュアな難読化を提供する
・難読化は暗号化の種としてアプリケーションによって提供される3つのフィールドを利用する

 1. A salt - 各難読化に対して使われるランダムなバイト配列
 2. アプリケーション固有の文字列、典型的にはアプリケーションのパッケージ名
 3. デバイス固有の文字列、利用可能な多くのデバイス固有なソースから求める、ユニークになるよう作成される

・AESObfuscator を使うには、まず Activity にインポートする
・salt bytes を保持するための private static final array を宣言し、20 個のランダムに生成したバイトで初期化する


...
// Generate 20 random bytes, and put them here.
private static final byte[] SALT = new byte[] {
-46, 65, 30, -128, -103, -57, 74, -64, 51, 88, -95,
-45, 77, -117, -36, -113, -11, 32, -64, 89
};
...


・次に、デバイス識別子を保持する変数を宣言し、必要な方法を使ってその値を生成する
・例えば、LVL に含まれるサンプルアプリケーションでは、システム設定の android.Settings.Secure.ANDROID_ID をデバイスの識別子として参照している

・使用する API に依存して、デバイス固有の情報を得るためにアプリケーションは付加的なパーミッションをリクエストする必要がある
・例えば、デバイスの IMEI やそれに関連するデータを得るには、TelephonyManager を参照する必要があり、アプリケーションは android.permission.READ_PHONE_STATE パーミッションをリクエストする必要がある

・難読化で使うためのデバイス固有の情報を得る”ためだけに”新しいパーミッションをリクエストする前に、それがどのようにアプリケーションや Android Market 上でのフィルタリングに影響を与えるか考える(なぜなら、いくつかのパーミッションは SDK ビルドツールに関連した <uses-feature> を追加する要因になる)

・最後に、AESObfuscator のインスタンスを生成し、salt、アプリケーション識別子、デバイス識別子を送る
・Policy と LicenseChecker の生成時に、インスタンスを直接生成することができる


...
// Construct the LicenseChecker with a Policy.
mChecker = new LicenseChecker(
this, new ServerManagedPolicy(this,
new AESObfuscator(SALT, getPackageName(), deviceId)),
BASE64_PUBLIC_KEY // Your public licensing key.
);
...


完全な例は、LVL サンプルアプリケーションの MainActivity を参照すること


Checking the license from your application's main Activity

・一度アプリケーションへのアクセスを管理する Policy を実装したら、次のステップでアプリケーションへライセンスチェックを追加する
・必要であればライセンスサーバーへのクエリーを開始し、ライセンスレスポンスに基づいてアプリケーションへのアクセスを管理する
・ライセンスチェックの追加とレスポンスの処理の仕事のすべてはあなたのメイン Acitivity ソースファイル内で行われる

ライセンスチェックを追加し、レスポンスを処理するには、次のことを行う必要がある:

 1. imports を追加
 2. private inner class として LicenseCheckerCallback を実装する
 3. LicenseCheckerCallback から UI thread へ post するための Handler を作成する
 4. LicenseCheckerCallback と LicenseChecker を初期化する
 5. checkAccess() を呼んで、ライセンスチェックを開始する
 6. ライセンス用の公開鍵を埋め込む
 7. IPC 接続を閉じるために、LicenseChecker の onDestroy() メソッドを呼ぶ


Overview of license check and response

・ほとんどの場合、アプリケーションのメイン Activity の onCreate() メソッドにライセンスチェックを追加する
・ユーザーがアプリケーションを直接起動したときにライセンスチェックが直ちに呼ばれるということ
・いくつかのケースでは、他の場所にライセンスチェックを追加したほうがいい場合もある
・例えば、あるアプリケーションが別のアプリケーションから Intent 呼出によって開始される複数の Activity を持っている場合、それらの Activity にもライセンスチェックを追加したほうがいい

ライセンスチェックは2つのメインアクションから構成される:

  ・ライセンスチェックを開始するためのメソッドの呼び出し - LVL では、LicenseChecker オブジェクトの checkAccess() メソッドの呼び出し

  ・ライセンスチェックの結果を戻す callback。LVL では、実装した LicenseCheckerCallback インタフェース。インタフェースは allow()dontAllow() の 2つのメソッド宣言する。これらは、ライセンスチェックの結果に基づいて呼ばれる。どんなロジックを使用する場合でも、アプリケーションへのユーザーアクセスを許可・不許可するために、これら 2 つのメソッドは実装する。これらのメソッドはアクセス許可を決定するものではないことに注意する - 決定は Policy 実装が責任をもつ。こられのメソッドは単純にアクセスの許可・不許可時のアプリケーションの振る舞い(とアプリケーションエラー処理)を提供する



このダイアグラムは典型的なライセンスチェックの流れを示している

1. アプリケーションのメイン Activity 内で、LicenseCheckerCallback と LicenseChecker オブジェクトを初期化する。LicenseChecker の生成時に、使用する Policy 実装と公開者アカウントの公開鍵ををライセンスパラメータとして Context に渡す

2. 次に、LicenseChecker オブジェクトの checkAccess() メソッドを呼ぶ。このメソッド実装は有効なライセンスレスポンスが SharedPreference 内にローカルにキャッシュされているか決定するために Policy を呼び出す
  ・有効なライセンスレスポンスがキャッシュされているならば、checkAccess() 実装は allow() を呼び出す
  ・有効なライセンスレスポンスがキャッシュされていないならば、LicenseChecker はライセンスサーバーに送られるライセンスチェックリクエストを初期化する

3. レスポンスを受信したら、LicenseChcker は署名されたライセンスデータを検証しレスポンスのフィールドを抜き出す LicenseValidator を生成する。生成した LicenseValidator を次の評価のためにを Policy に渡す
  ・ライセンスが有効なら、Policy は SharedPreferences にレスポンスをキャッシュし、validator に知らせる。validator は LicenseCheckerCallback オブジェクトの allow() メソッドを呼ぶ
  ・ライセンスが有効でないなら、Policy は validator に LicenseCheckerCallback の dontAllow() メソッド を呼ぶよう知らせる

4. 回復可能なローカルもしくはサーバーのエラーの場合(例えば、ネットワークがリクエストを送ることができないなど)、LicenseChecker は RETRY レスポンスを Policy の processServerResponse() メソッドに渡す

5. アプリケーションエラーの場合(アプリケーションが不正なパッケージ名でライセンスチェックを試みたときなど)、LicenseChecker は LicenseCheckerCallback の applicationError() メソッドにエラーレスポンスを渡す

ライセンスチェックの開始と結果の処理に加え、アプリケーションは Policy の実装を提供する必要があることに注意する。そして、Policy がレスポンスデータを保存する場合(ServerManagerPolicy など) 、難読化実装が必要なことにも注意する。


Add imports

最初に、アプリケーションのメイン Activity のクラスファイルを開き、LVL パッケージの LicenseChecker と LicenseCheckerCallback を import する


import com.android.vending.licensing.LicenseChecker;
import com.android.vending.licensing.LicenseCheckerCallback;


もし LVL が提供する デフォルト Policy の ServerManagedPolicy を使うなら、AESObfuscator と一緒にそれも import する
もしカスタム Policy や Obfuscator の使う場合は、代わりにそれらを import する


import com.android.vending.licensing.ServerManagedPolicy;
import com.android.vending.licensing.AESObfuscator;



Implement LicenseCheckerCallback as a private inner class

・LicenseCheckerCallback は LVL によって提供される、ライセンスチェックの結果を処理するためのインタフェースである
・LVL を使ってライセンスをサポートするには LicenseCheckerCallback とアプリケーションへのアクセスを許可・不許可するための LicenseCheckerCallback のメソッドを実装しなければならない

・レスポンスペイロード、サーバーのレスポンスコード、その他 Policy によって提供される付加的な処理の検証に基づいて作成されるライセンスチェックの結果は、常に LicenseCheckerCallback メソッドのどれか 1 つを呼び出す
・アプリケーションは必要な方法でそれらのメソッドを実装できる
・一般的には、UI状態とアプリケーションへのアクセス管理だけに制限し、メソッドをシンプルに保つのがよい
・もし、バックエンドサーバーへの接続やカスタム制限を適用するなど、ライセンスレスポンスのさらなる処理を追加したい場合は、LicenseCheckerCallback メソッドに入れるよりも、Policy に組み込むことを検討するべき

・ほとんどのケースでは、LicenseCheckerCallback の実装はアプリケーションのメイン Activity クラスの内に private class として宣言する

allow()dontAllow() メソッドの実装は必須である
・まずはじめに、ライセンスの結果をダイアログに表示するような、結果処理の単純な振る舞いを実装する
・これによりアプリケーションを直ぐに起動しデバッグする助けになる
・意図した振る舞いが行われていることを確認したのち、複雑な処理を追加する

dontAllow() で、ライセンスされていないレンスポンスを処理する際の提案 :

  ・"Try again" ダイアログをユーザーに表示する。ダイアログには新しくライセンスチェックを開始するボタンをつける
  ・"Purchase this application" (このアプリを購入する)ダイアログを表示する。ダイアログにはマーケットのアプリケーション詳細ページへのリンクをつける。このようなリンクをつける方法のより詳しい情報は、Using Intents to Launch the Market Application on a Device を参照する
  ・ライセンスされていないために、アプリケーションの機能が制限されていることを Toast で通知する

・LVL サンプルアプリケーションで LicenseCheckerCallback を実装している部分。
ライセンスチェック結果をダイアログで表示している。


private class MyLicenseCheckerCallback implements LicenseCheckerCallback {
public void allow() {
if (isFinishing()) {
// Don't update UI if Activity is finishing.
return;
}
// Should allow user access.
displayResult(getString(R.string.allow));
}

public void dontAllow() {
if (isFinishing()) {
// Don't update UI if Activity is finishing.
return;
}
displayResult(getString(R.string.dont_allow));
// Should not allow access. An app can handle as needed,
// typically by informing the user that the app is not licensed
// and then shutting down the app or limiting the user to a
// restricted set of features.
// In this example, we show a dialog that takes the user to Market.
showDialog(0);
}
}


・加えて、applicationError() メソッドも実装すべき。
・これは LVL がアプリケーションに再試行できないエラーを処理させるために呼ぶ
・このようなエラーのリストは Server Response Codes にある
・必要に応じて任意の方法でメソッドを実装できる
・ほとんどの場合、メソッドはエラーコードをログに吐いて、dontAllow() を呼ぶべき


Create a Handler for posting from LicenseCheckerCallback to the UI thread

・ライセンスチェックの間、LVL は Android Market アプリケーションにリクエストを投げ、マーケットアプリはライセンスサーバーとやりとりする
・LVLは非同期の IPC (Binder を使う) を通してリクエストを投げる。そのため、実際の処理とネットワーク通信はアプリケーションが管理するスレッドでは行われない
・同様に、Android Market アプリケーションは結果を受信し、IPC を通して callback メソッドを呼び出す
・callback メソッドは、アプリケーションのプロセスでのIPCスレッドプールで順番に実行される

・LicenseChecker クラスはアプリケーションと Android Market アプリケーション間の IPC 通信を管理する。これにはリクエスト送信の呼び出しと、レスポンス受信の callback の呼び出しが含まれる
・ライセンスチェック結果の全てを処理するスレッドでは、結果はサーバーから受け取ったレスポンスかタイムアウトエラーのいずれかになる
・処理の最終段階で、LVL はバックグラウンドスレッドから LicenseCheckerCallback メソッドを呼ぶ

つまり、あなたのアプリケーションにとって意味することは :

  1. LicenseCheckerCallback メソッドは、多くの場合、バックグラウンドスレッドから呼ばれる
  2. これらのメソッドは状態をアップデートしたり UI スレッドでの処理を呼んだりできない
   そのため、UI スレッドで Handler を作成し、callback メソッドは Handler に post する

・もし、LicenseCheckerCallback メソッドから UI スレッドをアップデートしたいなら、メイン Activity の onCreate() メソッドで Handler を生成する
・以下の例では、LVL サンプルアプリケーションの LicenseCheckerCallback メソッドでは、 Handler の post() メソッドを通して UI スレッドをアップデートするために displayResult() を呼んでいる


private Handler mHandler;

@Override
public void onCreate(Bundle savedInstanceState) {
...
mHandler = new Handler();
}


・LicenseCheckerCallback メソッド内で、Handler メソッドを使って Runnable もしくは Message オブジェクトを post できる

ライセンス状態を表示するために、LVL サンプルアプリケーションが UI スレッドの Handler に Runnable を post している部分

private void displayResult(final String result) {
mHandler.post(new Runnable() {
public void run() {
mStatusText.setText(result);
setProgressBarIndeterminateVisibility(false);
mCheckLicenseButton.setEnabled(true);
}
});
}



Instantiate LicenseChecker and LicenseCheckerCallback

・メイン Activity の onCreate() メソッドで、LicenseCheckerCallback と LicenseChecker の private インスタンスを生成する
・LicenseChecker のコンストラクタを呼ぶときに、LicenseCheckerCallback インスタンスのリファレンスを渡す必要があるので、最初に LicenseCheckerCallback を生成しなければならない

・LicenseChecker を初期化する際、以下のパラメータを渡す必要がある

  ・アプリケーションの Context
  ・ライセンスチェックに使用する Policy 実装へのリファレンス
   ほとんどの場合、LVL が提供するデフォルトの Policy 実装である ServerManagedPolicy を使うでしょう
  ・公開者アカウントのライセンス用公開鍵を保持している String 変数

・ServerManagedPolicy を使用しているなら、クラスに直接アクセスする必要はない
・次の例のように LicenseChecker のコンストラクタ内で生成することができる
・ServerManagedPolicy のコンストラクタで、新しい Obfuscator インスタンスへのリファレンスが必要なことに注意する

下記の例は Activity クラスの onCreate() メソッドで LicenseChecker と LicenseCheckerCallback の初期化を行なっている

public class MainActivity extends Activity {
...
private LicenseCheckerCallback mLicenseCheckerCallback;
private LicenseChecker mChecker;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
// Construct the LicenseCheckerCallback. The library calls this when done.
mLicenseCheckerCallback = new MyLicenseCheckerCallback();

// Construct the LicenseChecker with a Policy.
mChecker = new LicenseChecker(
this, new ServerManagedPolicy(this,
new AESObfuscator(SALT, getPackageName(), deviceId)),
BASE64_PUBLIC_KEY // Your public licensing key.
);
...
}
}


・有効なライセンスレスポンスがローカルにキャッシュされている場合、LicenseChecker は LicenseCheckerCallback メソッドを UI スレッドからのみ呼ぶことに注意する
・ライセンスチェックがサーバーに送られた場合、ネットワークエラーであっても、callback は常にバックグラウンドスレッドから派生する


Call checkAccess() to initiate the license check

・メイン Activity 内で、LicenseChecker インスタンスの checkAccess() メソッドの呼び出しを追加する
・呼び出しでは、LicenseCheckerCallback インスタンスのリファレンスをパラメータとして渡す
・呼び出しの前になにか特別な UI 効果や状態管理が必要な場合、checkAccess() をラッパーメソッドから呼ぶのが便利である
・例えば、LVL サンプルアプリケーションでは doCheck() ラッパーメソッドから checkAccess() を呼んでいる


@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
// Call a wrapper method that initiates the license check
doCheck();
...
}
...
private void doCheck() {
mCheckLicenseButton.setEnabled(false);
setProgressBarIndeterminateVisibility(true);
mStatusText.setText(R.string.checking_license);
mChecker.checkAccess(mLicenseCheckerCallback);
}



Embed your public key for licensing

・公開者アカウントごとに、Android Market サービスは自動的にライセンス利用を目的とした専用の 2048-bit RSA 公開/秘密 鍵のペアを生成する
・このキーペアは公開者アカウントに対して一意で、そのアカウントで公開されたアプリケーションすべてで共通である
・公開者アカウントに関連しているため、キーペアはアプリケーションの署名(もしくはそれから派生したもの)として使われる鍵とは異なる

・Android Market 公開者サイトはライセンス用公開鍵をサインインした全ての公開者アカウントに対して表示しているが、秘密鍵はセキュアな場所に置かれ全てのユーザーから隠されている
・あなたのアカウントで公開されたアプリケーションに対して、ライセンスチェックがリクエストされたとき、ライセンスサーバーはアカウントキーペアの秘密鍵を使ってライセンスレスポンスに署名する
・LVL がレスポンスを受信すると、アプリケーションから提供された公開鍵を使ってライセンスレスポンスの署名を検証する

・ライセンスをアプリケーションに追加するには、公開者アカウントのライセンス用公開鍵を得て、アプリケーション内にコピーする必要がある

自分のアカウントのライセンス用公開鍵を見つけるには :

  1. Android Market の publisher site に行ってサインインする。ライセンスをつけたいアプリケーションを公開している(もしくは公開したい)アカウントでサインインすること
  2. アカウントのホームページにある "Edit profile" リンクをクリック
  3. Edit Profile page 内の下のほうにある "Licensing" パネルの "Public key" テキストボックスにライセンス用公開鍵が表示されている

・公開鍵をアプリケーションに追加するには、単に鍵の文字列をテキストボックスからアプリケーションの String 変数 BASE64_PUBLIC_KEY へコピー&ペーストすればよい
・コピー時に、きちんと文字列全体を選択しているかどうか確かめる

LVL サンプルアプリケーションの例 :


public class MainActivity extends Activity {
private static final String BASE64_PUBLIC_KEY = "MIIBIjANBgkqhkiG ... "; //truncated for this example
...
}



Call your LicenseChecker's onDestroy() method to close IPC connections

・最後に、アプリケーションの Context が変わる前に LVL をクリーンアップ するため、Activity の onDestroy() から LicenseChecker の onDestroy() メソッドを呼び出す実装を追加する
・この呼び出しによって、LicenseChecker は Android Market アプリケーションの ILicensingService との全てのオープンな IPC 接続を適切にクローズし、サービスとハンドラーへのすべてのローカルリファレンスを削除する

・LicenseChecker の onDestroy() メソッドの呼び出しが失敗すると、アプリケーションのライフサイクルを超えた問題を引き起こす可能性がある
・例えば、ライセンスチェックがアクティブな間にユーザーが画面の向きを変更すると、アプリケーション Context は終了する(android:configChanges を設定していない場合)
・アプリケーションが適切に LicenseChecker の IPC 接続をクローズしないと、アプリケーションはレンポンスを受信したときにクラッシュしてしまう
・同様に、ユーザーがライセンスチェック中にアプリケーションを終了した場合、サービスから切断する LicenseChecker の onDestroy() が適切に呼ばれないと、レンスポンスの受信時にクラッシュしてしまう

LVL サンプルアプリケーションの例 :
(mChecker は LicenseChekcer のインスタンス)

@Override
protected void onDestroy() {
super.onDestroy();
mChecker.onDestroy();
...
}




■ Test 方法

# 次のエントリで、、、


 

0 件のコメント:

コメントを投稿

'},ClipboardSwf:null,Version:'1.5.1'}};dp.SyntaxHighlighter=dp.sh;dp.sh.Toolbar.Commands={ExpandSource:{label:'+ expand source',check:function(highlighter){return highlighter.collapse;},func:function(sender,highlighter) {sender.parentNode.removeChild(sender);highlighter.div.className=highlighter.div.className.replace('collapsed','');}},ViewSource:{label:'view plain',func:function(sender,highlighter) {var code=dp.sh.Utils.FixForBlogger(highlighter.originalCode).replace(/'+code+'');wnd.document.close();}},CopyToClipboard:{label:'copy to clipboard',check:function(){return window.clipboardData!=null||dp.sh.ClipboardSwf!=null;},func:function(sender,highlighter) {var code=dp.sh.Utils.FixForBlogger(highlighter.originalCode).replace(/</g,'<').replace(/>/g,'>').replace(/&/g,'&');if(window.clipboardData) {window.clipboardData.setData('text',code);} else if(dp.sh.ClipboardSwf!=null) {var flashcopier=highlighter.flashCopier;if(flashcopier==null) {flashcopier=document.createElement('div');highlighter.flashCopier=flashcopier;highlighter.div.appendChild(flashcopier);} flashcopier.innerHTML='';} alert('The code is in your clipboard now');}},PrintSource:{label:'print',func:function(sender,highlighter) {var iframe=document.createElement('IFRAME');var doc=null;iframe.style.cssText='position:absolute;width:0px;height:0px;left:-500px;top:-500px;';document.body.appendChild(iframe);doc=iframe.contentWindow.document;dp.sh.Utils.CopyStyles(doc,window.document);doc.write('

'+highlighter.div.innerHTML+'

');doc.close();iframe.contentWindow.focus();iframe.contentWindow.print();alert('Printing...');document.body.removeChild(iframe);}},About:{label:'?',func:function(highlighter) {var wnd=window.open('','_blank','dialog,width=300,height=150,scrollbars=0');var doc=wnd.document;dp.sh.Utils.CopyStyles(doc,window.document);doc.write(dp.sh.Strings.AboutDialog.replace('{V}',dp.sh.Version));doc.close();wnd.focus();}}};dp.sh.Toolbar.Create=function(highlighter) {var div=document.createElement('DIV');div.className='tools';for(var name in dp.sh.Toolbar.Commands) {var cmd=dp.sh.Toolbar.Commands[name];if(cmd.check!=null&&!cmd.check(highlighter)) continue;div.innerHTML+=''+cmd.label+'';} return div;} dp.sh.Toolbar.Command=function(name,sender) {var n=sender;while(n!=null&&n.className.indexOf('dp-highlighter')==-1) n=n.parentNode;if(n!=null) dp.sh.Toolbar.Commands[name].func(sender,n.highlighter);} dp.sh.Utils.CopyStyles=function(destDoc,sourceDoc) {var links=sourceDoc.getElementsByTagName('link');for(var i=0;i');} dp.sh.Utils.FixForBlogger=function(str) {return(dp.sh.isBloggerMode==true)?str.replace(/
|<br\s*\/?>/gi,''):str;} dp.sh.RegexLib={MultiLineCComments:new RegExp('/\\*[\\s\\S]*?\\*/','gm'),SingleLineCComments:new RegExp('//.*$','gm'),SingleLinePerlComments:new RegExp('#.*$','gm'),DoubleQuotedString:new RegExp('"(?:\\.|(\\\\\\")|[^\\""\\n])*"','g'),SingleQuotedString:new RegExp("'(?:\\.|(\\\\\\')|[^\\''\\n])*'",'g')};dp.sh.Match=function(value,index,css) {this.value=value;this.index=index;this.length=value.length;this.css=css;} dp.sh.Highlighter=function() {this.noGutter=false;this.addControls=true;this.collapse=false;this.tabsToSpaces=true;this.wrapColumn=80;this.showColumns=true;} dp.sh.Highlighter.SortCallback=function(m1,m2) {if(m1.indexm2.index) return 1;else {if(m1.lengthm2.length) return 1;} return 0;} dp.sh.Highlighter.prototype.CreateElement=function(name) {var result=document.createElement(name);result.highlighter=this;return result;} dp.sh.Highlighter.prototype.GetMatches=function(regex,css) {var index=0;var match=null;while((match=regex.exec(this.code))!=null) this.matches[this.matches.length]=new dp.sh.Match(match[0],match.index,css);} dp.sh.Highlighter.prototype.AddBit=function(str,css) {if(str==null||str.length==0) return;var span=this.CreateElement('SPAN');str=str.replace(/ /g,' ');str=str.replace(/');if(css!=null) {if((/br/gi).test(str)) {var lines=str.split(' 
');for(var i=0;ic.index)&&(match.index/gi,'\n');var lines=html.split('\n');if(this.addControls==true) this.bar.appendChild(dp.sh.Toolbar.Create(this));if(this.showColumns) {var div=this.CreateElement('div');var columns=this.CreateElement('div');var showEvery=10;var i=1;while(i<=150) {if(i%showEvery==0) {div.innerHTML+=i;i+=(i+'').length;} else {div.innerHTML+='·';i++;}} columns.className='columns';columns.appendChild(div);this.bar.appendChild(columns);} for(var i=0,lineIndex=this.firstLine;i0;i++) {if(Trim(lines[i]).length==0) continue;var matches=regex.exec(lines[i]);if(matches!=null&&matches.length>0) min=Math.min(matches[0].length,min);} if(min>0) for(var i=0;i

Blogger Syntax Highliter

Version: {V}

http://www.dreamprojections.com/syntaxhighlighter

©2004-2007 Alex Gorbatchev.

'},ClipboardSwf:null,Version:'1.5.1'}};dp.SyntaxHighlighter=dp.sh;dp.sh.Toolbar.Commands={ExpandSource:{label:'+ expand source',check:function(highlighter){return highlighter.collapse;},func:function(sender,highlighter) {sender.parentNode.removeChild(sender);highlighter.div.className=highlighter.div.className.replace('collapsed','');}},ViewSource:{label:'view plain',func:function(sender,highlighter) {var code=dp.sh.Utils.FixForBlogger(highlighter.originalCode).replace(/'+code+'');wnd.document.close();}},CopyToClipboard:{label:'copy to clipboard',check:function(){return window.clipboardData!=null||dp.sh.ClipboardSwf!=null;},func:function(sender,highlighter) {var code=dp.sh.Utils.FixForBlogger(highlighter.originalCode).replace(/</g,'<').replace(/>/g,'>').replace(/&/g,'&');if(window.clipboardData) {window.clipboardData.setData('text',code);} else if(dp.sh.ClipboardSwf!=null) {var flashcopier=highlighter.flashCopier;if(flashcopier==null) {flashcopier=document.createElement('div');highlighter.flashCopier=flashcopier;highlighter.div.appendChild(flashcopier);} flashcopier.innerHTML='';} alert('The code is in your clipboard now');}},PrintSource:{label:'print',func:function(sender,highlighter) {var iframe=document.createElement('IFRAME');var doc=null;iframe.style.cssText='position:absolute;width:0px;height:0px;left:-500px;top:-500px;';document.body.appendChild(iframe);doc=iframe.contentWindow.document;dp.sh.Utils.CopyStyles(doc,window.document);doc.write('

'+highlighter.div.innerHTML+'

');doc.close();iframe.contentWindow.focus();iframe.contentWindow.print();alert('Printing...');document.body.removeChild(iframe);}},About:{label:'?',func:function(highlighter) {var wnd=window.open('','_blank','dialog,width=300,height=150,scrollbars=0');var doc=wnd.document;dp.sh.Utils.CopyStyles(doc,window.document);doc.write(dp.sh.Strings.AboutDialog.replace('{V}',dp.sh.Version));doc.close();wnd.focus();}}};dp.sh.Toolbar.Create=function(highlighter) {var div=document.createElement('DIV');div.className='tools';for(var name in dp.sh.Toolbar.Commands) {var cmd=dp.sh.Toolbar.Commands[name];if(cmd.check!=null&&!cmd.check(highlighter)) continue;div.innerHTML+=''+cmd.label+'';} return div;} dp.sh.Toolbar.Command=function(name,sender) {var n=sender;while(n!=null&&n.className.indexOf('dp-highlighter')==-1) n=n.parentNode;if(n!=null) dp.sh.Toolbar.Commands[name].func(sender,n.highlighter);} dp.sh.Utils.CopyStyles=function(destDoc,sourceDoc) {var links=sourceDoc.getElementsByTagName('link');for(var i=0;i');} dp.sh.Utils.FixForBlogger=function(str) {return(dp.sh.isBloggerMode==true)?str.replace(/
|<br\s*\/?>/gi,'\n'):str;} dp.sh.RegexLib={MultiLineCComments:new RegExp('/\\*[\\s\\S]*?\\*/','gm'),SingleLineCComments:new RegExp('//.*$','gm'),SingleLinePerlComments:new RegExp('#.*$','gm'),DoubleQuotedString:new RegExp('"(?:\\.|(\\\\\\")|[^\\""\\n])*"','g'),SingleQuotedString:new RegExp("'(?:\\.|(\\\\\\')|[^\\''\\n])*'",'g')};dp.sh.Match=function(value,index,css) {this.value=value;this.index=index;this.length=value.length;this.css=css;} dp.sh.Highlighter=function() {this.noGutter=false;this.addControls=true;this.collapse=false;this.tabsToSpaces=true;this.wrapColumn=80;this.showColumns=true;} dp.sh.Highlighter.SortCallback=function(m1,m2) {if(m1.indexm2.index) return 1;else {if(m1.lengthm2.length) return 1;} return 0;} dp.sh.Highlighter.prototype.CreateElement=function(name) {var result=document.createElement(name);result.highlighter=this;return result;} dp.sh.Highlighter.prototype.GetMatches=function(regex,css) {var index=0;var match=null;while((match=regex.exec(this.code))!=null) this.matches[this.matches.length]=new dp.sh.Match(match[0],match.index,css);} dp.sh.Highlighter.prototype.AddBit=function(str,css) {if(str==null||str.length==0) return;var span=this.CreateElement('SPAN');str=str.replace(/ /g,' ');str=str.replace(/');if(css!=null) {if((/br/gi).test(str)) {var lines=str.split(' 
');for(var i=0;ic.index)&&(match.index/gi,'\n');var lines=html.split('\n');if(this.addControls==true) this.bar.appendChild(dp.sh.Toolbar.Create(this));if(this.showColumns) {var div=this.CreateElement('div');var columns=this.CreateElement('div');var showEvery=10;var i=1;while(i<=150) {if(i%showEvery==0) {div.innerHTML+=i;i+=(i+'').length;} else {div.innerHTML+='·';i++;}} columns.className='columns';columns.appendChild(div);this.bar.appendChild(columns);} for(var i=0,lineIndex=this.firstLine;i0;i++) {if(Trim(lines[i]).length==0) continue;var matches=regex.exec(lines[i]);if(matches!=null&&matches.length>0) min=Math.min(matches[0].length,min);} if(min>0) for(var i=0;i

ページビューの合計