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. 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(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 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(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 */ 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(false); 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 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 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 210 public void binderDied() { 211 synchronized (getSyncRoot()) { 212 handleBinderDiedLocked(mAppToken); 213 } 214 } 215 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 228 public boolean hasStableUniqueId() { 229 return false; 230 } 231 232 @Override 233 public Runnable requestDisplayStateLocked(int state, int brightness) { 234 if (state != mDisplayState) { 235 mDisplayState = state; 236 if (state == Display.STATE_OFF) { 237 mCallback.dispatchDisplayPaused(); 238 } else { 239 mCallback.dispatchDisplayResumed(); 240 } 241 } 242 return null; 243 } 244 245 @Override 246 public void performTraversalInTransactionLocked() { 247 if ((mPendingChanges & PENDING_RESIZE) != 0) { 248 SurfaceControl.setDisplaySize(getDisplayTokenLocked(), mWidth, mHeight); 249 } 250 if ((mPendingChanges & PENDING_SURFACE_CHANGE) != 0) { 251 setSurfaceInTransactionLocked(mSurface); 252 } 253 mPendingChanges = 0; 254 } 255 256 public void setSurfaceLocked(Surface surface) { 257 if (!mStopped && mSurface != surface) { 258 if ((mSurface != null) != (surface != null)) { 259 sendDisplayDeviceEventLocked(this, DISPLAY_DEVICE_EVENT_CHANGED); 260 } 261 sendTraversalRequestLocked(); 262 mSurface = surface; 263 mInfo = null; 264 mPendingChanges |= PENDING_SURFACE_CHANGE; 265 } 266 } 267 268 public void resizeLocked(int width, int height, int densityDpi) { 269 if (mWidth != width || mHeight != height || mDensityDpi != densityDpi) { 270 sendDisplayDeviceEventLocked(this, DISPLAY_DEVICE_EVENT_CHANGED); 271 sendTraversalRequestLocked(); 272 mWidth = width; 273 mHeight = height; 274 mMode = createMode(width, height, REFRESH_RATE); 275 mDensityDpi = densityDpi; 276 mInfo = null; 277 mPendingChanges |= PENDING_RESIZE; 278 } 279 } 280 281 public void stopLocked() { 282 setSurfaceLocked(null); 283 mStopped = true; 284 } 285 286 @Override 287 public void dumpLocked(PrintWriter pw) { 288 super.dumpLocked(pw); 289 pw.println("mFlags=" + mFlags); 290 pw.println("mDisplayState=" + Display.stateToString(mDisplayState)); 291 pw.println("mStopped=" + mStopped); 292 } 293 294 295 @Override 296 public DisplayDeviceInfo getDisplayDeviceInfoLocked() { 297 if (mInfo == null) { 298 mInfo = new DisplayDeviceInfo(); 299 mInfo.name = mName; 300 mInfo.uniqueId = getUniqueId(); 301 mInfo.width = mWidth; 302 mInfo.height = mHeight; 303 mInfo.modeId = mMode.getModeId(); 304 mInfo.defaultModeId = mMode.getModeId(); 305 mInfo.supportedModes = new Display.Mode[] { mMode }; 306 mInfo.densityDpi = mDensityDpi; 307 mInfo.xDpi = mDensityDpi; 308 mInfo.yDpi = mDensityDpi; 309 mInfo.presentationDeadlineNanos = 1000000000L / (int) REFRESH_RATE; // 1 frame 310 mInfo.flags = 0; 311 if ((mFlags & DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC) == 0) { 312 mInfo.flags |= DisplayDeviceInfo.FLAG_PRIVATE 313 | DisplayDeviceInfo.FLAG_NEVER_BLANK; 314 } 315 if ((mFlags & DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR) != 0) { 316 mInfo.flags &= ~DisplayDeviceInfo.FLAG_NEVER_BLANK; 317 } else { 318 mInfo.flags |= DisplayDeviceInfo.FLAG_OWN_CONTENT_ONLY; 319 } 320 321 if ((mFlags & DisplayManager.VIRTUAL_DISPLAY_FLAG_SECURE) != 0) { 322 mInfo.flags |= DisplayDeviceInfo.FLAG_SECURE; 323 } 324 if ((mFlags & DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION) != 0) { 325 mInfo.flags |= DisplayDeviceInfo.FLAG_PRESENTATION; 326 327 if ((mFlags & DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC) != 0) { 328 // For demonstration purposes, allow rotation of the external display. 329 // In the future we might allow the user to configure this directly. 330 if ("portrait".equals(SystemProperties.get( 331 "persist.demo.remoterotation"))) { 332 mInfo.rotation = Surface.ROTATION_270; 333 } 334 } 335 } 336 mInfo.type = Display.TYPE_VIRTUAL; 337 mInfo.touch = DisplayDeviceInfo.TOUCH_NONE; 338 mInfo.state = mSurface != null ? Display.STATE_ON : Display.STATE_OFF; 339 mInfo.ownerUid = mOwnerUid; 340 mInfo.ownerPackageName = mOwnerPackageName; 341 } 342 return mInfo; 343 } 344 } 345 346 private static class Callback extends Handler { 347 private static final int MSG_ON_DISPLAY_PAUSED = 0; 348 private static final int MSG_ON_DISPLAY_RESUMED = 1; 349 private static final int MSG_ON_DISPLAY_STOPPED = 2; 350 351 private final IVirtualDisplayCallback mCallback; 352 353 public Callback(IVirtualDisplayCallback callback, Handler handler) { 354 super(handler.getLooper()); 355 mCallback = callback; 356 } 357 358 @Override 359 public void handleMessage(Message msg) { 360 try { 361 switch (msg.what) { 362 case MSG_ON_DISPLAY_PAUSED: 363 mCallback.onPaused(); 364 break; 365 case MSG_ON_DISPLAY_RESUMED: 366 mCallback.onResumed(); 367 break; 368 case MSG_ON_DISPLAY_STOPPED: 369 mCallback.onStopped(); 370 break; 371 } 372 } catch (RemoteException e) { 373 Slog.w(TAG, "Failed to notify listener of virtual display event.", e); 374 } 375 } 376 377 public void dispatchDisplayPaused() { 378 sendEmptyMessage(MSG_ON_DISPLAY_PAUSED); 379 } 380 381 public void dispatchDisplayResumed() { 382 sendEmptyMessage(MSG_ON_DISPLAY_RESUMED); 383 } 384 385 public void dispatchDisplayStopped() { 386 sendEmptyMessage(MSG_ON_DISPLAY_STOPPED); 387 } 388 } 389 390 private final class MediaProjectionCallback extends IMediaProjectionCallback.Stub { 391 private IBinder mAppToken; 392 public MediaProjectionCallback(IBinder appToken) { 393 mAppToken = appToken; 394 } 395 396 @Override 397 public void onStop() { 398 synchronized (getSyncRoot()) { 399 handleMediaProjectionStoppedLocked(mAppToken); 400 } 401 } 402 } 403 } 404