LocalDisplayAdapter.java revision 49e7ff9647e6547c2b852944a5435a05794b9951
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.content.res.Resources;
20import com.android.server.LocalServices;
21import com.android.server.lights.Light;
22import com.android.server.lights.LightsManager;
23
24import android.content.Context;
25import android.os.Handler;
26import android.os.IBinder;
27import android.os.Looper;
28import android.os.PowerManager;
29import android.os.SystemProperties;
30import android.os.Trace;
31import android.util.Slog;
32import android.util.SparseArray;
33import android.view.Display;
34import android.view.DisplayEventReceiver;
35import android.view.Surface;
36import android.view.SurfaceControl;
37
38import java.io.PrintWriter;
39import java.util.ArrayList;
40
41/**
42 * A display adapter for the local displays managed by Surface Flinger.
43 * <p>
44 * Display adapters are guarded by the {@link DisplayManagerService.SyncRoot} lock.
45 * </p>
46 */
47final class LocalDisplayAdapter extends DisplayAdapter {
48    private static final String TAG = "LocalDisplayAdapter";
49    private static final boolean DEBUG = false;
50
51    private static final String UNIQUE_ID_PREFIX = "local:";
52
53    private static final int[] BUILT_IN_DISPLAY_IDS_TO_SCAN = new int[] {
54            SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN,
55            SurfaceControl.BUILT_IN_DISPLAY_ID_HDMI,
56    };
57
58    private final SparseArray<LocalDisplayDevice> mDevices =
59            new SparseArray<LocalDisplayDevice>();
60    @SuppressWarnings("unused")  // Becomes active at instantiation time.
61    private HotplugDisplayEventReceiver mHotplugReceiver;
62
63    // Called with SyncRoot lock held.
64    public LocalDisplayAdapter(DisplayManagerService.SyncRoot syncRoot,
65            Context context, Handler handler, Listener listener) {
66        super(syncRoot, context, handler, listener, TAG);
67    }
68
69    @Override
70    public void registerLocked() {
71        super.registerLocked();
72
73        mHotplugReceiver = new HotplugDisplayEventReceiver(getHandler().getLooper());
74
75        for (int builtInDisplayId : BUILT_IN_DISPLAY_IDS_TO_SCAN) {
76            tryConnectDisplayLocked(builtInDisplayId);
77        }
78    }
79
80    private void tryConnectDisplayLocked(int builtInDisplayId) {
81        IBinder displayToken = SurfaceControl.getBuiltInDisplay(builtInDisplayId);
82        if (displayToken != null) {
83            SurfaceControl.PhysicalDisplayInfo[] configs =
84                    SurfaceControl.getDisplayConfigs(displayToken);
85            if (configs == null) {
86                // There are no valid configs for this device, so we can't use it
87                Slog.w(TAG, "No valid configs found for display device " +
88                        builtInDisplayId);
89                return;
90            }
91            int activeConfig = SurfaceControl.getActiveConfig(displayToken);
92            if (activeConfig < 0) {
93                // There is no active config, and for now we don't have the
94                // policy to set one.
95                Slog.w(TAG, "No active config found for display device " +
96                        builtInDisplayId);
97                return;
98            }
99            LocalDisplayDevice device = mDevices.get(builtInDisplayId);
100            if (device == null) {
101                // Display was added.
102                device = new LocalDisplayDevice(displayToken, builtInDisplayId,
103                        configs, activeConfig);
104                mDevices.put(builtInDisplayId, device);
105                sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_ADDED);
106            } else if (device.updatePhysicalDisplayInfoLocked(configs, activeConfig)) {
107                // Display properties changed.
108                sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_CHANGED);
109            }
110        } else {
111            // The display is no longer available. Ignore the attempt to add it.
112            // If it was connected but has already been disconnected, we'll get a
113            // disconnect event that will remove it from mDevices.
114        }
115    }
116
117    private void tryDisconnectDisplayLocked(int builtInDisplayId) {
118        LocalDisplayDevice device = mDevices.get(builtInDisplayId);
119        if (device != null) {
120            // Display was removed.
121            mDevices.remove(builtInDisplayId);
122            sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_REMOVED);
123        }
124    }
125
126    static int getPowerModeForState(int state) {
127        switch (state) {
128            case Display.STATE_OFF:
129                return SurfaceControl.POWER_MODE_OFF;
130            case Display.STATE_DOZE:
131                return SurfaceControl.POWER_MODE_DOZE;
132            case Display.STATE_DOZE_SUSPEND:
133                return SurfaceControl.POWER_MODE_DOZE_SUSPEND;
134            default:
135                return SurfaceControl.POWER_MODE_NORMAL;
136        }
137    }
138
139    private final class LocalDisplayDevice extends DisplayDevice {
140        private final int mBuiltInDisplayId;
141        private final Light mBacklight;
142        private final SparseArray<DisplayModeRecord> mSupportedModes = new SparseArray<>();
143
144        private DisplayDeviceInfo mInfo;
145        private boolean mHavePendingChanges;
146        private int mState = Display.STATE_UNKNOWN;
147        private int mBrightness = PowerManager.BRIGHTNESS_DEFAULT;
148        private int mDefaultModeId;
149        private int mActiveModeId;
150        private boolean mActiveModeInvalid;
151
152        public LocalDisplayDevice(IBinder displayToken, int builtInDisplayId,
153                SurfaceControl.PhysicalDisplayInfo[] physicalDisplayInfos, int activeDisplayInfo) {
154            super(LocalDisplayAdapter.this, displayToken, UNIQUE_ID_PREFIX + builtInDisplayId);
155            mBuiltInDisplayId = builtInDisplayId;
156            updatePhysicalDisplayInfoLocked(physicalDisplayInfos, activeDisplayInfo);
157            if (mBuiltInDisplayId == SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN) {
158                LightsManager lights = LocalServices.getService(LightsManager.class);
159                mBacklight = lights.getLight(LightsManager.LIGHT_ID_BACKLIGHT);
160            } else {
161                mBacklight = null;
162            }
163        }
164
165        public boolean updatePhysicalDisplayInfoLocked(
166                SurfaceControl.PhysicalDisplayInfo[] physicalDisplayInfos, int activeDisplayInfo) {
167            // Build an updated list of all existing modes.
168            boolean modesAdded = false;
169            DisplayModeRecord activeRecord = null;
170            ArrayList<DisplayModeRecord> records = new ArrayList<DisplayModeRecord>();
171            for (int i = 0; i < physicalDisplayInfos.length; i++) {
172                SurfaceControl.PhysicalDisplayInfo info = physicalDisplayInfos[i];
173                DisplayModeRecord record = findDisplayModeRecord(info);
174                if (record != null) {
175                    record.mPhysIndex = i;
176                } else {
177                    record = new DisplayModeRecord(info, i);
178                    modesAdded = true;
179                }
180                records.add(record);
181                if (i == activeDisplayInfo) {
182                    activeRecord = record;
183                }
184            }
185            // Check whether surface flinger spontaneously changed modes out from under us. Schedule
186            // traversals to ensure that the correct state is reapplied if necessary.
187            if (mActiveModeId != 0
188                    && mActiveModeId != activeRecord.mMode.getModeId()) {
189                mActiveModeInvalid = true;
190                sendTraversalRequestLocked();
191            }
192            // If no modes were added and we have the same number of modes as before, then nothing
193            // actually changed except possibly the physical index (which we only care about when
194            // setting the mode) so we're done.
195            if (records.size() == mSupportedModes.size() && !modesAdded) {
196                return false;
197            }
198            // Update the index of modes.
199            mHavePendingChanges = true;
200            mSupportedModes.clear();
201            for (DisplayModeRecord record : records) {
202                mSupportedModes.put(record.mMode.getModeId(), record);
203            }
204            // Update the default mode if needed.
205            if (mSupportedModes.indexOfKey(mDefaultModeId) < 0) {
206                if (mDefaultModeId != 0) {
207                    Slog.w(TAG, "Default display mode no longer available, using currently active"
208                            + " mode as default.");
209                }
210                mDefaultModeId = activeRecord.mMode.getModeId();
211            }
212            // Determine whether the active mode is still there.
213            if (mSupportedModes.indexOfKey(mActiveModeId) < 0) {
214                if (mActiveModeId != 0) {
215                    Slog.w(TAG, "Active display mode no longer available, reverting to default"
216                            + " mode.");
217                }
218                mActiveModeId = mDefaultModeId;
219                mActiveModeInvalid = true;
220            }
221            // Schedule traversals so that we apply pending changes.
222            sendTraversalRequestLocked();
223            return true;
224        }
225
226        private DisplayModeRecord findDisplayModeRecord(SurfaceControl.PhysicalDisplayInfo info) {
227            for (int i = 0; i < mSupportedModes.size(); i++) {
228                DisplayModeRecord record = mSupportedModes.valueAt(i);
229                if (record.mPhys.equals(info)) {
230                    return record;
231                }
232            }
233            return null;
234        }
235
236        @Override
237        public void applyPendingDisplayDeviceInfoChangesLocked() {
238            if (mHavePendingChanges) {
239                mInfo = null;
240                mHavePendingChanges = false;
241            }
242        }
243
244        @Override
245        public DisplayDeviceInfo getDisplayDeviceInfoLocked() {
246            if (mInfo == null) {
247                SurfaceControl.PhysicalDisplayInfo phys = mSupportedModes.get(mActiveModeId).mPhys;
248                mInfo = new DisplayDeviceInfo();
249                mInfo.width = phys.width;
250                mInfo.height = phys.height;
251                mInfo.modeId = mActiveModeId;
252                mInfo.defaultModeId = mDefaultModeId;
253                mInfo.supportedModes = new Display.Mode[mSupportedModes.size()];
254                for (int i = 0; i < mSupportedModes.size(); i++) {
255                    DisplayModeRecord record = mSupportedModes.valueAt(i);
256                    mInfo.supportedModes[i] = record.mMode;
257                }
258                mInfo.appVsyncOffsetNanos = phys.appVsyncOffsetNanos;
259                mInfo.presentationDeadlineNanos = phys.presentationDeadlineNanos;
260                mInfo.state = mState;
261                mInfo.uniqueId = getUniqueId();
262
263                // Assume that all built-in displays that have secure output (eg. HDCP) also
264                // support compositing from gralloc protected buffers.
265                if (phys.secure) {
266                    mInfo.flags = DisplayDeviceInfo.FLAG_SECURE
267                            | DisplayDeviceInfo.FLAG_SUPPORTS_PROTECTED_BUFFERS;
268                }
269
270                if (mBuiltInDisplayId == SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN) {
271                    final Resources res = getContext().getResources();
272                    mInfo.name = res.getString(
273                            com.android.internal.R.string.display_manager_built_in_display_name);
274                    mInfo.flags |= DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY
275                            | DisplayDeviceInfo.FLAG_ROTATES_WITH_CONTENT;
276                    if (res.getBoolean(
277                            com.android.internal.R.bool.config_mainBuiltInDisplayIsRound)) {
278                        mInfo.flags |= DisplayDeviceInfo.FLAG_ROUND;
279                    }
280                    mInfo.type = Display.TYPE_BUILT_IN;
281                    mInfo.densityDpi = (int)(phys.density * 160 + 0.5f);
282                    mInfo.xDpi = phys.xDpi;
283                    mInfo.yDpi = phys.yDpi;
284                    mInfo.touch = DisplayDeviceInfo.TOUCH_INTERNAL;
285                } else {
286                    mInfo.type = Display.TYPE_HDMI;
287                    mInfo.flags |= DisplayDeviceInfo.FLAG_PRESENTATION;
288                    mInfo.name = getContext().getResources().getString(
289                            com.android.internal.R.string.display_manager_hdmi_display_name);
290                    mInfo.touch = DisplayDeviceInfo.TOUCH_EXTERNAL;
291                    mInfo.setAssumedDensityForExternalDisplay(phys.width, phys.height);
292
293                    // For demonstration purposes, allow rotation of the external display.
294                    // In the future we might allow the user to configure this directly.
295                    if ("portrait".equals(SystemProperties.get("persist.demo.hdmirotation"))) {
296                        mInfo.rotation = Surface.ROTATION_270;
297                    }
298
299                    // For demonstration purposes, allow rotation of the external display
300                    // to follow the built-in display.
301                    if (SystemProperties.getBoolean("persist.demo.hdmirotates", false)) {
302                        mInfo.flags |= DisplayDeviceInfo.FLAG_ROTATES_WITH_CONTENT;
303                    }
304                }
305            }
306            return mInfo;
307        }
308
309        @Override
310        public Runnable requestDisplayStateLocked(final int state, final int brightness) {
311            // Assume that the brightness is off if the display is being turned off.
312            assert state != Display.STATE_OFF || brightness == PowerManager.BRIGHTNESS_OFF;
313
314            final boolean stateChanged = (mState != state);
315            final boolean brightnessChanged = (mBrightness != brightness) && mBacklight != null;
316            if (stateChanged || brightnessChanged) {
317                final int displayId = mBuiltInDisplayId;
318                final IBinder token = getDisplayTokenLocked();
319                final int oldState = mState;
320
321                if (stateChanged) {
322                    mState = state;
323                    updateDeviceInfoLocked();
324                }
325
326                if (brightnessChanged) {
327                    mBrightness = brightness;
328                }
329
330                // Defer actually setting the display state until after we have exited
331                // the critical section since it can take hundreds of milliseconds
332                // to complete.
333                return new Runnable() {
334                    @Override
335                    public void run() {
336                        // Exit a suspended state before making any changes.
337                        int currentState = oldState;
338                        if (Display.isSuspendedState(oldState)
339                                || oldState == Display.STATE_UNKNOWN) {
340                            if (!Display.isSuspendedState(state)) {
341                                setDisplayState(state);
342                                currentState = state;
343                            } else if (state == Display.STATE_DOZE_SUSPEND
344                                    || oldState == Display.STATE_DOZE_SUSPEND) {
345                                setDisplayState(Display.STATE_DOZE);
346                                currentState = Display.STATE_DOZE;
347                            } else {
348                                return; // old state and new state is off
349                            }
350                        }
351
352                        // Apply brightness changes given that we are in a non-suspended state.
353                        if (brightnessChanged) {
354                            setDisplayBrightness(brightness);
355                        }
356
357                        // Enter the final desired state, possibly suspended.
358                        if (state != currentState) {
359                            setDisplayState(state);
360                        }
361                    }
362
363                    private void setDisplayState(int state) {
364                        if (DEBUG) {
365                            Slog.d(TAG, "setDisplayState("
366                                    + "id=" + displayId
367                                    + ", state=" + Display.stateToString(state) + ")");
368                        }
369
370                        Trace.traceBegin(Trace.TRACE_TAG_POWER, "setDisplayState("
371                                + "id=" + displayId
372                                + ", state=" + Display.stateToString(state) + ")");
373                        try {
374                            final int mode = getPowerModeForState(state);
375                            SurfaceControl.setDisplayPowerMode(token, mode);
376                        } finally {
377                            Trace.traceEnd(Trace.TRACE_TAG_POWER);
378                        }
379                    }
380
381                    private void setDisplayBrightness(int brightness) {
382                        if (DEBUG) {
383                            Slog.d(TAG, "setDisplayBrightness("
384                                    + "id=" + displayId + ", brightness=" + brightness + ")");
385                        }
386
387                        Trace.traceBegin(Trace.TRACE_TAG_POWER, "setDisplayBrightness("
388                                + "id=" + displayId + ", brightness=" + brightness + ")");
389                        try {
390                            mBacklight.setBrightness(brightness);
391                        } finally {
392                            Trace.traceEnd(Trace.TRACE_TAG_POWER);
393                        }
394                    }
395                };
396            }
397            return null;
398        }
399
400        @Override
401        public void requestModeInTransactionLocked(int modeId) {
402            if (modeId == 0) {
403                modeId = mDefaultModeId;
404            } else if (mSupportedModes.indexOfKey(modeId) < 0) {
405                Slog.w(TAG, "Requested mode " + modeId + " is not supported by this display,"
406                        + " reverting to default display mode.");
407                modeId = mDefaultModeId;
408            }
409            if (mActiveModeId == modeId && !mActiveModeInvalid) {
410                return;
411            }
412            DisplayModeRecord record = mSupportedModes.get(modeId);
413            SurfaceControl.setActiveConfig(getDisplayTokenLocked(), record.mPhysIndex);
414            mActiveModeId = modeId;
415            mActiveModeInvalid = false;
416            updateDeviceInfoLocked();
417        }
418
419        @Override
420        public void dumpLocked(PrintWriter pw) {
421            super.dumpLocked(pw);
422            pw.println("mBuiltInDisplayId=" + mBuiltInDisplayId);
423            pw.println("mActiveModeId=" + mActiveModeId);
424            pw.println("mState=" + Display.stateToString(mState));
425            pw.println("mBrightness=" + mBrightness);
426            pw.println("mBacklight=" + mBacklight);
427        }
428
429        private void updateDeviceInfoLocked() {
430            mInfo = null;
431            sendDisplayDeviceEventLocked(this, DISPLAY_DEVICE_EVENT_CHANGED);
432        }
433    }
434
435    /**
436     * Keeps track of a display configuration.
437     */
438    private static final class DisplayModeRecord {
439        public final Display.Mode mMode;
440        public final SurfaceControl.PhysicalDisplayInfo mPhys;
441        public int mPhysIndex;
442
443        public DisplayModeRecord(SurfaceControl.PhysicalDisplayInfo phys, int physIndex) {
444            mMode = createMode(phys.width, phys.height, phys.refreshRate);
445            mPhys = phys;
446            mPhysIndex = physIndex;
447        }
448    }
449
450    private final class HotplugDisplayEventReceiver extends DisplayEventReceiver {
451        public HotplugDisplayEventReceiver(Looper looper) {
452            super(looper);
453        }
454
455        @Override
456        public void onHotplug(long timestampNanos, int builtInDisplayId, boolean connected) {
457            synchronized (getSyncRoot()) {
458                if (connected) {
459                    tryConnectDisplayLocked(builtInDisplayId);
460                } else {
461                    tryDisconnectDisplayLocked(builtInDisplayId);
462                }
463            }
464        }
465    }
466}
467