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