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