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