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 static com.android.systemui.Dependency.BG_HANDLER_NAME; 19 20 import android.app.ActivityManager; 21 import android.content.ContentResolver; 22 import android.content.Context; 23 import android.content.Intent; 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.Looper; 29 import android.os.UserManager; 30 import android.provider.Settings; 31 import android.provider.Settings.Secure; 32 import android.text.TextUtils; 33 import android.util.ArrayMap; 34 import android.util.ArraySet; 35 36 import com.android.internal.util.ArrayUtils; 37 import com.android.systemui.DemoMode; 38 import com.android.systemui.qs.QSTileHost; 39 import com.android.systemui.settings.CurrentUserTracker; 40 import com.android.systemui.statusbar.phone.StatusBarIconController; 41 import com.android.systemui.util.leak.LeakDetector; 42 43 import java.util.HashMap; 44 import java.util.HashSet; 45 import java.util.Set; 46 47 import javax.inject.Inject; 48 import javax.inject.Named; 49 import javax.inject.Singleton; 50 51 52 /** 53 */ 54 @Singleton 55 public class TunerServiceImpl extends TunerService { 56 57 private static final String TUNER_VERSION = "sysui_tuner_version"; 58 59 private static final int CURRENT_TUNER_VERSION = 4; 60 61 // Things that use the tunable infrastructure but are now real user settings and 62 // shouldn't be reset with tuner settings. 63 private static final String[] RESET_BLACKLIST = new String[] { 64 QSTileHost.TILES_SETTING, 65 Settings.Secure.DOZE_ALWAYS_ON 66 }; 67 68 private final Observer mObserver = new Observer(); 69 // Map of Uris we listen on to their settings keys. 70 private final ArrayMap<Uri, String> mListeningUris = new ArrayMap<>(); 71 // Map of settings keys to the listener. 72 private final HashMap<String, Set<Tunable>> mTunableLookup = new HashMap<>(); 73 // Set of all tunables, used for leak detection. 74 private final HashSet<Tunable> mTunables = LeakDetector.ENABLED ? new HashSet<>() : null; 75 private final Context mContext; 76 private final LeakDetector mLeakDetector; 77 78 private ContentResolver mContentResolver; 79 private int mCurrentUser; 80 private CurrentUserTracker mUserTracker; 81 82 /** 83 */ 84 @Inject 85 public TunerServiceImpl(Context context, @Named(BG_HANDLER_NAME) Handler bgHandler, 86 LeakDetector leakDetector) { 87 mContext = context; 88 mContentResolver = mContext.getContentResolver(); 89 mLeakDetector = leakDetector; 90 91 for (UserInfo user : UserManager.get(mContext).getUsers()) { 92 mCurrentUser = user.getUserHandle().getIdentifier(); 93 if (getValue(TUNER_VERSION, 0) != CURRENT_TUNER_VERSION) { 94 upgradeTuner(getValue(TUNER_VERSION, 0), CURRENT_TUNER_VERSION, bgHandler); 95 } 96 } 97 98 mCurrentUser = ActivityManager.getCurrentUser(); 99 mUserTracker = new CurrentUserTracker(mContext) { 100 @Override 101 public void onUserSwitched(int newUserId) { 102 mCurrentUser = newUserId; 103 reloadAll(); 104 reregisterAll(); 105 } 106 }; 107 mUserTracker.startTracking(); 108 } 109 110 @Override 111 public void destroy() { 112 mUserTracker.stopTracking(); 113 } 114 115 private void upgradeTuner(int oldVersion, int newVersion, Handler bgHandler) { 116 if (oldVersion < 1) { 117 String blacklistStr = getValue(StatusBarIconController.ICON_BLACKLIST); 118 if (blacklistStr != null) { 119 ArraySet<String> iconBlacklist = 120 StatusBarIconController.getIconBlacklist(blacklistStr); 121 122 iconBlacklist.add("rotate"); 123 iconBlacklist.add("headset"); 124 125 Settings.Secure.putStringForUser(mContentResolver, 126 StatusBarIconController.ICON_BLACKLIST, 127 TextUtils.join(",", iconBlacklist), mCurrentUser); 128 } 129 } 130 if (oldVersion < 2) { 131 setTunerEnabled(mContext, false); 132 } 133 // 3 Removed because of a revert. 134 if (oldVersion < 4) { 135 // Delay this so that we can wait for everything to be registered first. 136 final int user = mCurrentUser; 137 bgHandler.postDelayed( 138 () -> clearAllFromUser(user), 5000); 139 } 140 setValue(TUNER_VERSION, newVersion); 141 } 142 143 @Override 144 public String getValue(String setting) { 145 return Settings.Secure.getStringForUser(mContentResolver, setting, mCurrentUser); 146 } 147 148 @Override 149 public void setValue(String setting, String value) { 150 Settings.Secure.putStringForUser(mContentResolver, setting, value, mCurrentUser); 151 } 152 153 @Override 154 public int getValue(String setting, int def) { 155 return Settings.Secure.getIntForUser(mContentResolver, setting, def, mCurrentUser); 156 } 157 158 @Override 159 public String getValue(String setting, String def) { 160 String ret = Secure.getStringForUser(mContentResolver, setting, mCurrentUser); 161 if (ret == null) return def; 162 return ret; 163 } 164 165 @Override 166 public void setValue(String setting, int value) { 167 Settings.Secure.putIntForUser(mContentResolver, setting, value, mCurrentUser); 168 } 169 170 @Override 171 public void addTunable(Tunable tunable, String... keys) { 172 for (String key : keys) { 173 addTunable(tunable, key); 174 } 175 } 176 177 private void addTunable(Tunable tunable, String key) { 178 if (!mTunableLookup.containsKey(key)) { 179 mTunableLookup.put(key, new ArraySet<Tunable>()); 180 } 181 mTunableLookup.get(key).add(tunable); 182 if (LeakDetector.ENABLED) { 183 mTunables.add(tunable); 184 mLeakDetector.trackCollection(mTunables, "TunerService.mTunables"); 185 } 186 Uri uri = Settings.Secure.getUriFor(key); 187 if (!mListeningUris.containsKey(uri)) { 188 mListeningUris.put(uri, key); 189 mContentResolver.registerContentObserver(uri, false, mObserver, mCurrentUser); 190 } 191 // Send the first state. 192 String value = Settings.Secure.getStringForUser(mContentResolver, key, mCurrentUser); 193 tunable.onTuningChanged(key, value); 194 } 195 196 @Override 197 public void removeTunable(Tunable tunable) { 198 for (Set<Tunable> list : mTunableLookup.values()) { 199 list.remove(tunable); 200 } 201 if (LeakDetector.ENABLED) { 202 mTunables.remove(tunable); 203 } 204 } 205 206 protected void reregisterAll() { 207 if (mListeningUris.size() == 0) { 208 return; 209 } 210 mContentResolver.unregisterContentObserver(mObserver); 211 for (Uri uri : mListeningUris.keySet()) { 212 mContentResolver.registerContentObserver(uri, false, mObserver, mCurrentUser); 213 } 214 } 215 216 private void reloadSetting(Uri uri) { 217 String key = mListeningUris.get(uri); 218 Set<Tunable> tunables = mTunableLookup.get(key); 219 if (tunables == null) { 220 return; 221 } 222 String value = Settings.Secure.getStringForUser(mContentResolver, key, mCurrentUser); 223 for (Tunable tunable : tunables) { 224 tunable.onTuningChanged(key, value); 225 } 226 } 227 228 private void reloadAll() { 229 for (String key : mTunableLookup.keySet()) { 230 String value = Settings.Secure.getStringForUser(mContentResolver, key, 231 mCurrentUser); 232 for (Tunable tunable : mTunableLookup.get(key)) { 233 tunable.onTuningChanged(key, value); 234 } 235 } 236 } 237 238 @Override 239 public void clearAll() { 240 clearAllFromUser(mCurrentUser); 241 } 242 243 public void clearAllFromUser(int user) { 244 // A couple special cases. 245 Settings.Global.putString(mContentResolver, DemoMode.DEMO_MODE_ALLOWED, null); 246 Intent intent = new Intent(DemoMode.ACTION_DEMO); 247 intent.putExtra(DemoMode.EXTRA_COMMAND, DemoMode.COMMAND_EXIT); 248 mContext.sendBroadcast(intent); 249 250 for (String key : mTunableLookup.keySet()) { 251 if (ArrayUtils.contains(RESET_BLACKLIST, key)) { 252 continue; 253 } 254 Settings.Secure.putStringForUser(mContentResolver, key, null, user); 255 } 256 } 257 258 private class Observer extends ContentObserver { 259 public Observer() { 260 super(new Handler(Looper.getMainLooper())); 261 } 262 263 @Override 264 public void onChange(boolean selfChange, Uri uri, int userId) { 265 if (userId == ActivityManager.getCurrentUser()) { 266 reloadSetting(uri); 267 } 268 } 269 } 270 } 271