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