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