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 android.content.Context; 20import android.hardware.display.DisplayManager; 21import android.hardware.display.IVirtualDisplayCallback; 22import android.media.projection.IMediaProjection; 23import android.media.projection.IMediaProjectionCallback; 24import android.os.Handler; 25import android.os.IBinder; 26import android.os.SystemProperties; 27import android.os.IBinder.DeathRecipient; 28import android.os.Message; 29import android.os.RemoteException; 30import android.util.ArrayMap; 31import android.util.Slog; 32import android.view.Display; 33import android.view.Surface; 34import android.view.SurfaceControl; 35 36import java.io.PrintWriter; 37import 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 */ 45final 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. 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 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(); 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 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 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 110 public DisplayDevice releaseVirtualDisplayLocked(IBinder appToken) { 111 VirtualDisplayDevice device = mVirtualDisplayDevices.remove(appToken); 112 if (device != null) { 113 device.destroyLocked(); 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 */ 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 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(); 151 sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_REMOVED); 152 } 153 } 154 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 final IBinder mAppToken; 169 private final int mOwnerUid; 170 final String mOwnerPackageName; 171 final String mName; 172 private final int mFlags; 173 private final Callback mCallback; 174 175 private int mWidth; 176 private int mHeight; 177 private int mDensityDpi; 178 private Surface mSurface; 179 private DisplayDeviceInfo mInfo; 180 private int mDisplayState; 181 private boolean mStopped; 182 private int mPendingChanges; 183 private int mUniqueIndex; 184 185 public VirtualDisplayDevice(IBinder displayToken, IBinder appToken, 186 int ownerUid, String ownerPackageName, 187 String name, int width, int height, int densityDpi, Surface surface, int flags, 188 Callback callback, String uniqueId, int uniqueIndex) { 189 super(VirtualDisplayAdapter.this, displayToken, uniqueId); 190 mAppToken = appToken; 191 mOwnerUid = ownerUid; 192 mOwnerPackageName = ownerPackageName; 193 mName = name; 194 mWidth = width; 195 mHeight = height; 196 mDensityDpi = densityDpi; 197 mSurface = surface; 198 mFlags = flags; 199 mCallback = callback; 200 mDisplayState = Display.STATE_UNKNOWN; 201 mPendingChanges |= PENDING_SURFACE_CHANGE; 202 mUniqueIndex = uniqueIndex; 203 } 204 205 @Override 206 public void binderDied() { 207 synchronized (getSyncRoot()) { 208 if (mSurface != null) { 209 handleBinderDiedLocked(mAppToken); 210 } 211 } 212 } 213 214 public void destroyLocked() { 215 if (mSurface != null) { 216 mSurface.release(); 217 mSurface = null; 218 } 219 SurfaceControl.destroyDisplay(getDisplayTokenLocked()); 220 mCallback.dispatchDisplayStopped(); 221 } 222 223 @Override 224 public Runnable requestDisplayStateLocked(int state) { 225 if (state != mDisplayState) { 226 mDisplayState = state; 227 if (state == Display.STATE_OFF) { 228 mCallback.dispatchDisplayPaused(); 229 } else { 230 mCallback.dispatchDisplayResumed(); 231 } 232 } 233 return null; 234 } 235 236 @Override 237 public void performTraversalInTransactionLocked() { 238 if ((mPendingChanges & PENDING_RESIZE) != 0) { 239 SurfaceControl.setDisplaySize(getDisplayTokenLocked(), mWidth, mHeight); 240 } 241 if ((mPendingChanges & PENDING_SURFACE_CHANGE) != 0) { 242 setSurfaceInTransactionLocked(mSurface); 243 } 244 mPendingChanges = 0; 245 } 246 247 public void setSurfaceLocked(Surface surface) { 248 if (!mStopped && mSurface != surface) { 249 if ((mSurface != null) != (surface != null)) { 250 sendDisplayDeviceEventLocked(this, DISPLAY_DEVICE_EVENT_CHANGED); 251 } 252 sendTraversalRequestLocked(); 253 mSurface = surface; 254 mInfo = null; 255 mPendingChanges |= PENDING_SURFACE_CHANGE; 256 } 257 } 258 259 public void resizeLocked(int width, int height, int densityDpi) { 260 if (mWidth != width || mHeight != height || mDensityDpi != densityDpi) { 261 sendDisplayDeviceEventLocked(this, DISPLAY_DEVICE_EVENT_CHANGED); 262 sendTraversalRequestLocked(); 263 mWidth = width; 264 mHeight = height; 265 mDensityDpi = densityDpi; 266 mInfo = null; 267 mPendingChanges |= PENDING_RESIZE; 268 } 269 } 270 271 public void stopLocked() { 272 setSurfaceLocked(null); 273 mStopped = true; 274 } 275 276 @Override 277 public void dumpLocked(PrintWriter pw) { 278 super.dumpLocked(pw); 279 pw.println("mFlags=" + mFlags); 280 pw.println("mDisplayState=" + Display.stateToString(mDisplayState)); 281 pw.println("mStopped=" + mStopped); 282 } 283 284 285 @Override 286 public DisplayDeviceInfo getDisplayDeviceInfoLocked() { 287 if (mInfo == null) { 288 mInfo = new DisplayDeviceInfo(); 289 mInfo.name = mName; 290 mInfo.uniqueId = getUniqueId(); 291 mInfo.width = mWidth; 292 mInfo.height = mHeight; 293 mInfo.refreshRate = 60; 294 mInfo.supportedRefreshRates = new float[] { 60.0f }; 295 mInfo.densityDpi = mDensityDpi; 296 mInfo.xDpi = mDensityDpi; 297 mInfo.yDpi = mDensityDpi; 298 mInfo.presentationDeadlineNanos = 1000000000L / (int) mInfo.refreshRate; // 1 frame 299 mInfo.flags = 0; 300 if ((mFlags & DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC) == 0) { 301 mInfo.flags |= DisplayDeviceInfo.FLAG_PRIVATE 302 | DisplayDeviceInfo.FLAG_NEVER_BLANK; 303 } 304 if ((mFlags & DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR) != 0) { 305 mInfo.flags &= ~DisplayDeviceInfo.FLAG_NEVER_BLANK; 306 } else { 307 mInfo.flags |= DisplayDeviceInfo.FLAG_OWN_CONTENT_ONLY; 308 } 309 310 if ((mFlags & DisplayManager.VIRTUAL_DISPLAY_FLAG_SECURE) != 0) { 311 mInfo.flags |= DisplayDeviceInfo.FLAG_SECURE; 312 } 313 if ((mFlags & DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION) != 0) { 314 mInfo.flags |= DisplayDeviceInfo.FLAG_PRESENTATION; 315 316 if ((mFlags & DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC) != 0) { 317 // For demonstration purposes, allow rotation of the external display. 318 // In the future we might allow the user to configure this directly. 319 if ("portrait".equals(SystemProperties.get( 320 "persist.demo.remoterotation"))) { 321 mInfo.rotation = Surface.ROTATION_270; 322 } 323 } 324 } 325 mInfo.type = Display.TYPE_VIRTUAL; 326 mInfo.touch = DisplayDeviceInfo.TOUCH_NONE; 327 mInfo.state = mSurface != null ? Display.STATE_ON : Display.STATE_OFF; 328 mInfo.ownerUid = mOwnerUid; 329 mInfo.ownerPackageName = mOwnerPackageName; 330 } 331 return mInfo; 332 } 333 } 334 335 private static class Callback extends Handler { 336 private static final int MSG_ON_DISPLAY_PAUSED = 0; 337 private static final int MSG_ON_DISPLAY_RESUMED = 1; 338 private static final int MSG_ON_DISPLAY_STOPPED = 2; 339 340 private final IVirtualDisplayCallback mCallback; 341 342 public Callback(IVirtualDisplayCallback callback, Handler handler) { 343 super(handler.getLooper()); 344 mCallback = callback; 345 } 346 347 @Override 348 public void handleMessage(Message msg) { 349 try { 350 switch (msg.what) { 351 case MSG_ON_DISPLAY_PAUSED: 352 mCallback.onPaused(); 353 break; 354 case MSG_ON_DISPLAY_RESUMED: 355 mCallback.onResumed(); 356 break; 357 case MSG_ON_DISPLAY_STOPPED: 358 mCallback.onStopped(); 359 break; 360 } 361 } catch (RemoteException e) { 362 Slog.w(TAG, "Failed to notify listener of virtual display event.", e); 363 } 364 } 365 366 public void dispatchDisplayPaused() { 367 sendEmptyMessage(MSG_ON_DISPLAY_PAUSED); 368 } 369 370 public void dispatchDisplayResumed() { 371 sendEmptyMessage(MSG_ON_DISPLAY_RESUMED); 372 } 373 374 public void dispatchDisplayStopped() { 375 sendEmptyMessage(MSG_ON_DISPLAY_STOPPED); 376 } 377 } 378 379 private final class MediaProjectionCallback extends IMediaProjectionCallback.Stub { 380 private IBinder mAppToken; 381 public MediaProjectionCallback(IBinder appToken) { 382 mAppToken = appToken; 383 } 384 385 @Override 386 public void onStop() { 387 synchronized (getSyncRoot()) { 388 handleMediaProjectionStoppedLocked(mAppToken); 389 } 390 } 391 } 392} 393