LocalDisplayAdapter.java revision 66692500344cab2f53cdb6ee1545c567fff7cb16
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.SparseArray;
25import android.view.DisplayEventReceiver;
26import android.view.Surface;
27import android.view.Surface.PhysicalDisplayInfo;
28
29import java.io.PrintWriter;
30
31/**
32 * A display adapter for the local displays managed by Surface Flinger.
33 * <p>
34 * Display adapters are guarded by the {@link DisplayManagerService.SyncRoot} lock.
35 * </p>
36 */
37final class LocalDisplayAdapter extends DisplayAdapter {
38    private static final String TAG = "LocalDisplayAdapter";
39
40    private static final int[] BUILT_IN_DISPLAY_IDS_TO_SCAN = new int[] {
41            Surface.BUILT_IN_DISPLAY_ID_MAIN,
42            Surface.BUILT_IN_DISPLAY_ID_HDMI,
43    };
44
45    private final SparseArray<LocalDisplayDevice> mDevices =
46            new SparseArray<LocalDisplayDevice>();
47    private HotplugDisplayEventReceiver mHotplugReceiver;
48
49    private final PhysicalDisplayInfo mTempPhys = new PhysicalDisplayInfo();
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        scanDisplaysLocked();
63    }
64
65    private void scanDisplaysLocked() {
66        for (int builtInDisplayId : BUILT_IN_DISPLAY_IDS_TO_SCAN) {
67            IBinder displayToken = Surface.getBuiltInDisplay(builtInDisplayId);
68            if (displayToken != null && Surface.getDisplayInfo(displayToken, mTempPhys)) {
69                LocalDisplayDevice device = mDevices.get(builtInDisplayId);
70                if (device == null) {
71                    // Display was added.
72                    device = new LocalDisplayDevice(displayToken, builtInDisplayId, mTempPhys);
73                    mDevices.put(builtInDisplayId, device);
74                    sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_ADDED);
75                } else if (device.updatePhysicalDisplayInfoLocked(mTempPhys)) {
76                    // Display properties changed.
77                    sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_CHANGED);
78                }
79            } else {
80                LocalDisplayDevice device = mDevices.get(builtInDisplayId);
81                if (device != null) {
82                    // Display was removed.
83                    mDevices.remove(builtInDisplayId);
84                    sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_REMOVED);
85                }
86            }
87        }
88    }
89
90    private final class LocalDisplayDevice extends DisplayDevice {
91        private final int mBuiltInDisplayId;
92        private final PhysicalDisplayInfo mPhys;
93
94        private DisplayDeviceInfo mInfo;
95        private boolean mHavePendingChanges;
96        private boolean mBlanked;
97
98        public LocalDisplayDevice(IBinder displayToken, int builtInDisplayId,
99                PhysicalDisplayInfo phys) {
100            super(LocalDisplayAdapter.this, displayToken);
101            mBuiltInDisplayId = builtInDisplayId;
102            mPhys = new PhysicalDisplayInfo(phys);
103        }
104
105        public boolean updatePhysicalDisplayInfoLocked(PhysicalDisplayInfo phys) {
106            if (!mPhys.equals(phys)) {
107                mPhys.copyFrom(phys);
108                mHavePendingChanges = true;
109                return true;
110            }
111            return false;
112        }
113
114        @Override
115        public void applyPendingDisplayDeviceInfoChangesLocked() {
116            if (mHavePendingChanges) {
117                mInfo = null;
118                mHavePendingChanges = false;
119            }
120        }
121
122        @Override
123        public DisplayDeviceInfo getDisplayDeviceInfoLocked() {
124            if (mInfo == null) {
125                mInfo = new DisplayDeviceInfo();
126                mInfo.width = mPhys.width;
127                mInfo.height = mPhys.height;
128                mInfo.refreshRate = mPhys.refreshRate;
129
130                // Assume that all built-in displays have secure output (eg. HDCP) and
131                // support compositing from gralloc protected buffers.
132                mInfo.flags = DisplayDeviceInfo.FLAG_SECURE
133                        | DisplayDeviceInfo.FLAG_SUPPORTS_PROTECTED_BUFFERS;
134
135                if (mBuiltInDisplayId == Surface.BUILT_IN_DISPLAY_ID_MAIN) {
136                    mInfo.name = getContext().getResources().getString(
137                            com.android.internal.R.string.display_manager_built_in_display_name);
138                    mInfo.flags |= DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY
139                            | DisplayDeviceInfo.FLAG_ROTATES_WITH_CONTENT;
140                    mInfo.densityDpi = (int)(mPhys.density * 160 + 0.5f);
141                    mInfo.xDpi = mPhys.xDpi;
142                    mInfo.yDpi = mPhys.yDpi;
143                    mInfo.touch = DisplayDeviceInfo.TOUCH_INTERNAL;
144                } else {
145                    mInfo.name = getContext().getResources().getString(
146                            com.android.internal.R.string.display_manager_hdmi_display_name);
147                    mInfo.touch = DisplayDeviceInfo.TOUCH_EXTERNAL;
148                    mInfo.setAssumedDensityForExternalDisplay(mPhys.width, mPhys.height);
149
150                    // For demonstration purposes, allow rotation of the external display.
151                    // In the future we might allow the user to configure this directly.
152                    if ("portrait".equals(SystemProperties.get("persist.demo.hdmirotation"))) {
153                        mInfo.rotation = Surface.ROTATION_270;
154                    }
155                }
156            }
157            return mInfo;
158        }
159
160        @Override
161        public void blankLocked() {
162            mBlanked = true;
163            Surface.blankDisplay(getDisplayTokenLocked());
164        }
165
166        @Override
167        public void unblankLocked() {
168            mBlanked = false;
169            Surface.unblankDisplay(getDisplayTokenLocked());
170        }
171
172        @Override
173        public void dumpLocked(PrintWriter pw) {
174            super.dumpLocked(pw);
175            pw.println("mBuiltInDisplayId=" + mBuiltInDisplayId);
176            pw.println("mPhys=" + mPhys);
177            pw.println("mBlanked=" + mBlanked);
178        }
179    }
180
181    private final class HotplugDisplayEventReceiver extends DisplayEventReceiver {
182        public HotplugDisplayEventReceiver(Looper looper) {
183            super(looper);
184        }
185
186        @Override
187        public void onHotplug(long timestampNanos, int builtInDisplayId, boolean connected) {
188            synchronized (getSyncRoot()) {
189                scanDisplaysLocked();
190            }
191        }
192    }
193}