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
17package com.android.server.display;
18
19import android.app.ActivityThread;
20import android.content.res.Resources;
21import com.android.server.LocalServices;
22import com.android.server.lights.Light;
23import com.android.server.lights.LightsManager;
24
25import android.content.Context;
26import android.hardware.sidekick.SidekickInternal;
27import android.os.Build;
28import android.os.Handler;
29import android.os.IBinder;
30import android.os.Looper;
31import android.os.PowerManager;
32import android.os.SystemProperties;
33import android.os.Trace;
34import android.text.TextUtils;
35import android.util.PathParser;
36import android.util.Slog;
37import android.util.SparseArray;
38import android.view.Display;
39import android.view.DisplayCutout;
40import android.view.DisplayEventReceiver;
41import android.view.Surface;
42import android.view.SurfaceControl;
43import java.io.PrintWriter;
44import java.util.ArrayList;
45import java.util.Arrays;
46import java.util.Collections;
47import 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 */
55final 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