| /* |
| * Copyright (C) 2012 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.server.display; |
| |
| import com.android.internal.util.IndentingPrintWriter; |
| |
| import android.Manifest; |
| import android.content.Context; |
| import android.content.pm.PackageManager; |
| import android.hardware.display.DisplayManagerGlobal; |
| import android.hardware.display.IDisplayManager; |
| import android.hardware.display.IDisplayManagerCallback; |
| import android.os.Binder; |
| import android.os.Handler; |
| import android.os.IBinder; |
| import android.os.Looper; |
| import android.os.Message; |
| import android.os.RemoteException; |
| import android.os.SystemClock; |
| import android.os.SystemProperties; |
| import android.util.Slog; |
| import android.util.SparseArray; |
| import android.view.Display; |
| import android.view.DisplayInfo; |
| import android.view.Surface; |
| |
| import java.io.FileDescriptor; |
| import java.io.PrintWriter; |
| import java.io.StringWriter; |
| import java.util.ArrayList; |
| |
| /** |
| * Manages attached displays. |
| * <p> |
| * The {@link DisplayManagerService} manages the global lifecycle of displays, |
| * decides how to configure logical displays based on the physical display devices currently |
| * attached, sends notifications to the system and to applications when the state |
| * changes, and so on. |
| * </p><p> |
| * The display manager service relies on a collection of {@link DisplayAdapter} components, |
| * for discovering and configuring physical display devices attached to the system. |
| * There are separate display adapters for each manner that devices are attached: |
| * one display adapter for built-in local displays, one for simulated non-functional |
| * displays when the system is headless, one for simulated overlay displays used for |
| * development, one for wifi displays, etc. |
| * </p><p> |
| * Display adapters are only weakly coupled to the display manager service. |
| * Display adapters communicate changes in display device state to the display manager |
| * service asynchronously via a {@link DisplayAdapter.DisplayAdapterListener} registered |
| * by the display manager service. This separation of concerns is important for |
| * two main reasons. First, it neatly encapsulates the responsibilities of these |
| * two classes: display adapters handle individual display devices whereas |
| * the display manager service handles the global state. Second, it eliminates |
| * the potential for deadlocks resulting from asynchronous display device discovery. |
| * </p><p> |
| * To keep things simple, display adapters and display devices are single-threaded |
| * and are only accessed on the display manager's handler thread. Of course, the |
| * display manager must be accessible by multiple thread (especially for |
| * incoming binder calls) so all of the display manager's state is synchronized |
| * and guarded by a lock. |
| * </p> |
| */ |
| public final class DisplayManagerService extends IDisplayManager.Stub { |
| private static final String TAG = "DisplayManagerService"; |
| private static final boolean DEBUG = false; |
| |
| private static final String SYSTEM_HEADLESS = "ro.config.headless"; |
| private static final long WAIT_FOR_DEFAULT_DISPLAY_TIMEOUT = 10000; |
| |
| private static final int MSG_REGISTER_DEFAULT_DISPLAY_ADAPTER = 1; |
| private static final int MSG_REGISTER_ADDITIONAL_DISPLAY_ADAPTERS = 2; |
| private static final int MSG_DELIVER_DISPLAY_EVENT = 3; |
| |
| private final Object mLock = new Object(); |
| |
| private final Context mContext; |
| private final boolean mHeadless; |
| |
| private final DisplayManagerHandler mHandler; |
| private final DisplayAdapterListener mDisplayAdapterListener = new DisplayAdapterListener(); |
| private final SparseArray<CallbackRecord> mCallbacks = |
| new SparseArray<CallbackRecord>(); |
| |
| // List of all currently registered display adapters. |
| private final ArrayList<DisplayAdapter> mDisplayAdapters = new ArrayList<DisplayAdapter>(); |
| |
| // List of all currently connected display devices. |
| private final ArrayList<DisplayDevice> mDisplayDevices = new ArrayList<DisplayDevice>(); |
| |
| // List of all logical displays, indexed by logical display id. |
| private final SparseArray<LogicalDisplay> mLogicalDisplays = new SparseArray<LogicalDisplay>(); |
| private int mNextNonDefaultDisplayId = Display.DEFAULT_DISPLAY + 1; |
| |
| // True if in safe mode. |
| // This option may disable certain display adapters. |
| private boolean mSafeMode; |
| |
| // True if we are in a special boot mode where only core applications and |
| // services should be started. This option may disable certain display adapters. |
| private boolean mOnlyCore; |
| |
| // Temporary callback list, used when sending display events to applications. |
| private ArrayList<CallbackRecord> mTempCallbacks = new ArrayList<CallbackRecord>(); |
| |
| public DisplayManagerService(Context context, Handler uiHandler) { |
| mContext = context; |
| mHeadless = SystemProperties.get(SYSTEM_HEADLESS).equals("1"); |
| |
| mHandler = new DisplayManagerHandler(uiHandler.getLooper()); |
| mHandler.sendEmptyMessage(MSG_REGISTER_DEFAULT_DISPLAY_ADAPTER); |
| } |
| |
| /** |
| * Pauses the boot process to wait for the first display to be initialized. |
| */ |
| public boolean waitForDefaultDisplay() { |
| synchronized (mLock) { |
| long timeout = SystemClock.uptimeMillis() + WAIT_FOR_DEFAULT_DISPLAY_TIMEOUT; |
| while (mLogicalDisplays.get(Display.DEFAULT_DISPLAY) == null) { |
| long delay = timeout - SystemClock.uptimeMillis(); |
| if (delay <= 0) { |
| return false; |
| } |
| if (DEBUG) { |
| Slog.d(TAG, "waitForDefaultDisplay: waiting, timeout=" + delay); |
| } |
| try { |
| mLock.wait(delay); |
| } catch (InterruptedException ex) { |
| } |
| } |
| } |
| return true; |
| } |
| |
| /** |
| * Called when the system is ready to go. |
| */ |
| public void systemReady(boolean safeMode, boolean onlyCore) { |
| synchronized (mLock) { |
| mSafeMode = safeMode; |
| mOnlyCore = onlyCore; |
| } |
| mHandler.sendEmptyMessage(MSG_REGISTER_ADDITIONAL_DISPLAY_ADAPTERS); |
| } |
| |
| // Runs on handler. |
| private void registerDefaultDisplayAdapter() { |
| // Register default display adapter. |
| if (mHeadless) { |
| registerDisplayAdapter(new HeadlessDisplayAdapter(mContext)); |
| } else { |
| registerDisplayAdapter(new LocalDisplayAdapter(mContext)); |
| } |
| } |
| |
| // Runs on handler. |
| private void registerAdditionalDisplayAdapters() { |
| if (shouldRegisterNonEssentialDisplayAdapters()) { |
| registerDisplayAdapter(new OverlayDisplayAdapter(mContext)); |
| } |
| } |
| |
| private boolean shouldRegisterNonEssentialDisplayAdapters() { |
| // In safe mode, we disable non-essential display adapters to give the user |
| // an opportunity to fix broken settings or other problems that might affect |
| // system stability. |
| // In only-core mode, we disable non-essential display adapters to minimize |
| // the number of dependencies that are started while in this mode and to |
| // prevent problems that might occur due to the device being encrypted. |
| synchronized (mLock) { |
| return !mSafeMode && !mOnlyCore; |
| } |
| } |
| |
| // Runs on handler. |
| private void registerDisplayAdapter(DisplayAdapter adapter) { |
| synchronized (mLock) { |
| mDisplayAdapters.add(adapter); |
| } |
| |
| adapter.register(mDisplayAdapterListener); |
| } |
| |
| // FIXME: this isn't the right API for the long term |
| public void getDefaultExternalDisplayDeviceInfo(DisplayDeviceInfo info) { |
| // hardcoded assuming 720p touch screen plugged into HDMI and USB |
| // need to redesign this |
| info.width = 1280; |
| info.height = 720; |
| } |
| |
| /** |
| * Returns true if the device is headless. |
| * |
| * @return True if the device is headless. |
| */ |
| public boolean isHeadless() { |
| return mHeadless; |
| } |
| |
| /** |
| * Sets the new logical display orientation. |
| * |
| * @param displayId The logical display id. |
| * @param orientation One of the Surface.ROTATION_* constants. |
| */ |
| public void setDisplayOrientation(int displayId, int orientation) { |
| synchronized (mLock) { |
| // TODO: update mirror transforms |
| LogicalDisplay display = mLogicalDisplays.get(displayId); |
| if (display != null && display.mPrimaryDisplayDevice != null) { |
| IBinder displayToken = display.mPrimaryDisplayDevice.getDisplayToken(); |
| if (displayToken != null) { |
| Surface.openTransaction(); |
| try { |
| Surface.setDisplayOrientation(displayToken, orientation); |
| } finally { |
| Surface.closeTransaction(); |
| } |
| } |
| |
| display.mBaseDisplayInfo.rotation = orientation; |
| sendDisplayEventLocked(displayId, DisplayManagerGlobal.EVENT_DISPLAY_CHANGED); |
| } |
| } |
| } |
| |
| /** |
| * Overrides the display information of a particular logical display. |
| * This is used by the window manager to control the size and characteristics |
| * of the default display. |
| * |
| * @param displayId The logical display id. |
| * @param info The new data to be stored. |
| */ |
| public void setDisplayInfoOverrideFromWindowManager(int displayId, DisplayInfo info) { |
| synchronized (mLock) { |
| LogicalDisplay display = mLogicalDisplays.get(displayId); |
| if (display != null) { |
| if (info != null) { |
| if (display.mOverrideDisplayInfo == null) { |
| display.mOverrideDisplayInfo = new DisplayInfo(); |
| } |
| display.mOverrideDisplayInfo.copyFrom(info); |
| } else { |
| display.mOverrideDisplayInfo = null; |
| } |
| |
| sendDisplayEventLocked(displayId, DisplayManagerGlobal.EVENT_DISPLAY_CHANGED); |
| } |
| } |
| } |
| |
| /** |
| * Returns information about the specified logical display. |
| * |
| * @param displayId The logical display id. |
| * @param The logical display info, or null if the display does not exist. |
| */ |
| @Override // Binder call |
| public DisplayInfo getDisplayInfo(int displayId) { |
| synchronized (mLock) { |
| LogicalDisplay display = mLogicalDisplays.get(displayId); |
| if (display != null) { |
| if (display.mOverrideDisplayInfo != null) { |
| return new DisplayInfo(display.mOverrideDisplayInfo); |
| } |
| return new DisplayInfo(display.mBaseDisplayInfo); |
| } |
| return null; |
| } |
| } |
| |
| @Override // Binder call |
| public int[] getDisplayIds() { |
| synchronized (mLock) { |
| final int count = mLogicalDisplays.size(); |
| int[] displayIds = new int[count]; |
| for (int i = 0; i > count; i++) { |
| displayIds[i] = mLogicalDisplays.keyAt(i); |
| } |
| return displayIds; |
| } |
| } |
| |
| @Override // Binder call |
| public void registerCallback(IDisplayManagerCallback callback) { |
| if (callback == null) { |
| throw new IllegalArgumentException("listener must not be null"); |
| } |
| |
| synchronized (mLock) { |
| int callingPid = Binder.getCallingPid(); |
| if (mCallbacks.get(callingPid) != null) { |
| throw new SecurityException("The calling process has already " |
| + "registered an IDisplayManagerCallback."); |
| } |
| |
| CallbackRecord record = new CallbackRecord(callingPid, callback); |
| try { |
| IBinder binder = callback.asBinder(); |
| binder.linkToDeath(record, 0); |
| } catch (RemoteException ex) { |
| // give up |
| throw new RuntimeException(ex); |
| } |
| |
| mCallbacks.put(callingPid, record); |
| } |
| } |
| |
| private void onCallbackDied(int pid) { |
| synchronized (mLock) { |
| mCallbacks.remove(pid); |
| } |
| } |
| |
| // Runs on handler. |
| private void handleDisplayDeviceAdded(DisplayDevice device) { |
| synchronized (mLock) { |
| if (mDisplayDevices.contains(device)) { |
| Slog.w(TAG, "Attempted to add already added display device: " + device); |
| return; |
| } |
| |
| mDisplayDevices.add(device); |
| |
| LogicalDisplay display = new LogicalDisplay(device); |
| display.updateFromPrimaryDisplayDevice(); |
| |
| boolean isDefault = (display.mPrimaryDisplayDeviceInfo.flags |
| & DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY) != 0; |
| if (isDefault && mLogicalDisplays.get(Display.DEFAULT_DISPLAY) != null) { |
| Slog.w(TAG, "Attempted to add a second default device: " + device); |
| isDefault = false; |
| } |
| |
| int displayId = isDefault ? Display.DEFAULT_DISPLAY : mNextNonDefaultDisplayId++; |
| mLogicalDisplays.put(displayId, display); |
| |
| sendDisplayEventLocked(displayId, DisplayManagerGlobal.EVENT_DISPLAY_ADDED); |
| |
| // Wake up waitForDefaultDisplay. |
| if (isDefault) { |
| mLock.notifyAll(); |
| } |
| } |
| } |
| |
| // Runs on handler. |
| private void handleDisplayDeviceChanged(DisplayDevice device) { |
| synchronized (mLock) { |
| if (!mDisplayDevices.contains(device)) { |
| Slog.w(TAG, "Attempted to change non-existent display device: " + device); |
| return; |
| } |
| |
| for (int i = mLogicalDisplays.size(); i-- > 0; ) { |
| LogicalDisplay display = mLogicalDisplays.valueAt(i); |
| if (display.mPrimaryDisplayDevice == device) { |
| final int displayId = mLogicalDisplays.keyAt(i); |
| display.updateFromPrimaryDisplayDevice(); |
| sendDisplayEventLocked(displayId, DisplayManagerGlobal.EVENT_DISPLAY_CHANGED); |
| } |
| } |
| } |
| } |
| |
| // Runs on handler. |
| private void handleDisplayDeviceRemoved(DisplayDevice device) { |
| synchronized (mLock) { |
| if (!mDisplayDevices.remove(device)) { |
| Slog.w(TAG, "Attempted to remove non-existent display device: " + device); |
| return; |
| } |
| |
| for (int i = mLogicalDisplays.size(); i-- > 0; ) { |
| LogicalDisplay display = mLogicalDisplays.valueAt(i); |
| if (display.mPrimaryDisplayDevice == device) { |
| final int displayId = mLogicalDisplays.keyAt(i); |
| mLogicalDisplays.removeAt(i); |
| sendDisplayEventLocked(displayId, DisplayManagerGlobal.EVENT_DISPLAY_REMOVED); |
| } |
| } |
| } |
| } |
| |
| // Posts a message to send a display event at the next opportunity. |
| private void sendDisplayEventLocked(int displayId, int event) { |
| Message msg = mHandler.obtainMessage(MSG_DELIVER_DISPLAY_EVENT, displayId, event); |
| mHandler.sendMessage(msg); |
| } |
| |
| // Runs on handler. |
| // This method actually sends display event notifications. |
| // Note that it must be very careful not to be holding the lock while sending |
| // is in progress. |
| private void deliverDisplayEvent(int displayId, int event) { |
| if (DEBUG) { |
| Slog.d(TAG, "Delivering display event: displayId=" + displayId + ", event=" + event); |
| } |
| |
| final int count; |
| synchronized (mLock) { |
| count = mCallbacks.size(); |
| mTempCallbacks.clear(); |
| for (int i = 0; i < count; i++) { |
| mTempCallbacks.add(mCallbacks.valueAt(i)); |
| } |
| } |
| |
| for (int i = 0; i < count; i++) { |
| mTempCallbacks.get(i).notifyDisplayEventAsync(displayId, event); |
| } |
| mTempCallbacks.clear(); |
| } |
| |
| @Override // Binder call |
| public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) { |
| if (mContext == null |
| || mContext.checkCallingOrSelfPermission(Manifest.permission.DUMP) |
| != PackageManager.PERMISSION_GRANTED) { |
| pw.println("Permission Denial: can't dump DisplayManager from from pid=" |
| + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()); |
| return; |
| } |
| |
| pw.println("DISPLAY MANAGER (dumpsys display)"); |
| pw.println(" mHeadless=" + mHeadless); |
| |
| mHandler.runWithScissors(new Runnable() { |
| @Override |
| public void run() { |
| dumpLocal(pw); |
| } |
| }); |
| } |
| |
| // Runs on handler. |
| private void dumpLocal(PrintWriter pw) { |
| synchronized (mLock) { |
| IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); |
| |
| pw.println(); |
| pw.println("Display Adapters: size=" + mDisplayAdapters.size()); |
| for (DisplayAdapter adapter : mDisplayAdapters) { |
| pw.println(" " + adapter.getName()); |
| adapter.dump(ipw); |
| } |
| |
| pw.println(); |
| pw.println("Display Devices: size=" + mDisplayDevices.size()); |
| for (DisplayDevice device : mDisplayDevices) { |
| pw.println(" " + device); |
| } |
| |
| final int logicalDisplayCount = mLogicalDisplays.size(); |
| pw.println(); |
| pw.println("Logical Displays: size=" + logicalDisplayCount); |
| for (int i = 0; i < logicalDisplayCount; i++) { |
| int displayId = mLogicalDisplays.keyAt(i); |
| LogicalDisplay display = mLogicalDisplays.valueAt(i); |
| pw.println(" Display " + displayId + ":"); |
| pw.println(" mPrimaryDisplayDevice=" + display.mPrimaryDisplayDevice); |
| pw.println(" mBaseDisplayInfo=" + display.mBaseDisplayInfo); |
| pw.println(" mOverrideDisplayInfo=" |
| + display.mOverrideDisplayInfo); |
| } |
| } |
| } |
| |
| private final class DisplayManagerHandler extends Handler { |
| public DisplayManagerHandler(Looper looper) { |
| super(looper, null, true /*async*/); |
| } |
| |
| @Override |
| public void handleMessage(Message msg) { |
| switch (msg.what) { |
| case MSG_REGISTER_DEFAULT_DISPLAY_ADAPTER: |
| registerDefaultDisplayAdapter(); |
| break; |
| |
| case MSG_REGISTER_ADDITIONAL_DISPLAY_ADAPTERS: |
| registerAdditionalDisplayAdapters(); |
| break; |
| |
| case MSG_DELIVER_DISPLAY_EVENT: |
| deliverDisplayEvent(msg.arg1, msg.arg2); |
| break; |
| } |
| } |
| } |
| |
| private final class DisplayAdapterListener implements DisplayAdapter.Listener { |
| @Override |
| public void onDisplayDeviceEvent(DisplayDevice device, int event) { |
| switch (event) { |
| case DisplayAdapter.DISPLAY_DEVICE_EVENT_ADDED: |
| handleDisplayDeviceAdded(device); |
| break; |
| |
| case DisplayAdapter.DISPLAY_DEVICE_EVENT_CHANGED: |
| handleDisplayDeviceChanged(device); |
| break; |
| |
| case DisplayAdapter.DISPLAY_DEVICE_EVENT_REMOVED: |
| handleDisplayDeviceRemoved(device); |
| break; |
| } |
| } |
| } |
| |
| private final class CallbackRecord implements DeathRecipient { |
| private final int mPid; |
| private final IDisplayManagerCallback mCallback; |
| |
| public CallbackRecord(int pid, IDisplayManagerCallback callback) { |
| mPid = pid; |
| mCallback = callback; |
| } |
| |
| @Override |
| public void binderDied() { |
| if (DEBUG) { |
| Slog.d(TAG, "Display listener for pid " + mPid + " died."); |
| } |
| onCallbackDied(mPid); |
| } |
| |
| public void notifyDisplayEventAsync(int displayId, int event) { |
| try { |
| mCallback.onDisplayEvent(displayId, event); |
| } catch (RemoteException ex) { |
| Slog.w(TAG, "Failed to notify process " |
| + mPid + " that displays changed, assuming it died.", ex); |
| binderDied(); |
| } |
| } |
| } |
| |
| /** |
| * Each logical display is primarily associated with one display device. |
| * The primary display device is nominally responsible for the basic properties |
| * of the logical display such as its size, refresh rate, and dpi. |
| * |
| * A logical display may be mirrored onto other display devices besides its |
| * primary display device, but it always remains bound to its primary. |
| * Note that the contents of a logical display may not always be visible, even |
| * on its primary display device, such as in the case where the logical display's |
| * primary display device is currently mirroring content from a different logical display. |
| */ |
| private final static class LogicalDisplay { |
| public final DisplayInfo mBaseDisplayInfo = new DisplayInfo(); |
| public DisplayInfo mOverrideDisplayInfo; // set by the window manager |
| |
| public final DisplayDevice mPrimaryDisplayDevice; |
| public final DisplayDeviceInfo mPrimaryDisplayDeviceInfo = new DisplayDeviceInfo(); |
| |
| public LogicalDisplay(DisplayDevice primaryDisplayDevice) { |
| mPrimaryDisplayDevice = primaryDisplayDevice; |
| } |
| |
| public void updateFromPrimaryDisplayDevice() { |
| // Bootstrap the logical display using its associated primary physical display. |
| mPrimaryDisplayDevice.getInfo(mPrimaryDisplayDeviceInfo); |
| |
| mBaseDisplayInfo.appWidth = mPrimaryDisplayDeviceInfo.width; |
| mBaseDisplayInfo.appHeight = mPrimaryDisplayDeviceInfo.height; |
| mBaseDisplayInfo.logicalWidth = mPrimaryDisplayDeviceInfo.width; |
| mBaseDisplayInfo.logicalHeight = mPrimaryDisplayDeviceInfo.height; |
| mBaseDisplayInfo.rotation = Surface.ROTATION_0; |
| mBaseDisplayInfo.refreshRate = mPrimaryDisplayDeviceInfo.refreshRate; |
| mBaseDisplayInfo.logicalDensityDpi = mPrimaryDisplayDeviceInfo.densityDpi; |
| mBaseDisplayInfo.physicalXDpi = mPrimaryDisplayDeviceInfo.xDpi; |
| mBaseDisplayInfo.physicalYDpi = mPrimaryDisplayDeviceInfo.yDpi; |
| mBaseDisplayInfo.smallestNominalAppWidth = mPrimaryDisplayDeviceInfo.width; |
| mBaseDisplayInfo.smallestNominalAppHeight = mPrimaryDisplayDeviceInfo.height; |
| mBaseDisplayInfo.largestNominalAppWidth = mPrimaryDisplayDeviceInfo.width; |
| mBaseDisplayInfo.largestNominalAppHeight = mPrimaryDisplayDeviceInfo.height; |
| } |
| } |
| } |