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