OSDN Git Service

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