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 static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR;
     20 import static android.hardware.display.DisplayManager
     21         .VIRTUAL_DISPLAY_FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD;
     22 import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION;
     23 import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC;
     24 import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_SECURE;
     25 import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_SUPPORTS_TOUCH;
     26 import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_ROTATES_WITH_CONTENT;
     27 import static android.hardware.display.DisplayManager
     28         .VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL;
     29 
     30 import android.content.Context;
     31 import android.hardware.display.IVirtualDisplayCallback;
     32 import android.media.projection.IMediaProjection;
     33 import android.media.projection.IMediaProjectionCallback;
     34 import android.os.Handler;
     35 import android.os.IBinder;
     36 import android.os.SystemProperties;
     37 import android.os.IBinder.DeathRecipient;
     38 import android.os.Message;
     39 import android.os.RemoteException;
     40 import android.util.ArrayMap;
     41 import android.util.Slog;
     42 import android.view.Display;
     43 import android.view.Surface;
     44 import android.view.SurfaceControl;
     45 
     46 import com.android.internal.annotations.VisibleForTesting;
     47 
     48 import java.io.PrintWriter;
     49 import java.util.Iterator;
     50 
     51 /**
     52  * A display adapter that provides virtual displays on behalf of applications.
     53  * <p>
     54  * Display adapters are guarded by the {@link DisplayManagerService.SyncRoot} lock.
     55  * </p>
     56  */
     57 @VisibleForTesting
     58 public class VirtualDisplayAdapter extends DisplayAdapter {
     59     static final String TAG = "VirtualDisplayAdapter";
     60     static final boolean DEBUG = false;
     61 
     62     // Unique id prefix for virtual displays
     63     private static final String UNIQUE_ID_PREFIX = "virtual:";
     64 
     65     private final ArrayMap<IBinder, VirtualDisplayDevice> mVirtualDisplayDevices =
     66             new ArrayMap<IBinder, VirtualDisplayDevice>();
     67     private final Handler mHandler;
     68     private final SurfaceControlDisplayFactory mSurfaceControlDisplayFactory;
     69 
     70     // Called with SyncRoot lock held.
     71     public VirtualDisplayAdapter(DisplayManagerService.SyncRoot syncRoot,
     72             Context context, Handler handler, Listener listener) {
     73         this(syncRoot, context, handler, listener,
     74                 (String name, boolean secure) -> SurfaceControl.createDisplay(name, secure));
     75     }
     76 
     77     @VisibleForTesting
     78     VirtualDisplayAdapter(DisplayManagerService.SyncRoot syncRoot,
     79             Context context, Handler handler, Listener listener,
     80             SurfaceControlDisplayFactory surfaceControlDisplayFactory) {
     81         super(syncRoot, context, handler, listener, TAG);
     82         mHandler = handler;
     83         mSurfaceControlDisplayFactory = surfaceControlDisplayFactory;
     84     }
     85 
     86     public DisplayDevice createVirtualDisplayLocked(IVirtualDisplayCallback callback,
     87             IMediaProjection projection, int ownerUid, String ownerPackageName, String name,
     88             int width, int height, int densityDpi, Surface surface, int flags, String uniqueId) {
     89         boolean secure = (flags & VIRTUAL_DISPLAY_FLAG_SECURE) != 0;
     90         IBinder appToken = callback.asBinder();
     91         IBinder displayToken = mSurfaceControlDisplayFactory.createDisplay(name, secure);
     92         final String baseUniqueId =
     93                 UNIQUE_ID_PREFIX + ownerPackageName + "," + ownerUid + "," + name + ",";
     94         final int uniqueIndex = getNextUniqueIndex(baseUniqueId);
     95         if (uniqueId == null) {
     96             uniqueId = baseUniqueId + uniqueIndex;
     97         } else {
     98             uniqueId = UNIQUE_ID_PREFIX + ownerPackageName + ":" + uniqueId;
     99         }
    100         VirtualDisplayDevice device = new VirtualDisplayDevice(displayToken, appToken,
    101                 ownerUid, ownerPackageName, name, width, height, densityDpi, surface, flags,
    102                 new Callback(callback, mHandler), uniqueId, uniqueIndex);
    103 
    104         mVirtualDisplayDevices.put(appToken, device);
    105 
    106         try {
    107             if (projection != null) {
    108                 projection.registerCallback(new MediaProjectionCallback(appToken));
    109             }
    110             appToken.linkToDeath(device, 0);
    111         } catch (RemoteException ex) {
    112             mVirtualDisplayDevices.remove(appToken);
    113             device.destroyLocked(false);
    114             return null;
    115         }
    116 
    117         // Return the display device without actually sending the event indicating
    118         // that it was added.  The caller will handle it.
    119         return device;
    120     }
    121 
    122     public void resizeVirtualDisplayLocked(IBinder appToken,
    123             int width, int height, int densityDpi) {
    124         VirtualDisplayDevice device = mVirtualDisplayDevices.get(appToken);
    125         if (device != null) {
    126             device.resizeLocked(width, height, densityDpi);
    127         }
    128     }
    129 
    130 
    131     public void setVirtualDisplaySurfaceLocked(IBinder appToken, Surface surface) {
    132         VirtualDisplayDevice device = mVirtualDisplayDevices.get(appToken);
    133         if (device != null) {
    134             device.setSurfaceLocked(surface);
    135         }
    136     }
    137 
    138     public DisplayDevice releaseVirtualDisplayLocked(IBinder appToken) {
    139         VirtualDisplayDevice device = mVirtualDisplayDevices.remove(appToken);
    140         if (device != null) {
    141             device.destroyLocked(true);
    142             appToken.unlinkToDeath(device, 0);
    143         }
    144 
    145         // Return the display device that was removed without actually sending the
    146         // event indicating that it was removed.  The caller will handle it.
    147         return device;
    148     }
    149 
    150     /**
    151      * Returns the next unique index for the uniqueIdPrefix
    152      */
    153     private int getNextUniqueIndex(String uniqueIdPrefix) {
    154         if (mVirtualDisplayDevices.isEmpty()) {
    155             return 0;
    156         }
    157 
    158         int nextUniqueIndex = 0;
    159         Iterator<VirtualDisplayDevice> it = mVirtualDisplayDevices.values().iterator();
    160         while (it.hasNext()) {
    161             VirtualDisplayDevice device = it.next();
    162             if (device.getUniqueId().startsWith(uniqueIdPrefix)
    163                     && device.mUniqueIndex >= nextUniqueIndex) {
    164                 // Increment the next unique index to be greater than ones we have already ran
    165                 // across for displays that have the same unique Id prefix.
    166                 nextUniqueIndex = device.mUniqueIndex + 1;
    167             }
    168         }
    169 
    170         return nextUniqueIndex;
    171     }
    172 
    173     private void handleBinderDiedLocked(IBinder appToken) {
    174         mVirtualDisplayDevices.remove(appToken);
    175     }
    176 
    177     private void handleMediaProjectionStoppedLocked(IBinder appToken) {
    178         VirtualDisplayDevice device = mVirtualDisplayDevices.remove(appToken);
    179         if (device != null) {
    180             Slog.i(TAG, "Virtual display device released because media projection stopped: "
    181                     + device.mName);
    182             device.stopLocked();
    183         }
    184     }
    185 
    186     private final class VirtualDisplayDevice extends DisplayDevice implements DeathRecipient {
    187         private static final int PENDING_SURFACE_CHANGE = 0x01;
    188         private static final int PENDING_RESIZE = 0x02;
    189 
    190         private static final float REFRESH_RATE = 60.0f;
    191 
    192         private final IBinder mAppToken;
    193         private final int mOwnerUid;
    194         final String mOwnerPackageName;
    195         final String mName;
    196         private final int mFlags;
    197         private final Callback mCallback;
    198 
    199         private int mWidth;
    200         private int mHeight;
    201         private int mDensityDpi;
    202         private Surface mSurface;
    203         private DisplayDeviceInfo mInfo;
    204         private int mDisplayState;
    205         private boolean mStopped;
    206         private int mPendingChanges;
    207         private int mUniqueIndex;
    208         private Display.Mode mMode;
    209 
    210         public VirtualDisplayDevice(IBinder displayToken, IBinder appToken,
    211                 int ownerUid, String ownerPackageName,
    212                 String name, int width, int height, int densityDpi, Surface surface, int flags,
    213                 Callback callback, String uniqueId, int uniqueIndex) {
    214             super(VirtualDisplayAdapter.this, displayToken, uniqueId);
    215             mAppToken = appToken;
    216             mOwnerUid = ownerUid;
    217             mOwnerPackageName = ownerPackageName;
    218             mName = name;
    219             mWidth = width;
    220             mHeight = height;
    221             mMode = createMode(width, height, REFRESH_RATE);
    222             mDensityDpi = densityDpi;
    223             mSurface = surface;
    224             mFlags = flags;
    225             mCallback = callback;
    226             mDisplayState = Display.STATE_UNKNOWN;
    227             mPendingChanges |= PENDING_SURFACE_CHANGE;
    228             mUniqueIndex = uniqueIndex;
    229         }
    230 
    231         @Override
    232         public void binderDied() {
    233             synchronized (getSyncRoot()) {
    234                 handleBinderDiedLocked(mAppToken);
    235                 Slog.i(TAG, "Virtual display device released because application token died: "
    236                     + mOwnerPackageName);
    237                 destroyLocked(false);
    238                 sendDisplayDeviceEventLocked(this, DISPLAY_DEVICE_EVENT_REMOVED);
    239             }
    240         }
    241 
    242         public void destroyLocked(boolean binderAlive) {
    243             if (mSurface != null) {
    244                 mSurface.release();
    245                 mSurface = null;
    246             }
    247             SurfaceControl.destroyDisplay(getDisplayTokenLocked());
    248             if (binderAlive) {
    249                 mCallback.dispatchDisplayStopped();
    250             }
    251         }
    252 
    253         @Override
    254         public boolean hasStableUniqueId() {
    255             return false;
    256         }
    257 
    258         @Override
    259         public Runnable requestDisplayStateLocked(int state, int brightness) {
    260             if (state != mDisplayState) {
    261                 mDisplayState = state;
    262                 if (state == Display.STATE_OFF) {
    263                     mCallback.dispatchDisplayPaused();
    264                 } else {
    265                     mCallback.dispatchDisplayResumed();
    266                 }
    267             }
    268             return null;
    269         }
    270 
    271         @Override
    272         public void performTraversalLocked(SurfaceControl.Transaction t) {
    273             if ((mPendingChanges & PENDING_RESIZE) != 0) {
    274                 t.setDisplaySize(getDisplayTokenLocked(), mWidth, mHeight);
    275             }
    276             if ((mPendingChanges & PENDING_SURFACE_CHANGE) != 0) {
    277                 setSurfaceLocked(t, mSurface);
    278             }
    279             mPendingChanges = 0;
    280         }
    281 
    282         public void setSurfaceLocked(Surface surface) {
    283             if (!mStopped && mSurface != surface) {
    284                 if ((mSurface != null) != (surface != null)) {
    285                     sendDisplayDeviceEventLocked(this, DISPLAY_DEVICE_EVENT_CHANGED);
    286                 }
    287                 sendTraversalRequestLocked();
    288                 mSurface = surface;
    289                 mInfo = null;
    290                 mPendingChanges |= PENDING_SURFACE_CHANGE;
    291             }
    292         }
    293 
    294         public void resizeLocked(int width, int height, int densityDpi) {
    295             if (mWidth != width || mHeight != height || mDensityDpi != densityDpi) {
    296                 sendDisplayDeviceEventLocked(this, DISPLAY_DEVICE_EVENT_CHANGED);
    297                 sendTraversalRequestLocked();
    298                 mWidth = width;
    299                 mHeight = height;
    300                 mMode = createMode(width, height, REFRESH_RATE);
    301                 mDensityDpi = densityDpi;
    302                 mInfo = null;
    303                 mPendingChanges |= PENDING_RESIZE;
    304             }
    305         }
    306 
    307         public void stopLocked() {
    308             setSurfaceLocked(null);
    309             mStopped = true;
    310         }
    311 
    312         @Override
    313         public void dumpLocked(PrintWriter pw) {
    314             super.dumpLocked(pw);
    315             pw.println("mFlags=" + mFlags);
    316             pw.println("mDisplayState=" + Display.stateToString(mDisplayState));
    317             pw.println("mStopped=" + mStopped);
    318         }
    319 
    320 
    321         @Override
    322         public DisplayDeviceInfo getDisplayDeviceInfoLocked() {
    323             if (mInfo == null) {
    324                 mInfo = new DisplayDeviceInfo();
    325                 mInfo.name = mName;
    326                 mInfo.uniqueId = getUniqueId();
    327                 mInfo.width = mWidth;
    328                 mInfo.height = mHeight;
    329                 mInfo.modeId = mMode.getModeId();
    330                 mInfo.defaultModeId = mMode.getModeId();
    331                 mInfo.supportedModes = new Display.Mode[] { mMode };
    332                 mInfo.densityDpi = mDensityDpi;
    333                 mInfo.xDpi = mDensityDpi;
    334                 mInfo.yDpi = mDensityDpi;
    335                 mInfo.presentationDeadlineNanos = 1000000000L / (int) REFRESH_RATE; // 1 frame
    336                 mInfo.flags = 0;
    337                 if ((mFlags & VIRTUAL_DISPLAY_FLAG_PUBLIC) == 0) {
    338                     mInfo.flags |= DisplayDeviceInfo.FLAG_PRIVATE
    339                             | DisplayDeviceInfo.FLAG_NEVER_BLANK;
    340                 }
    341                 if ((mFlags & VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR) != 0) {
    342                     mInfo.flags &= ~DisplayDeviceInfo.FLAG_NEVER_BLANK;
    343                 } else {
    344                     mInfo.flags |= DisplayDeviceInfo.FLAG_OWN_CONTENT_ONLY;
    345                 }
    346 
    347                 if ((mFlags & VIRTUAL_DISPLAY_FLAG_SECURE) != 0) {
    348                     mInfo.flags |= DisplayDeviceInfo.FLAG_SECURE;
    349                 }
    350                 if ((mFlags & VIRTUAL_DISPLAY_FLAG_PRESENTATION) != 0) {
    351                     mInfo.flags |= DisplayDeviceInfo.FLAG_PRESENTATION;
    352 
    353                     if ((mFlags & VIRTUAL_DISPLAY_FLAG_PUBLIC) != 0) {
    354                         // For demonstration purposes, allow rotation of the external display.
    355                         // In the future we might allow the user to configure this directly.
    356                         if ("portrait".equals(SystemProperties.get(
    357                                 "persist.demo.remoterotation"))) {
    358                             mInfo.rotation = Surface.ROTATION_270;
    359                         }
    360                     }
    361                 }
    362                 if ((mFlags & VIRTUAL_DISPLAY_FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD) != 0) {
    363                     mInfo.flags |= DisplayDeviceInfo.FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD;
    364                 }
    365                 if ((mFlags & VIRTUAL_DISPLAY_FLAG_ROTATES_WITH_CONTENT) != 0) {
    366                     mInfo.flags |= DisplayDeviceInfo.FLAG_ROTATES_WITH_CONTENT;
    367                 }
    368                 if ((mFlags & VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL) != 0) {
    369                   mInfo.flags |= DisplayDeviceInfo.FLAG_DESTROY_CONTENT_ON_REMOVAL;
    370                 }
    371 
    372                 mInfo.type = Display.TYPE_VIRTUAL;
    373                 mInfo.touch = ((mFlags & VIRTUAL_DISPLAY_FLAG_SUPPORTS_TOUCH) == 0) ?
    374                         DisplayDeviceInfo.TOUCH_NONE : DisplayDeviceInfo.TOUCH_VIRTUAL;
    375                 mInfo.state = mSurface != null ? Display.STATE_ON : Display.STATE_OFF;
    376                 mInfo.ownerUid = mOwnerUid;
    377                 mInfo.ownerPackageName = mOwnerPackageName;
    378             }
    379             return mInfo;
    380         }
    381     }
    382 
    383     private static class Callback extends Handler {
    384         private static final int MSG_ON_DISPLAY_PAUSED = 0;
    385         private static final int MSG_ON_DISPLAY_RESUMED = 1;
    386         private static final int MSG_ON_DISPLAY_STOPPED = 2;
    387 
    388         private final IVirtualDisplayCallback mCallback;
    389 
    390         public Callback(IVirtualDisplayCallback callback, Handler handler) {
    391             super(handler.getLooper());
    392             mCallback = callback;
    393         }
    394 
    395         @Override
    396         public void handleMessage(Message msg) {
    397             try {
    398                 switch (msg.what) {
    399                     case MSG_ON_DISPLAY_PAUSED:
    400                         mCallback.onPaused();
    401                         break;
    402                     case MSG_ON_DISPLAY_RESUMED:
    403                         mCallback.onResumed();
    404                         break;
    405                     case MSG_ON_DISPLAY_STOPPED:
    406                         mCallback.onStopped();
    407                         break;
    408                 }
    409             } catch (RemoteException e) {
    410                 Slog.w(TAG, "Failed to notify listener of virtual display event.", e);
    411             }
    412         }
    413 
    414         public void dispatchDisplayPaused() {
    415             sendEmptyMessage(MSG_ON_DISPLAY_PAUSED);
    416         }
    417 
    418         public void dispatchDisplayResumed() {
    419             sendEmptyMessage(MSG_ON_DISPLAY_RESUMED);
    420         }
    421 
    422         public void dispatchDisplayStopped() {
    423             sendEmptyMessage(MSG_ON_DISPLAY_STOPPED);
    424         }
    425     }
    426 
    427     private final class MediaProjectionCallback extends IMediaProjectionCallback.Stub {
    428         private IBinder mAppToken;
    429         public MediaProjectionCallback(IBinder appToken) {
    430             mAppToken = appToken;
    431         }
    432 
    433         @Override
    434         public void onStop() {
    435             synchronized (getSyncRoot()) {
    436                 handleMediaProjectionStoppedLocked(mAppToken);
    437             }
    438         }
    439     }
    440 
    441     @VisibleForTesting
    442     public interface SurfaceControlDisplayFactory {
    443         public IBinder createDisplay(String name, boolean secure);
    444     }
    445 }
    446