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.res.Resources;
     20 import com.android.server.LocalServices;
     21 import com.android.server.lights.Light;
     22 import com.android.server.lights.LightsManager;
     23 
     24 import android.content.Context;
     25 import android.os.Build;
     26 import android.os.Handler;
     27 import android.os.IBinder;
     28 import android.os.Looper;
     29 import android.os.PowerManager;
     30 import android.os.SystemProperties;
     31 import android.os.Trace;
     32 import android.util.Slog;
     33 import android.util.SparseArray;
     34 import android.view.Display;
     35 import android.view.DisplayEventReceiver;
     36 import android.view.Surface;
     37 import android.view.SurfaceControl;
     38 
     39 import java.io.PrintWriter;
     40 import java.util.ArrayList;
     41 import java.util.Arrays;
     42 import java.util.Collections;
     43 import java.util.List;
     44 
     45 /**
     46  * A display adapter for the local displays managed by Surface Flinger.
     47  * <p>
     48  * Display adapters are guarded by the {@link DisplayManagerService.SyncRoot} lock.
     49  * </p>
     50  */
     51 final class LocalDisplayAdapter extends DisplayAdapter {
     52     private static final String TAG = "LocalDisplayAdapter";
     53     private static final boolean DEBUG = false;
     54 
     55     private static final String UNIQUE_ID_PREFIX = "local:";
     56 
     57     private static final String PROPERTY_EMULATOR_CIRCULAR = "ro.emulator.circular";
     58 
     59     private static final int[] BUILT_IN_DISPLAY_IDS_TO_SCAN = new int[] {
     60             SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN,
     61             SurfaceControl.BUILT_IN_DISPLAY_ID_HDMI,
     62     };
     63 
     64     private final SparseArray<LocalDisplayDevice> mDevices =
     65             new SparseArray<LocalDisplayDevice>();
     66     @SuppressWarnings("unused")  // Becomes active at instantiation time.
     67     private HotplugDisplayEventReceiver mHotplugReceiver;
     68 
     69     // Called with SyncRoot lock held.
     70     public LocalDisplayAdapter(DisplayManagerService.SyncRoot syncRoot,
     71             Context context, Handler handler, Listener listener) {
     72         super(syncRoot, context, handler, listener, TAG);
     73     }
     74 
     75     @Override
     76     public void registerLocked() {
     77         super.registerLocked();
     78 
     79         mHotplugReceiver = new HotplugDisplayEventReceiver(getHandler().getLooper());
     80 
     81         for (int builtInDisplayId : BUILT_IN_DISPLAY_IDS_TO_SCAN) {
     82             tryConnectDisplayLocked(builtInDisplayId);
     83         }
     84     }
     85 
     86     private void tryConnectDisplayLocked(int builtInDisplayId) {
     87         IBinder displayToken = SurfaceControl.getBuiltInDisplay(builtInDisplayId);
     88         if (displayToken != null) {
     89             SurfaceControl.PhysicalDisplayInfo[] configs =
     90                     SurfaceControl.getDisplayConfigs(displayToken);
     91             if (configs == null) {
     92                 // There are no valid configs for this device, so we can't use it
     93                 Slog.w(TAG, "No valid configs found for display device " +
     94                         builtInDisplayId);
     95                 return;
     96             }
     97             int activeConfig = SurfaceControl.getActiveConfig(displayToken);
     98             if (activeConfig < 0) {
     99                 // There is no active config, and for now we don't have the
    100                 // policy to set one.
    101                 Slog.w(TAG, "No active config found for display device " +
    102                         builtInDisplayId);
    103                 return;
    104             }
    105             int activeColorMode = SurfaceControl.getActiveColorMode(displayToken);
    106             if (activeColorMode < 0) {
    107                 // We failed to get the active color mode. We don't bail out here since on the next
    108                 // configuration pass we'll go ahead and set it to whatever it was set to last (or
    109                 // COLOR_MODE_NATIVE if this is the first configuration).
    110                 Slog.w(TAG, "Unable to get active color mode for display device " +
    111                         builtInDisplayId);
    112                 activeColorMode = Display.COLOR_MODE_INVALID;
    113             }
    114             int[] colorModes = SurfaceControl.getDisplayColorModes(displayToken);
    115             LocalDisplayDevice device = mDevices.get(builtInDisplayId);
    116             if (device == null) {
    117                 // Display was added.
    118                 device = new LocalDisplayDevice(displayToken, builtInDisplayId,
    119                         configs, activeConfig, colorModes, activeColorMode);
    120                 mDevices.put(builtInDisplayId, device);
    121                 sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_ADDED);
    122             } else if (device.updatePhysicalDisplayInfoLocked(configs, activeConfig,
    123                         colorModes, activeColorMode)) {
    124                 // Display properties changed.
    125                 sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_CHANGED);
    126             }
    127         } else {
    128             // The display is no longer available. Ignore the attempt to add it.
    129             // If it was connected but has already been disconnected, we'll get a
    130             // disconnect event that will remove it from mDevices.
    131         }
    132     }
    133 
    134     private void tryDisconnectDisplayLocked(int builtInDisplayId) {
    135         LocalDisplayDevice device = mDevices.get(builtInDisplayId);
    136         if (device != null) {
    137             // Display was removed.
    138             mDevices.remove(builtInDisplayId);
    139             sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_REMOVED);
    140         }
    141     }
    142 
    143     static int getPowerModeForState(int state) {
    144         switch (state) {
    145             case Display.STATE_OFF:
    146                 return SurfaceControl.POWER_MODE_OFF;
    147             case Display.STATE_DOZE:
    148                 return SurfaceControl.POWER_MODE_DOZE;
    149             case Display.STATE_DOZE_SUSPEND:
    150                 return SurfaceControl.POWER_MODE_DOZE_SUSPEND;
    151             default:
    152                 return SurfaceControl.POWER_MODE_NORMAL;
    153         }
    154     }
    155 
    156     private final class LocalDisplayDevice extends DisplayDevice {
    157         private final int mBuiltInDisplayId;
    158         private final Light mBacklight;
    159         private final SparseArray<DisplayModeRecord> mSupportedModes = new SparseArray<>();
    160         private final ArrayList<Integer> mSupportedColorModes = new ArrayList<>();
    161 
    162         private DisplayDeviceInfo mInfo;
    163         private boolean mHavePendingChanges;
    164         private int mState = Display.STATE_UNKNOWN;
    165         private int mBrightness = PowerManager.BRIGHTNESS_DEFAULT;
    166         private int mActivePhysIndex;
    167         private int mDefaultModeId;
    168         private int mActiveModeId;
    169         private boolean mActiveModeInvalid;
    170         private int mActiveColorMode;
    171         private boolean mActiveColorModeInvalid;
    172         private Display.HdrCapabilities mHdrCapabilities;
    173 
    174         private  SurfaceControl.PhysicalDisplayInfo mDisplayInfos[];
    175 
    176         public LocalDisplayDevice(IBinder displayToken, int builtInDisplayId,
    177                 SurfaceControl.PhysicalDisplayInfo[] physicalDisplayInfos, int activeDisplayInfo,
    178                 int[] colorModes, int activeColorMode) {
    179             super(LocalDisplayAdapter.this, displayToken, UNIQUE_ID_PREFIX + builtInDisplayId);
    180             mBuiltInDisplayId = builtInDisplayId;
    181             updatePhysicalDisplayInfoLocked(physicalDisplayInfos, activeDisplayInfo,
    182                     colorModes, activeColorMode);
    183             updateColorModesLocked(colorModes, activeColorMode);
    184             if (mBuiltInDisplayId == SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN) {
    185                 LightsManager lights = LocalServices.getService(LightsManager.class);
    186                 mBacklight = lights.getLight(LightsManager.LIGHT_ID_BACKLIGHT);
    187             } else {
    188                 mBacklight = null;
    189             }
    190             mHdrCapabilities = SurfaceControl.getHdrCapabilities(displayToken);
    191         }
    192 
    193         @Override
    194         public boolean hasStableUniqueId() {
    195             return true;
    196         }
    197 
    198         public boolean updatePhysicalDisplayInfoLocked(
    199                 SurfaceControl.PhysicalDisplayInfo[] physicalDisplayInfos, int activeDisplayInfo,
    200                 int[] colorModes, int activeColorMode) {
    201             mDisplayInfos = Arrays.copyOf(physicalDisplayInfos, physicalDisplayInfos.length);
    202             mActivePhysIndex = activeDisplayInfo;
    203             // Build an updated list of all existing modes.
    204             ArrayList<DisplayModeRecord> records = new ArrayList<DisplayModeRecord>();
    205             boolean modesAdded = false;
    206             for (int i = 0; i < physicalDisplayInfos.length; i++) {
    207                 SurfaceControl.PhysicalDisplayInfo info = physicalDisplayInfos[i];
    208                 // First, check to see if we've already added a matching mode. Since not all
    209                 // configuration options are exposed via Display.Mode, it's possible that we have
    210                 // multiple PhysicalDisplayInfos that would generate the same Display.Mode.
    211                 boolean existingMode = false;
    212                 for (int j = 0; j < records.size(); j++) {
    213                     if (records.get(j).hasMatchingMode(info)) {
    214                         existingMode = true;
    215                         break;
    216                     }
    217                 }
    218                 if (existingMode) {
    219                     continue;
    220                 }
    221                 // If we haven't already added a mode for this configuration to the new set of
    222                 // supported modes then check to see if we have one in the prior set of supported
    223                 // modes to reuse.
    224                 DisplayModeRecord record = findDisplayModeRecord(info);
    225                 if (record == null) {
    226                     record = new DisplayModeRecord(info);
    227                     modesAdded = true;
    228                 }
    229                 records.add(record);
    230             }
    231 
    232             // Get the currently active mode
    233             DisplayModeRecord activeRecord = null;
    234             for (int i = 0; i < records.size(); i++) {
    235                 DisplayModeRecord record = records.get(i);
    236                 if (record.hasMatchingMode(physicalDisplayInfos[activeDisplayInfo])){
    237                     activeRecord = record;
    238                     break;
    239                 }
    240             }
    241             // Check whether surface flinger spontaneously changed modes out from under us. Schedule
    242             // traversals to ensure that the correct state is reapplied if necessary.
    243             if (mActiveModeId != 0
    244                     && mActiveModeId != activeRecord.mMode.getModeId()) {
    245                 mActiveModeInvalid = true;
    246                 sendTraversalRequestLocked();
    247             }
    248 
    249             boolean recordsChanged = records.size() != mSupportedModes.size() || modesAdded;
    250             // If the records haven't changed then we're done here.
    251             if (!recordsChanged) {
    252                 return false;
    253             }
    254             // Update the index of modes.
    255             mHavePendingChanges = true;
    256 
    257             mSupportedModes.clear();
    258             for (DisplayModeRecord record : records) {
    259                 mSupportedModes.put(record.mMode.getModeId(), record);
    260             }
    261             // Update the default mode, if needed.
    262             if (findDisplayInfoIndexLocked(mDefaultModeId) < 0) {
    263                 if (mDefaultModeId != 0) {
    264                     Slog.w(TAG, "Default display mode no longer available, using currently"
    265                             + " active mode as default.");
    266                 }
    267                 mDefaultModeId = activeRecord.mMode.getModeId();
    268             }
    269             // Determine whether the active mode is still there.
    270             if (mSupportedModes.indexOfKey(mActiveModeId) < 0) {
    271                 if (mActiveModeId != 0) {
    272                     Slog.w(TAG, "Active display mode no longer available, reverting to default"
    273                             + " mode.");
    274                 }
    275                 mActiveModeId = mDefaultModeId;
    276                 mActiveModeInvalid = true;
    277             }
    278 
    279             // Schedule traversals so that we apply pending changes.
    280             sendTraversalRequestLocked();
    281             return true;
    282         }
    283 
    284         private boolean updateColorModesLocked(int[] colorModes,
    285                 int activeColorMode) {
    286             List<Integer> pendingColorModes = new ArrayList<>();
    287 
    288             if (colorModes == null) return false;
    289             // Build an updated list of all existing color modes.
    290             boolean colorModesAdded = false;
    291             for (int colorMode: colorModes) {
    292                 if (!mSupportedColorModes.contains(colorMode)) {
    293                     colorModesAdded = true;
    294                 }
    295                 pendingColorModes.add(colorMode);
    296             }
    297 
    298             boolean colorModesChanged =
    299                     pendingColorModes.size() != mSupportedColorModes.size()
    300                     || colorModesAdded;
    301 
    302             // If the supported color modes haven't changed then we're done here.
    303             if (!colorModesChanged) {
    304                 return false;
    305             }
    306 
    307             mHavePendingChanges = true;
    308 
    309             mSupportedColorModes.clear();
    310             mSupportedColorModes.addAll(pendingColorModes);
    311             Collections.sort(mSupportedColorModes);
    312 
    313             // Determine whether the active color mode is still there.
    314             if (!mSupportedColorModes.contains(mActiveColorMode)) {
    315                 if (mActiveColorMode != 0) {
    316                     Slog.w(TAG, "Active color mode no longer available, reverting"
    317                             + " to default mode.");
    318                     mActiveColorMode = Display.COLOR_MODE_DEFAULT;
    319                     mActiveColorModeInvalid = true;
    320                 } else {
    321                     if (!mSupportedColorModes.isEmpty()) {
    322                         // This should never happen.
    323                         Slog.e(TAG, "Default and active color mode is no longer available!"
    324                                 + " Reverting to first available mode.");
    325                         mActiveColorMode = mSupportedColorModes.get(0);
    326                         mActiveColorModeInvalid = true;
    327                     } else {
    328                         // This should really never happen.
    329                         Slog.e(TAG, "No color modes available!");
    330                     }
    331                 }
    332             }
    333             return true;
    334         }
    335 
    336         private DisplayModeRecord findDisplayModeRecord(SurfaceControl.PhysicalDisplayInfo info) {
    337             for (int i = 0; i < mSupportedModes.size(); i++) {
    338                 DisplayModeRecord record = mSupportedModes.valueAt(i);
    339                 if (record.hasMatchingMode(info)) {
    340                     return record;
    341                 }
    342             }
    343             return null;
    344         }
    345 
    346         @Override
    347         public void applyPendingDisplayDeviceInfoChangesLocked() {
    348             if (mHavePendingChanges) {
    349                 mInfo = null;
    350                 mHavePendingChanges = false;
    351             }
    352         }
    353 
    354         @Override
    355         public DisplayDeviceInfo getDisplayDeviceInfoLocked() {
    356             if (mInfo == null) {
    357                 SurfaceControl.PhysicalDisplayInfo phys = mDisplayInfos[mActivePhysIndex];
    358                 mInfo = new DisplayDeviceInfo();
    359                 mInfo.width = phys.width;
    360                 mInfo.height = phys.height;
    361                 mInfo.modeId = mActiveModeId;
    362                 mInfo.defaultModeId = mDefaultModeId;
    363                 mInfo.supportedModes = new Display.Mode[mSupportedModes.size()];
    364                 for (int i = 0; i < mSupportedModes.size(); i++) {
    365                     DisplayModeRecord record = mSupportedModes.valueAt(i);
    366                     mInfo.supportedModes[i] = record.mMode;
    367                 }
    368                 mInfo.colorMode = mActiveColorMode;
    369                 mInfo.supportedColorModes =
    370                         new int[mSupportedColorModes.size()];
    371                 for (int i = 0; i < mSupportedColorModes.size(); i++) {
    372                     mInfo.supportedColorModes[i] = mSupportedColorModes.get(i);
    373                 }
    374                 mInfo.hdrCapabilities = mHdrCapabilities;
    375                 mInfo.appVsyncOffsetNanos = phys.appVsyncOffsetNanos;
    376                 mInfo.presentationDeadlineNanos = phys.presentationDeadlineNanos;
    377                 mInfo.state = mState;
    378                 mInfo.uniqueId = getUniqueId();
    379 
    380                 // Assume that all built-in displays that have secure output (eg. HDCP) also
    381                 // support compositing from gralloc protected buffers.
    382                 if (phys.secure) {
    383                     mInfo.flags = DisplayDeviceInfo.FLAG_SECURE
    384                             | DisplayDeviceInfo.FLAG_SUPPORTS_PROTECTED_BUFFERS;
    385                 }
    386 
    387                 final Resources res = getContext().getResources();
    388                 if (mBuiltInDisplayId == SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN) {
    389                     mInfo.name = res.getString(
    390                             com.android.internal.R.string.display_manager_built_in_display_name);
    391                     mInfo.flags |= DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY
    392                             | DisplayDeviceInfo.FLAG_ROTATES_WITH_CONTENT;
    393                     if (res.getBoolean(com.android.internal.R.bool.config_mainBuiltInDisplayIsRound)
    394                             || (Build.IS_EMULATOR
    395                             && SystemProperties.getBoolean(PROPERTY_EMULATOR_CIRCULAR, false))) {
    396                         mInfo.flags |= DisplayDeviceInfo.FLAG_ROUND;
    397                     }
    398                     mInfo.type = Display.TYPE_BUILT_IN;
    399                     mInfo.densityDpi = (int)(phys.density * 160 + 0.5f);
    400                     mInfo.xDpi = phys.xDpi;
    401                     mInfo.yDpi = phys.yDpi;
    402                     mInfo.touch = DisplayDeviceInfo.TOUCH_INTERNAL;
    403                 } else {
    404                     mInfo.type = Display.TYPE_HDMI;
    405                     mInfo.flags |= DisplayDeviceInfo.FLAG_PRESENTATION;
    406                     mInfo.name = getContext().getResources().getString(
    407                             com.android.internal.R.string.display_manager_hdmi_display_name);
    408                     mInfo.touch = DisplayDeviceInfo.TOUCH_EXTERNAL;
    409                     mInfo.setAssumedDensityForExternalDisplay(phys.width, phys.height);
    410 
    411                     // For demonstration purposes, allow rotation of the external display.
    412                     // In the future we might allow the user to configure this directly.
    413                     if ("portrait".equals(SystemProperties.get("persist.demo.hdmirotation"))) {
    414                         mInfo.rotation = Surface.ROTATION_270;
    415                     }
    416 
    417                     // For demonstration purposes, allow rotation of the external display
    418                     // to follow the built-in display.
    419                     if (SystemProperties.getBoolean("persist.demo.hdmirotates", false)) {
    420                         mInfo.flags |= DisplayDeviceInfo.FLAG_ROTATES_WITH_CONTENT;
    421                     }
    422 
    423                     if (!res.getBoolean(
    424                                 com.android.internal.R.bool.config_localDisplaysMirrorContent)) {
    425                         mInfo.flags |= DisplayDeviceInfo.FLAG_OWN_CONTENT_ONLY;
    426                     }
    427                 }
    428             }
    429             return mInfo;
    430         }
    431 
    432         @Override
    433         public Runnable requestDisplayStateLocked(final int state, final int brightness) {
    434             // Assume that the brightness is off if the display is being turned off.
    435             assert state != Display.STATE_OFF || brightness == PowerManager.BRIGHTNESS_OFF;
    436 
    437             final boolean stateChanged = (mState != state);
    438             final boolean brightnessChanged = (mBrightness != brightness) && mBacklight != null;
    439             if (stateChanged || brightnessChanged) {
    440                 final int displayId = mBuiltInDisplayId;
    441                 final IBinder token = getDisplayTokenLocked();
    442                 final int oldState = mState;
    443 
    444                 if (stateChanged) {
    445                     mState = state;
    446                     updateDeviceInfoLocked();
    447                 }
    448 
    449                 if (brightnessChanged) {
    450                     mBrightness = brightness;
    451                 }
    452 
    453                 // Defer actually setting the display state until after we have exited
    454                 // the critical section since it can take hundreds of milliseconds
    455                 // to complete.
    456                 return new Runnable() {
    457                     @Override
    458                     public void run() {
    459                         // Exit a suspended state before making any changes.
    460                         int currentState = oldState;
    461                         if (Display.isSuspendedState(oldState)
    462                                 || oldState == Display.STATE_UNKNOWN) {
    463                             if (!Display.isSuspendedState(state)) {
    464                                 setDisplayState(state);
    465                                 currentState = state;
    466                             } else if (state == Display.STATE_DOZE_SUSPEND
    467                                     || oldState == Display.STATE_DOZE_SUSPEND) {
    468                                 setDisplayState(Display.STATE_DOZE);
    469                                 currentState = Display.STATE_DOZE;
    470                             } else {
    471                                 return; // old state and new state is off
    472                             }
    473                         }
    474 
    475                         // If the state change was from or to VR, then we need to tell the light
    476                         // so that it can apply appropriate VR brightness settings. This should
    477                         // happen prior to changing the brightness but also if there is no
    478                         // brightness change at all.
    479                         if ((state == Display.STATE_VR || currentState == Display.STATE_VR) &&
    480                                 currentState != state) {
    481                             setVrMode(state == Display.STATE_VR);
    482                         }
    483 
    484 
    485                         // Apply brightness changes given that we are in a non-suspended state.
    486                         if (brightnessChanged) {
    487                             setDisplayBrightness(brightness);
    488                         }
    489 
    490                         // Enter the final desired state, possibly suspended.
    491                         if (state != currentState) {
    492                             setDisplayState(state);
    493                         }
    494                     }
    495 
    496                     private void setVrMode(boolean isVrEnabled) {
    497                         if (DEBUG) {
    498                             Slog.d(TAG, "setVrMode("
    499                                     + "id=" + displayId
    500                                     + ", state=" + Display.stateToString(state) + ")");
    501                         }
    502                         mBacklight.setVrMode(isVrEnabled);
    503                     }
    504 
    505                     private void setDisplayState(int state) {
    506                         if (DEBUG) {
    507                             Slog.d(TAG, "setDisplayState("
    508                                     + "id=" + displayId
    509                                     + ", state=" + Display.stateToString(state) + ")");
    510                         }
    511 
    512                         Trace.traceBegin(Trace.TRACE_TAG_POWER, "setDisplayState("
    513                                 + "id=" + displayId
    514                                 + ", state=" + Display.stateToString(state) + ")");
    515                         try {
    516                             final int mode = getPowerModeForState(state);
    517                             SurfaceControl.setDisplayPowerMode(token, mode);
    518                         } finally {
    519                             Trace.traceEnd(Trace.TRACE_TAG_POWER);
    520                         }
    521                     }
    522 
    523                     private void setDisplayBrightness(int brightness) {
    524                         if (DEBUG) {
    525                             Slog.d(TAG, "setDisplayBrightness("
    526                                     + "id=" + displayId + ", brightness=" + brightness + ")");
    527                         }
    528 
    529                         Trace.traceBegin(Trace.TRACE_TAG_POWER, "setDisplayBrightness("
    530                                 + "id=" + displayId + ", brightness=" + brightness + ")");
    531                         try {
    532                             mBacklight.setBrightness(brightness);
    533                         } finally {
    534                             Trace.traceEnd(Trace.TRACE_TAG_POWER);
    535                         }
    536                     }
    537                 };
    538             }
    539             return null;
    540         }
    541 
    542         @Override
    543         public void requestDisplayModesInTransactionLocked(
    544                 int colorMode, int modeId) {
    545             if (requestModeInTransactionLocked(modeId) ||
    546                     requestColorModeInTransactionLocked(colorMode)) {
    547                 updateDeviceInfoLocked();
    548             }
    549         }
    550 
    551         public boolean requestModeInTransactionLocked(int modeId) {
    552             if (modeId == 0) {
    553                 modeId = mDefaultModeId;
    554             } else if (mSupportedModes.indexOfKey(modeId) < 0) {
    555                 Slog.w(TAG, "Requested mode " + modeId + " is not supported by this display,"
    556                         + " reverting to default display mode.");
    557                 modeId = mDefaultModeId;
    558             }
    559 
    560             int physIndex = findDisplayInfoIndexLocked(modeId);
    561             if (physIndex < 0) {
    562                 Slog.w(TAG, "Requested mode ID " + modeId + " not available,"
    563                         + " trying with default mode ID");
    564                 modeId = mDefaultModeId;
    565                 physIndex = findDisplayInfoIndexLocked(modeId);
    566             }
    567             if (mActivePhysIndex == physIndex) {
    568                 return false;
    569             }
    570             SurfaceControl.setActiveConfig(getDisplayTokenLocked(), physIndex);
    571             mActivePhysIndex = physIndex;
    572             mActiveModeId = modeId;
    573             mActiveModeInvalid = false;
    574             return true;
    575         }
    576 
    577         public boolean requestColorModeInTransactionLocked(int colorMode) {
    578             if (mActiveColorMode == colorMode) {
    579                 return false;
    580             }
    581             if (!mSupportedColorModes.contains(colorMode)) {
    582                 Slog.w(TAG, "Unable to find color mode " + colorMode
    583                         + ", ignoring request.");
    584                 return false;
    585             }
    586             SurfaceControl.setActiveColorMode(getDisplayTokenLocked(), colorMode);
    587             mActiveColorMode = colorMode;
    588             mActiveColorModeInvalid = false;
    589             return true;
    590         }
    591 
    592         @Override
    593         public void dumpLocked(PrintWriter pw) {
    594             super.dumpLocked(pw);
    595             pw.println("mBuiltInDisplayId=" + mBuiltInDisplayId);
    596             pw.println("mActivePhysIndex=" + mActivePhysIndex);
    597             pw.println("mActiveModeId=" + mActiveModeId);
    598             pw.println("mActiveColorMode=" + mActiveColorMode);
    599             pw.println("mState=" + Display.stateToString(mState));
    600             pw.println("mBrightness=" + mBrightness);
    601             pw.println("mBacklight=" + mBacklight);
    602             pw.println("mDisplayInfos=");
    603             for (int i = 0; i < mDisplayInfos.length; i++) {
    604                 pw.println("  " + mDisplayInfos[i]);
    605             }
    606             pw.println("mSupportedModes=");
    607             for (int i = 0; i < mSupportedModes.size(); i++) {
    608                 pw.println("  " + mSupportedModes.valueAt(i));
    609             }
    610             pw.print("mSupportedColorModes=[");
    611             for (int i = 0; i < mSupportedColorModes.size(); i++) {
    612                 if (i != 0) {
    613                     pw.print(", ");
    614                 }
    615                 pw.print(mSupportedColorModes.get(i));
    616             }
    617             pw.println("]");
    618         }
    619 
    620         private int findDisplayInfoIndexLocked(int modeId) {
    621             DisplayModeRecord record = mSupportedModes.get(modeId);
    622             if (record != null) {
    623                 for (int i = 0; i < mDisplayInfos.length; i++) {
    624                     SurfaceControl.PhysicalDisplayInfo info = mDisplayInfos[i];
    625                     if (record.hasMatchingMode(info)){
    626                         return i;
    627                     }
    628                 }
    629             }
    630             return -1;
    631         }
    632 
    633         private void updateDeviceInfoLocked() {
    634             mInfo = null;
    635             sendDisplayDeviceEventLocked(this, DISPLAY_DEVICE_EVENT_CHANGED);
    636         }
    637     }
    638 
    639     /**
    640      * Keeps track of a display configuration.
    641      */
    642     private static final class DisplayModeRecord {
    643         public final Display.Mode mMode;
    644 
    645         public DisplayModeRecord(SurfaceControl.PhysicalDisplayInfo phys) {
    646             mMode = createMode(phys.width, phys.height, phys.refreshRate);
    647         }
    648 
    649         /**
    650          * Returns whether the mode generated by the given PhysicalDisplayInfo matches the mode
    651          * contained by the record modulo mode ID.
    652          *
    653          * Note that this doesn't necessarily mean the the PhysicalDisplayInfos are identical, just
    654          * that they generate identical modes.
    655          */
    656         public boolean hasMatchingMode(SurfaceControl.PhysicalDisplayInfo info) {
    657             int modeRefreshRate = Float.floatToIntBits(mMode.getRefreshRate());
    658             int displayInfoRefreshRate = Float.floatToIntBits(info.refreshRate);
    659             return mMode.getPhysicalWidth() == info.width
    660                     && mMode.getPhysicalHeight() == info.height
    661                     && modeRefreshRate == displayInfoRefreshRate;
    662         }
    663 
    664         public String toString() {
    665             return "DisplayModeRecord{mMode=" + mMode + "}";
    666         }
    667     }
    668 
    669     private final class HotplugDisplayEventReceiver extends DisplayEventReceiver {
    670         public HotplugDisplayEventReceiver(Looper looper) {
    671             super(looper, VSYNC_SOURCE_APP);
    672         }
    673 
    674         @Override
    675         public void onHotplug(long timestampNanos, int builtInDisplayId, boolean connected) {
    676             synchronized (getSyncRoot()) {
    677                 if (connected) {
    678                     tryConnectDisplayLocked(builtInDisplayId);
    679                 } else {
    680                     tryDisconnectDisplayLocked(builtInDisplayId);
    681                 }
    682             }
    683         }
    684     }
    685 }
    686