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