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