DisplayManagerService.java revision 4f67ba6ba4e861b287a3ff0323c107aa77f66264
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.Manifest;
20import android.content.Context;
21import android.content.pm.PackageManager;
22import android.hardware.display.IDisplayManager;
23import android.os.Binder;
24import android.os.SystemProperties;
25import android.util.Slog;
26import android.util.SparseArray;
27import android.view.Display;
28import android.view.DisplayInfo;
29import android.view.Surface;
30
31import java.io.FileDescriptor;
32import java.io.PrintWriter;
33import java.util.ArrayList;
34
35/**
36 * Manages the properties, media routing and power state of attached displays.
37 * <p>
38 * The display manager service does not own or directly control the displays.
39 * Instead, other components in the system register their display adapters with the
40 * display manager service which acts as a central controller.
41 * </p>
42 */
43public final class DisplayManagerService extends IDisplayManager.Stub {
44    private static final String TAG = "DisplayManagerService";
45
46    private static final String SYSTEM_HEADLESS = "ro.config.headless";
47
48    private final Object mLock = new Object();
49
50    private Context mContext;
51    private final boolean mHeadless;
52
53    private int mDisplayIdSeq = Display.DEFAULT_DISPLAY;
54
55    /** All registered DisplayAdapters. */
56    private final ArrayList<DisplayAdapter> mDisplayAdapters = new ArrayList<DisplayAdapter>();
57
58    /** All the DisplayAdapters showing the given displayId. */
59    private final SparseArray<ArrayList<DisplayAdapter>> mLogicalToPhysicals =
60            new SparseArray<ArrayList<DisplayAdapter>>();
61
62    /** All the DisplayInfos in the system indexed by deviceId */
63    private final SparseArray<DisplayInfo> mDisplayInfos = new SparseArray<DisplayInfo>();
64
65    public DisplayManagerService() {
66        mHeadless = SystemProperties.get(SYSTEM_HEADLESS).equals("1");
67        registerDefaultDisplayAdapter();
68    }
69
70    private void registerDefaultDisplayAdapter() {
71        if (mHeadless) {
72            registerDisplayAdapter(new HeadlessDisplayAdapter());
73        } else {
74            registerDisplayAdapter(new SurfaceFlingerDisplayAdapter());
75        }
76    }
77
78    public void setContext(Context context) {
79        mContext = context;
80    }
81
82    // FIXME: this isn't the right API for the long term
83    public void getDefaultExternalDisplayDeviceInfo(DisplayDeviceInfo info) {
84        // hardcoded assuming 720p touch screen plugged into HDMI and USB
85        // need to redesign this
86        info.width = 1280;
87        info.height = 720;
88    }
89
90    public boolean isHeadless() {
91        return mHeadless;
92    }
93
94    /**
95     * Save away new DisplayInfo data.
96     * @param displayId The local DisplayInfo to store the new data in.
97     * @param info The new data to be stored.
98     */
99    public void setDisplayInfo(int displayId, DisplayInfo info) {
100        synchronized (mLock) {
101            DisplayInfo localInfo = mDisplayInfos.get(displayId);
102            if (localInfo == null) {
103                localInfo = new DisplayInfo();
104                mDisplayInfos.put(displayId, localInfo);
105            }
106            localInfo.copyFrom(info);
107        }
108    }
109
110    /**
111     * Return requested DisplayInfo.
112     * @param displayId The data to retrieve.
113     * @param outInfo The structure to receive the data.
114     */
115    @Override // Binder call
116    public boolean getDisplayInfo(int displayId, DisplayInfo outInfo) {
117        synchronized (mLock) {
118            DisplayInfo localInfo = mDisplayInfos.get(displayId);
119            if (localInfo == null) {
120                return false;
121            }
122            outInfo.copyFrom(localInfo);
123            return true;
124        }
125    }
126
127    /**
128     * Inform the service of a new physical display. A new logical displayId is created and the new
129     * physical display is immediately bound to it. Use removeAdapterFromDisplay to disconnect it.
130     *
131     * @param adapter The wrapper for information associated with the physical display.
132     */
133    public void registerDisplayAdapter(DisplayAdapter adapter) {
134        synchronized (mLock) {
135            int displayId = mDisplayIdSeq++;
136            adapter.setDisplayId(displayId);
137
138            createDisplayInfoLocked(displayId, adapter);
139
140            ArrayList<DisplayAdapter> list = new ArrayList<DisplayAdapter>();
141            list.add(adapter);
142            mLogicalToPhysicals.put(displayId, list);
143
144            mDisplayAdapters.add(adapter);
145        }
146
147        // TODO: Notify SurfaceFlinger of new addition.
148    }
149
150    /**
151     * Connect a logical display to a physical display. Will remove the physical display from any
152     * logical display it is currently attached to.
153     *
154     * @param displayId The logical display. Will be created if it does not already exist.
155     * @param adapter The physical display.
156     */
157    public void addAdapterToDisplay(int displayId, DisplayAdapter adapter) {
158        if (adapter == null) {
159            // TODO: Or throw NPE?
160            Slog.e(TAG, "addDeviceToDisplay: Attempt to add null adapter");
161            return;
162        }
163
164        synchronized (mLock) {
165            if (!mDisplayAdapters.contains(adapter)) {
166                // TOOD: Handle unregistered adapter with exception or return value.
167                Slog.e(TAG, "addDeviceToDisplay: Attempt to add an unregistered adapter");
168                return;
169            }
170
171            DisplayInfo displayInfo = mDisplayInfos.get(displayId);
172            if (displayInfo == null) {
173                createDisplayInfoLocked(displayId, adapter);
174            }
175
176            Integer oldDisplayId = adapter.getDisplayId();
177            if (oldDisplayId != Display.NO_DISPLAY) {
178                if (oldDisplayId == displayId) {
179                    // adapter already added to displayId.
180                    return;
181                }
182
183                removeAdapterLocked(adapter);
184            }
185
186            ArrayList<DisplayAdapter> list = mLogicalToPhysicals.get(displayId);
187            if (list == null) {
188                list = new ArrayList<DisplayAdapter>();
189                mLogicalToPhysicals.put(displayId, list);
190            }
191
192            list.add(adapter);
193            adapter.setDisplayId(displayId);
194        }
195
196        // TODO: Notify SurfaceFlinger of new addition.
197    }
198
199    /**
200     * Disconnect the physical display from whichever logical display it is attached to.
201     * @param adapter The physical display to detach.
202     */
203    public void removeAdapterFromDisplay(DisplayAdapter adapter) {
204        if (adapter == null) {
205            // TODO: Or throw NPE?
206            return;
207        }
208
209        synchronized (mLock) {
210            if (!mDisplayAdapters.contains(adapter)) {
211                // TOOD: Handle unregistered adapter with exception or return value.
212                Slog.e(TAG, "removeDeviceFromDisplay: Attempt to remove an unregistered adapter");
213                return;
214            }
215
216            removeAdapterLocked(adapter);
217        }
218
219        // TODO: Notify SurfaceFlinger of removal.
220    }
221
222    /**
223     * Create a new logical DisplayInfo and fill it in with information from the physical display.
224     * @param displayId The logical identifier.
225     * @param adapter The physical display for initial values.
226     */
227    private void createDisplayInfoLocked(int displayId, DisplayAdapter adapter) {
228        DisplayInfo displayInfo = new DisplayInfo();
229        DisplayDeviceInfo deviceInfo = new DisplayDeviceInfo();
230        adapter.getDisplayDevice().getInfo(deviceInfo);
231        copyDisplayInfoFromDeviceInfo(displayInfo, deviceInfo);
232        mDisplayInfos.put(displayId, displayInfo);
233    }
234
235    /**
236     * Disconnect a physical display from its logical display. If there are no more physical
237     * displays attached to the logical display, delete the logical display.
238     * @param adapter The physical display to detach.
239     */
240    void removeAdapterLocked(DisplayAdapter adapter) {
241        int displayId = adapter.getDisplayId();
242        adapter.setDisplayId(Display.NO_DISPLAY);
243
244        ArrayList<DisplayAdapter> list = mLogicalToPhysicals.get(displayId);
245        if (list != null) {
246            list.remove(adapter);
247            if (list.isEmpty()) {
248                mLogicalToPhysicals.remove(displayId);
249                // TODO: Keep count of Windows attached to logical display and don't delete if
250                // there are any outstanding. Also, what keeps the WindowManager from continuing
251                // to use the logical display?
252                mDisplayInfos.remove(displayId);
253            }
254        }
255    }
256
257    private void copyDisplayInfoFromDeviceInfo(DisplayInfo displayInfo,
258                                               DisplayDeviceInfo deviceInfo) {
259        // Bootstrap the logical display using the physical display.
260        displayInfo.appWidth = deviceInfo.width;
261        displayInfo.appHeight = deviceInfo.height;
262        displayInfo.logicalWidth = deviceInfo.width;
263        displayInfo.logicalHeight = deviceInfo.height;
264        displayInfo.rotation = Surface.ROTATION_0;
265        displayInfo.refreshRate = deviceInfo.refreshRate;
266        displayInfo.logicalDensityDpi = deviceInfo.densityDpi;
267        displayInfo.physicalXDpi = deviceInfo.xDpi;
268        displayInfo.physicalYDpi = deviceInfo.yDpi;
269        displayInfo.smallestNominalAppWidth = deviceInfo.width;
270        displayInfo.smallestNominalAppHeight = deviceInfo.height;
271        displayInfo.largestNominalAppWidth = deviceInfo.width;
272        displayInfo.largestNominalAppHeight = deviceInfo.height;
273    }
274
275    @Override // Binder call
276    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
277        if (mContext == null
278                || mContext.checkCallingOrSelfPermission(Manifest.permission.DUMP)
279                        != PackageManager.PERMISSION_GRANTED) {
280            pw.println("Permission Denial: can't dump DisplayManager from from pid="
281                    + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid());
282            return;
283        }
284
285        pw.println("DISPLAY MANAGER (dumpsys display)\n");
286
287        pw.println("Headless: " + mHeadless);
288
289        DisplayDeviceInfo info = new DisplayDeviceInfo();
290        for (DisplayAdapter adapter : mDisplayAdapters) {
291            pw.println("Display for adapter " + adapter.getName());
292            DisplayDevice device = adapter.getDisplayDevice();
293            pw.print("  ");
294            device.getInfo(info);
295            pw.println(info);
296        }
297    }
298}
299