/* * Copyright (C) 2015 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License */ package com.android.systemui.tuner; import android.app.ActivityManager; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.pm.UserInfo; import android.database.ContentObserver; import android.net.Uri; import android.os.Handler; import android.os.Looper; import android.os.UserManager; import android.provider.Settings; import android.provider.Settings.Secure; import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; import com.android.internal.util.ArrayUtils; import com.android.systemui.DemoMode; import com.android.systemui.Dependency; import com.android.systemui.qs.QSTileHost; import com.android.systemui.settings.CurrentUserTracker; import com.android.systemui.statusbar.phone.StatusBarIconController; import com.android.systemui.util.leak.LeakDetector; import java.util.HashMap; import java.util.HashSet; import java.util.Set; public class TunerServiceImpl extends TunerService { private static final String TUNER_VERSION = "sysui_tuner_version"; private static final int CURRENT_TUNER_VERSION = 4; // Things that use the tunable infrastructure but are now real user settings and // shouldn't be reset with tuner settings. private static final String[] RESET_BLACKLIST = new String[] { QSTileHost.TILES_SETTING, }; private final Observer mObserver = new Observer(); // Map of Uris we listen on to their settings keys. private final ArrayMap mListeningUris = new ArrayMap<>(); // Map of settings keys to the listener. private final HashMap> mTunableLookup = new HashMap<>(); // Set of all tunables, used for leak detection. private final HashSet mTunables = LeakDetector.ENABLED ? new HashSet<>() : null; private final Context mContext; private ContentResolver mContentResolver; private int mCurrentUser; private CurrentUserTracker mUserTracker; public TunerServiceImpl(Context context) { mContext = context; mContentResolver = mContext.getContentResolver(); for (UserInfo user : UserManager.get(mContext).getUsers()) { mCurrentUser = user.getUserHandle().getIdentifier(); if (getValue(TUNER_VERSION, 0) != CURRENT_TUNER_VERSION) { upgradeTuner(getValue(TUNER_VERSION, 0), CURRENT_TUNER_VERSION); } } mCurrentUser = ActivityManager.getCurrentUser(); mUserTracker = new CurrentUserTracker(mContext) { @Override public void onUserSwitched(int newUserId) { mCurrentUser = newUserId; reloadAll(); reregisterAll(); } }; mUserTracker.startTracking(); } @Override public void destroy() { mUserTracker.stopTracking(); } private void upgradeTuner(int oldVersion, int newVersion) { if (oldVersion < 1) { String blacklistStr = getValue(StatusBarIconController.ICON_BLACKLIST); if (blacklistStr != null) { ArraySet iconBlacklist = StatusBarIconController.getIconBlacklist(blacklistStr); iconBlacklist.add("rotate"); iconBlacklist.add("headset"); Settings.Secure.putStringForUser(mContentResolver, StatusBarIconController.ICON_BLACKLIST, TextUtils.join(",", iconBlacklist), mCurrentUser); } } if (oldVersion < 2) { setTunerEnabled(mContext, false); } // 3 Removed because of a revert. if (oldVersion < 4) { // Delay this so that we can wait for everything to be registered first. new Handler(Dependency.get(Dependency.BG_LOOPER)).postDelayed(() -> clearAll(), 5000); } setValue(TUNER_VERSION, newVersion); } @Override public String getValue(String setting) { return Settings.Secure.getStringForUser(mContentResolver, setting, mCurrentUser); } @Override public void setValue(String setting, String value) { Settings.Secure.putStringForUser(mContentResolver, setting, value, mCurrentUser); } @Override public int getValue(String setting, int def) { return Settings.Secure.getIntForUser(mContentResolver, setting, def, mCurrentUser); } @Override public String getValue(String setting, String def) { String ret = Secure.getStringForUser(mContentResolver, setting, mCurrentUser); if (ret == null) return def; return ret; } @Override public void setValue(String setting, int value) { Settings.Secure.putIntForUser(mContentResolver, setting, value, mCurrentUser); } @Override public void addTunable(Tunable tunable, String... keys) { for (String key : keys) { addTunable(tunable, key); } } private void addTunable(Tunable tunable, String key) { if (!mTunableLookup.containsKey(key)) { mTunableLookup.put(key, new ArraySet()); } mTunableLookup.get(key).add(tunable); if (LeakDetector.ENABLED) { mTunables.add(tunable); Dependency.get(LeakDetector.class).trackCollection(mTunables, "TunerService.mTunables"); } Uri uri = Settings.Secure.getUriFor(key); if (!mListeningUris.containsKey(uri)) { mListeningUris.put(uri, key); mContentResolver.registerContentObserver(uri, false, mObserver, mCurrentUser); } // Send the first state. String value = Settings.Secure.getStringForUser(mContentResolver, key, mCurrentUser); tunable.onTuningChanged(key, value); } @Override public void removeTunable(Tunable tunable) { for (Set list : mTunableLookup.values()) { list.remove(tunable); } if (LeakDetector.ENABLED) { mTunables.remove(tunable); } } protected void reregisterAll() { if (mListeningUris.size() == 0) { return; } mContentResolver.unregisterContentObserver(mObserver); for (Uri uri : mListeningUris.keySet()) { mContentResolver.registerContentObserver(uri, false, mObserver, mCurrentUser); } } private void reloadSetting(Uri uri) { String key = mListeningUris.get(uri); Set tunables = mTunableLookup.get(key); if (tunables == null) { return; } String value = Settings.Secure.getStringForUser(mContentResolver, key, mCurrentUser); for (Tunable tunable : tunables) { tunable.onTuningChanged(key, value); } } private void reloadAll() { for (String key : mTunableLookup.keySet()) { String value = Settings.Secure.getStringForUser(mContentResolver, key, mCurrentUser); for (Tunable tunable : mTunableLookup.get(key)) { tunable.onTuningChanged(key, value); } } } @Override public void clearAll() { // A couple special cases. Settings.Global.putString(mContentResolver, DemoMode.DEMO_MODE_ALLOWED, null); Intent intent = new Intent(DemoMode.ACTION_DEMO); intent.putExtra(DemoMode.EXTRA_COMMAND, DemoMode.COMMAND_EXIT); mContext.sendBroadcast(intent); for (String key : mTunableLookup.keySet()) { if (ArrayUtils.contains(RESET_BLACKLIST, key)) { continue; } Settings.Secure.putString(mContentResolver, key, null); } } private class Observer extends ContentObserver { public Observer() { super(new Handler(Looper.getMainLooper())); } @Override public void onChange(boolean selfChange, Uri uri, int userId) { if (userId == ActivityManager.getCurrentUser()) { reloadSetting(uri); } } } }