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 android.app.settings.SettingsEnums; 20 import android.content.ComponentName; 21 import android.content.Context; 22 import android.content.pm.PackageManager; 23 import android.hardware.SensorPrivacyManager; 24 import android.app.KeyguardManager; 25 import android.os.IBinder; 26 import android.os.Parcel; 27 import android.os.RemoteException; 28 import android.os.ServiceManager; 29 import android.os.SystemProperties; 30 import android.provider.Settings; 31 import android.service.quicksettings.Tile; 32 import android.service.quicksettings.TileService; 33 import android.sysprop.DisplayProperties; 34 import android.util.Log; 35 import android.view.IWindowManager; 36 import android.view.ThreadedRenderer; 37 import android.view.WindowManagerGlobal; 38 import android.widget.Toast; 39 40 import androidx.annotation.VisibleForTesting; 41 42 import com.android.internal.app.LocalePicker; 43 import com.android.internal.statusbar.IStatusBarService; 44 import com.android.settings.overlay.FeatureFactory; 45 import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; 46 import com.android.settingslib.development.DevelopmentSettingsEnabler; 47 import com.android.settingslib.development.SystemPropPoker; 48 49 public abstract class DevelopmentTiles extends TileService { 50 private static final String TAG = "DevelopmentTiles"; 51 isEnabled()52 protected abstract boolean isEnabled(); 53 setIsEnabled(boolean isEnabled)54 protected abstract void setIsEnabled(boolean isEnabled); 55 56 @Override onStartListening()57 public void onStartListening() { 58 super.onStartListening(); 59 refresh(); 60 } 61 refresh()62 public void refresh() { 63 final int state; 64 if (!DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(this)) { 65 // Reset to disabled state if dev option is off. 66 if (isEnabled()) { 67 setIsEnabled(false); 68 SystemPropPoker.getInstance().poke(); 69 } 70 final ComponentName cn = new ComponentName(getPackageName(), getClass().getName()); 71 try { 72 getPackageManager().setComponentEnabledSetting( 73 cn, PackageManager.COMPONENT_ENABLED_STATE_DISABLED, 74 PackageManager.DONT_KILL_APP); 75 final IStatusBarService statusBarService = IStatusBarService.Stub.asInterface( 76 ServiceManager.checkService(Context.STATUS_BAR_SERVICE)); 77 if (statusBarService != null) { 78 statusBarService.remTile(cn); 79 } 80 } catch (RemoteException e) { 81 Log.e(TAG, "Failed to modify QS tile for component " + 82 cn.toString(), e); 83 } 84 state = Tile.STATE_UNAVAILABLE; 85 } else { 86 state = isEnabled() ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE; 87 } 88 getQsTile().setState(state); 89 getQsTile().updateTile(); 90 } 91 92 @Override onClick()93 public void onClick() { 94 setIsEnabled(getQsTile().getState() == Tile.STATE_INACTIVE); 95 SystemPropPoker.getInstance().poke(); // Settings app magic 96 refresh(); 97 } 98 99 /** 100 * Tile to control the "Show layout bounds" developer setting 101 */ 102 public static class ShowLayout extends DevelopmentTiles { 103 104 @Override isEnabled()105 protected boolean isEnabled() { 106 return DisplayProperties.debug_layout().orElse(false); 107 } 108 109 @Override setIsEnabled(boolean isEnabled)110 protected void setIsEnabled(boolean isEnabled) { 111 DisplayProperties.debug_layout(isEnabled); 112 } 113 } 114 115 /** 116 * Tile to control the "GPU profiling" developer setting 117 */ 118 public static class GPUProfiling extends DevelopmentTiles { 119 120 @Override isEnabled()121 protected boolean isEnabled() { 122 final String value = SystemProperties.get(ThreadedRenderer.PROFILE_PROPERTY); 123 return value.equals("visual_bars"); 124 } 125 126 @Override setIsEnabled(boolean isEnabled)127 protected void setIsEnabled(boolean isEnabled) { 128 SystemProperties.set(ThreadedRenderer.PROFILE_PROPERTY, isEnabled ? "visual_bars" : ""); 129 } 130 } 131 132 /** 133 * Tile to control the "Force RTL" developer setting 134 */ 135 public static class ForceRTL extends DevelopmentTiles { 136 137 @Override isEnabled()138 protected boolean isEnabled() { 139 return Settings.Global.getInt( 140 getContentResolver(), Settings.Global.DEVELOPMENT_FORCE_RTL, 0) != 0; 141 } 142 143 @Override setIsEnabled(boolean isEnabled)144 protected void setIsEnabled(boolean isEnabled) { 145 Settings.Global.putInt( 146 getContentResolver(), Settings.Global.DEVELOPMENT_FORCE_RTL, isEnabled ? 1 : 0); 147 DisplayProperties.debug_force_rtl(isEnabled); 148 LocalePicker.updateLocales(getResources().getConfiguration().getLocales()); 149 } 150 } 151 152 /** 153 * Tile to control the "Animation speed" developer setting 154 */ 155 public static class AnimationSpeed extends DevelopmentTiles { 156 157 @Override isEnabled()158 protected boolean isEnabled() { 159 IWindowManager wm = WindowManagerGlobal.getWindowManagerService(); 160 try { 161 return wm.getAnimationScale(0) != 1; 162 } catch (RemoteException e) { 163 } 164 return false; 165 } 166 167 @Override setIsEnabled(boolean isEnabled)168 protected void setIsEnabled(boolean isEnabled) { 169 IWindowManager wm = WindowManagerGlobal.getWindowManagerService(); 170 float scale = isEnabled ? 10 : 1; 171 try { 172 wm.setAnimationScale(0, scale); 173 wm.setAnimationScale(1, scale); 174 wm.setAnimationScale(2, scale); 175 } catch (RemoteException e) { 176 } 177 } 178 } 179 180 /** 181 * Tile to toggle Winscope trace which consists of Window and Layer traces. 182 */ 183 public static class WinscopeTrace extends DevelopmentTiles { 184 @VisibleForTesting 185 static final int SURFACE_FLINGER_LAYER_TRACE_CONTROL_CODE = 1025; 186 @VisibleForTesting 187 static final int SURFACE_FLINGER_LAYER_TRACE_STATUS_CODE = 1026; 188 private IBinder mSurfaceFlinger; 189 private IWindowManager mWindowManager; 190 private Toast mToast; 191 192 @Override onCreate()193 public void onCreate() { 194 super.onCreate(); 195 mWindowManager = WindowManagerGlobal.getWindowManagerService(); 196 mSurfaceFlinger = ServiceManager.getService("SurfaceFlinger"); 197 Context context = getApplicationContext(); 198 CharSequence text = "Trace files written to /data/misc/wmtrace"; 199 mToast = Toast.makeText(context, text, Toast.LENGTH_LONG); 200 } 201 isWindowTraceEnabled()202 private boolean isWindowTraceEnabled() { 203 try { 204 return mWindowManager.isWindowTraceEnabled(); 205 } catch (RemoteException e) { 206 Log.e(TAG, 207 "Could not get window trace status, defaulting to false." + e.toString()); 208 } 209 return false; 210 } 211 isLayerTraceEnabled()212 private boolean isLayerTraceEnabled() { 213 boolean layerTraceEnabled = false; 214 Parcel reply = null; 215 Parcel data = null; 216 try { 217 if (mSurfaceFlinger != null) { 218 reply = Parcel.obtain(); 219 data = Parcel.obtain(); 220 data.writeInterfaceToken("android.ui.ISurfaceComposer"); 221 mSurfaceFlinger.transact(SURFACE_FLINGER_LAYER_TRACE_STATUS_CODE, 222 data, reply, 0 /* flags */); 223 layerTraceEnabled = reply.readBoolean(); 224 } 225 } catch (RemoteException e) { 226 Log.e(TAG, "Could not get layer trace status, defaulting to false." + e.toString()); 227 } finally { 228 if (data != null) { 229 data.recycle(); 230 reply.recycle(); 231 } 232 } 233 return layerTraceEnabled; 234 } 235 236 @Override isEnabled()237 protected boolean isEnabled() { 238 return isWindowTraceEnabled() || isLayerTraceEnabled(); 239 } 240 setWindowTraceEnabled(boolean isEnabled)241 private void setWindowTraceEnabled(boolean isEnabled) { 242 try { 243 if (isEnabled) { 244 mWindowManager.startWindowTrace(); 245 } else { 246 mWindowManager.stopWindowTrace(); 247 } 248 } catch (RemoteException e) { 249 Log.e(TAG, "Could not set window trace status." + e.toString()); 250 } 251 } 252 setLayerTraceEnabled(boolean isEnabled)253 private void setLayerTraceEnabled(boolean isEnabled) { 254 Parcel data = null; 255 try { 256 if (mSurfaceFlinger != null) { 257 data = Parcel.obtain(); 258 data.writeInterfaceToken("android.ui.ISurfaceComposer"); 259 data.writeInt(isEnabled ? 1 : 0); 260 mSurfaceFlinger.transact(SURFACE_FLINGER_LAYER_TRACE_CONTROL_CODE, 261 data, null, 0 /* flags */); 262 } 263 } catch (RemoteException e) { 264 Log.e(TAG, "Could not set layer tracing." + e.toString()); 265 } finally { 266 if (data != null) { 267 data.recycle(); 268 } 269 } 270 } 271 272 @Override setIsEnabled(boolean isEnabled)273 protected void setIsEnabled(boolean isEnabled) { 274 setWindowTraceEnabled(isEnabled); 275 setLayerTraceEnabled(isEnabled); 276 if (!isEnabled) { 277 mToast.show(); 278 } 279 } 280 } 281 282 /** 283 * Tile to toggle sensors off to control camera, mic, and sensors managed by the SensorManager. 284 */ 285 public static class SensorsOff extends DevelopmentTiles { 286 private Context mContext; 287 private SensorPrivacyManager mSensorPrivacyManager; 288 private KeyguardManager mKeyguardManager; 289 private MetricsFeatureProvider mMetricsFeatureProvider; 290 private boolean mIsEnabled; 291 292 @Override onCreate()293 public void onCreate() { 294 super.onCreate(); 295 mContext = getApplicationContext(); 296 mSensorPrivacyManager = (SensorPrivacyManager) mContext.getSystemService( 297 Context.SENSOR_PRIVACY_SERVICE); 298 mIsEnabled = mSensorPrivacyManager.isSensorPrivacyEnabled(); 299 mMetricsFeatureProvider = FeatureFactory.getFactory( 300 mContext).getMetricsFeatureProvider(); 301 mKeyguardManager = (KeyguardManager) mContext.getSystemService( 302 Context.KEYGUARD_SERVICE); 303 } 304 305 @Override isEnabled()306 protected boolean isEnabled() { 307 return mIsEnabled; 308 } 309 310 @Override setIsEnabled(boolean isEnabled)311 public void setIsEnabled(boolean isEnabled) { 312 // Don't allow sensors to be reenabled from the lock screen. 313 if (mIsEnabled && mKeyguardManager.isKeyguardLocked()) { 314 return; 315 } 316 mMetricsFeatureProvider.action(getApplicationContext(), SettingsEnums.QS_SENSOR_PRIVACY, 317 isEnabled); 318 mIsEnabled = isEnabled; 319 mSensorPrivacyManager.setSensorPrivacy(isEnabled); 320 } 321 } 322 } 323