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