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