#navi_header|技術| AndroidManifest.xmlでの""と"android:permission"属性の使用例+メモ。 #more|| #outline|| ---- * 実験 - "PermissionTest1" : Activity, BroadcastReceiver, Serviceを用意しているアプリ。全てのコンポーネントでIntentFilterを設定せず、パッケージ名とクラス名で直接呼び出すものとする。それぞれ次の5パターンを用意。 -- android:exported=false : 外部非公開。IntentFilterが無いので、特に指定しない場合のデフォルト設定。 -- android:exported=true : 外部公開。 -- android:permission="test.perms1.TESTPERM_NORMAL" : "normal"レベルのpermissionを要求(+外部公開)。 -- android:permission="test.perms1.TESTPERM_DANGEROUS" : "dangerous"レベルのpermissionを要求(+外部公開)。 -- android:permission="test.perms1.TESTPERM_SIGNATURE" : "signature"レベルのpermissionを要求(+外部公開)。 - "PermissionTest2" : PermissionTest1の各コンポーネントを呼び出すアプリ。AndroidManifest.xmlで必要な""を定義済み。PermissionTest1と同じ証明書で署名する。 - "PermissionTest3" : PermissionTest2と内容は同じだが、PermissionTest1,2と異なる証明書で署名する。 Activityは表示されたか否か、BroadcastReceiver/ServiceはLogを出力するようにしてそれで判定する。PermissionTest2,3でもstartActivity()/sendBroadcast()/startService()それぞれtry-catchで囲み、ThrowableをLogに出力するようにしている。 予想としては、外部非公開は全て呼べない、それ以外はPermissionTest3だけ、"signature"レベルのpermissionが署名違いで呼べない結果になると思われる。 ** サンプルコード [[yb://medias/android/PermissionTestEx01.zip]] ** PermissionTest1のAndroidManifest.xml #pre||> ||< ** PermissionTest2で使っている呼び出しコード PermissionTest3も同様。 test.perms2.Main.java: #code|java|> package test.perms2; import android.app.Activity; import android.content.Intent; import android.net.Uri; import android.os.Bundle; import android.view.View; import android.widget.AdapterView; import android.widget.AdapterView.OnItemSelectedListener; import android.widget.ArrayAdapter; import android.widget.Spinner; public class Main extends Activity { /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); ArrayAdapter classnameArrayAdapter = ArrayAdapter.createFromResource( this, R.array.classname_array, android.R.layout.simple_spinner_item); Spinner spinner = (Spinner) findViewById(R.id.main_spn_activities); classnameArrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); spinner.setAdapter(classnameArrayAdapter); spinner.setOnItemSelectedListener(new ActivitiesSelectedListener()); spinner = (Spinner) findViewById(R.id.main_spn_receivers); classnameArrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); spinner.setAdapter(classnameArrayAdapter); spinner.setOnItemSelectedListener(new ReceiversSelectedListener()); spinner = (Spinner) findViewById(R.id.main_spn_services); classnameArrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); spinner.setAdapter(classnameArrayAdapter); spinner.setOnItemSelectedListener(new ServicesSelectedListener()); } public class ActivitiesSelectedListener implements OnItemSelectedListener { @Override public void onItemSelected(AdapterView parent, View view, int pos, long id) { if (0 == pos) { return; } String classname = "test.perms1.activities." + parent.getItemAtPosition(pos).toString(); Intent i = new Intent(); i.setClassName("test.perms1", classname); view.getContext().startActivity(i); } @Override public void onNothingSelected(AdapterView parent) { } } public class ReceiversSelectedListener implements OnItemSelectedListener { @Override public void onItemSelected(AdapterView parent, View view, int pos, long id) { if (0 == pos) { return; } String classname = "test.perms1.receivers." + parent.getItemAtPosition(pos).toString(); Intent i = new Intent(); i.setClassName("test.perms1", classname); i.setData(Uri.parse("test://" + classname)); view.getContext().sendBroadcast(i); } @Override public void onNothingSelected(AdapterView parent) { } } public class ServicesSelectedListener implements OnItemSelectedListener { @Override public void onItemSelected(AdapterView parent, View view, int pos, long id) { if (0 == pos) { return; } String classname = "test.perms1.services." + parent.getItemAtPosition(pos).toString(); Intent i = new Intent(); i.setClassName("test.perms1", classname); i.setData(Uri.parse("test://" + classname)); view.getContext().startService(i); } @Override public void onNothingSelected(AdapterView parent) { } } } ||< res/values/strings.xml: #pre||> Hello World, Main! PermissionTest2 Choose Activity Choose Receiver Choose Service - NoPermHidden NoPermPublic NormalPerm DangerousPerm SignaturePerm ||< ** 結果 予想通り + android:exported="false"の非公開は呼べない。 + PermissionTest3でのみ、"signature"レベルのpermissionが署名違いで呼べない。 という結果になった。 Activity: | | PermissionTest2 | PermissionTest3 | | android:exported=false | SecurityException: Permission Denial | 同左 | | android:exported=true | o | o | | "normal" level | o | o | | "dangerous" level | o | o | | "signature" level | o | SecurityException: Permission Denial | BroadcastReceiver: | | PermissionTest2 | PermissionTest3 | | android:exported=false | (Log-WARN): Permission Denial | 同左 | | android:exported=true | o | o | | "normal" level | o | o | | "dangerous" level | o | o | | "signature" level | o | (Log-WARN): Permission Denial | Service: | | PermissionTest2 | PermissionTest3 | | android:exported=false | SecurityException: Not allowed to start service Intent | 同左 | | android:exported=true | o | o | | "normal" level | o | o | | "dangerous" level | o | o | | "signature" level | o | SecurityException: Not allowed to start service Intent | ** 参考資料 - Security and Permissions | Android Developers -- http://developer.android.com/guide/topics/security/security.html - コンテントプロバイダを公開しない方法 - haruserのめもちょ -- http://d.hatena.ne.jp/haruser/20090823/1251042430 - ContentProvider で特定のアプリにのみ利用を許可する - おともだち革命 -- http://d.hatena.ne.jp/s5r/20110225/1298613278 #navi_footer|技術|