Home | History | Annotate | Download | only in display
      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();
     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