前回の続きです。
Quick Settings にTileを表示するところまで実装したので、次はそのTileをタップした時に何か便利な挙動をするのを目指してみます。
具体的には下記の二つどちらかできたら便利だなぁと思って調べてみました。
どちらもできませんでした:;(∩´﹏`∩);:
- USB debuggingのON/OFF
- 「Activityを保持しない」の設定のON/OFF
それではそれぞれなぜダメだったのか、調べた結果を書き残します。
USB debuggingのON/OFF
まず、開発者オプションの画面のコードを追っていきます。
それぞれの項目のlabelのstring resourceとかから適当にOpen Grokを検索すると、 DevelopmentSettings.java
にたどり着きます。
で、ここのON/OFFをどうやってとっているかというと、こんな感じで Settings.Global.ADB_ENABLED
の値を見ています。
642 private void updateAllOptions() { 643 final Context context = getActivity(); 644 final ContentResolver cr = context.getContentResolver(); (省略) 646 updateSwitchPreference(mEnableAdb, Settings.Global.getInt(cr, 647 Settings.Global.ADB_ENABLED, 0) != 0);
長くなるので省略しますが、この Settings.Global.getInt()
は content://settings/global
のcontent providerから値を取得しようとします。
1428 public static final String AUTHORITY = "settings"; 6524 public static final class Global extends NameValueTable { 6525 /** 6526 * The content:// style URL for global secure settings items. Not public. 6527 */ 6528 public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/global");
SettingsProvider.java
の中で、getの方は問題ないのですが、updateの方はpermission checkが入っています。
754 private Setting getGlobalSetting(String name) { 755 if (DEBUG) { 756 Slog.v(LOG_TAG, "getGlobalSetting(" + name + ")"); 757 } 758 759 // Get the value. 760 synchronized (mLock) { 761 return mSettingsRegistry.getSettingLocked(SETTINGS_TYPE_GLOBAL, 762 UserHandle.USER_SYSTEM, name); 763 } 764 } 766 private boolean updateGlobalSetting(String name, String value, int requestingUserId, 767 boolean forceNotify) { 768 if (DEBUG) { 769 Slog.v(LOG_TAG, "updateGlobalSetting(" + name + ", " + value + ")"); 770 } 771 return mutateGlobalSetting(name, value, requestingUserId, MUTATION_OPERATION_UPDATE, 772 forceNotify); 773 } 792 private boolean mutateGlobalSetting(String name, String value, int requestingUserId, 793 int operation, boolean forceNotify) { 794 // Make sure the caller can change the settings - treated as secure. 795 enforceWritePermission(Manifest.permission.WRITE_SECURE_SETTINGS);
書き込みを行うにはWRITE_SECURE_SETTINGS
が必要なのですが、こちらは残念ながら3rd partyのアプリは設定できないようになっています。
このpermissionがない場合、Security Exceptionで落ちます。。。
1305 private void enforceWritePermission(String permission) { 1306 if (getContext().checkCallingOrSelfPermission(permission) 1307 != PackageManager.PERMISSION_GRANTED) { 1308 throw new SecurityException("Permission denial: writing to settings requires:" 1309 + permission); 1310 } 1311 }
「Activityを保持しない」の設定のON/OFF
USB debuggingと同様にDevelopmentSettings.java
を見ていきましょう。
「Activityを保持しない」のpreference keyは IMMEDIATELY_DESTROY_ACTIVITIES_KEY
です。ちょっとアグレッシブな名前です。
読み込みの方はUSB debuggingと同様に Settings.Global
に定義してある値を読みに行きます。
1578 private void updateImmediatelyDestroyActivitiesOptions() { 1579 updateSwitchPreference(mImmediatelyDestroyActivities, Settings.Global.getInt( 1580 getActivity().getContentResolver(), Settings.Global.ALWAYS_FINISH_ACTIVITIES, 0) != 0); 1581 }
書き込みの方はちょっと挙動が変わります。
1570 private void writeImmediatelyDestroyActivitiesOptions() { 1571 try { 1572 ActivityManagerNative.getDefault().setAlwaysFinish( 1573 mImmediatelyDestroyActivities.isChecked()); 1574 } catch (RemoteException ex) { 1575 } 1576 }
こんな感じでActivityManagerNative
を操作しています。ActivityManagerNative
自体は@hideなAPIなので残念ながら3rd partyのアプリでは使えません。
ActivityManagerService経由で操作する方法もありますが、こちらを経由するにしてもpermissionが必要です。
11940 @Override 11941 public void setAlwaysFinish(boolean enabled) { 11942 enforceCallingPermission(android.Manifest.permission.SET_ALWAYS_FINISH, 11943 "setAlwaysFinish()"); 11944 11945 long ident = Binder.clearCallingIdentity(); 11946 try { 11947 Settings.Global.putInt( 11948 mContext.getContentResolver(), 11949 Settings.Global.ALWAYS_FINISH_ACTIVITIES, enabled ? 1 : 0); 11950 11951 synchronized (this) { 11952 mAlwaysFinishActivities = enabled; 11953 } 11954 } finally { 11955 Binder.restoreCallingIdentity(ident); 11956 } 11957 }
SET_ALWAYS_FINISH
のpermissionもWRITE_SECURE_SETTINGS
と同様に3rd partyのアプリでは設定できません。
というわけでどちらも3rd partyのアプリでは書き換えられないというお話でした(´・_・`)