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