1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5  * use this file except in compliance with the License. You may obtain a copy of
6  * 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, WITHOUT
12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13  * License for the specific language governing permissions and limitations under
14  * the License.
15  */
16 
17 package com.android.settings.development.qstile;
18 
19 import static com.android.settings.development.AdbPreferenceController.ADB_SETTING_OFF;
20 import static com.android.settings.development.AdbPreferenceController.ADB_SETTING_ON;
21 
22 import android.app.KeyguardManager;
23 import android.app.settings.SettingsEnums;
24 import android.content.ComponentName;
25 import android.content.Context;
26 import android.content.Intent;
27 import android.content.pm.PackageManager;
28 import android.database.ContentObserver;
29 import android.hardware.SensorPrivacyManager;
30 import android.net.Uri;
31 import android.os.Handler;
32 import android.os.Looper;
33 import android.os.RemoteException;
34 import android.os.ServiceManager;
35 import android.os.SystemProperties;
36 import android.provider.Settings;
37 import android.service.quicksettings.Tile;
38 import android.service.quicksettings.TileService;
39 import android.sysprop.DisplayProperties;
40 import android.util.Log;
41 import android.view.IWindowManager;
42 import android.view.ThreadedRenderer;
43 import android.view.WindowManagerGlobal;
44 import android.widget.Toast;
45 
46 import com.android.internal.app.LocalePicker;
47 import com.android.internal.statusbar.IStatusBarService;
48 import com.android.settings.development.WirelessDebuggingPreferenceController;
49 import com.android.settings.overlay.FeatureFactory;
50 import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
51 import com.android.settingslib.development.DevelopmentSettingsEnabler;
52 import com.android.settingslib.development.SystemPropPoker;
53 
54 public abstract class DevelopmentTiles extends TileService {
55 
56     /**
57      * Meta-data for a development tile to declare a sysprop flag that needs to be enabled for
58      * the tile to be available.
59      *
60      * To define the flag, set this meta-data on the tile's manifest declaration.
61      * <pre class="prettyprint">
62      * {@literal
63      * <meta-data android:name="com.android.settings.development.qstile.REQUIRES_SYSTEM_PROPERTY"
64      *     android:value="persist.debug.flag_name_here" />
65      * }
66      * </pre>
67      */
68     public static final String META_DATA_REQUIRES_SYSTEM_PROPERTY =
69             "com.android.settings.development.qstile.REQUIRES_SYSTEM_PROPERTY";
70 
71     private static final String TAG = "DevelopmentTiles";
72 
isEnabled()73     protected abstract boolean isEnabled();
74 
setIsEnabled(boolean isEnabled)75     protected abstract void setIsEnabled(boolean isEnabled);
76 
77     @Override
onStartListening()78     public void onStartListening() {
79         super.onStartListening();
80         refresh();
81     }
82 
refresh()83     public void refresh() {
84         final int state;
85         if (!DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(this)) {
86             // Reset to disabled state if dev option is off.
87             if (isEnabled()) {
88                 setIsEnabled(false);
89                 SystemPropPoker.getInstance().poke();
90             }
91             final ComponentName cn = new ComponentName(getPackageName(), getClass().getName());
92             try {
93                 getPackageManager().setComponentEnabledSetting(
94                         cn, PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
95                         PackageManager.DONT_KILL_APP);
96                 final IStatusBarService statusBarService = IStatusBarService.Stub.asInterface(
97                         ServiceManager.checkService(Context.STATUS_BAR_SERVICE));
98                 if (statusBarService != null) {
99                     statusBarService.remTile(cn);
100                 }
101             } catch (RemoteException e) {
102                 Log.e(TAG, "Failed to modify QS tile for component " +
103                         cn.toString(), e);
104             }
105             state = Tile.STATE_UNAVAILABLE;
106         } else {
107             state = isEnabled() ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE;
108         }
109         getQsTile().setState(state);
110         getQsTile().updateTile();
111     }
112 
113     @Override
onClick()114     public void onClick() {
115         setIsEnabled(getQsTile().getState() == Tile.STATE_INACTIVE);
116         SystemPropPoker.getInstance().poke(); // Settings app magic
117         refresh();
118     }
119 
120     /**
121      * Tile to control the "Show layout bounds" developer setting
122      */
123     public static class ShowLayout extends DevelopmentTiles {
124 
125         @Override
isEnabled()126         protected boolean isEnabled() {
127             return DisplayProperties.debug_layout().orElse(false);
128         }
129 
130         @Override
setIsEnabled(boolean isEnabled)131         protected void setIsEnabled(boolean isEnabled) {
132             DisplayProperties.debug_layout(isEnabled);
133         }
134     }
135 
136     /**
137      * Tile to control the "GPU profiling" developer setting
138      */
139     public static class GPUProfiling extends DevelopmentTiles {
140 
141         @Override
isEnabled()142         protected boolean isEnabled() {
143             final String value = SystemProperties.get(ThreadedRenderer.PROFILE_PROPERTY);
144             return value.equals("visual_bars");
145         }
146 
147         @Override
setIsEnabled(boolean isEnabled)148         protected void setIsEnabled(boolean isEnabled) {
149             SystemProperties.set(ThreadedRenderer.PROFILE_PROPERTY, isEnabled ? "visual_bars" : "");
150         }
151     }
152 
153     /**
154      * Tile to control the "Force RTL" developer setting
155      */
156     public static class ForceRTL extends DevelopmentTiles {
157 
158         @Override
isEnabled()159         protected boolean isEnabled() {
160             return Settings.Global.getInt(
161                     getContentResolver(), Settings.Global.DEVELOPMENT_FORCE_RTL, 0) != 0;
162         }
163 
164         @Override
setIsEnabled(boolean isEnabled)165         protected void setIsEnabled(boolean isEnabled) {
166             Settings.Global.putInt(
167                     getContentResolver(), Settings.Global.DEVELOPMENT_FORCE_RTL, isEnabled ? 1 : 0);
168             DisplayProperties.debug_force_rtl(isEnabled);
169             LocalePicker.updateLocales(getResources().getConfiguration().getLocales());
170         }
171     }
172 
173     /**
174      * Tile to control the "Animation speed" developer setting
175      */
176     public static class AnimationSpeed extends DevelopmentTiles {
177 
178         @Override
isEnabled()179         protected boolean isEnabled() {
180             IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
181             try {
182                 return wm.getAnimationScale(0) != 1;
183             } catch (RemoteException e) {
184             }
185             return false;
186         }
187 
188         @Override
setIsEnabled(boolean isEnabled)189         protected void setIsEnabled(boolean isEnabled) {
190             IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
191             float scale = isEnabled ? 10 : 1;
192             try {
193                 wm.setAnimationScale(0, scale);
194                 wm.setAnimationScale(1, scale);
195                 wm.setAnimationScale(2, scale);
196             } catch (RemoteException e) {
197             }
198         }
199     }
200 
201     /**
202      * Tile to toggle sensors off to control camera, mic, and sensors managed by the SensorManager.
203      */
204     public static class SensorsOff extends DevelopmentTiles {
205         private Context mContext;
206         private SensorPrivacyManager mSensorPrivacyManager;
207         private KeyguardManager mKeyguardManager;
208         private MetricsFeatureProvider mMetricsFeatureProvider;
209         private boolean mIsEnabled;
210 
211         @Override
onCreate()212         public void onCreate() {
213             super.onCreate();
214             mContext = getApplicationContext();
215             mSensorPrivacyManager = (SensorPrivacyManager) mContext.getSystemService(
216                     Context.SENSOR_PRIVACY_SERVICE);
217             mIsEnabled = mSensorPrivacyManager.isAllSensorPrivacyEnabled();
218             mMetricsFeatureProvider = FeatureFactory.getFeatureFactory()
219                     .getMetricsFeatureProvider();
220             mKeyguardManager = (KeyguardManager) mContext.getSystemService(
221                     Context.KEYGUARD_SERVICE);
222         }
223 
224         @Override
isEnabled()225         protected boolean isEnabled() {
226             return mIsEnabled;
227         }
228 
229         @Override
setIsEnabled(boolean isEnabled)230         public void setIsEnabled(boolean isEnabled) {
231             // Don't allow sensors to be toggled from the lock screen.
232             if (mKeyguardManager.isKeyguardLocked()) {
233                 return;
234             }
235             mMetricsFeatureProvider.action(getApplicationContext(), SettingsEnums.QS_SENSOR_PRIVACY,
236                     isEnabled);
237             mIsEnabled = isEnabled;
238             mSensorPrivacyManager.setAllSensorPrivacy(isEnabled);
239         }
240     }
241 
242     /**
243      * Tile to control the "Wireless debugging" developer setting
244      */
245     public static class WirelessDebugging extends DevelopmentTiles {
246         private Context mContext;
247         private KeyguardManager mKeyguardManager;
248         private Toast mToast;
249         private final Handler mHandler = new Handler(Looper.getMainLooper());
250         private final ContentObserver mSettingsObserver = new ContentObserver(mHandler) {
251             @Override
252             public void onChange(boolean selfChange, Uri uri) {
253                 refresh();
254             }
255         };
256 
257         @Override
onCreate()258         public void onCreate() {
259             super.onCreate();
260             mContext = getApplicationContext();
261             mKeyguardManager = (KeyguardManager) mContext.getSystemService(
262                     Context.KEYGUARD_SERVICE);
263             mToast = Toast.makeText(mContext,
264                     com.android.settingslib.R.string.adb_wireless_no_network_msg,
265                     Toast.LENGTH_LONG);
266         }
267 
268         @Override
onStartListening()269         public void onStartListening() {
270             super.onStartListening();
271             getContentResolver().registerContentObserver(
272                     Settings.Global.getUriFor(Settings.Global.ADB_WIFI_ENABLED), false,
273                     mSettingsObserver);
274         }
275 
276         @Override
onStopListening()277         public void onStopListening() {
278             super.onStopListening();
279             getContentResolver().unregisterContentObserver(mSettingsObserver);
280         }
281 
282         @Override
isEnabled()283         protected boolean isEnabled() {
284             return isAdbWifiEnabled();
285         }
286 
287         @Override
setIsEnabled(boolean isEnabled)288         public void setIsEnabled(boolean isEnabled) {
289             // Don't allow Wireless Debugging to be enabled from the lock screen.
290             if (isEnabled && mKeyguardManager.isKeyguardLocked()) {
291                 return;
292             }
293 
294             // Show error toast if not connected to Wi-Fi
295             if (isEnabled && !WirelessDebuggingPreferenceController.isWifiConnected(mContext)) {
296                 // Close quick shade
297                 sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
298                 mToast.show();
299                 return;
300             }
301 
302             writeAdbWifiSetting(isEnabled);
303         }
304 
isAdbWifiEnabled()305         private boolean isAdbWifiEnabled() {
306             return Settings.Global.getInt(getContentResolver(), Settings.Global.ADB_WIFI_ENABLED,
307                     ADB_SETTING_OFF) != ADB_SETTING_OFF;
308         }
309 
writeAdbWifiSetting(boolean enabled)310         protected void writeAdbWifiSetting(boolean enabled) {
311             Settings.Global.putInt(getContentResolver(), Settings.Global.ADB_WIFI_ENABLED,
312                     enabled ? ADB_SETTING_ON : ADB_SETTING_OFF);
313         }
314     }
315 
316     /**
317      * Tile to control the "Show taps" developer setting
318      */
319     public static class ShowTaps extends DevelopmentTiles {
320         private static final int SETTING_VALUE_ON = 1;
321         private static final int SETTING_VALUE_OFF = 0;
322         private Context mContext;
323 
324         @Override
onCreate()325         public void onCreate() {
326             super.onCreate();
327             mContext = getApplicationContext();
328         }
329 
330         @Override
isEnabled()331         protected boolean isEnabled() {
332             return Settings.System.getInt(mContext.getContentResolver(),
333                 Settings.System.SHOW_TOUCHES, SETTING_VALUE_OFF) == SETTING_VALUE_ON;
334         }
335 
336         @Override
setIsEnabled(boolean isEnabled)337         protected void setIsEnabled(boolean isEnabled) {
338             Settings.System.putInt(mContext.getContentResolver(),
339                 Settings.System.SHOW_TOUCHES, isEnabled ? SETTING_VALUE_ON : SETTING_VALUE_OFF);
340         }
341     }
342 }
343