Home | History | Annotate | Download | only in display
      1 /*
      2  * Copyright (C) 2012 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.os.Handler;
     21 import android.os.IBinder;
     22 import android.os.Looper;
     23 import android.os.SystemProperties;
     24 import android.os.Trace;
     25 import android.util.Slog;
     26 import android.util.SparseArray;
     27 import android.view.Display;
     28 import android.view.DisplayEventReceiver;
     29 import android.view.Surface;
     30 import android.view.SurfaceControl;
     31 
     32 import java.io.PrintWriter;
     33 import java.util.Arrays;
     34 
     35 /**
     36  * A display adapter for the local displays managed by Surface Flinger.
     37  * <p>
     38  * Display adapters are guarded by the {@link DisplayManagerService.SyncRoot} lock.
     39  * </p>
     40  */
     41 final class LocalDisplayAdapter extends DisplayAdapter {
     42     private static final String TAG = "LocalDisplayAdapter";
     43 
     44     private static final String UNIQUE_ID_PREFIX = "local:";
     45 
     46     private static final int[] BUILT_IN_DISPLAY_IDS_TO_SCAN = new int[] {
     47             SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN,
     48             SurfaceControl.BUILT_IN_DISPLAY_ID_HDMI,
     49     };
     50 
     51     private final SparseArray<LocalDisplayDevice> mDevices =
     52             new SparseArray<LocalDisplayDevice>();
     53     private HotplugDisplayEventReceiver mHotplugReceiver;
     54 
     55     // Called with SyncRoot lock held.
     56     public LocalDisplayAdapter(DisplayManagerService.SyncRoot syncRoot,
     57             Context context, Handler handler, Listener listener) {
     58         super(syncRoot, context, handler, listener, TAG);
     59     }
     60 
     61     @Override
     62     public void registerLocked() {
     63         super.registerLocked();
     64 
     65         mHotplugReceiver = new HotplugDisplayEventReceiver(getHandler().getLooper());
     66 
     67         for (int builtInDisplayId : BUILT_IN_DISPLAY_IDS_TO_SCAN) {
     68             tryConnectDisplayLocked(builtInDisplayId);
     69         }
     70     }
     71 
     72     private void tryConnectDisplayLocked(int builtInDisplayId) {
     73         IBinder displayToken = SurfaceControl.getBuiltInDisplay(builtInDisplayId);
     74         if (displayToken != null) {
     75             SurfaceControl.PhysicalDisplayInfo[] configs =
     76                     SurfaceControl.getDisplayConfigs(displayToken);
     77             if (configs == null) {
     78                 // There are no valid configs for this device, so we can't use it
     79                 Slog.w(TAG, "No valid configs found for display device " +
     80                         builtInDisplayId);
     81                 return;
     82             }
     83             int activeConfig = SurfaceControl.getActiveConfig(displayToken);
     84             if (activeConfig < 0) {
     85                 // There is no active config, and for now we don't have the
     86                 // policy to set one.
     87                 Slog.w(TAG, "No active config found for display device " +
     88                         builtInDisplayId);
     89                 return;
     90             }
     91             LocalDisplayDevice device = mDevices.get(builtInDisplayId);
     92             if (device == null) {
     93                 // Display was added.
     94                 device = new LocalDisplayDevice(displayToken, builtInDisplayId,
     95                         configs, activeConfig);
     96                 mDevices.put(builtInDisplayId, device);
     97                 sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_ADDED);
     98             } else if (device.updatePhysicalDisplayInfoLocked(configs, activeConfig)) {
     99                 // Display properties changed.
    100                 sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_CHANGED);
    101             }
    102         } else {
    103             // The display is no longer available. Ignore the attempt to add it.
    104             // If it was connected but has already been disconnected, we'll get a
    105             // disconnect event that will remove it from mDevices.
    106         }
    107     }
    108 
    109     private void tryDisconnectDisplayLocked(int builtInDisplayId) {
    110         LocalDisplayDevice device = mDevices.get(builtInDisplayId);
    111         if (device != null) {
    112             // Display was removed.
    113             mDevices.remove(builtInDisplayId);
    114             sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_REMOVED);
    115         }
    116     }
    117 
    118     static int getPowerModeForState(int state) {
    119         switch (state) {
    120             case Display.STATE_OFF:
    121                 return SurfaceControl.POWER_MODE_OFF;
    122             case Display.STATE_DOZE:
    123                 return SurfaceControl.POWER_MODE_DOZE;
    124             case Display.STATE_DOZE_SUSPEND:
    125                 return SurfaceControl.POWER_MODE_DOZE_SUSPEND;
    126             default:
    127                 return SurfaceControl.POWER_MODE_NORMAL;
    128         }
    129     }
    130 
    131     private final class LocalDisplayDevice extends DisplayDevice {
    132         private final int mBuiltInDisplayId;
    133         private final SurfaceControl.PhysicalDisplayInfo mPhys;
    134         private final int mDefaultPhysicalDisplayInfo;
    135 
    136         private DisplayDeviceInfo mInfo;
    137         private boolean mHavePendingChanges;
    138         private int mState = Display.STATE_UNKNOWN;
    139         private float[] mSupportedRefreshRates;
    140         private int[] mRefreshRateConfigIndices;
    141         private float mLastRequestedRefreshRate;
    142 
    143         public LocalDisplayDevice(IBinder displayToken, int builtInDisplayId,
    144                 SurfaceControl.PhysicalDisplayInfo[] physicalDisplayInfos, int activeDisplayInfo) {
    145             super(LocalDisplayAdapter.this, displayToken, UNIQUE_ID_PREFIX + builtInDisplayId);
    146             mBuiltInDisplayId = builtInDisplayId;
    147             mPhys = new SurfaceControl.PhysicalDisplayInfo(
    148                     physicalDisplayInfos[activeDisplayInfo]);
    149             mDefaultPhysicalDisplayInfo = activeDisplayInfo;
    150             updateSupportedRefreshRatesLocked(physicalDisplayInfos, mPhys);
    151         }
    152 
    153         public boolean updatePhysicalDisplayInfoLocked(
    154                 SurfaceControl.PhysicalDisplayInfo[] physicalDisplayInfos, int activeDisplayInfo) {
    155             SurfaceControl.PhysicalDisplayInfo newPhys = physicalDisplayInfos[activeDisplayInfo];
    156             if (!mPhys.equals(newPhys)) {
    157                 mPhys.copyFrom(newPhys);
    158                 updateSupportedRefreshRatesLocked(physicalDisplayInfos, mPhys);
    159                 mHavePendingChanges = true;
    160                 return true;
    161             }
    162             return false;
    163         }
    164 
    165         @Override
    166         public void applyPendingDisplayDeviceInfoChangesLocked() {
    167             if (mHavePendingChanges) {
    168                 mInfo = null;
    169                 mHavePendingChanges = false;
    170             }
    171         }
    172 
    173         @Override
    174         public DisplayDeviceInfo getDisplayDeviceInfoLocked() {
    175             if (mInfo == null) {
    176                 mInfo = new DisplayDeviceInfo();
    177                 mInfo.width = mPhys.width;
    178                 mInfo.height = mPhys.height;
    179                 mInfo.refreshRate = mPhys.refreshRate;
    180                 mInfo.supportedRefreshRates = mSupportedRefreshRates;
    181                 mInfo.appVsyncOffsetNanos = mPhys.appVsyncOffsetNanos;
    182                 mInfo.presentationDeadlineNanos = mPhys.presentationDeadlineNanos;
    183                 mInfo.state = mState;
    184                 mInfo.uniqueId = getUniqueId();
    185 
    186                 // Assume that all built-in displays that have secure output (eg. HDCP) also
    187                 // support compositing from gralloc protected buffers.
    188                 if (mPhys.secure) {
    189                     mInfo.flags = DisplayDeviceInfo.FLAG_SECURE
    190                             | DisplayDeviceInfo.FLAG_SUPPORTS_PROTECTED_BUFFERS;
    191                 }
    192 
    193                 if (mBuiltInDisplayId == SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN) {
    194                     mInfo.name = getContext().getResources().getString(
    195                             com.android.internal.R.string.display_manager_built_in_display_name);
    196                     mInfo.flags |= DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY
    197                             | DisplayDeviceInfo.FLAG_ROTATES_WITH_CONTENT;
    198                     mInfo.type = Display.TYPE_BUILT_IN;
    199                     mInfo.densityDpi = (int)(mPhys.density * 160 + 0.5f);
    200                     mInfo.xDpi = mPhys.xDpi;
    201                     mInfo.yDpi = mPhys.yDpi;
    202                     mInfo.touch = DisplayDeviceInfo.TOUCH_INTERNAL;
    203                 } else {
    204                     mInfo.type = Display.TYPE_HDMI;
    205                     mInfo.flags |= DisplayDeviceInfo.FLAG_PRESENTATION;
    206                     mInfo.name = getContext().getResources().getString(
    207                             com.android.internal.R.string.display_manager_hdmi_display_name);
    208                     mInfo.touch = DisplayDeviceInfo.TOUCH_EXTERNAL;
    209                     mInfo.setAssumedDensityForExternalDisplay(mPhys.width, mPhys.height);
    210 
    211                     // For demonstration purposes, allow rotation of the external display.
    212                     // In the future we might allow the user to configure this directly.
    213                     if ("portrait".equals(SystemProperties.get("persist.demo.hdmirotation"))) {
    214                         mInfo.rotation = Surface.ROTATION_270;
    215                     }
    216 
    217                     // For demonstration purposes, allow rotation of the external display
    218                     // to follow the built-in display.
    219                     if (SystemProperties.getBoolean("persist.demo.hdmirotates", false)) {
    220                         mInfo.flags |= DisplayDeviceInfo.FLAG_ROTATES_WITH_CONTENT;
    221                     }
    222                 }
    223             }
    224             return mInfo;
    225         }
    226 
    227         @Override
    228         public Runnable requestDisplayStateLocked(final int state) {
    229             if (mState != state) {
    230                 final int displayId = mBuiltInDisplayId;
    231                 final IBinder token = getDisplayTokenLocked();
    232                 final int mode = getPowerModeForState(state);
    233                 mState = state;
    234                 updateDeviceInfoLocked();
    235 
    236                 // Defer actually setting the display power mode until we have exited
    237                 // the critical section since it can take hundreds of milliseconds
    238                 // to complete.
    239                 return new Runnable() {
    240                     @Override
    241                     public void run() {
    242                         Trace.traceBegin(Trace.TRACE_TAG_POWER, "requestDisplayState("
    243                                 + Display.stateToString(state) + ", id=" + displayId + ")");
    244                         try {
    245                             SurfaceControl.setDisplayPowerMode(token, mode);
    246                         } finally {
    247                             Trace.traceEnd(Trace.TRACE_TAG_POWER);
    248                         }
    249                     }
    250                 };
    251             }
    252             return null;
    253         }
    254 
    255         @Override
    256         public void requestRefreshRateLocked(float refreshRate) {
    257             if (mLastRequestedRefreshRate == refreshRate) {
    258                 return;
    259             }
    260             mLastRequestedRefreshRate = refreshRate;
    261             if (refreshRate != 0) {
    262                 final int N = mSupportedRefreshRates.length;
    263                 for (int i = 0; i < N; i++) {
    264                     if (refreshRate == mSupportedRefreshRates[i]) {
    265                         final int configIndex = mRefreshRateConfigIndices[i];
    266                         SurfaceControl.setActiveConfig(getDisplayTokenLocked(), configIndex);
    267                         return;
    268                     }
    269                 }
    270                 Slog.w(TAG, "Requested refresh rate " + refreshRate + " is unsupported.");
    271             }
    272             SurfaceControl.setActiveConfig(getDisplayTokenLocked(), mDefaultPhysicalDisplayInfo);
    273         }
    274 
    275         @Override
    276         public void dumpLocked(PrintWriter pw) {
    277             super.dumpLocked(pw);
    278             pw.println("mBuiltInDisplayId=" + mBuiltInDisplayId);
    279             pw.println("mPhys=" + mPhys);
    280             pw.println("mState=" + Display.stateToString(mState));
    281         }
    282 
    283         private void updateDeviceInfoLocked() {
    284             mInfo = null;
    285             sendDisplayDeviceEventLocked(this, DISPLAY_DEVICE_EVENT_CHANGED);
    286         }
    287 
    288         private void updateSupportedRefreshRatesLocked(
    289                 SurfaceControl.PhysicalDisplayInfo[] physicalDisplayInfos,
    290                 SurfaceControl.PhysicalDisplayInfo activePhys) {
    291             final int N = physicalDisplayInfos.length;
    292             int idx = 0;
    293             mSupportedRefreshRates = new float[N];
    294             mRefreshRateConfigIndices = new int[N];
    295             for (int i = 0; i < N; i++) {
    296                 final SurfaceControl.PhysicalDisplayInfo phys = physicalDisplayInfos[i];
    297                 if (activePhys.width == phys.width
    298                         && activePhys.height == phys.height
    299                         && activePhys.density == phys.density
    300                         && activePhys.xDpi == phys.xDpi
    301                         && activePhys.yDpi == phys.yDpi) {
    302                     mSupportedRefreshRates[idx] = phys.refreshRate;
    303                     mRefreshRateConfigIndices[idx++] = i;
    304                 }
    305             }
    306             if (idx != N) {
    307                 mSupportedRefreshRates = Arrays.copyOfRange(mSupportedRefreshRates, 0, idx);
    308                 mRefreshRateConfigIndices = Arrays.copyOfRange(mRefreshRateConfigIndices, 0, idx);
    309             }
    310         }
    311     }
    312 
    313     private final class HotplugDisplayEventReceiver extends DisplayEventReceiver {
    314         public HotplugDisplayEventReceiver(Looper looper) {
    315             super(looper);
    316         }
    317 
    318         @Override
    319         public void onHotplug(long timestampNanos, int builtInDisplayId, boolean connected) {
    320             synchronized (getSyncRoot()) {
    321                 if (connected) {
    322                     tryConnectDisplayLocked(builtInDisplayId);
    323                 } else {
    324                     tryDisconnectDisplayLocked(builtInDisplayId);
    325                 }
    326             }
    327         }
    328     }
    329 }
    330