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 17 package com.android.server.display; 18 19 import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR; 20 import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD; 21 import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL; 22 import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_DISPLAY_GROUP; 23 import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION; 24 import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC; 25 import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_ROTATES_WITH_CONTENT; 26 import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_SECURE; 27 import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS; 28 import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_SUPPORTS_TOUCH; 29 import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_TRUSTED; 30 31 import static com.android.server.display.DisplayDeviceInfo.FLAG_OWN_DISPLAY_GROUP; 32 import static com.android.server.display.DisplayDeviceInfo.FLAG_TRUSTED; 33 34 import android.annotation.Nullable; 35 import android.content.Context; 36 import android.graphics.Point; 37 import android.hardware.display.IVirtualDisplayCallback; 38 import android.hardware.display.VirtualDisplayConfig; 39 import android.media.projection.IMediaProjection; 40 import android.media.projection.IMediaProjectionCallback; 41 import android.os.Handler; 42 import android.os.IBinder; 43 import android.os.IBinder.DeathRecipient; 44 import android.os.Message; 45 import android.os.RemoteException; 46 import android.os.SystemProperties; 47 import android.util.ArrayMap; 48 import android.util.Slog; 49 import android.view.Display; 50 import android.view.Surface; 51 import android.view.SurfaceControl; 52 53 import com.android.internal.annotations.VisibleForTesting; 54 55 import java.io.PrintWriter; 56 import java.util.Iterator; 57 58 /** 59 * A display adapter that provides virtual displays on behalf of applications. 60 * <p> 61 * Display adapters are guarded by the {@link DisplayManagerService.SyncRoot} lock. 62 * </p> 63 */ 64 @VisibleForTesting 65 public class VirtualDisplayAdapter extends DisplayAdapter { 66 static final String TAG = "VirtualDisplayAdapter"; 67 static final boolean DEBUG = false; 68 69 // Unique id prefix for virtual displays 70 @VisibleForTesting 71 static final String UNIQUE_ID_PREFIX = "virtual:"; 72 73 private final ArrayMap<IBinder, VirtualDisplayDevice> mVirtualDisplayDevices = 74 new ArrayMap<IBinder, VirtualDisplayDevice>(); 75 private final Handler mHandler; 76 private final SurfaceControlDisplayFactory mSurfaceControlDisplayFactory; 77 78 // Called with SyncRoot lock held. VirtualDisplayAdapter(DisplayManagerService.SyncRoot syncRoot, Context context, Handler handler, Listener listener)79 public VirtualDisplayAdapter(DisplayManagerService.SyncRoot syncRoot, 80 Context context, Handler handler, Listener listener) { 81 this(syncRoot, context, handler, listener, 82 (String name, boolean secure) -> SurfaceControl.createDisplay(name, secure)); 83 } 84 85 @VisibleForTesting VirtualDisplayAdapter(DisplayManagerService.SyncRoot syncRoot, Context context, Handler handler, Listener listener, SurfaceControlDisplayFactory surfaceControlDisplayFactory)86 VirtualDisplayAdapter(DisplayManagerService.SyncRoot syncRoot, 87 Context context, Handler handler, Listener listener, 88 SurfaceControlDisplayFactory surfaceControlDisplayFactory) { 89 super(syncRoot, context, handler, listener, TAG); 90 mHandler = handler; 91 mSurfaceControlDisplayFactory = surfaceControlDisplayFactory; 92 } 93 createVirtualDisplayLocked(IVirtualDisplayCallback callback, IMediaProjection projection, int ownerUid, String ownerPackageName, Surface surface, int flags, VirtualDisplayConfig virtualDisplayConfig)94 public DisplayDevice createVirtualDisplayLocked(IVirtualDisplayCallback callback, 95 IMediaProjection projection, int ownerUid, String ownerPackageName, Surface surface, 96 int flags, VirtualDisplayConfig virtualDisplayConfig) { 97 String name = virtualDisplayConfig.getName(); 98 boolean secure = (flags & VIRTUAL_DISPLAY_FLAG_SECURE) != 0; 99 IBinder appToken = callback.asBinder(); 100 IBinder displayToken = mSurfaceControlDisplayFactory.createDisplay(name, secure); 101 final String baseUniqueId = 102 UNIQUE_ID_PREFIX + ownerPackageName + "," + ownerUid + "," + name + ","; 103 final int uniqueIndex = getNextUniqueIndex(baseUniqueId); 104 String uniqueId = virtualDisplayConfig.getUniqueId(); 105 if (uniqueId == null) { 106 uniqueId = baseUniqueId + uniqueIndex; 107 } else { 108 uniqueId = UNIQUE_ID_PREFIX + ownerPackageName + ":" + uniqueId; 109 } 110 VirtualDisplayDevice device = new VirtualDisplayDevice(displayToken, appToken, 111 ownerUid, ownerPackageName, surface, flags, new Callback(callback, mHandler), 112 uniqueId, uniqueIndex, virtualDisplayConfig); 113 114 mVirtualDisplayDevices.put(appToken, device); 115 116 try { 117 if (projection != null) { 118 projection.registerCallback(new MediaProjectionCallback(appToken)); 119 } 120 appToken.linkToDeath(device, 0); 121 } catch (RemoteException ex) { 122 mVirtualDisplayDevices.remove(appToken); 123 device.destroyLocked(false); 124 return null; 125 } 126 127 // Return the display device without actually sending the event indicating 128 // that it was added. The caller will handle it. 129 return device; 130 } 131 resizeVirtualDisplayLocked(IBinder appToken, int width, int height, int densityDpi)132 public void resizeVirtualDisplayLocked(IBinder appToken, 133 int width, int height, int densityDpi) { 134 VirtualDisplayDevice device = mVirtualDisplayDevices.get(appToken); 135 if (device != null) { 136 device.resizeLocked(width, height, densityDpi); 137 } 138 } 139 140 @VisibleForTesting getVirtualDisplaySurfaceLocked(IBinder appToken)141 Surface getVirtualDisplaySurfaceLocked(IBinder appToken) { 142 VirtualDisplayDevice device = mVirtualDisplayDevices.get(appToken); 143 if (device != null) { 144 return device.getSurfaceLocked(); 145 } 146 return null; 147 } 148 setVirtualDisplaySurfaceLocked(IBinder appToken, Surface surface)149 public void setVirtualDisplaySurfaceLocked(IBinder appToken, Surface surface) { 150 VirtualDisplayDevice device = mVirtualDisplayDevices.get(appToken); 151 if (device != null) { 152 device.setSurfaceLocked(surface); 153 } 154 } 155 releaseVirtualDisplayLocked(IBinder appToken)156 public DisplayDevice releaseVirtualDisplayLocked(IBinder appToken) { 157 VirtualDisplayDevice device = mVirtualDisplayDevices.remove(appToken); 158 if (device != null) { 159 device.destroyLocked(true); 160 appToken.unlinkToDeath(device, 0); 161 } 162 163 // Return the display device that was removed without actually sending the 164 // event indicating that it was removed. The caller will handle it. 165 return device; 166 } 167 setVirtualDisplayStateLocked(IBinder appToken, boolean isOn)168 void setVirtualDisplayStateLocked(IBinder appToken, boolean isOn) { 169 VirtualDisplayDevice device = mVirtualDisplayDevices.get(appToken); 170 if (device != null) { 171 device.setDisplayState(isOn); 172 } 173 } 174 175 /** 176 * Returns the next unique index for the uniqueIdPrefix 177 */ getNextUniqueIndex(String uniqueIdPrefix)178 private int getNextUniqueIndex(String uniqueIdPrefix) { 179 if (mVirtualDisplayDevices.isEmpty()) { 180 return 0; 181 } 182 183 int nextUniqueIndex = 0; 184 Iterator<VirtualDisplayDevice> it = mVirtualDisplayDevices.values().iterator(); 185 while (it.hasNext()) { 186 VirtualDisplayDevice device = it.next(); 187 if (device.getUniqueId().startsWith(uniqueIdPrefix) 188 && device.mUniqueIndex >= nextUniqueIndex) { 189 // Increment the next unique index to be greater than ones we have already ran 190 // across for displays that have the same unique Id prefix. 191 nextUniqueIndex = device.mUniqueIndex + 1; 192 } 193 } 194 195 return nextUniqueIndex; 196 } 197 handleBinderDiedLocked(IBinder appToken)198 private void handleBinderDiedLocked(IBinder appToken) { 199 mVirtualDisplayDevices.remove(appToken); 200 } 201 handleMediaProjectionStoppedLocked(IBinder appToken)202 private void handleMediaProjectionStoppedLocked(IBinder appToken) { 203 VirtualDisplayDevice device = mVirtualDisplayDevices.remove(appToken); 204 if (device != null) { 205 Slog.i(TAG, "Virtual display device released because media projection stopped: " 206 + device.mName); 207 device.stopLocked(); 208 } 209 } 210 211 private final class VirtualDisplayDevice extends DisplayDevice implements DeathRecipient { 212 private static final int PENDING_SURFACE_CHANGE = 0x01; 213 private static final int PENDING_RESIZE = 0x02; 214 215 private static final float REFRESH_RATE = 60.0f; 216 217 private final IBinder mAppToken; 218 private final int mOwnerUid; 219 final String mOwnerPackageName; 220 final String mName; 221 private final int mFlags; 222 private final Callback mCallback; 223 224 private int mWidth; 225 private int mHeight; 226 private int mDensityDpi; 227 private Surface mSurface; 228 private DisplayDeviceInfo mInfo; 229 private int mDisplayState; 230 private boolean mStopped; 231 private int mPendingChanges; 232 private int mUniqueIndex; 233 private Display.Mode mMode; 234 private boolean mIsDisplayOn; 235 private int mDisplayIdToMirror; 236 private IBinder mWindowTokenClientToMirror; 237 VirtualDisplayDevice(IBinder displayToken, IBinder appToken, int ownerUid, String ownerPackageName, Surface surface, int flags, Callback callback, String uniqueId, int uniqueIndex, VirtualDisplayConfig virtualDisplayConfig)238 public VirtualDisplayDevice(IBinder displayToken, IBinder appToken, 239 int ownerUid, String ownerPackageName, Surface surface, int flags, 240 Callback callback, String uniqueId, int uniqueIndex, 241 VirtualDisplayConfig virtualDisplayConfig) { 242 super(VirtualDisplayAdapter.this, displayToken, uniqueId, getContext()); 243 mAppToken = appToken; 244 mOwnerUid = ownerUid; 245 mOwnerPackageName = ownerPackageName; 246 mName = virtualDisplayConfig.getName(); 247 mWidth = virtualDisplayConfig.getWidth(); 248 mHeight = virtualDisplayConfig.getHeight(); 249 mMode = createMode(mWidth, mHeight, REFRESH_RATE); 250 mDensityDpi = virtualDisplayConfig.getDensityDpi(); 251 mSurface = surface; 252 mFlags = flags; 253 mCallback = callback; 254 mDisplayState = Display.STATE_UNKNOWN; 255 mPendingChanges |= PENDING_SURFACE_CHANGE; 256 mUniqueIndex = uniqueIndex; 257 mIsDisplayOn = surface != null; 258 mDisplayIdToMirror = virtualDisplayConfig.getDisplayIdToMirror(); 259 mWindowTokenClientToMirror = virtualDisplayConfig.getWindowTokenClientToMirror(); 260 } 261 262 @Override binderDied()263 public void binderDied() { 264 synchronized (getSyncRoot()) { 265 handleBinderDiedLocked(mAppToken); 266 Slog.i(TAG, "Virtual display device released because application token died: " 267 + mOwnerPackageName); 268 destroyLocked(false); 269 sendDisplayDeviceEventLocked(this, DISPLAY_DEVICE_EVENT_REMOVED); 270 } 271 } 272 destroyLocked(boolean binderAlive)273 public void destroyLocked(boolean binderAlive) { 274 if (mSurface != null) { 275 mSurface.release(); 276 mSurface = null; 277 } 278 SurfaceControl.destroyDisplay(getDisplayTokenLocked()); 279 if (binderAlive) { 280 mCallback.dispatchDisplayStopped(); 281 } 282 } 283 284 @Override getDisplayIdToMirrorLocked()285 public int getDisplayIdToMirrorLocked() { 286 return mDisplayIdToMirror; 287 } 288 289 @Override 290 @Nullable getWindowTokenClientToMirrorLocked()291 public IBinder getWindowTokenClientToMirrorLocked() { 292 return mWindowTokenClientToMirror; 293 } 294 295 @Override setWindowTokenClientToMirrorLocked(IBinder windowToken)296 public void setWindowTokenClientToMirrorLocked(IBinder windowToken) { 297 if (mWindowTokenClientToMirror != windowToken) { 298 mWindowTokenClientToMirror = windowToken; 299 sendDisplayDeviceEventLocked(this, DISPLAY_DEVICE_EVENT_CHANGED); 300 sendTraversalRequestLocked(); 301 } 302 } 303 304 @Override getDisplaySurfaceDefaultSize()305 public Point getDisplaySurfaceDefaultSize() { 306 if (mSurface == null) { 307 return null; 308 } 309 return mSurface.getDefaultSize(); 310 } 311 312 @VisibleForTesting getSurfaceLocked()313 Surface getSurfaceLocked() { 314 return mSurface; 315 } 316 317 @Override hasStableUniqueId()318 public boolean hasStableUniqueId() { 319 return false; 320 } 321 322 @Override requestDisplayStateLocked(int state, float brightnessState, float sdrBrightnessState)323 public Runnable requestDisplayStateLocked(int state, float brightnessState, 324 float sdrBrightnessState) { 325 if (state != mDisplayState) { 326 mDisplayState = state; 327 if (state == Display.STATE_OFF) { 328 mCallback.dispatchDisplayPaused(); 329 } else { 330 mCallback.dispatchDisplayResumed(); 331 } 332 } 333 return null; 334 } 335 336 @Override performTraversalLocked(SurfaceControl.Transaction t)337 public void performTraversalLocked(SurfaceControl.Transaction t) { 338 if ((mPendingChanges & PENDING_RESIZE) != 0) { 339 t.setDisplaySize(getDisplayTokenLocked(), mWidth, mHeight); 340 } 341 if ((mPendingChanges & PENDING_SURFACE_CHANGE) != 0) { 342 setSurfaceLocked(t, mSurface); 343 } 344 mPendingChanges = 0; 345 } 346 setSurfaceLocked(Surface surface)347 public void setSurfaceLocked(Surface surface) { 348 if (!mStopped && mSurface != surface) { 349 if ((mSurface != null) != (surface != null)) { 350 sendDisplayDeviceEventLocked(this, DISPLAY_DEVICE_EVENT_CHANGED); 351 } 352 sendTraversalRequestLocked(); 353 mSurface = surface; 354 mInfo = null; 355 mPendingChanges |= PENDING_SURFACE_CHANGE; 356 } 357 } 358 resizeLocked(int width, int height, int densityDpi)359 public void resizeLocked(int width, int height, int densityDpi) { 360 if (mWidth != width || mHeight != height || mDensityDpi != densityDpi) { 361 sendDisplayDeviceEventLocked(this, DISPLAY_DEVICE_EVENT_CHANGED); 362 sendTraversalRequestLocked(); 363 mWidth = width; 364 mHeight = height; 365 mMode = createMode(width, height, REFRESH_RATE); 366 mDensityDpi = densityDpi; 367 mInfo = null; 368 mPendingChanges |= PENDING_RESIZE; 369 } 370 } 371 setDisplayState(boolean isOn)372 void setDisplayState(boolean isOn) { 373 if (mIsDisplayOn != isOn) { 374 mIsDisplayOn = isOn; 375 mInfo = null; 376 sendDisplayDeviceEventLocked(this, DISPLAY_DEVICE_EVENT_CHANGED); 377 } 378 } 379 stopLocked()380 public void stopLocked() { 381 setSurfaceLocked(null); 382 mStopped = true; 383 } 384 385 @Override dumpLocked(PrintWriter pw)386 public void dumpLocked(PrintWriter pw) { 387 super.dumpLocked(pw); 388 pw.println("mFlags=" + mFlags); 389 pw.println("mDisplayState=" + Display.stateToString(mDisplayState)); 390 pw.println("mStopped=" + mStopped); 391 pw.println("mDisplayIdToMirror=" + mDisplayIdToMirror); 392 pw.println("mWindowTokenClientToMirror=" + mWindowTokenClientToMirror); 393 } 394 395 396 @Override getDisplayDeviceInfoLocked()397 public DisplayDeviceInfo getDisplayDeviceInfoLocked() { 398 if (mInfo == null) { 399 mInfo = new DisplayDeviceInfo(); 400 mInfo.name = mName; 401 mInfo.uniqueId = getUniqueId(); 402 mInfo.width = mWidth; 403 mInfo.height = mHeight; 404 mInfo.modeId = mMode.getModeId(); 405 mInfo.defaultModeId = mMode.getModeId(); 406 mInfo.supportedModes = new Display.Mode[] { mMode }; 407 mInfo.densityDpi = mDensityDpi; 408 mInfo.xDpi = mDensityDpi; 409 mInfo.yDpi = mDensityDpi; 410 mInfo.presentationDeadlineNanos = 1000000000L / (int) REFRESH_RATE; // 1 frame 411 mInfo.flags = 0; 412 if ((mFlags & VIRTUAL_DISPLAY_FLAG_PUBLIC) == 0) { 413 mInfo.flags |= DisplayDeviceInfo.FLAG_PRIVATE 414 | DisplayDeviceInfo.FLAG_NEVER_BLANK; 415 } 416 if ((mFlags & VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR) != 0) { 417 mInfo.flags &= ~DisplayDeviceInfo.FLAG_NEVER_BLANK; 418 } else { 419 mInfo.flags |= DisplayDeviceInfo.FLAG_OWN_CONTENT_ONLY; 420 421 if ((mFlags & VIRTUAL_DISPLAY_FLAG_OWN_DISPLAY_GROUP) != 0) { 422 mInfo.flags |= FLAG_OWN_DISPLAY_GROUP; 423 } 424 } 425 426 if ((mFlags & VIRTUAL_DISPLAY_FLAG_SECURE) != 0) { 427 mInfo.flags |= DisplayDeviceInfo.FLAG_SECURE; 428 } 429 if ((mFlags & VIRTUAL_DISPLAY_FLAG_PRESENTATION) != 0) { 430 mInfo.flags |= DisplayDeviceInfo.FLAG_PRESENTATION; 431 432 if ((mFlags & VIRTUAL_DISPLAY_FLAG_PUBLIC) != 0) { 433 // For demonstration purposes, allow rotation of the external display. 434 // In the future we might allow the user to configure this directly. 435 if ("portrait".equals(SystemProperties.get( 436 "persist.demo.remoterotation"))) { 437 mInfo.rotation = Surface.ROTATION_270; 438 } 439 } 440 } 441 if ((mFlags & VIRTUAL_DISPLAY_FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD) != 0) { 442 mInfo.flags |= DisplayDeviceInfo.FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD; 443 } 444 if ((mFlags & VIRTUAL_DISPLAY_FLAG_ROTATES_WITH_CONTENT) != 0) { 445 mInfo.flags |= DisplayDeviceInfo.FLAG_ROTATES_WITH_CONTENT; 446 } 447 if ((mFlags & VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL) != 0) { 448 mInfo.flags |= DisplayDeviceInfo.FLAG_DESTROY_CONTENT_ON_REMOVAL; 449 } 450 if ((mFlags & VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS) != 0) { 451 mInfo.flags |= DisplayDeviceInfo.FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS; 452 } 453 if ((mFlags & VIRTUAL_DISPLAY_FLAG_TRUSTED) != 0) { 454 mInfo.flags |= FLAG_TRUSTED; 455 } 456 457 mInfo.type = Display.TYPE_VIRTUAL; 458 mInfo.touch = ((mFlags & VIRTUAL_DISPLAY_FLAG_SUPPORTS_TOUCH) == 0) ? 459 DisplayDeviceInfo.TOUCH_NONE : DisplayDeviceInfo.TOUCH_VIRTUAL; 460 461 mInfo.state = mIsDisplayOn ? Display.STATE_ON : Display.STATE_OFF; 462 463 mInfo.ownerUid = mOwnerUid; 464 mInfo.ownerPackageName = mOwnerPackageName; 465 } 466 return mInfo; 467 } 468 } 469 470 private static class Callback extends Handler { 471 private static final int MSG_ON_DISPLAY_PAUSED = 0; 472 private static final int MSG_ON_DISPLAY_RESUMED = 1; 473 private static final int MSG_ON_DISPLAY_STOPPED = 2; 474 475 private final IVirtualDisplayCallback mCallback; 476 Callback(IVirtualDisplayCallback callback, Handler handler)477 public Callback(IVirtualDisplayCallback callback, Handler handler) { 478 super(handler.getLooper()); 479 mCallback = callback; 480 } 481 482 @Override handleMessage(Message msg)483 public void handleMessage(Message msg) { 484 try { 485 switch (msg.what) { 486 case MSG_ON_DISPLAY_PAUSED: 487 mCallback.onPaused(); 488 break; 489 case MSG_ON_DISPLAY_RESUMED: 490 mCallback.onResumed(); 491 break; 492 case MSG_ON_DISPLAY_STOPPED: 493 mCallback.onStopped(); 494 break; 495 } 496 } catch (RemoteException e) { 497 Slog.w(TAG, "Failed to notify listener of virtual display event.", e); 498 } 499 } 500 dispatchDisplayPaused()501 public void dispatchDisplayPaused() { 502 sendEmptyMessage(MSG_ON_DISPLAY_PAUSED); 503 } 504 dispatchDisplayResumed()505 public void dispatchDisplayResumed() { 506 sendEmptyMessage(MSG_ON_DISPLAY_RESUMED); 507 } 508 dispatchDisplayStopped()509 public void dispatchDisplayStopped() { 510 sendEmptyMessage(MSG_ON_DISPLAY_STOPPED); 511 } 512 } 513 514 private final class MediaProjectionCallback extends IMediaProjectionCallback.Stub { 515 private IBinder mAppToken; MediaProjectionCallback(IBinder appToken)516 public MediaProjectionCallback(IBinder appToken) { 517 mAppToken = appToken; 518 } 519 520 @Override onStop()521 public void onStop() { 522 synchronized (getSyncRoot()) { 523 handleMediaProjectionStoppedLocked(mAppToken); 524 } 525 } 526 } 527 528 @VisibleForTesting 529 public interface SurfaceControlDisplayFactory { createDisplay(String name, boolean secure)530 public IBinder createDisplay(String name, boolean secure); 531 } 532 } 533