Fustion Tables へのアクセスがうまくいかず結構悩んだ

id:yellow_73:20110809#p2 のつづきです。

unauthenticated が出まくる

まず、昨日まで SHOW TABLES がうまくいってたのに、突然うまくいかなくなりました。
unauthenticated という文言が見え、HTTP応答ステータスは 401 でした。
他のクエリではどうだろう、と思い、SELECT * FROM [id] をリクエストしましたが、これはうまくいきました。
つまり、特定のクエリだけがうまくいかない。これには正直悩みました。
で、ふと気づいた。Fusion Tables ではパブリックなテーブルが作れる、これを引っ張ってきてるのではなかいと。さきほどの SELECT の対象テーブルは、プライベートなものではありませんでした。これは単に認証トークンを見てないので成功しているのではないか、と。
今度は、プライベートなものを見に行こうとしたら、見事に unauthenticated とされました。
ここまでくれば私でも分かる、認証トークンが賞味期限切れになってたんだ、と。

認証トークンが適切でアカウントに権限が無いコマンドの場合は unauthenticated でない

プライベートなテーブルを権限の無いアカウントから見ようとすると、同じ 401 を返されるのですが、ボディには "User does not have permission to view the underlying data" という文言が出ます。

考えられる認証手順

上記より、考えらる認証手順は次の通りです。
わざわざ書くほどでもないかも知れないけれども、まとめとして書いておく。

  1. リクエストしたコマンドが認証を要しないレベルならそのまま実行
  2. 認証を要するならトークンをもとにアカウント認証。賞味期限切れならここで 401 unauthenticated を返す。
  3. 認証されたアカウントに権限があるかチェックして、権限が無いなら 401 does not have permission を返す。

Androidで賞味期限切れのトークンを破棄する

破棄するのは AccountManager#invalidateAuthToken(String, String) です。

  • 第1引数は accountType で、この場合は "com.google" が入ります。authTokenType("fusiontables")ではないですよーこれでちょっと破棄できなくて困ったんですよー。
  • 第2引数は authToken で、トークン本体。

これで、トークンが破棄されるので、次のトークン取得時には新しいトークンが出てくれます。

ベストでないgetBestProvider

id:yellow_73:20110815#p1 のつづき。
LocationManager#getBestProvider() を使うと、Criteriaで指定した優先順位に沿って最も適切なロケーションプロバイダを探してくれるというものです。メソッド名からして「ベスト」なんだしこれで良いと思ってました。
しかしどっこい、ベストとは言えない事例が出てきました。
プライベートでマーケットに放流しているもののうち低評価な方のアプリについて、南米あたりから「他のアプリではGPSが使えるけど、このアプリでは位置特定できないよー」というメールを頂きました。
しばらく考えて、ふと思いついたので実験してみました。

ネットワークにつながってない状態でネットワーク位置特定はできない

Androidの「位置情報とセキュリティ」設定でネットワーク位置特定にもGPS位置特定にもチェックを入れ、SIM抜いて、無線LAN切って、某アプリを立ち上げました。
結果は、予想通り、位置特定はできませんでした。
次に、ネットワーク位置特定のチェックを外し、GPSのみにチェックが入った状態にして、某アプリを起動。
こちらは、位置特定ができました。
この二つのことから、ネットワークにつながってない状態でも getBestProvider() は、設定を見て、ネットワーク位置特定を試みようとしていたと考えられます。
つまり、環境によっては、最適なロケーションプロバイダを選んだつもりが、最悪なものを選ぶこともある、ということです。

本当にそういうケースってあるの?

たぶん、あります。
通信ユニットを持たないタブレットとか、携帯でも不意な通信に起因する通信費をセーブするためにSIMを抜くとか、平気でやりますね。これで携帯電話回線を切ったうえで、無線LANの電波が届かないと、ネットワークに当然つながりません。
ちなみにレポートメールを送って下さった方は、Galaxy Pro をお使いのようなので、SIM抜いてるんじゃないかと思ってます。

解決策(保留)

最も簡単な回避策はネットワーク位置特定を切ってもらうこと。ただ、無線LANを使ってる人にとっては、アクセスポイントを拾うたびに設定の切り替えが必要になり、面倒な気がします。少なくとも私はやりたくない。
やはり、あれかな、と。getBestProvider()で最適と思われるプロバイダで20秒ほど受信して、それでもダメなら、ほかのプロバイダに切り替えて位置特定を続行させる必要があるかなーと。