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