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.app.ActivityManager;
19 import android.content.BroadcastReceiver;
20 import android.content.ComponentName;
21 import android.content.ContentResolver;
22 import android.content.Context;
23 import android.content.DialogInterface;
24 import android.content.DialogInterface.OnClickListener;
25 import android.content.Intent;
26 import android.content.pm.PackageManager;
27 import android.content.pm.PackageManager.NameNotFoundException;
28 import android.content.pm.UserInfo;
29 import android.database.ContentObserver;
30 import android.net.Uri;
31 import android.os.Handler;
32 import android.os.Looper;
33 import android.os.UserHandle;
34 import android.os.UserManager;
35 import android.provider.Settings;
36 import android.text.TextUtils;
37 import android.util.ArrayMap;
38 import android.util.ArraySet;
39 
40 import com.android.systemui.BatteryMeterDrawable;
41 import com.android.systemui.DemoMode;
42 import com.android.systemui.R;
43 import com.android.systemui.SystemUI;
44 import com.android.systemui.SystemUIApplication;
45 import com.android.systemui.settings.CurrentUserTracker;
46 import com.android.systemui.statusbar.phone.StatusBarIconController;
47 import com.android.systemui.statusbar.phone.SystemUIDialog;
48 
49 import java.util.HashMap;
50 import java.util.Set;
51 
52 
53 public class TunerService extends SystemUI {
54 
55     public static final String ACTION_CLEAR = "com.android.systemui.action.CLEAR_TUNER";
56 
57     private static final String TUNER_VERSION = "sysui_tuner_version";
58 
59     private static final int CURRENT_TUNER_VERSION = 1;
60 
61     private final Observer mObserver = new Observer();
62     // Map of Uris we listen on to their settings keys.
63     private final ArrayMap<Uri, String> mListeningUris = new ArrayMap<>();
64     // Map of settings keys to the listener.
65     private final HashMap<String, Set<Tunable>> mTunableLookup = new HashMap<>();
66 
67     private ContentResolver mContentResolver;
68     private int mCurrentUser;
69     private CurrentUserTracker mUserTracker;
70 
71     @Override
start()72     public void start() {
73         mContentResolver = mContext.getContentResolver();
74 
75         for (UserInfo user : UserManager.get(mContext).getUsers()) {
76             mCurrentUser = user.getUserHandle().getIdentifier();
77             if (getValue(TUNER_VERSION, 0) != CURRENT_TUNER_VERSION) {
78                 upgradeTuner(getValue(TUNER_VERSION, 0), CURRENT_TUNER_VERSION);
79             }
80         }
81         putComponent(TunerService.class, this);
82 
83         mCurrentUser = ActivityManager.getCurrentUser();
84         mUserTracker = new CurrentUserTracker(mContext) {
85             @Override
86             public void onUserSwitched(int newUserId) {
87                 mCurrentUser = newUserId;
88                 reloadAll();
89                 reregisterAll();
90             }
91         };
92         mUserTracker.startTracking();
93     }
94 
upgradeTuner(int oldVersion, int newVersion)95     private void upgradeTuner(int oldVersion, int newVersion) {
96         if (oldVersion < 1) {
97             String blacklistStr = getValue(StatusBarIconController.ICON_BLACKLIST);
98             if (blacklistStr != null) {
99                 ArraySet<String> iconBlacklist =
100                         StatusBarIconController.getIconBlacklist(blacklistStr);
101 
102                 iconBlacklist.add("rotate");
103                 iconBlacklist.add("headset");
104 
105                 Settings.Secure.putStringForUser(mContentResolver,
106                         StatusBarIconController.ICON_BLACKLIST,
107                         TextUtils.join(",", iconBlacklist), mCurrentUser);
108             }
109         }
110         setValue(TUNER_VERSION, newVersion);
111     }
112 
getValue(String setting)113     public String getValue(String setting) {
114         return Settings.Secure.getStringForUser(mContentResolver, setting, mCurrentUser);
115     }
116 
setValue(String setting, String value)117     public void setValue(String setting, String value) {
118          Settings.Secure.putStringForUser(mContentResolver, setting, value, mCurrentUser);
119     }
120 
getValue(String setting, int def)121     public int getValue(String setting, int def) {
122         return Settings.Secure.getIntForUser(mContentResolver, setting, def, mCurrentUser);
123     }
124 
setValue(String setting, int value)125     public void setValue(String setting, int value) {
126          Settings.Secure.putIntForUser(mContentResolver, setting, value, mCurrentUser);
127     }
128 
addTunable(Tunable tunable, String... keys)129     public void addTunable(Tunable tunable, String... keys) {
130         for (String key : keys) {
131             addTunable(tunable, key);
132         }
133     }
134 
addTunable(Tunable tunable, String key)135     private void addTunable(Tunable tunable, String key) {
136         if (!mTunableLookup.containsKey(key)) {
137             mTunableLookup.put(key, new ArraySet<Tunable>());
138         }
139         mTunableLookup.get(key).add(tunable);
140         Uri uri = Settings.Secure.getUriFor(key);
141         if (!mListeningUris.containsKey(uri)) {
142             mListeningUris.put(uri, key);
143             mContentResolver.registerContentObserver(uri, false, mObserver, mCurrentUser);
144         }
145         // Send the first state.
146         String value = Settings.Secure.getStringForUser(mContentResolver, key, mCurrentUser);
147         tunable.onTuningChanged(key, value);
148     }
149 
removeTunable(Tunable tunable)150     public void removeTunable(Tunable tunable) {
151         for (Set<Tunable> list : mTunableLookup.values()) {
152             list.remove(tunable);
153         }
154     }
155 
reregisterAll()156     protected void reregisterAll() {
157         if (mListeningUris.size() == 0) {
158             return;
159         }
160         mContentResolver.unregisterContentObserver(mObserver);
161         for (Uri uri : mListeningUris.keySet()) {
162             mContentResolver.registerContentObserver(uri, false, mObserver, mCurrentUser);
163         }
164     }
165 
reloadSetting(Uri uri)166     public void reloadSetting(Uri uri) {
167         String key = mListeningUris.get(uri);
168         Set<Tunable> tunables = mTunableLookup.get(key);
169         if (tunables == null) {
170             return;
171         }
172         String value = Settings.Secure.getStringForUser(mContentResolver, key, mCurrentUser);
173         for (Tunable tunable : tunables) {
174             tunable.onTuningChanged(key, value);
175         }
176     }
177 
reloadAll()178     private void reloadAll() {
179         for (String key : mTunableLookup.keySet()) {
180             String value = Settings.Secure.getStringForUser(mContentResolver, key,
181                     mCurrentUser);
182             for (Tunable tunable : mTunableLookup.get(key)) {
183                 tunable.onTuningChanged(key, value);
184             }
185         }
186     }
187 
clearAll()188     public void clearAll() {
189         // A couple special cases.
190         Settings.Global.putString(mContentResolver, DemoMode.DEMO_MODE_ALLOWED, null);
191         Settings.System.putString(mContentResolver, BatteryMeterDrawable.SHOW_PERCENT_SETTING, null);
192         Intent intent = new Intent(DemoMode.ACTION_DEMO);
193         intent.putExtra(DemoMode.EXTRA_COMMAND, DemoMode.COMMAND_EXIT);
194         mContext.sendBroadcast(intent);
195 
196         for (String key : mTunableLookup.keySet()) {
197             Settings.Secure.putString(mContentResolver, key, null);
198         }
199     }
200 
201     // Only used in other processes, such as the tuner.
202     private static TunerService sInstance;
203 
get(Context context)204     public static TunerService get(Context context) {
205         TunerService service = null;
206         if (context.getApplicationContext() instanceof SystemUIApplication) {
207             SystemUIApplication sysUi = (SystemUIApplication) context.getApplicationContext();
208             service = sysUi.getComponent(TunerService.class);
209         }
210         if (service == null) {
211             // Can't get it as a component, must in the tuner, lets just create one for now.
212             return getStaticService(context);
213         }
214         return service;
215     }
216 
getStaticService(Context context)217     private static TunerService getStaticService(Context context) {
218         if (sInstance == null) {
219             sInstance = new TunerService();
220             sInstance.mContext = context.getApplicationContext();
221             sInstance.mComponents = new HashMap<>();
222             sInstance.start();
223         }
224         return sInstance;
225     }
226 
showResetRequest(final Context context, final Runnable onDisabled)227     public static final void showResetRequest(final Context context, final Runnable onDisabled) {
228         SystemUIDialog dialog = new SystemUIDialog(context);
229         dialog.setShowForAllUsers(true);
230         dialog.setMessage(R.string.remove_from_settings_prompt);
231         dialog.setButton(DialogInterface.BUTTON_NEGATIVE, context.getString(R.string.cancel),
232                 (OnClickListener) null);
233         dialog.setButton(DialogInterface.BUTTON_POSITIVE,
234                 context.getString(R.string.guest_exit_guest_dialog_remove), new OnClickListener() {
235             @Override
236             public void onClick(DialogInterface dialog, int which) {
237                 // Tell the tuner (in main SysUI process) to clear all its settings.
238                 context.sendBroadcast(new Intent(TunerService.ACTION_CLEAR));
239                 // Disable access to tuner.
240                 TunerService.setTunerEnabled(context, false);
241                 // Make them sit through the warning dialog again.
242                 Settings.Secure.putInt(context.getContentResolver(),
243                         TunerFragment.SETTING_SEEN_TUNER_WARNING, 0);
244                 if (onDisabled != null) {
245                     onDisabled.run();
246                 }
247             }
248         });
249         dialog.show();
250     }
251 
setTunerEnabled(Context context, boolean enabled)252     public static final void setTunerEnabled(Context context, boolean enabled) {
253         userContext(context).getPackageManager().setComponentEnabledSetting(
254                 new ComponentName(context, TunerActivity.class),
255                 enabled ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
256                         : PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
257                         PackageManager.DONT_KILL_APP);
258     }
259 
isTunerEnabled(Context context)260     public static final boolean isTunerEnabled(Context context) {
261         return userContext(context).getPackageManager().getComponentEnabledSetting(
262                 new ComponentName(context, TunerActivity.class))
263                 == PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
264     }
265 
userContext(Context context)266     private static Context userContext(Context context) {
267         try {
268             return context.createPackageContextAsUser(context.getPackageName(), 0,
269                     new UserHandle(ActivityManager.getCurrentUser()));
270         } catch (NameNotFoundException e) {
271             return context;
272         }
273     }
274 
275     private class Observer extends ContentObserver {
Observer()276         public Observer() {
277             super(new Handler(Looper.getMainLooper()));
278         }
279 
280         @Override
onChange(boolean selfChange, Uri uri, int userId)281         public void onChange(boolean selfChange, Uri uri, int userId) {
282             if (userId == ActivityManager.getCurrentUser()) {
283                 reloadSetting(uri);
284             }
285         }
286     }
287 
288     public interface Tunable {
onTuningChanged(String key, String newValue)289         void onTuningChanged(String key, String newValue);
290     }
291 
292     public static class ClearReceiver extends BroadcastReceiver {
293         @Override
onReceive(Context context, Intent intent)294         public void onReceive(Context context, Intent intent) {
295             if (ACTION_CLEAR.equals(intent.getAction())) {
296                 get(context).clearAll();
297             }
298         }
299     }
300 }
301