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