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.content.ComponentName; 19 import android.content.ContentResolver; 20 import android.content.Context; 21 import android.content.DialogInterface; 22 import android.content.Intent; 23 import android.content.pm.PackageManager; 24 import android.content.pm.UserInfo; 25 import android.database.ContentObserver; 26 import android.net.Uri; 27 import android.os.Handler; 28 import android.os.HandlerExecutor; 29 import android.os.Looper; 30 import android.os.UserManager; 31 import android.provider.Settings; 32 import android.provider.Settings.Secure; 33 import android.text.TextUtils; 34 import android.util.ArrayMap; 35 import android.util.ArraySet; 36 37 import com.android.internal.util.ArrayUtils; 38 import com.android.systemui.DejankUtils; 39 import com.android.systemui.R; 40 import com.android.systemui.dagger.SysUISingleton; 41 import com.android.systemui.dagger.qualifiers.Main; 42 import com.android.systemui.demomode.DemoModeController; 43 import com.android.systemui.qs.QSTileHost; 44 import com.android.systemui.settings.UserTracker; 45 import com.android.systemui.statusbar.phone.StatusBarIconController; 46 import com.android.systemui.statusbar.phone.SystemUIDialog; 47 import com.android.systemui.util.leak.LeakDetector; 48 49 import java.util.HashSet; 50 import java.util.Set; 51 import java.util.concurrent.ConcurrentHashMap; 52 53 import javax.inject.Inject; 54 55 56 /** 57 */ 58 @SysUISingleton 59 public class TunerServiceImpl extends TunerService { 60 61 private static final String TAG = "TunerService"; 62 private static final String TUNER_VERSION = "sysui_tuner_version"; 63 64 private static final int CURRENT_TUNER_VERSION = 4; 65 66 // Things that use the tunable infrastructure but are now real user settings and 67 // shouldn't be reset with tuner settings. 68 private static final String[] RESET_EXCEPTION_LIST = new String[] { 69 QSTileHost.TILES_SETTING, 70 Settings.Secure.DOZE_ALWAYS_ON, 71 Settings.Secure.MEDIA_CONTROLS_RESUME, 72 Settings.Secure.MEDIA_CONTROLS_RECOMMENDATION 73 }; 74 75 private final Observer mObserver = new Observer(); 76 // Map of Uris we listen on to their settings keys. 77 private final ArrayMap<Uri, String> mListeningUris = new ArrayMap<>(); 78 // Map of settings keys to the listener. 79 private final ConcurrentHashMap<String, Set<Tunable>> mTunableLookup = 80 new ConcurrentHashMap<>(); 81 // Set of all tunables, used for leak detection. 82 private final HashSet<Tunable> mTunables = LeakDetector.ENABLED ? new HashSet<>() : null; 83 private final Context mContext; 84 private final LeakDetector mLeakDetector; 85 private final DemoModeController mDemoModeController; 86 87 private ContentResolver mContentResolver; 88 private int mCurrentUser; 89 private UserTracker.Callback mCurrentUserTracker; 90 private UserTracker mUserTracker; 91 private final ComponentName mTunerComponent; 92 93 /** 94 */ 95 @Inject TunerServiceImpl( Context context, @Main Handler mainHandler, LeakDetector leakDetector, DemoModeController demoModeController, UserTracker userTracker)96 public TunerServiceImpl( 97 Context context, 98 @Main Handler mainHandler, 99 LeakDetector leakDetector, 100 DemoModeController demoModeController, 101 UserTracker userTracker) { 102 super(context); 103 mContext = context; 104 mContentResolver = mContext.getContentResolver(); 105 mLeakDetector = leakDetector; 106 mDemoModeController = demoModeController; 107 mUserTracker = userTracker; 108 mTunerComponent = new ComponentName(mContext, TunerActivity.class); 109 110 for (UserInfo user : UserManager.get(mContext).getUsers()) { 111 mCurrentUser = user.getUserHandle().getIdentifier(); 112 if (getValue(TUNER_VERSION, 0) != CURRENT_TUNER_VERSION) { 113 upgradeTuner(getValue(TUNER_VERSION, 0), CURRENT_TUNER_VERSION, mainHandler); 114 } 115 } 116 117 mCurrentUser = mUserTracker.getUserId(); 118 mCurrentUserTracker = new UserTracker.Callback() { 119 @Override 120 public void onUserChanged(int newUser, Context userContext) { 121 mCurrentUser = newUser; 122 reloadAll(); 123 reregisterAll(); 124 } 125 }; 126 mUserTracker.addCallback(mCurrentUserTracker, 127 new HandlerExecutor(mainHandler)); 128 } 129 130 @Override destroy()131 public void destroy() { 132 mUserTracker.removeCallback(mCurrentUserTracker); 133 } 134 upgradeTuner(int oldVersion, int newVersion, Handler mainHandler)135 private void upgradeTuner(int oldVersion, int newVersion, Handler mainHandler) { 136 if (oldVersion < 1) { 137 String hideListStr = getValue(StatusBarIconController.ICON_HIDE_LIST); 138 if (hideListStr != null) { 139 ArraySet<String> iconHideList = 140 StatusBarIconController.getIconHideList(mContext, hideListStr); 141 142 iconHideList.add("rotate"); 143 iconHideList.add("headset"); 144 145 Settings.Secure.putStringForUser(mContentResolver, 146 StatusBarIconController.ICON_HIDE_LIST, 147 TextUtils.join(",", iconHideList), mCurrentUser); 148 } 149 } 150 if (oldVersion < 2) { 151 setTunerEnabled(false); 152 } 153 // 3 Removed because of a revert. 154 if (oldVersion < 4) { 155 // Delay this so that we can wait for everything to be registered first. 156 final int user = mCurrentUser; 157 mainHandler.postDelayed( 158 () -> clearAllFromUser(user), 5000); 159 } 160 setValue(TUNER_VERSION, newVersion); 161 } 162 163 @Override getValue(String setting)164 public String getValue(String setting) { 165 return Settings.Secure.getStringForUser(mContentResolver, setting, mCurrentUser); 166 } 167 168 @Override setValue(String setting, String value)169 public void setValue(String setting, String value) { 170 Settings.Secure.putStringForUser(mContentResolver, setting, value, mCurrentUser); 171 } 172 173 @Override getValue(String setting, int def)174 public int getValue(String setting, int def) { 175 return Settings.Secure.getIntForUser(mContentResolver, setting, def, mCurrentUser); 176 } 177 178 @Override getValue(String setting, String def)179 public String getValue(String setting, String def) { 180 String ret = Secure.getStringForUser(mContentResolver, setting, mCurrentUser); 181 if (ret == null) return def; 182 return ret; 183 } 184 185 @Override setValue(String setting, int value)186 public void setValue(String setting, int value) { 187 Settings.Secure.putIntForUser(mContentResolver, setting, value, mCurrentUser); 188 } 189 190 @Override addTunable(Tunable tunable, String... keys)191 public void addTunable(Tunable tunable, String... keys) { 192 for (String key : keys) { 193 addTunable(tunable, key); 194 } 195 } 196 addTunable(Tunable tunable, String key)197 private void addTunable(Tunable tunable, String key) { 198 if (!mTunableLookup.containsKey(key)) { 199 mTunableLookup.put(key, new ArraySet<Tunable>()); 200 } 201 mTunableLookup.get(key).add(tunable); 202 if (LeakDetector.ENABLED) { 203 mTunables.add(tunable); 204 mLeakDetector.trackCollection(mTunables, "TunerService.mTunables"); 205 } 206 Uri uri = Settings.Secure.getUriFor(key); 207 if (!mListeningUris.containsKey(uri)) { 208 mListeningUris.put(uri, key); 209 mContentResolver.registerContentObserver(uri, false, mObserver, mCurrentUser); 210 } 211 // Send the first state. 212 String value = DejankUtils.whitelistIpcs(() -> Settings.Secure 213 .getStringForUser(mContentResolver, key, mCurrentUser)); 214 tunable.onTuningChanged(key, value); 215 } 216 217 @Override removeTunable(Tunable tunable)218 public void removeTunable(Tunable tunable) { 219 for (Set<Tunable> list : mTunableLookup.values()) { 220 list.remove(tunable); 221 } 222 if (LeakDetector.ENABLED) { 223 mTunables.remove(tunable); 224 } 225 } 226 reregisterAll()227 protected void reregisterAll() { 228 if (mListeningUris.size() == 0) { 229 return; 230 } 231 mContentResolver.unregisterContentObserver(mObserver); 232 for (Uri uri : mListeningUris.keySet()) { 233 mContentResolver.registerContentObserver(uri, false, mObserver, mCurrentUser); 234 } 235 } 236 reloadSetting(Uri uri)237 private void reloadSetting(Uri uri) { 238 String key = mListeningUris.get(uri); 239 Set<Tunable> tunables = mTunableLookup.get(key); 240 if (tunables == null) { 241 return; 242 } 243 String value = Settings.Secure.getStringForUser(mContentResolver, key, mCurrentUser); 244 for (Tunable tunable : tunables) { 245 tunable.onTuningChanged(key, value); 246 } 247 } 248 reloadAll()249 private void reloadAll() { 250 for (String key : mTunableLookup.keySet()) { 251 String value = Settings.Secure.getStringForUser(mContentResolver, key, 252 mCurrentUser); 253 for (Tunable tunable : mTunableLookup.get(key)) { 254 tunable.onTuningChanged(key, value); 255 } 256 } 257 } 258 259 @Override clearAll()260 public void clearAll() { 261 clearAllFromUser(mCurrentUser); 262 } 263 clearAllFromUser(int user)264 public void clearAllFromUser(int user) { 265 // Turn off demo mode 266 mDemoModeController.requestFinishDemoMode(); 267 mDemoModeController.requestSetDemoModeAllowed(false); 268 269 // A couple special cases. 270 for (String key : mTunableLookup.keySet()) { 271 if (ArrayUtils.contains(RESET_EXCEPTION_LIST, key)) { 272 continue; 273 } 274 Settings.Secure.putStringForUser(mContentResolver, key, null, user); 275 } 276 } 277 278 279 @Override setTunerEnabled(boolean enabled)280 public void setTunerEnabled(boolean enabled) { 281 mUserTracker.getUserContext().getPackageManager().setComponentEnabledSetting( 282 mTunerComponent, 283 enabled ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED 284 : PackageManager.COMPONENT_ENABLED_STATE_DISABLED, 285 PackageManager.DONT_KILL_APP 286 ); 287 } 288 289 @Override isTunerEnabled()290 public boolean isTunerEnabled() { 291 return mUserTracker.getUserContext().getPackageManager().getComponentEnabledSetting( 292 mTunerComponent) == PackageManager.COMPONENT_ENABLED_STATE_ENABLED; 293 } 294 295 @Override showResetRequest(Runnable onDisabled)296 public void showResetRequest(Runnable onDisabled) { 297 SystemUIDialog dialog = new SystemUIDialog(mContext); 298 dialog.setShowForAllUsers(true); 299 dialog.setMessage(R.string.remove_from_settings_prompt); 300 dialog.setButton(DialogInterface.BUTTON_NEGATIVE, mContext.getString(R.string.cancel), 301 (DialogInterface.OnClickListener) null); 302 dialog.setButton(DialogInterface.BUTTON_POSITIVE, 303 mContext.getString(R.string.qs_customize_remove), (d, which) -> { 304 // Tell the tuner (in main SysUI process) to clear all its settings. 305 mContext.sendBroadcast(new Intent(TunerService.ACTION_CLEAR)); 306 // Disable access to tuner. 307 setTunerEnabled(false); 308 // Make them sit through the warning dialog again. 309 Secure.putInt(mContext.getContentResolver(), 310 TunerFragment.SETTING_SEEN_TUNER_WARNING, 0); 311 if (onDisabled != null) { 312 onDisabled.run(); 313 } 314 }); 315 dialog.show(); 316 } 317 318 private class Observer extends ContentObserver { Observer()319 public Observer() { 320 super(new Handler(Looper.getMainLooper())); 321 } 322 323 @Override onChange(boolean selfChange, java.util.Collection<Uri> uris, int flags, int userId)324 public void onChange(boolean selfChange, java.util.Collection<Uri> uris, 325 int flags, int userId) { 326 if (userId == mUserTracker.getUserId()) { 327 for (Uri u : uris) { 328 reloadSetting(u); 329 } 330 } 331 } 332 333 } 334 } 335