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