VirtualDisplayAdapter.java revision 7211d2eba8e02b5e7462313798fc25c0bd36ab2d
1/* 2 * Copyright (C) 2013 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 static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR; 20import static android.hardware.display.DisplayManager 21 .VIRTUAL_DISPLAY_FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD; 22import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION; 23import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC; 24import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_SECURE; 25 26import android.content.Context; 27import android.hardware.display.IVirtualDisplayCallback; 28import android.media.projection.IMediaProjection; 29import android.media.projection.IMediaProjectionCallback; 30import android.os.Handler; 31import android.os.IBinder; 32import android.os.SystemProperties; 33import android.os.IBinder.DeathRecipient; 34import android.os.Message; 35import android.os.RemoteException; 36import android.util.ArrayMap; 37import android.util.Slog; 38import android.view.Display; 39import android.view.Surface; 40import android.view.SurfaceControl; 41 42import java.io.PrintWriter; 43import java.util.Iterator; 44 45/** 46 * A display adapter that provides virtual displays on behalf of applications. 47 * <p> 48 * Display adapters are guarded by the {@link DisplayManagerService.SyncRoot} lock. 49 * </p> 50 */ 51final class VirtualDisplayAdapter extends DisplayAdapter { 52 static final String TAG = "VirtualDisplayAdapter"; 53 static final boolean DEBUG = false; 54 55 // Unique id prefix for virtual displays 56 private static final String UNIQUE_ID_PREFIX = "virtual:"; 57 58 private final ArrayMap<IBinder, VirtualDisplayDevice> mVirtualDisplayDevices = 59 new ArrayMap<IBinder, VirtualDisplayDevice>(); 60 private Handler mHandler; 61 62 // Called with SyncRoot lock held. 63 public VirtualDisplayAdapter(DisplayManagerService.SyncRoot syncRoot, 64 Context context, Handler handler, Listener listener) { 65 super(syncRoot, context, handler, listener, TAG); 66 mHandler = handler; 67 } 68 69 public DisplayDevice createVirtualDisplayLocked(IVirtualDisplayCallback callback, 70 IMediaProjection projection, int ownerUid, String ownerPackageName, 71 String name, int width, int height, int densityDpi, Surface surface, int flags) { 72 boolean secure = (flags & VIRTUAL_DISPLAY_FLAG_SECURE) != 0; 73 IBinder appToken = callback.asBinder(); 74 IBinder displayToken = SurfaceControl.createDisplay(name, secure); 75 final String baseUniqueId = 76 UNIQUE_ID_PREFIX + ownerPackageName + "," + ownerUid + "," + name + ","; 77 final int uniqueIndex = getNextUniqueIndex(baseUniqueId); 78 VirtualDisplayDevice device = new VirtualDisplayDevice(displayToken, appToken, 79 ownerUid, ownerPackageName, name, width, height, densityDpi, surface, flags, 80 new Callback(callback, mHandler), baseUniqueId + uniqueIndex, uniqueIndex); 81 82 mVirtualDisplayDevices.put(appToken, device); 83 84 try { 85 if (projection != null) { 86 projection.registerCallback(new MediaProjectionCallback(appToken)); 87 } 88 appToken.linkToDeath(device, 0); 89 } catch (RemoteException ex) { 90 mVirtualDisplayDevices.remove(appToken); 91 device.destroyLocked(false); 92 return null; 93 } 94 95 // Return the display device without actually sending the event indicating 96 // that it was added. The caller will handle it. 97 return device; 98 } 99 100 public void resizeVirtualDisplayLocked(IBinder appToken, 101 int width, int height, int densityDpi) { 102 VirtualDisplayDevice device = mVirtualDisplayDevices.get(appToken); 103 if (device != null) { 104 device.resizeLocked(width, height, densityDpi); 105 } 106 } 107 108 109 public void setVirtualDisplaySurfaceLocked(IBinder appToken, Surface surface) { 110 VirtualDisplayDevice device = mVirtualDisplayDevices.get(appToken); 111 if (device != null) { 112 device.setSurfaceLocked(surface); 113 } 114 } 115 116 public DisplayDevice releaseVirtualDisplayLocked(IBinder appToken) { 117 VirtualDisplayDevice device = mVirtualDisplayDevices.remove(appToken); 118 if (device != null) { 119 device.destroyLocked(true); 120 appToken.unlinkToDeath(device, 0); 121 } 122 123 // Return the display device that was removed without actually sending the 124 // event indicating that it was removed. The caller will handle it. 125 return device; 126 } 127 128 /** 129 * Returns the next unique index for the uniqueIdPrefix 130 */ 131 private int getNextUniqueIndex(String uniqueIdPrefix) { 132 if (mVirtualDisplayDevices.isEmpty()) { 133 return 0; 134 } 135 136 int nextUniqueIndex = 0; 137 Iterator<VirtualDisplayDevice> it = mVirtualDisplayDevices.values().iterator(); 138 while (it.hasNext()) { 139 VirtualDisplayDevice device = it.next(); 140 if (device.getUniqueId().startsWith(uniqueIdPrefix) 141 && device.mUniqueIndex >= nextUniqueIndex) { 142 // Increment the next unique index to be greater than ones we have already ran 143 // across for displays that have the same unique Id prefix. 144 nextUniqueIndex = device.mUniqueIndex + 1; 145 } 146 } 147 148 return nextUniqueIndex; 149 } 150 151 private void handleBinderDiedLocked(IBinder appToken) { 152 VirtualDisplayDevice device = mVirtualDisplayDevices.remove(appToken); 153 if (device != null) { 154 Slog.i(TAG, "Virtual display device released because application token died: " 155 + device.mOwnerPackageName); 156 device.destroyLocked(false); 157 sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_REMOVED); 158 } 159 } 160 161 private void handleMediaProjectionStoppedLocked(IBinder appToken) { 162 VirtualDisplayDevice device = mVirtualDisplayDevices.remove(appToken); 163 if (device != null) { 164 Slog.i(TAG, "Virtual display device released because media projection stopped: " 165 + device.mName); 166 device.stopLocked(); 167 } 168 } 169 170 private final class VirtualDisplayDevice extends DisplayDevice implements DeathRecipient { 171 private static final int PENDING_SURFACE_CHANGE = 0x01; 172 private static final int PENDING_RESIZE = 0x02; 173 174 private static final float REFRESH_RATE = 60.0f; 175 176 private final IBinder mAppToken; 177 private final int mOwnerUid; 178 final String mOwnerPackageName; 179 final String mName; 180 private final int mFlags; 181 private final Callback mCallback; 182 183 private int mWidth; 184 private int mHeight; 185 private int mDensityDpi; 186 private Surface mSurface; 187 private DisplayDeviceInfo mInfo; 188 private int mDisplayState; 189 private boolean mStopped; 190 private int mPendingChanges; 191 private int mUniqueIndex; 192 private Display.Mode mMode; 193 194 public VirtualDisplayDevice(IBinder displayToken, IBinder appToken, 195 int ownerUid, String ownerPackageName, 196 String name, int width, int height, int densityDpi, Surface surface, int flags, 197 Callback callback, String uniqueId, int uniqueIndex) { 198 super(VirtualDisplayAdapter.this, displayToken, uniqueId); 199 mAppToken = appToken; 200 mOwnerUid = ownerUid; 201 mOwnerPackageName = ownerPackageName; 202 mName = name; 203 mWidth = width; 204 mHeight = height; 205 mMode = createMode(width, height, REFRESH_RATE); 206 mDensityDpi = densityDpi; 207 mSurface = surface; 208 mFlags = flags; 209 mCallback = callback; 210 mDisplayState = Display.STATE_UNKNOWN; 211 mPendingChanges |= PENDING_SURFACE_CHANGE; 212 mUniqueIndex = uniqueIndex; 213 } 214 215 @Override 216 public void binderDied() { 217 synchronized (getSyncRoot()) { 218 handleBinderDiedLocked(mAppToken); 219 } 220 } 221 222 public void destroyLocked(boolean binderAlive) { 223 if (mSurface != null) { 224 mSurface.release(); 225 mSurface = null; 226 } 227 SurfaceControl.destroyDisplay(getDisplayTokenLocked()); 228 if (binderAlive) { 229 mCallback.dispatchDisplayStopped(); 230 } 231 } 232 233 @Override 234 public boolean hasStableUniqueId() { 235 return false; 236 } 237 238 @Override 239 public Runnable requestDisplayStateLocked(int state, int brightness) { 240 if (state != mDisplayState) { 241 mDisplayState = state; 242 if (state == Display.STATE_OFF) { 243 mCallback.dispatchDisplayPaused(); 244 } else { 245 mCallback.dispatchDisplayResumed(); 246 } 247 } 248 return null; 249 } 250 251 @Override 252 public void performTraversalInTransactionLocked() { 253 if ((mPendingChanges & PENDING_RESIZE) != 0) { 254 SurfaceControl.setDisplaySize(getDisplayTokenLocked(), mWidth, mHeight); 255 } 256 if ((mPendingChanges & PENDING_SURFACE_CHANGE) != 0) { 257 setSurfaceInTransactionLocked(mSurface); 258 } 259 mPendingChanges = 0; 260 } 261 262 public void setSurfaceLocked(Surface surface) { 263 if (!mStopped && mSurface != surface) { 264 if ((mSurface != null) != (surface != null)) { 265 sendDisplayDeviceEventLocked(this, DISPLAY_DEVICE_EVENT_CHANGED); 266 } 267 sendTraversalRequestLocked(); 268 mSurface = surface; 269 mInfo = null; 270 mPendingChanges |= PENDING_SURFACE_CHANGE; 271 } 272 } 273 274 public void resizeLocked(int width, int height, int densityDpi) { 275 if (mWidth != width || mHeight != height || mDensityDpi != densityDpi) { 276 sendDisplayDeviceEventLocked(this, DISPLAY_DEVICE_EVENT_CHANGED); 277 sendTraversalRequestLocked(); 278 mWidth = width; 279 mHeight = height; 280 mMode = createMode(width, height, REFRESH_RATE); 281 mDensityDpi = densityDpi; 282 mInfo = null; 283 mPendingChanges |= PENDING_RESIZE; 284 } 285 } 286 287 public void stopLocked() { 288 setSurfaceLocked(null); 289 mStopped = true; 290 } 291 292 @Override 293 public void dumpLocked(PrintWriter pw) { 294 super.dumpLocked(pw); 295 pw.println("mFlags=" + mFlags); 296 pw.println("mDisplayState=" + Display.stateToString(mDisplayState)); 297 pw.println("mStopped=" + mStopped); 298 } 299 300 301 @Override 302 public DisplayDeviceInfo getDisplayDeviceInfoLocked() { 303 if (mInfo == null) { 304 mInfo = new DisplayDeviceInfo(); 305 mInfo.name = mName; 306 mInfo.uniqueId = getUniqueId(); 307 mInfo.width = mWidth; 308 mInfo.height = mHeight; 309 mInfo.modeId = mMode.getModeId(); 310 mInfo.defaultModeId = mMode.getModeId(); 311 mInfo.supportedModes = new Display.Mode[] { mMode }; 312 mInfo.densityDpi = mDensityDpi; 313 mInfo.xDpi = mDensityDpi; 314 mInfo.yDpi = mDensityDpi; 315 mInfo.presentationDeadlineNanos = 1000000000L / (int) REFRESH_RATE; // 1 frame 316 mInfo.flags = 0; 317 if ((mFlags & VIRTUAL_DISPLAY_FLAG_PUBLIC) == 0) { 318 mInfo.flags |= DisplayDeviceInfo.FLAG_PRIVATE 319 | DisplayDeviceInfo.FLAG_NEVER_BLANK; 320 } 321 if ((mFlags & VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR) != 0) { 322 mInfo.flags &= ~DisplayDeviceInfo.FLAG_NEVER_BLANK; 323 } else { 324 mInfo.flags |= DisplayDeviceInfo.FLAG_OWN_CONTENT_ONLY; 325 } 326 327 if ((mFlags & VIRTUAL_DISPLAY_FLAG_SECURE) != 0) { 328 mInfo.flags |= DisplayDeviceInfo.FLAG_SECURE; 329 } 330 if ((mFlags & VIRTUAL_DISPLAY_FLAG_PRESENTATION) != 0) { 331 mInfo.flags |= DisplayDeviceInfo.FLAG_PRESENTATION; 332 333 if ((mFlags & VIRTUAL_DISPLAY_FLAG_PUBLIC) != 0) { 334 // For demonstration purposes, allow rotation of the external display. 335 // In the future we might allow the user to configure this directly. 336 if ("portrait".equals(SystemProperties.get( 337 "persist.demo.remoterotation"))) { 338 mInfo.rotation = Surface.ROTATION_270; 339 } 340 } 341 } 342 if ((mFlags & VIRTUAL_DISPLAY_FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD) != 0) { 343 mInfo.flags |= DisplayDeviceInfo.FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD; 344 } 345 mInfo.type = Display.TYPE_VIRTUAL; 346 mInfo.touch = DisplayDeviceInfo.TOUCH_NONE; 347 mInfo.state = mSurface != null ? Display.STATE_ON : Display.STATE_OFF; 348 mInfo.ownerUid = mOwnerUid; 349 mInfo.ownerPackageName = mOwnerPackageName; 350 } 351 return mInfo; 352 } 353 } 354 355 private static class Callback extends Handler { 356 private static final int MSG_ON_DISPLAY_PAUSED = 0; 357 private static final int MSG_ON_DISPLAY_RESUMED = 1; 358 private static final int MSG_ON_DISPLAY_STOPPED = 2; 359 360 private final IVirtualDisplayCallback mCallback; 361 362 public Callback(IVirtualDisplayCallback callback, Handler handler) { 363 super(handler.getLooper()); 364 mCallback = callback; 365 } 366 367 @Override 368 public void handleMessage(Message msg) { 369 try { 370 switch (msg.what) { 371 case MSG_ON_DISPLAY_PAUSED: 372 mCallback.onPaused(); 373 break; 374 case MSG_ON_DISPLAY_RESUMED: 375 mCallback.onResumed(); 376 break; 377 case MSG_ON_DISPLAY_STOPPED: 378 mCallback.onStopped(); 379 break; 380 } 381 } catch (RemoteException e) { 382 Slog.w(TAG, "Failed to notify listener of virtual display event.", e); 383 } 384 } 385 386 public void dispatchDisplayPaused() { 387 sendEmptyMessage(MSG_ON_DISPLAY_PAUSED); 388 } 389 390 public void dispatchDisplayResumed() { 391 sendEmptyMessage(MSG_ON_DISPLAY_RESUMED); 392 } 393 394 public void dispatchDisplayStopped() { 395 sendEmptyMessage(MSG_ON_DISPLAY_STOPPED); 396 } 397 } 398 399 private final class MediaProjectionCallback extends IMediaProjectionCallback.Stub { 400 private IBinder mAppToken; 401 public MediaProjectionCallback(IBinder appToken) { 402 mAppToken = appToken; 403 } 404 405 @Override 406 public void onStop() { 407 synchronized (getSyncRoot()) { 408 handleMediaProjectionStoppedLocked(mAppToken); 409 } 410 } 411 } 412} 413