ひだまりソケットは壊れない

ソフトウェア開発に関する話を書きます。 最近は主に Android アプリ、Windows アプリ (UWP アプリ)、Java 関係です。

まじめなことを書くつもりでやっています。 適当なことは 「一角獣は夜に啼く」 に書いています。

WebDriver によるスクリプト実行の現状 (geckodriver と ChromeDriver)

WebDriver とは、Web ブラウザを外部から操作するための標準化された API です。 詳細は先日書きましたのでご参照ください!

今回は、WebDriver のコマンドで JS スクリプトを実行させる方法の説明です。 W3C WebDriver API を見ながらどういう API になっているのか説明します。 また、geckodriver および ChromeDriver での現在の実装状況についても書いています。

これらは 2017 年 5 月 10 日現在の情報ですので、最新の情報は最新の W3C 勧告や Driver 実装を見てください。

スクリプトを実行させる API

W3C WebDriver での仕様

W3C WebDriver 勧告では、スクリプトを実行するための下記の 2 つのコマンドが定義されています。

前者は同期的なスクリプト実行をサポートし、後者は非同期的なスクリプト実行をサポートする、という風に思ってしまうところですが、なんとどちらも非同期のスクリプト実行をサポートしています! (な、なんだってー

どちらも渡されたスクリプト (リクエストボディに含まれる script プロパティの値) を関数本体 (FunctionBody) として扱って promise-calling の形で実行するのですが、前者の方は関数の返り値をレスポンスに使うのに対して、後者の方は Promiseresolve を引数リストに追加したうえでスクリプトを呼び出し、関数の返り値を無視する (つまり、引数リストに追加された resolve を関数内で呼ぶことで結果を渡す) という違いがあります。 *1

前者の方は以下のようなスクリプトを受け付けるわけですね。

var waitTime = arguments[0] || 2000;
var p =
  new Promise(function (resolve, reject) {
    setTimeout(function () { resolve() }, waitTime);
  }).then(function () {
    return "Hello!";
  });
return p;

後者で同様の処理を実行させるには、以下のようにする必要があります。

// 引数リストの最後に追加された resolve 関数を受け取る。
var callback = arguments[arguments.length - 1];
var waitTime = arguments[0] || 2000;
var p =
  new Promise(function (resolve, reject) {
    setTimeout(function () { resolve() }, waitTime);
  }).then(function () {
    return "Hello!";
  });
// 引数リストの最後に追加された resolve 関数を呼ぶことで結果を返す。
callback(p);

ちなみに関数に渡される引数は、 リクエストボディの args プロパティで指定できます。 つまり、リクエストボディは以下の形式です。

{
  "script": "return 100 + arguments[0];",
  "args": [200]
}

JSON wire protocol での仕様

さて、どちらも非同期なスクリプト実行を扱えるのであれば、なぜ W3C WebDriver には 2 つのスクリプト実行コマンドが用意されているのでしょうか? おそらく、W3C 勧告よりも古い仕様である SeleniumJSON wire protocol から引き継いだものだと思われます。 JSON wire protocol でも 2 つのスクリプト実行コマンドが用意されていました。

これら 2 つは、(W3C 勧告の 2 つとは違って) 前者が同期的なスクリプト実行用、後者が非同期的なスクリプト実行用と、明確に役割が分かれています。 リクエストボディの型や、非同期実行での結果の返し方 (引数リストの最後に追加されたコールバック関数に値を渡す) などは W3C 勧告のコマンドと同じです。 ただし、これらは Promise を扱えません。

JSON wire protocol でスクリプトをそのまま W3C 勧告の仕様にあった Driver 実装でも使えるように、W3C 勧告の方でも 2 種類のコマンドが定義されたのだろうと思われます。

ChromeDriver で非同期スクリプトを実行すると

普通に ChromeDriver で非同期スクリプトを実行させようとすると、以下のメッセージが返ってくることがあります。

"value": { "message": "asynchronous script timeout: result was not received in 0 seconds..." }

これは、スクリプトタイムアウトが 0 s に設定されているためです。 タイムアウト時間を設定することができるので、先にタイムアウト時間を設定する必要があります。 POST /session/{session_id}/timeouts というエンドポイントに、以下のようなリクエストボディで HTTP リクエストを発行しましょう。

{
  "type": "script",
  "ms": 2000
}

ちなみにこれも W3C 勧告とは違う JSON wire protocol に沿ったものになっています。

geckodriver と ChromeDriver の現状

上記 Driver のそれぞれの実装状況を見てみました。

geckodriver の実装状況

  • W3C 勧告のコマンド (Execute Script コマンドと Execute Async Script コマンド) は実装されている。 Promise も扱える。
  • JSON wire protocol で定義されていたエンドポイントはバージョン 0.16.0 で削除済み!

ChromeDriver の実装状況

  • W3C 勧告のコマンド (Execute Script コマンドと Execute Async Script コマンド) は実装されていない。
  • JSON wire protocol で定義されていたエンドポイントは使用可能。 (Promise は扱えない。)

どちらでも動かすために

WebDriverIO の実装を見ると、古い仕様のエンドポイントでコマンドを発行してみて、エラーになったら新しい仕様でコマンドを発行しなおす、ということをやっていました。

厳しい世界ですね……。

*1:W3C 勧告を読んだ感じ、なんか remote end steps の文書がおかしい感じがしますね。 まあ意図は掴めるんだけども。

WebDriver について私が知っていること (2017 年版)

もともと Selenium 2.0 で導入された WebDriver ですが、単に 「WebDriver」 と言ってもいろいろなものを指すことがあり *1、WebDriver を初めて使おうとする人にはややこしい状況だと感じています。 (私は数か月前に WebDriver を使おうとして混乱しました。)

というわけなので、私が調べた 「WebDriver とは何か」 を簡単にまとめておきます。

WebDriver とは何か

WebDriver とは、ユーザーエージェント (web ブラウザ) を外部のソフトウェアから操作したり情報を取得したりできるようにするためのものです。 テストの自動実行などに便利ですね。

W3C WebDriver と Selenium WebDriver

冒頭で述べた通り、Selenium 2.0 の目玉機能として導入された WebDriver なのですが、現在は W3C が発行する WebDriver 勧告も存在します。 W3C WebDriver は Selenium WebDriver をもとに策定されていて、概念的には基本的には同じなのですが、言葉の定義が違っていてややこしいので分けて説明します。


W3C WebDriver
  • ドキュメント : WebDriver (W3C)
  • ユーザーエージェントの内部状態の取得や操作を可能にする遠隔操作のためのインターフェイス
  • HTTP 準拠の wire プロトコル となっている。
  • WebDriver のクライアント側を Local End といい、ユーザーエージェント側を Remote End という。
    • Remote End は 2 種類のノードに分けられて、他の Remote End のノードのプロキシとして働く Intermediary node と、ユーザーエージェント (または類似のプログラム) によって提供される Endpoint node がある。 ( https://w3c.github.io/webdriver/webdriver-spec.html#nodes )
  • WebDriver 仕様では、リモートエンドがどのように振る舞うべきかも書かれている。
Selemium WebDriver

各種 Driver 実装

初期には Selenium が独自に Driver を実装していたようなのですが、現在では各ブラウザベンダーがそれぞれ Driver を実装しているという状況です。

仕様と実装について

W3C WebDriver の仕様は最近もいろいろ変化していて、Driver 実装と W3C WebDriver のドキュメントがしばしば異なっていることがあります。 Obsolete ではあるのですが、SeleniumJSON wire protocol のドキュメントの方が実際の Driver の実装にあっていることもあるので、そちらもあわせて参考にするといいでしょう。

また、WebDriverIO の API ドキュメント も参考になります。 各コマンドのドキュメントページに 「View Source」 のリンクがあるので、どういう実装になっているのか見てみるといいでしょう。

とりあえず使ってみる (スクリーンショットを撮る例)

とりあえず WebDriver を使ってみるには、ブラウザとその Driver、それから HTTP を発行できるコマンドさえ使えれば大丈夫です。

ここでは WebDriver API (HTTP プロトコル) を使い、Firefox でスクショを撮ってみましょう。

Driver (geckodriver) の準備

Firefox のバイナリは既に存在するとします。 Firefox 用の Driver である geckodriver は GitHub 上でリリースされていますので、ダウンロードしてきます。

下記のようなコマンドで geckodriver を起動します。 (Linux での例です。 各自環境に合わせていい感じに実行してください。)

# geckodriver 起動
/opt/geckodriver/geckodriver --host 0.0.0.0 --port 9516 --binary /usr/bin/firefox

WebDriver コマンドの発行

あとは WebDriver のコマンドを発行していくだけです。 以下は PowerShell の例ですが、各自環境でいい感じに HTTP リクエストを発行してください。

# セッション開始 (レスポンス中の sessionId の値を後続のコマンドで使用する)
$session = Invoke-RestMethod -Method POST -Uri http://localhost:9516/session -Body $(ConvertTo-Json(@{}))

# ページ遷移
Invoke-RestMethod -Method POST -Uri http://localhost:9516/session/$([System.Web.HttpUtility]::UrlEncode($session.value.sessionId))/url -Body $(ConvertTo-Json(@{ url = "http://vividcode.hatenablog.com/" }))

# スクリーンショット取得
$screenshot = Invoke-RestMethod -Method GET -Uri http://localhost:9516/session/$([System.Web.HttpUtility]::UrlEncode($session.value.sessionId))/screenshot

# Base64 デコードしてファイルに出力
[System.IO.File]::WriteAllBytes($(Join-Path $(Convert-Path .) test.png), $([System.Convert]::FromBase64String($screenshot.value)))

# セッション終了
Invoke-RestMethod -Method DELETE -Uri http://localhost:9516/session/$([System.Web.HttpUtility]::UrlEncode($session.value.sessionId))

スクリーンショット{"value":"xxxx"} のような JSON で返ってきます。 value の値は Base64 エンコードされた画像の値なので、Base64 でデコードしてやってファイルに書き出すとスクリーンショットを見ることができます。 上の例ではコマンドで変換してファイル出力しちゃってますが、コマンドでの変換が難しい環境の場合は、http://www.convertstring.com/ja/EncodeDecode/Base64Decode のような web 上の Base64 デコーダを使ってデコードするのが楽でしょう。

今回の例では、以下のような感じでスクショを撮ることができました。

f:id:nobuoka:20170507214630p:plain

Remote End を Docker 環境で用意する

自動テストで使用する場合など、ブラウザ自体や Driver を含んだ環境を手軽に用意できると便利ですよね。

FirefoxChrome については、wakaba さんが公開している Docker イメージがあります。

Docker 環境さえあれば docker run -it --rm -p 9516:9516 -t quay.io/wakaba/firefoxdriver:stable という感じで Firefox と geckodriver の環境を手に入れられるので便利です。 これらのイメージは Selenium に依存していません。

Selenium が公開している Docker イメージもあります。

こちらは Selenium Server が入っているので、Selenium Server 経由で使いたい場合はこちらを使うといいでしょう。

*1:クライアントライブラリだったり Driver だったりプロトコルだったり

ThreeTenABP と ThreeTenBP の関係について (Android における JSR-310 バックポート)

「ThreeTenABP って Android フレームワークに依存するけど、ドメインレイヤとかで Android 依存を排除したい場合どうするのがいいんですかね」 という相談を受けて、ThreeTenABP 周りを調べたのでメモ。

ThreeTenABP って何?

JSR 310 Date and Time API 周りの話

ThreeTenABP が存在する意味は?

  • ThreeTen Backport (threetenbp) があるならそれでいいのでは? → ThreeTen Backport はタイムゾーン周りの情報を JAR から読み込むという仕組みになっており、Android では非効率。

というわけで

ThreeTenABP を使いたいけど Android フレームワークに依存させたくないというモジュールでは、ThreeTen Backport に依存させて、テストを書く際にタイムゾーン情報の扱いだけなんとかする、みたいなことをすれば良さそう。

タイムゾーン情報周りを厳密にテストしなくていいなら、compileOnly "org.threeten:threetenbp:${threetenbp_version}"testCompile "org.threeten:threetenbp:${threetenbp_version}" を依存に追加しちゃうのが手軽でいい気がする。 (バージョン周りはよしなに。 これでうまくいくだろうと思って書いてるけど特に何も確認はしてない。)

関連ページ

読んだ : .NET のエンタープライズアプリケーションアーキテクチャ 第 2 版 / Dino Esposito、Andrea Saltarello 著

ドメイン駆動開発 (DDD) 関係の読書会に参加していて、最近読んでいるのがこれです。

本書は、『再利用可能で、入手が容易な、ソフトウェアアーキテクチャの確かな知識ベースを提供』 することを考えて執筆されています。 すなわち、DDD の書籍というよりは、開発者とアーキテクトのための、ソフトウェアアーキテクチャ (その中でも特にエンタープライズアプリケーションや web アプリケーションのアーキテクチャ) の虎の巻のようなものです。 その中にドメイン駆動設計の考え方も含まれています。

内容

本書の内容を紹介します。

1 部 「基礎」 では、ソフトウェアアーキテクチャの基礎ということで、アーキテクトの役割であったり、プロジェクトを成功させるためにソフトウェアディザスタ (技術的負債だらけであること) を何故避ける必要があるのかといったこと、ソフトウェアの設計原則やテストが重要であることなどが書かれています。 ここら辺は割と他の書籍でも書かれていることなので、わりとサラッと読んでしまって良さそうです。 (ここら辺の内容をちゃんと吸収するなら 『CODE COMPLETE』 などを読むのが良いと思います。)

第 2 部 「アーキテクチャの考案」 には、プレゼンテーション層とビジネス層についてが書かれています。 目玉は 5 章の 「ドメインアーキテクチャの発見」 で、要約すると 『ドメインを深く理解することだけが適切なアーキテクチャの発見につながる』 ということと 『サブドメインの存在に気づいた場合は、それらをサブアプリケーションとしてモデリングし、最適なアーキテクチャをそれぞれに定義でき』 るということです。 ユビキタス言語と境界づけられたコンテキストが重要である、ということですね。

DDD 関係の本を読むとドメインモデルを構築するための技術的な部分に着目しがちですが、本書ではドメインモデルはあくまでサポートアーキテクチャの 1 つにすぎないと明確に述べていて、ユビキタス言語と境界づけられたコンテキストの重要性を前面に押し出しているのが特徴的です。 また、システムを設計するための手法として、ユーザーエクスペリエンスファースト (UX ファースト) と呼ばれるアプローチをとっているのも特徴ですね。 これは、システム設計の作業をプレゼンテーション層から開始して、予備的な分析を並行して行うという設計理念です。 つまり、最初にビジネスドメインと UX に関するデータを集めてユーザーとの対話モデルを設計して、その後でデータのワークフローやロジック、サービス、ストレージの定義に取り掛かるというものです。

第 3 部 「サポートアーキテクチャ」 では、ドメインモデル、CQRS、イベントソーシングの 3 つのサポートアーキテクチャについての説明がなされます。 ドメインモデルを用いた場合は、ドメインのすべての側面に適合する単一のモデルを設計する必要があり、モデルが複雑になってしまうという問題があるということが述べられ、本書ではやや CQRS 推しな感じです。 (もちろんドメインモデルが適している場面ではドメインモデルを使えば良いのですが。) 個人的にもドメインモデルをがっつり設計するのが常にいいとは思っていなかったので、「DDD の分析部分 (ユビキタス言語や境界づけられたコンテキストの発見) は常に重要だけど、戦略部分 (各境界づけられたコンテキストに最適なアーキテクチャを割り当てる) ではドメインモデルを使う以外の方法もある。 トランザクションスクリプトでもいいし、CQRS という選択肢もある」 というのを明確に示している本書は参考になりました。

第 4 部は 「インフラストラクチャ」 で、永続化レイヤーについて説明されます。

感想

DDD の 「分析部分」 と 「戦略部分」 を分けて、「戦略部分に置いてどういうアーキテクチャを選択するのが最適なのかは場合による」 というのを明確に示しているのが良いなーと思いました。 上でも書いたように、DDD というと 「ドメインモデルをいかに設計するのか」 に着目しがちなのですが、分析部分がまず重要であるというのが読んでいて伝わってきました。 (ユビキタス言語と境界づけられたコンテキストが重要であるということは DroidKaigi 2017 でのあんざいゆきさんの発表でもなされてましたね。)

戦略部分については、サポートアーキテクチャとして DDD において従来から使われているドメインモデルだけでなく、CQRS とイベントソーシングについても述べられているのが良い点だと思います。 どういうものかの紹介や思想の説明もありますし、実装の例も紹介されています。 実際に設計する際にどういう形にすればよいかがイメージしやすいようになっていて参考になります。

翻訳については、1 部の日本語が難しくなっていて読みづらいという問題はありました。 2 部以降はそれほど気にならなかったです。

全体としては、エンタープライズアプリケーションや web サービスの開発者やアーキテクトがアプリケーションのアーキテクチャについて検討する際に役立つ知識がまとまっていて、良い本だと思います。 『.NET の』 とタイトルにありますが、.NET に詳しくなくても問題なく読み進めていくことができますし、アプリケーションの全体のアーキテクチャについて考えている人は読んでみて損はないと思います。

Android のアカウントマネージャ (AccountManager) の概説

最近 Android のアカウントマネージャを仕事で触ったので、調べた内容としてアカウントマネージャの概要をまとめておきます。

Web 上を調べると AccountManager を使う処理の実装方法はいろいろ見つかるのですが、アカウントマネージャの概要を説明しているページはあんまりなくて全体像を掴みにくいと思っています。 そういう情報を探している人の役に立てば幸いです。

公式ドキュメント

アカウントマネージャに関する公式ドキュメントは以下のものぐらいしかなさそうです。 AccountManager クラスのドキュメントを全て (各メソッドの説明も含めて) 読むと大体理解できると思います。 (下記ドキュメントを見て理解したら本ページの内容を読む必要はありません。)

アカウントマネージャの概要

アカウントマネージャは、Android のシステムがユーザーのオンラインアカウントを中央管理するための機能です。 Android 端末の 「設定」 にずらずらとアカウントが並ぶと思いますが、これもアカウントマネージャの仕組みで実現されています。

f:id:nobuoka:20170308031030p:plain:w360

各種オンラインサービスはそれぞれ異なったアカウントの扱い方や認証方式を使うため、アカウントマネージャはアカウント種別ごとに authenticator モジュールを使い分けるようになっています。

使いどころ

複数アプリで認証情報を共有するときに便利です。 (認証情報の提供側、および利用側両方とも。)

また、単一アプリでのみ認証情報を使う場合でも便利に使えると思います。 なぜなら、アカウントマネージャを使うことで認証周りの処理を分離しやすくなり、また、統一された方法で認証トークンを扱えるようになるためです。

用語

  • Account Type : Authenticator を特定するための文字列。
  • Auth token (認証トークン) : オンラインサービスのユーザー認証に使われるトークン。
  • Auth token type : Authenticator 固有の、Auth token 種別。 1 つのアカウントが auth token を複数持つことができる。
  • Feature : Authenticator 固有の、アカウントプロパティを特定するトークン。
  • Client (クライアント) / Application : アカウントマネージャを使って認証情報を取得するアプリを指す。
  • Authenticator : トークンの払い出しなどを行うやつ。 実体は AbstractAccountAuthenticator を継承したクラス。
    • システム上で有効な authenticator (AbstractAccountAuthenticator) を持っているアプリを指して Authenticator と言ったりもしてる? (明示的にアプリを表す場合は 「Authenticator アプリ」 と呼ぶのが良さそう。)
    • クライアントであり、かつ Authenticator であるというようなアプリも存在しうる。

Authenticator とクライアントの関係

アカウントマネージャを扱う処理を大別すると、authenticator とクライアントに分かれます。

  • Authenticator (や、それに関連する処理) : オンラインサービスのアカウント取得や認証処理を行います。 典型的には、ユーザーに対してログイン画面を表示してユーザー名とパスワードを入力させ、それらのアカウント情報を使ってオンラインサービスから認証情報を取得して、アカウントマネージャに渡します。 普通はそのオンラインサービスの提供者がアプリの中に実装します。
    • Authenticator のメソッドはアカウントマネージャから呼ばれます。
  • クライアント : アカウントマネージャを通じて、オンラインサービスの認証情報 (アクセストークンなど) を取得し、それを使ってオンラインサービスにリクエストを投げる、というような処理です。
    • 自社サービスクライアント : 自社が提供するサービスの認証情報をアカウントマネージャから取得します。 このアプリのシグネチャは、対応する authenticator を含むアプリのシグネチャと同じになります。
    • 他社サービスクライアント : 他社が提供するサービスの認証情報をアカウントマネージャから取得します。 このアプリのシグネチャは、対応する authenticator を含むアプリのシグネチャと異なります。

クライアントが AccountManager のクライアント向けのメソッドを呼ぶと、(一部のメソッドは) 対応する Authenticator のメソッドを呼びます。 そして、典型的には Authenticator のメソッドは AccountManager の Authenticator 用のメソッドを呼びます。 クライアントも Authenticator も AccountManager のメソッドを呼ぶという点で混乱しやすいのですが、AccountManager のメソッドをクライアント用メソッド群と Authenticator 用メソッド群に分けると、理解しやすいと思います。

f:id:nobuoka:20170308034440p:plain

自社サービスクライアントと他社サービスクライアントについて公式ドキュメント上での区別はありませんが、こういう分け方で考えると理解しやすいと思うので、本ページではこのように表記します。 ちなみに厳密な区別としては、「扱うアカウント種別の authenticator アプリと同じシグネチャを持つアプリ」 が自社サービスクライアント、「異なるシグネチャを持つアプリ」 が他社サービスクライアントです。

AccountManager のメソッド一覧

アカウントマネージャについて理解するには AccountManager クラスに生えている全てのメソッドを把握すればいいのですが、ドキュメントそのままだと理解しづらいので、整理して一覧できるようにしておきます。 これらのメソッド一覧に目を通すと、アカウントマネージャについての理解が深まるはずです。

API level 25 のドキュメントを参照しています。

クライアント向け

他社サービスのクライアントが使えるメソッド

Authenticator 一覧を取得したり、アカウント一覧を取得したり (パーミッションがあれば他社サービスのアカウントも全て)、指定のアカウントの認証トークンを取得したり、指定の認証トークンが無効になっていることをアカウントマネージャに伝えたり、ユーザーが指定のアカウントのパスワードを知っていることを確認するよう依頼したり、といったことができます。

  • getAuthenticatorTypes()
  • addOnAccountsUpdatedListener
    • アカウントマネージャが管理するアカウントの変化 (追加や削除など) を検知するリスナ (OnAccountsUpdateListener) を追加する。
    • getAccounts メソッドで取得可能なアカウントの変化のみ検知できる。 (典型的には、任意のアカウントの変化を検知するには GET_ACCOUNTS パーミッションが必要。)
  • removeOnAccountsUpdatedListener(OnAccountsUpdateListener)
    • リスナの削除。
  • addAccount
    • 指定のアカウント種別のアカウントを追加するよう依頼する。 対応する Authenticator が適切な UI でこのリクエストを処理する。
    • API level 22 以下では MANAGE_ACCOUNTS パーミッションが必要。
  • アカウント取得系
    • getAccounts
      • バイスに登録されているすべてのアカウントのリストを取得する。
      • ただし、GET_ACCOUNTS パーミッションがない場合は、同じシグネチャの Authenticator が管理するアカウントしか取得できない。
      • API level 22 以下の場合は GET_ACCOUNTS パーミッションが必要。 (ドキュメントに明記されてないけど多分。)
    • getAccountsByType
      • バイスに登録されているアカウントのうち、指定のアカウント種別のものを取得する。
      • GET_ACCOUNTS パーミッションがない場合は、同じシグネチャの Authenticator が管理するアカウントのみ取得できる。
      • API level 22 以下の場合は GET_ACCOUNTS パーミッションが必要。
    • getAccountsByTypeAndFeatures
      • バイスに登録されているアカウントのうち、指定のアカウント種別で、指定の機能をもつものを取得する。
      • 機能を考慮しないアカウント取得のメソッド (上 2 つ) とは違い、Authentcator が処理を行う。 (機能の管理をするため。)
      • GET_ACCOUNTS パーミッションがない場合は、同じシグネチャの Authenticator が管理するアカウントのみ取得できる。
      • API level 22 以下の場合は GET_ACCOUNTS パーミッションが必要。 (ドキュメントに明記されてないけど多分。)
  • confirmCredentials
    • ユーザーが指定のアカウントのパスワードを知っていることを確認する。 対応する Authenticator がこのリクエストを処理する。
    • 買い物処理みたいな、重要な操作の前に呼び出す、というような使われ方だと思う。
    • 呼び出し側がパスワードを渡した場合はそれが使われて、そうでなければ Authenticator 側がパスワード入力画面を表示する必要があるっぽい。
    • API level 22 以下では MANAGE_ACCOUNTS パーミッションが必要。
  • updateCredentials(Account, String, Bundle, Activity, AccountManagerCallback<Bundle>, Handler)
    • ユーザーに対して、新しいパスワードを入力してもらうよう依頼する。 対応する Authenticator がこのリクエストを処理する。
    • 保存されているパスワードを更新するため? 使いどころをあんまりわかってない。
    • API level 22 以下では MANAGE_ACCOUNTS パーミッションが必要。
  • getPreviousName
    • 指定のアカウントの前の名前を返す。
    • LOGIN_ACCOUNTS_CHANGED_ACTION ブロードキャストを受け取ったときに、アカウント名が変更されているかどうか検知できるようにするためのメソッドらしい。
  • Auth token 取得系 : AccountManager がキャッシュしていたらそれを返し、なければ対応する Authenticator が認証トークンを取得する。
    • getAuthToken(Account, String, Bundle, boolean, AccountManagerCallback<Bundle>, Handler)
      • バックグラウンドの処理向け。
      • 指定のアカウントの指定のトークン種別の認証トークンを取得する。 アカウントマネージャがキャッシュしている場合はそれが返される。 キャッシュされていない場合は authenticator がリクエストを処理する。
      • Authenticator は、パスワードが使えるのであればそれでサーバーに問い合わせを行う。 それが無理ならユーザーにログイン画面を表示して、ユーザー入力によって認証トークンを取得する。
      • notifyAuthFailure に真を渡したならば、そのインテントを開始するステータスバーの通知も表示される。
      • その場合 (ログインする必要がある場合? それともステータスバーの通知が表示される場合?) には、ユーザーが対応するまで何時間も、何日も、あるいは永遠に待つことになる可能性がある。
      • notifyAuthFailure に偽を渡した場合は、アプリケーションが Intent を開始する責任がある。
      • API level 22 以下では USE_CREDENTIALS パーミッションが必要。
    • getAuthToken(Account, String, boolean, AccountManagerCallback<Bundle>, Handler)
      • API level 14 で Deprecated になってるので Bundle パラメータ有り版を呼ぶこと。
    • getAuthToken(Account, String, Bundle, Activity, AccountManagerCallback<Bundle>, Handler)
      • フォアグラウンドで動いている処理向け。
      • 指定のアカウントの指定のトークン種別の認証トークンを取得する。 アカウントマネージャがキャッシュしている場合はそれが返される。 キャッシュされていない場合は authenticator がリクエストを処理する。
      • API level 22 以下では USE_CREDENTIALS パーミッションが必要。
    • blockingGetAuthToken
  • getAuthTokenByFeatures(String, String, String[], Activity, Bundle, Bundle, AccountManagerCallback<Bundle>, Handler)
    • 他のメソッド (getAccountsByTypeAndFeaturesgetAuthTokenaddAccount) の処理を組み合わせた便利メソッド。
    • API level 22 以下では MANAGE_ACCOUNTS パーミッションが必要。
  • invalidateAuthToken
  • hasFeatures
    • 指定のアカウントが指定の全ての機能を持っているかどうかを調べる。
    • 呼び出し側は GET_ACCOUNTS パーミッションを持っているか、もしくは Authenticator と同じシグネチャである必要がある。
自社アプリ (同じシグネチャを持つアプリ) のみ
  • removeAccount(Account, Activity, AccountManagerCallback<Bundle>, Handler)
    • アカウントマネージャからアカウントを削除する。 Authenticator は固有のポリシーによってアカウント削除をしないという選択をしてよい。
    • 呼び出し側は Authenticator アプリと同じシグネチャを持つ必要がある。
    • API level 22 以下では MANAGE_ACCOUNTS パーミッションが必要。
  • removeAccount(Account, AccountManagerCallback<Boolean>, Handler)
    • 上に同じ。 Deprecated。
  • renameAccount(Account, String, AccountManagerCallback<Account>, Handler)
    • 古いアカウントを削除し、古いアカウントのユーザーデータを持つ新しいアカウントを追加するのと同じ効果。 (Auth token はどうなるんだろ?)
    • 呼び出し側は Authenticator と同じシグネチャを持つ必要がある。
    • API level 22 以下では AUTHENTICATE_ACCOUNTS パーミッションと、authenticator と同じ UID である必要がある。
  • clearPassword
    • アカウントマネージャが保持しているパスワードを削除する。 「ログアウト」 ボタンの処理などで使われることを想定しているらしい。
    • setPassword メソッドでも同じことができるけどこっちの方が必要な権限は少ないっぽい。
    • アカウントの Authenticator と同じシグネチャが必要。
  • editProperties
    • Authenticator 固有の、Authenticator のプロパティ (アカウントごとではない) を編集する。 対応する Authenticator がこのリクエストを処理する? (未調査。)
    • 全ての Authenticator がこの機能を実装しなければならないわけではない。
    • Authenticator と同じシグネチャが必要。
    • API level 22 以下の場合は MANAGE_ACCOUNTS パーミッションが必要。

Authenticator (または、それに関連する処理) 向け

  • addAccountExplicitly(Account, String, Bundle)
    • アカウントをアカウントマネージャに追加する。
    • Authenticator に関連するサインアップ処理向け。
    • addAccount メソッドなどのシステムがアカウント追加を検知できるメソッド経由で呼ばれたわけじゃない場合は、自前で notifyAccountAuthenticated メソッドを呼ぶべき。
  • removeAccountExplicitly(Account)
    • アカウントをアカウントマネージャから削除する。
    • クライアント用のアカウント削除メソッドとは違い、Authenticator でのポリシー設定は見られないのだと思う。 (未調査。)
    • API level 22 以下では AUTHENTICATE_ACCOUNTS パーミッションと、さらに Authenticator と同じ UID である必要がある。
  • notifyAccountAuthenticated(Account)
    • アカウントが認証されたことをシステムに伝える。
    • このイベント情報が他のアプリで使われたりするっぽい? (未調査。)
    • 呼び出し側はアカウントの Authenticator と同じシグネチャを持つ必要がある。
  • getPassword
    • アカウントマネージャが保持しているパスワードを取得する。
    • 呼び出し側は Authenticator と同じシグネチャを持っている必要がある。
    • API level 22 以下では AUTHENTICATE_ACCOUNTS パーミッションが必要。
  • setPassword(Account, String)
    • アカウントマネージャにパスワードを保存する。 (あるいは削除する。)
    • Authentiator と同じシグネチャを持つ必要がある。
    • API level 22 以下では AUTHENTICATE_ACCOUNTS パーミッションと、さらに Authenticator と同じ UID である必要がある。
  • getUserData
  • setUserData(Account, String, String)
  • peekAuthToken(Account, String)
    • AccountManager のキャッシュから auth token を取得する。 なければ null が返される。
    • API level 22 以下では AUTHENTICATE_ACCOUNTS パーミッションが必要で、さらに Authenticator アプリの UID と同じ UID を持っている必要がある。
  • setAuthToken(Account, String, String)
    • Auth token をアカウントマネージャのキャッシュに追加する。
    • Authentiator と同じシグネチャを持つ必要がある。
    • API level 22 以下では AUTHENTICATE_ACCOUNTS パーミッションと、さらに Authenticator と同じ UID である必要がある。

その他

関連ページ