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.IBinder; 33 import android.os.Looper; 34 import android.os.Parcel; 35 import android.os.RemoteException; 36 import android.os.ServiceManager; 37 import android.os.SystemProperties; 38 import android.provider.Settings; 39 import android.service.quicksettings.Tile; 40 import android.service.quicksettings.TileService; 41 import android.sysprop.DisplayProperties; 42 import android.util.Log; 43 import android.view.IWindowManager; 44 import android.view.ThreadedRenderer; 45 import android.view.WindowManagerGlobal; 46 import android.widget.Toast; 47 48 import androidx.annotation.VisibleForTesting; 49 50 import com.android.internal.app.LocalePicker; 51 import com.android.internal.statusbar.IStatusBarService; 52 import com.android.internal.view.IInputMethodManager; 53 import com.android.settings.R; 54 import com.android.settings.development.WirelessDebuggingPreferenceController; 55 import com.android.settings.overlay.FeatureFactory; 56 import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; 57 import com.android.settingslib.development.DevelopmentSettingsEnabler; 58 import com.android.settingslib.development.SystemPropPoker; 59 60 public abstract class DevelopmentTiles extends TileService { 61 private static final String TAG = "DevelopmentTiles"; 62 isEnabled()63 protected abstract boolean isEnabled(); 64 setIsEnabled(boolean isEnabled)65 protected abstract void setIsEnabled(boolean isEnabled); 66 67 @Override onStartListening()68 public void onStartListening() { 69 super.onStartListening(); 70 refresh(); 71 } 72 refresh()73 public void refresh() { 74 final int state; 75 if (!DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(this)) { 76 // Reset to disabled state if dev option is off. 77 if (isEnabled()) { 78 setIsEnabled(false); 79 SystemPropPoker.getInstance().poke(); 80 } 81 final ComponentName cn = new ComponentName(getPackageName(), getClass().getName()); 82 try { 83 getPackageManager().setComponentEnabledSetting( 84 cn, PackageManager.COMPONENT_ENABLED_STATE_DISABLED, 85 PackageManager.DONT_KILL_APP); 86 final IStatusBarService statusBarService = IStatusBarService.Stub.asInterface( 87 ServiceManager.checkService(Context.STATUS_BAR_SERVICE)); 88 if (statusBarService != null) { 89 statusBarService.remTile(cn); 90 } 91 } catch (RemoteException e) { 92 Log.e(TAG, "Failed to modify QS tile for component " + 93 cn.toString(), e); 94 } 95 state = Tile.STATE_UNAVAILABLE; 96 } else { 97 state = isEnabled() ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE; 98 } 99 getQsTile().setState(state); 100 getQsTile().updateTile(); 101 } 102 103 @Override onClick()104 public void onClick() { 105 setIsEnabled(getQsTile().getState() == Tile.STATE_INACTIVE); 106 SystemPropPoker.getInstance().poke(); // Settings app magic 107 refresh(); 108 } 109 110 /** 111 * Tile to control the "Show layout bounds" developer setting 112 */ 113 public static class ShowLayout extends DevelopmentTiles { 114 115 @Override isEnabled()116 protected boolean isEnabled() { 117 return DisplayProperties.debug_layout().orElse(false); 118 } 119 120 @Override setIsEnabled(boolean isEnabled)121 protected void setIsEnabled(boolean isEnabled) { 122 DisplayProperties.debug_layout(isEnabled); 123 } 124 } 125 126 /** 127 * Tile to control the "GPU profiling" developer setting 128 */ 129 public static class GPUProfiling extends DevelopmentTiles { 130 131 @Override isEnabled()132 protected boolean isEnabled() { 133 final String value = SystemProperties.get(ThreadedRenderer.PROFILE_PROPERTY); 134 return value.equals("visual_bars"); 135 } 136 137 @Override setIsEnabled(boolean isEnabled)138 protected void setIsEnabled(boolean isEnabled) { 139 SystemProperties.set(ThreadedRenderer.PROFILE_PROPERTY, isEnabled ? "visual_bars" : ""); 140 } 141 } 142 143 /** 144 * Tile to control the "Force RTL" developer setting 145 */ 146 public static class ForceRTL extends DevelopmentTiles { 147 148 @Override isEnabled()149 protected boolean isEnabled() { 150 return Settings.Global.getInt( 151 getContentResolver(), Settings.Global.DEVELOPMENT_FORCE_RTL, 0) != 0; 152 } 153 154 @Override setIsEnabled(boolean isEnabled)155 protected void setIsEnabled(boolean isEnabled) { 156 Settings.Global.putInt( 157 getContentResolver(), Settings.Global.DEVELOPMENT_FORCE_RTL, isEnabled ? 1 : 0); 158 DisplayProperties.debug_force_rtl(isEnabled); 159 LocalePicker.updateLocales(getResources().getConfiguration().getLocales()); 160 } 161 } 162 163 /** 164 * Tile to control the "Animation speed" developer setting 165 */ 166 public static class AnimationSpeed extends DevelopmentTiles { 167 168 @Override isEnabled()169 protected boolean isEnabled() { 170 IWindowManager wm = WindowManagerGlobal.getWindowManagerService(); 171 try { 172 return wm.getAnimationScale(0) != 1; 173 } catch (RemoteException e) { 174 } 175 return false; 176 } 177 178 @Override setIsEnabled(boolean isEnabled)179 protected void setIsEnabled(boolean isEnabled) { 180 IWindowManager wm = WindowManagerGlobal.getWindowManagerService(); 181 float scale = isEnabled ? 10 : 1; 182 try { 183 wm.setAnimationScale(0, scale); 184 wm.setAnimationScale(1, scale); 185 wm.setAnimationScale(2, scale); 186 } catch (RemoteException e) { 187 } 188 } 189 } 190 191 /** 192 * Tile to toggle Winscope trace which consists of Window and Layer traces. 193 */ 194 public static class WinscopeTrace extends DevelopmentTiles { 195 @VisibleForTesting 196 static final int SURFACE_FLINGER_LAYER_TRACE_CONTROL_CODE = 1025; 197 @VisibleForTesting 198 static final int SURFACE_FLINGER_LAYER_TRACE_STATUS_CODE = 1026; 199 private IBinder mSurfaceFlinger; 200 private IWindowManager mWindowManager; 201 private IInputMethodManager mInputMethodManager; 202 private Toast mToast; 203 204 @Override onCreate()205 public void onCreate() { 206 super.onCreate(); 207 mWindowManager = WindowManagerGlobal.getWindowManagerService(); 208 mSurfaceFlinger = ServiceManager.getService("SurfaceFlinger"); 209 mInputMethodManager = IInputMethodManager.Stub.asInterface( 210 ServiceManager.getService("input_method")); 211 Context context = getApplicationContext(); 212 CharSequence text = "Trace files written to /data/misc/wmtrace"; 213 mToast = Toast.makeText(context, text, Toast.LENGTH_LONG); 214 } 215 isWindowTraceEnabled()216 private boolean isWindowTraceEnabled() { 217 try { 218 return mWindowManager.isWindowTraceEnabled(); 219 } catch (RemoteException e) { 220 Log.e(TAG, 221 "Could not get window trace status, defaulting to false." + e.toString()); 222 } 223 return false; 224 } 225 isLayerTraceEnabled()226 private boolean isLayerTraceEnabled() { 227 boolean layerTraceEnabled = false; 228 Parcel reply = null; 229 Parcel data = null; 230 try { 231 if (mSurfaceFlinger != null) { 232 reply = Parcel.obtain(); 233 data = Parcel.obtain(); 234 data.writeInterfaceToken("android.ui.ISurfaceComposer"); 235 mSurfaceFlinger.transact(SURFACE_FLINGER_LAYER_TRACE_STATUS_CODE, 236 data, reply, 0 /* flags */); 237 layerTraceEnabled = reply.readBoolean(); 238 } 239 } catch (RemoteException e) { 240 Log.e(TAG, "Could not get layer trace status, defaulting to false." + e.toString()); 241 } finally { 242 if (data != null) { 243 data.recycle(); 244 reply.recycle(); 245 } 246 } 247 return layerTraceEnabled; 248 } 249 isSystemUiTracingEnabled()250 private boolean isSystemUiTracingEnabled() { 251 try { 252 final IStatusBarService statusBarService = IStatusBarService.Stub.asInterface( 253 ServiceManager.checkService(Context.STATUS_BAR_SERVICE)); 254 if (statusBarService != null) { 255 return statusBarService.isTracing(); 256 } 257 } catch (RemoteException e) { 258 Log.e(TAG, "Could not get system ui tracing status." + e.toString()); 259 } 260 return false; 261 } 262 isImeTraceEnabled()263 private boolean isImeTraceEnabled() { 264 try { 265 return mInputMethodManager.isImeTraceEnabled(); 266 } catch (RemoteException e) { 267 Log.e(TAG, "Could not get ime trace status, defaulting to false.", e); 268 } 269 return false; 270 } 271 272 @Override isEnabled()273 protected boolean isEnabled() { 274 return isWindowTraceEnabled() || isLayerTraceEnabled() || isSystemUiTracingEnabled() 275 || isImeTraceEnabled(); 276 } 277 setWindowTraceEnabled(boolean isEnabled)278 private void setWindowTraceEnabled(boolean isEnabled) { 279 try { 280 if (isEnabled) { 281 mWindowManager.startWindowTrace(); 282 } else { 283 mWindowManager.stopWindowTrace(); 284 } 285 } catch (RemoteException e) { 286 Log.e(TAG, "Could not set window trace status." + e.toString()); 287 } 288 } 289 setLayerTraceEnabled(boolean isEnabled)290 private void setLayerTraceEnabled(boolean isEnabled) { 291 Parcel data = null; 292 try { 293 if (mSurfaceFlinger != null) { 294 data = Parcel.obtain(); 295 data.writeInterfaceToken("android.ui.ISurfaceComposer"); 296 data.writeInt(isEnabled ? 1 : 0); 297 mSurfaceFlinger.transact(SURFACE_FLINGER_LAYER_TRACE_CONTROL_CODE, 298 data, null, 0 /* flags */); 299 } 300 } catch (RemoteException e) { 301 Log.e(TAG, "Could not set layer tracing." + e.toString()); 302 } finally { 303 if (data != null) { 304 data.recycle(); 305 } 306 } 307 } 308 setSystemUiTracing(boolean isEnabled)309 private void setSystemUiTracing(boolean isEnabled) { 310 try { 311 final IStatusBarService statusBarService = IStatusBarService.Stub.asInterface( 312 ServiceManager.checkService(Context.STATUS_BAR_SERVICE)); 313 if (statusBarService != null) { 314 if (isEnabled) { 315 statusBarService.startTracing(); 316 } else { 317 statusBarService.stopTracing(); 318 } 319 } 320 } catch (RemoteException e) { 321 Log.e(TAG, "Could not set system ui tracing." + e.toString()); 322 } 323 } 324 setImeTraceEnabled(boolean isEnabled)325 private void setImeTraceEnabled(boolean isEnabled) { 326 try { 327 if (isEnabled) { 328 mInputMethodManager.startImeTrace(); 329 } else { 330 mInputMethodManager.stopImeTrace(); 331 } 332 } catch (RemoteException e) { 333 Log.e(TAG, "Could not set ime trace status." + e.toString()); 334 } 335 } 336 337 @Override setIsEnabled(boolean isEnabled)338 protected void setIsEnabled(boolean isEnabled) { 339 setWindowTraceEnabled(isEnabled); 340 setLayerTraceEnabled(isEnabled); 341 setSystemUiTracing(isEnabled); 342 setImeTraceEnabled(isEnabled); 343 if (!isEnabled) { 344 mToast.show(); 345 } 346 } 347 } 348 349 /** 350 * Tile to toggle sensors off to control camera, mic, and sensors managed by the SensorManager. 351 */ 352 public static class SensorsOff extends DevelopmentTiles { 353 private Context mContext; 354 private SensorPrivacyManager mSensorPrivacyManager; 355 private KeyguardManager mKeyguardManager; 356 private MetricsFeatureProvider mMetricsFeatureProvider; 357 private boolean mIsEnabled; 358 359 @Override onCreate()360 public void onCreate() { 361 super.onCreate(); 362 mContext = getApplicationContext(); 363 mSensorPrivacyManager = (SensorPrivacyManager) mContext.getSystemService( 364 Context.SENSOR_PRIVACY_SERVICE); 365 mIsEnabled = mSensorPrivacyManager.isAllSensorPrivacyEnabled(); 366 mMetricsFeatureProvider = FeatureFactory.getFactory( 367 mContext).getMetricsFeatureProvider(); 368 mKeyguardManager = (KeyguardManager) mContext.getSystemService( 369 Context.KEYGUARD_SERVICE); 370 } 371 372 @Override isEnabled()373 protected boolean isEnabled() { 374 return mIsEnabled; 375 } 376 377 @Override setIsEnabled(boolean isEnabled)378 public void setIsEnabled(boolean isEnabled) { 379 // Don't allow sensors to be reenabled from the lock screen. 380 if (mIsEnabled && mKeyguardManager.isKeyguardLocked()) { 381 return; 382 } 383 mMetricsFeatureProvider.action(getApplicationContext(), SettingsEnums.QS_SENSOR_PRIVACY, 384 isEnabled); 385 mIsEnabled = isEnabled; 386 mSensorPrivacyManager.setAllSensorPrivacy(isEnabled); 387 } 388 } 389 390 /** 391 * Tile to control the "Wireless debugging" developer setting 392 */ 393 public static class WirelessDebugging extends DevelopmentTiles { 394 private Context mContext; 395 private KeyguardManager mKeyguardManager; 396 private Toast mToast; 397 private final Handler mHandler = new Handler(Looper.getMainLooper()); 398 private final ContentObserver mSettingsObserver = new ContentObserver(mHandler) { 399 @Override 400 public void onChange(boolean selfChange, Uri uri) { 401 refresh(); 402 } 403 }; 404 405 @Override onCreate()406 public void onCreate() { 407 super.onCreate(); 408 mContext = getApplicationContext(); 409 mKeyguardManager = (KeyguardManager) mContext.getSystemService( 410 Context.KEYGUARD_SERVICE); 411 mToast = Toast.makeText(mContext, R.string.adb_wireless_no_network_msg, 412 Toast.LENGTH_LONG); 413 } 414 415 @Override onStartListening()416 public void onStartListening() { 417 super.onStartListening(); 418 getContentResolver().registerContentObserver( 419 Settings.Global.getUriFor(Settings.Global.ADB_WIFI_ENABLED), false, 420 mSettingsObserver); 421 } 422 423 @Override onStopListening()424 public void onStopListening() { 425 super.onStopListening(); 426 getContentResolver().unregisterContentObserver(mSettingsObserver); 427 } 428 429 @Override isEnabled()430 protected boolean isEnabled() { 431 return isAdbWifiEnabled(); 432 } 433 434 @Override setIsEnabled(boolean isEnabled)435 public void setIsEnabled(boolean isEnabled) { 436 // Don't allow Wireless Debugging to be enabled from the lock screen. 437 if (isEnabled && mKeyguardManager.isKeyguardLocked()) { 438 return; 439 } 440 441 // Show error toast if not connected to Wi-Fi 442 if (isEnabled && !WirelessDebuggingPreferenceController.isWifiConnected(mContext)) { 443 // Close quick shade 444 sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)); 445 mToast.show(); 446 return; 447 } 448 449 writeAdbWifiSetting(isEnabled); 450 } 451 isAdbWifiEnabled()452 private boolean isAdbWifiEnabled() { 453 return Settings.Global.getInt(getContentResolver(), Settings.Global.ADB_WIFI_ENABLED, 454 ADB_SETTING_OFF) != ADB_SETTING_OFF; 455 } 456 writeAdbWifiSetting(boolean enabled)457 protected void writeAdbWifiSetting(boolean enabled) { 458 Settings.Global.putInt(getContentResolver(), Settings.Global.ADB_WIFI_ENABLED, 459 enabled ? ADB_SETTING_ON : ADB_SETTING_OFF); 460 } 461 } 462 463 /** 464 * Tile to control the "Show taps" developer setting 465 */ 466 public static class ShowTaps extends DevelopmentTiles { 467 private static final int SETTING_VALUE_ON = 1; 468 private static final int SETTING_VALUE_OFF = 0; 469 private Context mContext; 470 471 @Override onCreate()472 public void onCreate() { 473 super.onCreate(); 474 mContext = getApplicationContext(); 475 } 476 477 @Override isEnabled()478 protected boolean isEnabled() { 479 return Settings.System.getInt(mContext.getContentResolver(), 480 Settings.System.SHOW_TOUCHES, SETTING_VALUE_OFF) == SETTING_VALUE_ON; 481 } 482 483 @Override setIsEnabled(boolean isEnabled)484 protected void setIsEnabled(boolean isEnabled) { 485 Settings.System.putInt(mContext.getContentResolver(), 486 Settings.System.SHOW_TOUCHES, isEnabled ? SETTING_VALUE_ON : SETTING_VALUE_OFF); 487 } 488 } 489 } 490