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