2013/02/06
Android のマルチユーザ環境で ユーザID を取得する
ユーザとしては喜ばしい機能ですが、開発者としては新たな問題の種になりそうで辛いところです。
出来る事ならば、ユーザの違いなど意識しない作りにしたいところですが、どうしても意識しないといけない場面はありそう。
例えば、「SDカードにデータを保存せざるを得ないが、ユーザは分けたい」場合とか。
そんな時に、どのユーザで実行されているのかということをアプリが知る方法がないか調べてみました。
2013/2/24 追記
「SDカードにデータを保存せざるを得ないが、ユーザは分けたい」場合とか。と書いたのですが、SDカードにデータを保存するときもマルチユーザを意識する必要はありませんでした。
穀風: マルチユーザ環境でSDカードのディレクトリをユーザ毎に分ける
穀風: マルチユーザ環境でSDカードのディレクトリをユーザ毎に分ける
ユーザ識別のための ID の種類
android.os.UserManager
と android.os.UserHandle
あたりを読んでみると、ユーザ(アプリ)を識別するのに、以下のような ID が用意されていることがわかりました。UID | いわゆる Linux の UID アプリ毎に振られる |
UserId | UID / 100000 シングルユーザ環境の場合は0 |
AppId | UID % 100000 同じアプリの場合、ユーザが異なっても同じ |
SerialNumberForUser | ユーザ固有のID ユーザを削除後、追加しても同じ値が使われることはない |
Android は アプリ毎に UID が振られます。
その仕組みを利用して、下位5桁を AppId, 上位を UserId として使用しているわけです。
ちなみに、各ユーザ毎に用意されるアプリケーションデータディレクトリは、UserId で管理されます。
参考
どの ID を識別に使用すべきか?
現在、私が参照しているコードは AOSP の Android 4.2.1_r1.2 ですが、 UserId を取得するUserHandle.myUserId()
や AppId を取得する UserHandle.getAppId()
に @hide
属性がついています。つまり、正規の手段では呼ぶことが出来ません。
どうしても UserId が欲しい場合は、自分で uid を 100000 で割るか、リフレクションを使用するしかないようです。
しかし、そういう実装は新たな不具合の原因となるので、極力避けるべきでしょう。
それに対し、
UserManager.getSerialNumberForUser()
に @hide
属性は付いていませんし、その javadoc には以下のように書いてあります。Android の思想としては、「ユーザの識別には SerialNumberForUser を使用する」というのが正しそうです。Return the serial number for a user. This is a device-unique number assigned to that user; if the user is deleted and then a new user created, the new users will not be given the same serial number.
(意訳)
ユーザのシリアルナンバーを返します。 ユーザに紐付けられたデバイスユニークな番号です; もし、ユーザが削除されて、その後、新しいユーザが作成されても、同じシリアルナンバーが与えられることはありません。
SerialNumberForUser を取得する方法
以下のコードで SerialNumberForUser を取得することが出来ます。// 4.2 未満では動作しません if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { UserManager userManager = (UserManager) getSystemService(USER_SERVICE); UserHandle handle = Process.myUserHandle(); long serialNumberForUser = userManager.getSerialNumberForUser(handle); }
動作させてみる
Nexus7 上に 所有者+3人の新規ユーザを作成し、以下のコードを各ユーザで実行してみました。UserManager userManager = (UserManager) getSystemService(USER_SERVICE); UserHandle handle = Process.myUserHandle(); Log.d(TAG, "uid : " + Process.myUid()); Log.d(TAG, "dir : " + getFilesDir()); Log.d(TAG, "serialNumber : " + userManager.getSerialNumberForUser(handle));
以下がその結果です。
所有者 uid : 10092 dir : /data/data/com.kokufu.android.test.multipleusertest/files serialNumber : 0 ユーザ1 uid : 1010092 dir : /data/user/10/com.kokufu.android.test.multipleusertest/files serialNumber : 1 ユーザ2 uid : 1110092 dir : /data/user/11/com.kokufu.android.test.multipleusertest/files serialNumber : 2 ユーザ3 uid : 1210092 dir : /data/user/12/com.kokufu.android.test.multipleusertest/files serialNumber : 3
uid の下5桁が全ユーザで 10092 になっています。
そして、残りの上位2桁が UserId です。
アプリケーションディレクトリの識別子として使われれているのがわかります。
0 件のコメント:
コメントを投稿