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