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