OverlayDisplayAdapter.java revision 4ed8fe75e1dde1a2b9576f3862aecc5a572c56b5
1/* 2 * Copyright (C) 2012 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 17package com.android.server.display; 18 19import android.content.Context; 20import android.database.ContentObserver; 21import android.graphics.SurfaceTexture; 22import android.os.Handler; 23import android.os.IBinder; 24import android.provider.Settings; 25import android.util.DisplayMetrics; 26import android.util.Slog; 27import android.view.Gravity; 28import android.view.Surface; 29 30import java.io.PrintWriter; 31import java.io.StringWriter; 32import java.util.ArrayList; 33import java.util.regex.Matcher; 34import java.util.regex.Pattern; 35 36/** 37 * A display adapter that uses overlay windows to simulate secondary displays 38 * for development purposes. Use Development Settings to enable one or more 39 * overlay displays. 40 * <p> 41 * This object has two different handlers (which may be the same) which must not 42 * get confused. The main handler is used to posting messages to the display manager 43 * service as usual. The UI handler is only used by the {@link OverlayDisplayWindow}. 44 * </p><p> 45 * Display adapters are guarded by the {@link DisplayManagerService.SyncRoot} lock. 46 * </p> 47 */ 48final class OverlayDisplayAdapter extends DisplayAdapter { 49 static final String TAG = "OverlayDisplayAdapter"; 50 static final boolean DEBUG = false; 51 52 private static final int MIN_WIDTH = 100; 53 private static final int MIN_HEIGHT = 100; 54 private static final int MAX_WIDTH = 4096; 55 private static final int MAX_HEIGHT = 4096; 56 57 private static final Pattern SETTING_PATTERN = 58 Pattern.compile("(\\d+)x(\\d+)/(\\d+)"); 59 60 private final Handler mUiHandler; 61 private final ArrayList<OverlayDisplayHandle> mOverlays = 62 new ArrayList<OverlayDisplayHandle>(); 63 private String mCurrentOverlaySetting = ""; 64 65 public OverlayDisplayAdapter(DisplayManagerService.SyncRoot syncRoot, 66 Context context, Handler handler, Listener listener, Handler uiHandler) { 67 super(syncRoot, context, handler, listener, TAG); 68 mUiHandler = uiHandler; 69 } 70 71 @Override 72 public void dumpLocked(PrintWriter pw) { 73 super.dumpLocked(pw); 74 pw.println("mCurrentOverlaySetting=" + mCurrentOverlaySetting); 75 pw.println("mOverlays: size=" + mOverlays.size()); 76 for (OverlayDisplayHandle overlay : mOverlays) { 77 overlay.dumpLocked(pw); 78 } 79 } 80 81 @Override 82 public void registerLocked() { 83 super.registerLocked(); 84 getContext().getContentResolver().registerContentObserver( 85 Settings.System.getUriFor(Settings.Secure.OVERLAY_DISPLAY_DEVICES), true, 86 new ContentObserver(getHandler()) { 87 @Override 88 public void onChange(boolean selfChange) { 89 synchronized (getSyncRoot()) { 90 updateOverlayDisplayDevicesLocked(); 91 } 92 } 93 }); 94 updateOverlayDisplayDevicesLocked(); 95 } 96 97 private void updateOverlayDisplayDevicesLocked() { 98 String value = Settings.System.getString(getContext().getContentResolver(), 99 Settings.Secure.OVERLAY_DISPLAY_DEVICES); 100 if (value == null) { 101 value = ""; 102 } 103 104 if (value.equals(mCurrentOverlaySetting)) { 105 return; 106 } 107 mCurrentOverlaySetting = value; 108 109 if (!mOverlays.isEmpty()) { 110 Slog.i(TAG, "Dismissing all overlay display devices."); 111 for (OverlayDisplayHandle overlay : mOverlays) { 112 overlay.dismissLocked(); 113 } 114 mOverlays.clear(); 115 } 116 117 int count = 0; 118 for (String part : value.split(";")) { 119 Matcher matcher = SETTING_PATTERN.matcher(part); 120 if (matcher.matches()) { 121 if (count >= 4) { 122 Slog.w(TAG, "Too many overlay display devices specified: " + value); 123 break; 124 } 125 try { 126 int width = Integer.parseInt(matcher.group(1), 10); 127 int height = Integer.parseInt(matcher.group(2), 10); 128 int densityDpi = Integer.parseInt(matcher.group(3), 10); 129 if (width >= MIN_WIDTH && width <= MAX_WIDTH 130 && height >= MIN_HEIGHT && height <= MAX_HEIGHT 131 && densityDpi >= DisplayMetrics.DENSITY_LOW 132 && densityDpi <= DisplayMetrics.DENSITY_XXHIGH) { 133 int number = ++count; 134 String name = getContext().getResources().getString( 135 com.android.internal.R.string.display_manager_overlay_display_name, 136 number); 137 int gravity = chooseOverlayGravity(number); 138 139 Slog.i(TAG, "Showing overlay display device #" + number 140 + ": name=" + name + ", width=" + width + ", height=" + height 141 + ", densityDpi=" + densityDpi); 142 143 mOverlays.add(new OverlayDisplayHandle(name, 144 width, height, densityDpi, gravity)); 145 continue; 146 } 147 } catch (NumberFormatException ex) { 148 } 149 } else if (part.isEmpty()) { 150 continue; 151 } 152 Slog.w(TAG, "Malformed overlay display devices setting: " + value); 153 } 154 } 155 156 private static int chooseOverlayGravity(int overlayNumber) { 157 switch (overlayNumber) { 158 case 1: 159 return Gravity.TOP | Gravity.LEFT; 160 case 2: 161 return Gravity.BOTTOM | Gravity.RIGHT; 162 case 3: 163 return Gravity.TOP | Gravity.RIGHT; 164 case 4: 165 default: 166 return Gravity.BOTTOM | Gravity.LEFT; 167 } 168 } 169 170 private final class OverlayDisplayDevice extends DisplayDevice { 171 private final String mName; 172 private final int mWidth; 173 private final int mHeight; 174 private final float mRefreshRate; 175 private final int mDensityDpi; 176 177 private SurfaceTexture mSurfaceTexture; 178 private boolean mSurfaceTextureChanged; 179 180 private DisplayDeviceInfo mInfo; 181 182 public OverlayDisplayDevice(IBinder displayToken, String name, 183 int width, int height, float refreshRate, int densityDpi) { 184 super(OverlayDisplayAdapter.this, displayToken); 185 mName = name; 186 mWidth = width; 187 mHeight = height; 188 mRefreshRate = refreshRate; 189 mDensityDpi = densityDpi; 190 } 191 192 public void setSurfaceTextureLocked(SurfaceTexture surfaceTexture) { 193 if (surfaceTexture != mSurfaceTexture) { 194 mSurfaceTexture = surfaceTexture; 195 mSurfaceTextureChanged = true; 196 sendTraversalRequestLocked(); 197 } 198 } 199 200 @Override 201 public void performTraversalInTransactionLocked() { 202 if (mSurfaceTextureChanged) { 203 setSurfaceTextureInTransactionLocked(mSurfaceTexture); 204 mSurfaceTextureChanged = false; 205 } 206 } 207 208 @Override 209 public DisplayDeviceInfo getDisplayDeviceInfoLocked() { 210 if (mInfo == null) { 211 mInfo = new DisplayDeviceInfo(); 212 mInfo.name = mName; 213 mInfo.width = mWidth; 214 mInfo.height = mHeight; 215 mInfo.refreshRate = mRefreshRate; 216 mInfo.densityDpi = mDensityDpi; 217 mInfo.xDpi = mDensityDpi; 218 mInfo.yDpi = mDensityDpi; 219 mInfo.flags = DisplayDeviceInfo.FLAG_SECURE; 220 } 221 return mInfo; 222 } 223 } 224 225 /** 226 * Functions as a handle for overlay display devices which are created and 227 * destroyed asynchronously. 228 * 229 * Guarded by the {@link DisplayManagerService.SyncRoot} lock. 230 */ 231 private final class OverlayDisplayHandle implements OverlayDisplayWindow.Listener { 232 private final String mName; 233 private final int mWidth; 234 private final int mHeight; 235 private final int mDensityDpi; 236 private final int mGravity; 237 238 private OverlayDisplayWindow mWindow; 239 private OverlayDisplayDevice mDevice; 240 241 public OverlayDisplayHandle(String name, 242 int width, int height, int densityDpi, int gravity) { 243 mName = name; 244 mWidth = width; 245 mHeight = height; 246 mDensityDpi = densityDpi; 247 mGravity = gravity; 248 249 mUiHandler.post(mShowRunnable); 250 } 251 252 public void dismissLocked() { 253 mUiHandler.removeCallbacks(mShowRunnable); 254 mUiHandler.post(mDismissRunnable); 255 } 256 257 // Called on the UI thread. 258 @Override 259 public void onWindowCreated(SurfaceTexture surfaceTexture, float refreshRate) { 260 synchronized (getSyncRoot()) { 261 IBinder displayToken = Surface.createDisplay(mName); 262 mDevice = new OverlayDisplayDevice(displayToken, mName, 263 mWidth, mHeight, refreshRate, mDensityDpi); 264 mDevice.setSurfaceTextureLocked(surfaceTexture); 265 266 sendDisplayDeviceEventLocked(mDevice, DISPLAY_DEVICE_EVENT_ADDED); 267 } 268 } 269 270 // Called on the UI thread. 271 @Override 272 public void onWindowDestroyed() { 273 synchronized (getSyncRoot()) { 274 if (mDevice != null) { 275 mDevice.setSurfaceTextureLocked(null); 276 277 sendDisplayDeviceEventLocked(mDevice, DISPLAY_DEVICE_EVENT_REMOVED); 278 } 279 } 280 } 281 282 public void dumpLocked(PrintWriter pw) { 283 pw.println(" " + mName + ": "); 284 pw.println(" mWidth=" + mWidth); 285 pw.println(" mHeight=" + mHeight); 286 pw.println(" mDensityDpi=" + mDensityDpi); 287 pw.println(" mGravity=" + mGravity); 288 289 // Try to dump the window state. 290 // This call may hang if the UI thread is waiting to acquire our lock so 291 // we use a short timeout to recover just in case. 292 if (mWindow != null) { 293 final StringWriter sw = new StringWriter(); 294 final OverlayDisplayWindow window = mWindow; 295 if (mUiHandler.runWithScissors(new Runnable() { 296 @Override 297 public void run() { 298 PrintWriter lpw = new PrintWriter(sw); 299 window.dump(lpw); 300 lpw.close(); 301 } 302 }, 200)) { 303 for (String line : sw.toString().split("\n")) { 304 pw.println(line); 305 } 306 } else { 307 pw.println(" ... timed out while attempting to dump window state"); 308 } 309 } 310 } 311 312 // Runs on the UI thread. 313 private final Runnable mShowRunnable = new Runnable() { 314 @Override 315 public void run() { 316 OverlayDisplayWindow window = new OverlayDisplayWindow(getContext(), 317 mName, mWidth, mHeight, mDensityDpi, mGravity, 318 OverlayDisplayHandle.this); 319 window.show(); 320 321 synchronized (getSyncRoot()) { 322 mWindow = window; 323 } 324 } 325 }; 326 327 // Runs on the UI thread. 328 private final Runnable mDismissRunnable = new Runnable() { 329 @Override 330 public void run() { 331 OverlayDisplayWindow window; 332 synchronized (getSyncRoot()) { 333 window = mWindow; 334 mWindow = null; 335 } 336 337 if (window != null) { 338 window.dismiss(); 339 } 340 } 341 }; 342 } 343} 344