LocalDisplayAdapter.java revision 5dc219142a756d57355b511c8f8ab913c01710da
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.Context;
20import android.os.Handler;
21import android.os.IBinder;
22import android.os.Looper;
23import android.os.SystemProperties;
24import android.util.Slog;
25import android.util.SparseArray;
26import android.view.Display;
27import android.view.DisplayEventReceiver;
28import android.view.Surface;
29import android.view.SurfaceControl;
30
31import java.io.PrintWriter;
32
33/**
34 * A display adapter for the local displays managed by Surface Flinger.
35 * <p>
36 * Display adapters are guarded by the {@link DisplayManagerService.SyncRoot} lock.
37 * </p>
38 */
39final class LocalDisplayAdapter extends DisplayAdapter {
40    private static final String TAG = "LocalDisplayAdapter";
41
42    private static final int[] BUILT_IN_DISPLAY_IDS_TO_SCAN = new int[] {
43            SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN,
44            SurfaceControl.BUILT_IN_DISPLAY_ID_HDMI,
45    };
46
47    private final SparseArray<LocalDisplayDevice> mDevices =
48            new SparseArray<LocalDisplayDevice>();
49    private HotplugDisplayEventReceiver mHotplugReceiver;
50
51    // Called with SyncRoot lock held.
52    public LocalDisplayAdapter(DisplayManagerService.SyncRoot syncRoot,
53            Context context, Handler handler, Listener listener) {
54        super(syncRoot, context, handler, listener, TAG);
55    }
56
57    @Override
58    public void registerLocked() {
59        super.registerLocked();
60
61        mHotplugReceiver = new HotplugDisplayEventReceiver(getHandler().getLooper());
62
63        for (int builtInDisplayId : BUILT_IN_DISPLAY_IDS_TO_SCAN) {
64            tryConnectDisplayLocked(builtInDisplayId);
65        }
66    }
67
68    private void tryConnectDisplayLocked(int builtInDisplayId) {
69        IBinder displayToken = SurfaceControl.getBuiltInDisplay(builtInDisplayId);
70        if (displayToken != null) {
71            SurfaceControl.PhysicalDisplayInfo[] configs =
72                    SurfaceControl.getDisplayConfigs(displayToken);
73            if (configs == null) {
74                // There are no valid configs for this device, so we can't use it
75                Slog.w(TAG, "No valid configs found for display device " +
76                        builtInDisplayId);
77                return;
78            }
79            int activeConfig = SurfaceControl.getActiveConfig(displayToken);
80            if (activeConfig < 0) {
81                // There is no active config, and for now we don't have the
82                // policy to set one.
83                Slog.w(TAG, "No active config found for display device " +
84                        builtInDisplayId);
85                return;
86            }
87            LocalDisplayDevice device = mDevices.get(builtInDisplayId);
88            if (device == null) {
89                // Display was added.
90                device = new LocalDisplayDevice(displayToken, builtInDisplayId,
91                        configs[activeConfig]);
92                mDevices.put(builtInDisplayId, device);
93                sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_ADDED);
94            } else if (device.updatePhysicalDisplayInfoLocked(configs[activeConfig])) {
95                // Display properties changed.
96                sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_CHANGED);
97            }
98        } else {
99            // The display is no longer available. Ignore the attempt to add it.
100            // If it was connected but has already been disconnected, we'll get a
101            // disconnect event that will remove it from mDevices.
102        }
103    }
104
105    private void tryDisconnectDisplayLocked(int builtInDisplayId) {
106        LocalDisplayDevice device = mDevices.get(builtInDisplayId);
107        if (device != null) {
108            // Display was removed.
109            mDevices.remove(builtInDisplayId);
110            sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_REMOVED);
111        }
112    }
113
114    static int getPowerModeForState(int state) {
115        switch (state) {
116            case Display.STATE_OFF:
117                return SurfaceControl.POWER_MODE_OFF;
118            case Display.STATE_DOZE:
119                return SurfaceControl.POWER_MODE_DOZE;
120            case Display.STATE_DOZE_SUSPEND:
121                return SurfaceControl.POWER_MODE_DOZE_SUSPEND;
122            default:
123                return SurfaceControl.POWER_MODE_NORMAL;
124        }
125    }
126
127    private final class LocalDisplayDevice extends DisplayDevice {
128        private final int mBuiltInDisplayId;
129        private final SurfaceControl.PhysicalDisplayInfo mPhys;
130
131        private DisplayDeviceInfo mInfo;
132        private boolean mHavePendingChanges;
133        private int mState = Display.STATE_UNKNOWN;
134
135        public LocalDisplayDevice(IBinder displayToken, int builtInDisplayId,
136                SurfaceControl.PhysicalDisplayInfo phys) {
137            super(LocalDisplayAdapter.this, displayToken);
138            mBuiltInDisplayId = builtInDisplayId;
139            mPhys = new SurfaceControl.PhysicalDisplayInfo(phys);
140        }
141
142        public boolean updatePhysicalDisplayInfoLocked(SurfaceControl.PhysicalDisplayInfo phys) {
143            if (!mPhys.equals(phys)) {
144                mPhys.copyFrom(phys);
145                mHavePendingChanges = true;
146                return true;
147            }
148            return false;
149        }
150
151        @Override
152        public void applyPendingDisplayDeviceInfoChangesLocked() {
153            if (mHavePendingChanges) {
154                mInfo = null;
155                mHavePendingChanges = false;
156            }
157        }
158
159        @Override
160        public DisplayDeviceInfo getDisplayDeviceInfoLocked() {
161            if (mInfo == null) {
162                mInfo = new DisplayDeviceInfo();
163                mInfo.width = mPhys.width;
164                mInfo.height = mPhys.height;
165                mInfo.refreshRate = mPhys.refreshRate;
166                mInfo.appVsyncOffsetNanos = mPhys.appVsyncOffsetNanos;
167                mInfo.presentationDeadlineNanos = mPhys.presentationDeadlineNanos;
168                mInfo.state = mState;
169
170                // Assume that all built-in displays that have secure output (eg. HDCP) also
171                // support compositing from gralloc protected buffers.
172                if (mPhys.secure) {
173                    mInfo.flags = DisplayDeviceInfo.FLAG_SECURE
174                            | DisplayDeviceInfo.FLAG_SUPPORTS_PROTECTED_BUFFERS;
175                }
176
177                if (mBuiltInDisplayId == SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN) {
178                    mInfo.name = getContext().getResources().getString(
179                            com.android.internal.R.string.display_manager_built_in_display_name);
180                    mInfo.flags |= DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY
181                            | DisplayDeviceInfo.FLAG_ROTATES_WITH_CONTENT;
182                    mInfo.type = Display.TYPE_BUILT_IN;
183                    mInfo.densityDpi = (int)(mPhys.density * 160 + 0.5f);
184                    mInfo.xDpi = mPhys.xDpi;
185                    mInfo.yDpi = mPhys.yDpi;
186                    mInfo.touch = DisplayDeviceInfo.TOUCH_INTERNAL;
187                } else {
188                    mInfo.type = Display.TYPE_HDMI;
189                    mInfo.flags |= DisplayDeviceInfo.FLAG_PRESENTATION;
190                    mInfo.name = getContext().getResources().getString(
191                            com.android.internal.R.string.display_manager_hdmi_display_name);
192                    mInfo.touch = DisplayDeviceInfo.TOUCH_EXTERNAL;
193                    mInfo.setAssumedDensityForExternalDisplay(mPhys.width, mPhys.height);
194
195                    // For demonstration purposes, allow rotation of the external display.
196                    // In the future we might allow the user to configure this directly.
197                    if ("portrait".equals(SystemProperties.get("persist.demo.hdmirotation"))) {
198                        mInfo.rotation = Surface.ROTATION_270;
199                    }
200
201                    // For demonstration purposes, allow rotation of the external display
202                    // to follow the built-in display.
203                    if (SystemProperties.getBoolean("persist.demo.hdmirotates", false)) {
204                        mInfo.flags |= DisplayDeviceInfo.FLAG_ROTATES_WITH_CONTENT;
205                    }
206                }
207            }
208            return mInfo;
209        }
210
211        @Override
212        public void requestDisplayStateLocked(int state) {
213            if (mState != state) {
214                SurfaceControl.setDisplayPowerMode(getDisplayTokenLocked(),
215                        getPowerModeForState(state));
216                mState = state;
217                updateDeviceInfoLocked();
218            }
219        }
220
221        @Override
222        public void dumpLocked(PrintWriter pw) {
223            super.dumpLocked(pw);
224            pw.println("mBuiltInDisplayId=" + mBuiltInDisplayId);
225            pw.println("mPhys=" + mPhys);
226            pw.println("mState=" + Display.stateToString(mState));
227        }
228
229        private void updateDeviceInfoLocked() {
230            mInfo = null;
231            sendDisplayDeviceEventLocked(this, DISPLAY_DEVICE_EVENT_CHANGED);
232        }
233    }
234
235    private final class HotplugDisplayEventReceiver extends DisplayEventReceiver {
236        public HotplugDisplayEventReceiver(Looper looper) {
237            super(looper);
238        }
239
240        @Override
241        public void onHotplug(long timestampNanos, int builtInDisplayId, boolean connected) {
242            synchronized (getSyncRoot()) {
243                if (connected) {
244                    tryConnectDisplayLocked(builtInDisplayId);
245                } else {
246                    tryDisconnectDisplayLocked(builtInDisplayId);
247                }
248            }
249        }
250    }
251}
252