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