1 /* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License 15 */ 16 package com.android.systemui.tuner; 17 18 import android.app.ActivityManager; 19 import android.content.BroadcastReceiver; 20 import android.content.ComponentName; 21 import android.content.ContentResolver; 22 import android.content.Context; 23 import android.content.DialogInterface; 24 import android.content.DialogInterface.OnClickListener; 25 import android.content.Intent; 26 import android.content.pm.PackageManager; 27 import android.content.pm.PackageManager.NameNotFoundException; 28 import android.content.pm.UserInfo; 29 import android.database.ContentObserver; 30 import android.net.Uri; 31 import android.os.Handler; 32 import android.os.Looper; 33 import android.os.UserHandle; 34 import android.os.UserManager; 35 import android.provider.Settings; 36 import android.text.TextUtils; 37 import android.util.ArrayMap; 38 import android.util.ArraySet; 39 40 import com.android.systemui.BatteryMeterDrawable; 41 import com.android.systemui.DemoMode; 42 import com.android.systemui.R; 43 import com.android.systemui.SystemUI; 44 import com.android.systemui.SystemUIApplication; 45 import com.android.systemui.settings.CurrentUserTracker; 46 import com.android.systemui.statusbar.phone.StatusBarIconController; 47 import com.android.systemui.statusbar.phone.SystemUIDialog; 48 49 import java.util.HashMap; 50 import java.util.Set; 51 52 53 public class TunerService extends SystemUI { 54 55 public static final String ACTION_CLEAR = "com.android.systemui.action.CLEAR_TUNER"; 56 57 private static final String TUNER_VERSION = "sysui_tuner_version"; 58 59 private static final int CURRENT_TUNER_VERSION = 1; 60 61 private final Observer mObserver = new Observer(); 62 // Map of Uris we listen on to their settings keys. 63 private final ArrayMap<Uri, String> mListeningUris = new ArrayMap<>(); 64 // Map of settings keys to the listener. 65 private final HashMap<String, Set<Tunable>> mTunableLookup = new HashMap<>(); 66 67 private ContentResolver mContentResolver; 68 private int mCurrentUser; 69 private CurrentUserTracker mUserTracker; 70 71 @Override 72 public void start() { 73 mContentResolver = mContext.getContentResolver(); 74 75 for (UserInfo user : UserManager.get(mContext).getUsers()) { 76 mCurrentUser = user.getUserHandle().getIdentifier(); 77 if (getValue(TUNER_VERSION, 0) != CURRENT_TUNER_VERSION) { 78 upgradeTuner(getValue(TUNER_VERSION, 0), CURRENT_TUNER_VERSION); 79 } 80 } 81 putComponent(TunerService.class, this); 82 83 mCurrentUser = ActivityManager.getCurrentUser(); 84 mUserTracker = new CurrentUserTracker(mContext) { 85 @Override 86 public void onUserSwitched(int newUserId) { 87 mCurrentUser = newUserId; 88 reloadAll(); 89 reregisterAll(); 90 } 91 }; 92 mUserTracker.startTracking(); 93 } 94 95 private void upgradeTuner(int oldVersion, int newVersion) { 96 if (oldVersion < 1) { 97 String blacklistStr = getValue(StatusBarIconController.ICON_BLACKLIST); 98 if (blacklistStr != null) { 99 ArraySet<String> iconBlacklist = 100 StatusBarIconController.getIconBlacklist(blacklistStr); 101 102 iconBlacklist.add("rotate"); 103 iconBlacklist.add("headset"); 104 105 Settings.Secure.putStringForUser(mContentResolver, 106 StatusBarIconController.ICON_BLACKLIST, 107 TextUtils.join(",", iconBlacklist), mCurrentUser); 108 } 109 } 110 setValue(TUNER_VERSION, newVersion); 111 } 112 113 public String getValue(String setting) { 114 return Settings.Secure.getStringForUser(mContentResolver, setting, mCurrentUser); 115 } 116 117 public void setValue(String setting, String value) { 118 Settings.Secure.putStringForUser(mContentResolver, setting, value, mCurrentUser); 119 } 120 121 public int getValue(String setting, int def) { 122 return Settings.Secure.getIntForUser(mContentResolver, setting, def, mCurrentUser); 123 } 124 125 public void setValue(String setting, int value) { 126 Settings.Secure.putIntForUser(mContentResolver, setting, value, mCurrentUser); 127 } 128 129 public void addTunable(Tunable tunable, String... keys) { 130 for (String key : keys) { 131 addTunable(tunable, key); 132 } 133 } 134 135 private void addTunable(Tunable tunable, String key) { 136 if (!mTunableLookup.containsKey(key)) { 137 mTunableLookup.put(key, new ArraySet<Tunable>()); 138 } 139 mTunableLookup.get(key).add(tunable); 140 Uri uri = Settings.Secure.getUriFor(key); 141 if (!mListeningUris.containsKey(uri)) { 142 mListeningUris.put(uri, key); 143 mContentResolver.registerContentObserver(uri, false, mObserver, mCurrentUser); 144 } 145 // Send the first state. 146 String value = Settings.Secure.getStringForUser(mContentResolver, key, mCurrentUser); 147 tunable.onTuningChanged(key, value); 148 } 149 150 public void removeTunable(Tunable tunable) { 151 for (Set<Tunable> list : mTunableLookup.values()) { 152 list.remove(tunable); 153 } 154 } 155 156 protected void reregisterAll() { 157 if (mListeningUris.size() == 0) { 158 return; 159 } 160 mContentResolver.unregisterContentObserver(mObserver); 161 for (Uri uri : mListeningUris.keySet()) { 162 mContentResolver.registerContentObserver(uri, false, mObserver, mCurrentUser); 163 } 164 } 165 166 public void reloadSetting(Uri uri) { 167 String key = mListeningUris.get(uri); 168 Set<Tunable> tunables = mTunableLookup.get(key); 169 if (tunables == null) { 170 return; 171 } 172 String value = Settings.Secure.getStringForUser(mContentResolver, key, mCurrentUser); 173 for (Tunable tunable : tunables) { 174 tunable.onTuningChanged(key, value); 175 } 176 } 177 178 private void reloadAll() { 179 for (String key : mTunableLookup.keySet()) { 180 String value = Settings.Secure.getStringForUser(mContentResolver, key, 181 mCurrentUser); 182 for (Tunable tunable : mTunableLookup.get(key)) { 183 tunable.onTuningChanged(key, value); 184 } 185 } 186 } 187 188 public void clearAll() { 189 // A couple special cases. 190 Settings.Global.putString(mContentResolver, DemoMode.DEMO_MODE_ALLOWED, null); 191 Settings.System.putString(mContentResolver, BatteryMeterDrawable.SHOW_PERCENT_SETTING, null); 192 Intent intent = new Intent(DemoMode.ACTION_DEMO); 193 intent.putExtra(DemoMode.EXTRA_COMMAND, DemoMode.COMMAND_EXIT); 194 mContext.sendBroadcast(intent); 195 196 for (String key : mTunableLookup.keySet()) { 197 Settings.Secure.putString(mContentResolver, key, null); 198 } 199 } 200 201 // Only used in other processes, such as the tuner. 202 private static TunerService sInstance; 203 204 public static TunerService get(Context context) { 205 TunerService service = null; 206 if (context.getApplicationContext() instanceof SystemUIApplication) { 207 SystemUIApplication sysUi = (SystemUIApplication) context.getApplicationContext(); 208 service = sysUi.getComponent(TunerService.class); 209 } 210 if (service == null) { 211 // Can't get it as a component, must in the tuner, lets just create one for now. 212 return getStaticService(context); 213 } 214 return service; 215 } 216 217 private static TunerService getStaticService(Context context) { 218 if (sInstance == null) { 219 sInstance = new TunerService(); 220 sInstance.mContext = context.getApplicationContext(); 221 sInstance.mComponents = new HashMap<>(); 222 sInstance.start(); 223 } 224 return sInstance; 225 } 226 227 public static final void showResetRequest(final Context context, final Runnable onDisabled) { 228 SystemUIDialog dialog = new SystemUIDialog(context); 229 dialog.setShowForAllUsers(true); 230 dialog.setMessage(R.string.remove_from_settings_prompt); 231 dialog.setButton(DialogInterface.BUTTON_NEGATIVE, context.getString(R.string.cancel), 232 (OnClickListener) null); 233 dialog.setButton(DialogInterface.BUTTON_POSITIVE, 234 context.getString(R.string.guest_exit_guest_dialog_remove), new OnClickListener() { 235 @Override 236 public void onClick(DialogInterface dialog, int which) { 237 // Tell the tuner (in main SysUI process) to clear all its settings. 238 context.sendBroadcast(new Intent(TunerService.ACTION_CLEAR)); 239 // Disable access to tuner. 240 TunerService.setTunerEnabled(context, false); 241 // Make them sit through the warning dialog again. 242 Settings.Secure.putInt(context.getContentResolver(), 243 TunerFragment.SETTING_SEEN_TUNER_WARNING, 0); 244 if (onDisabled != null) { 245 onDisabled.run(); 246 } 247 } 248 }); 249 dialog.show(); 250 } 251 252 public static final void setTunerEnabled(Context context, boolean enabled) { 253 userContext(context).getPackageManager().setComponentEnabledSetting( 254 new ComponentName(context, TunerActivity.class), 255 enabled ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED 256 : PackageManager.COMPONENT_ENABLED_STATE_DISABLED, 257 PackageManager.DONT_KILL_APP); 258 } 259 260 public static final boolean isTunerEnabled(Context context) { 261 return userContext(context).getPackageManager().getComponentEnabledSetting( 262 new ComponentName(context, TunerActivity.class)) 263 == PackageManager.COMPONENT_ENABLED_STATE_ENABLED; 264 } 265 266 private static Context userContext(Context context) { 267 try { 268 return context.createPackageContextAsUser(context.getPackageName(), 0, 269 new UserHandle(ActivityManager.getCurrentUser())); 270 } catch (NameNotFoundException e) { 271 return context; 272 } 273 } 274 275 private class Observer extends ContentObserver { 276 public Observer() { 277 super(new Handler(Looper.getMainLooper())); 278 } 279 280 @Override 281 public void onChange(boolean selfChange, Uri uri, int userId) { 282 if (userId == ActivityManager.getCurrentUser()) { 283 reloadSetting(uri); 284 } 285 } 286 } 287 288 public interface Tunable { 289 void onTuningChanged(String key, String newValue); 290 } 291 292 public static class ClearReceiver extends BroadcastReceiver { 293 @Override 294 public void onReceive(Context context, Intent intent) { 295 if (ACTION_CLEAR.equals(intent.getAction())) { 296 get(context).clearAll(); 297 } 298 } 299 } 300 } 301