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(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