OSDN Git Service

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