diff options
Diffstat (limited to 'services/core/java/com/android/server/display/DisplayManagerService.java')
-rw-r--r-- | services/core/java/com/android/server/display/DisplayManagerService.java | 1312 |
1 files changed, 1312 insertions, 0 deletions
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java new file mode 100644 index 0000000..73040d5 --- /dev/null +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -0,0 +1,1312 @@ +/* + * 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.DisplayManager; +import android.hardware.display.DisplayManagerGlobal; +import android.hardware.display.IDisplayManager; +import android.hardware.display.IDisplayManagerCallback; +import android.hardware.display.WifiDisplayStatus; +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.text.TextUtils; +import android.util.Log; +import android.util.Slog; +import android.util.SparseArray; +import android.view.Display; +import android.view.DisplayInfo; +import android.view.Surface; + +import com.android.server.UiThread; + +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.concurrent.CopyOnWriteArrayList; + +/** + * 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.Listener} 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> + * + * <h3>Synchronization</h3> + * <p> + * Because the display manager may be accessed by multiple threads, the synchronization + * story gets a little complicated. In particular, the window manager may call into + * the display manager while holding a surface transaction with the expectation that + * it can apply changes immediately. Unfortunately, that means we can't just do + * everything asynchronously (*grump*). + * </p><p> + * To make this work, all of the objects that belong to the display manager must + * use the same lock. We call this lock the synchronization root and it has a unique + * type {@link DisplayManagerService.SyncRoot}. Methods that require this lock are + * named with the "Locked" suffix. + * </p><p> + * Where things get tricky is that the display manager is not allowed to make + * any potentially reentrant calls, especially into the window manager. We generally + * avoid this by making all potentially reentrant out-calls asynchronous. + * </p> + */ +public final class DisplayManagerService extends IDisplayManager.Stub { + private static final String TAG = "DisplayManagerService"; + private static final boolean DEBUG = false; + + // When this system property is set to 0, WFD is forcibly disabled on boot. + // When this system property is set to 1, WFD is forcibly enabled on boot. + // Otherwise WFD is enabled according to the value of config_enableWifiDisplay. + private static final String FORCE_WIFI_DISPLAY_ENABLE = "persist.debug.wfd.enable"; + + 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 static final int MSG_REQUEST_TRAVERSAL = 4; + private static final int MSG_UPDATE_VIEWPORT = 5; + + private static final int DISPLAY_BLANK_STATE_UNKNOWN = 0; + private static final int DISPLAY_BLANK_STATE_BLANKED = 1; + private static final int DISPLAY_BLANK_STATE_UNBLANKED = 2; + + private final Context mContext; + private final DisplayManagerHandler mHandler; + private final Handler mUiHandler; + private final DisplayAdapterListener mDisplayAdapterListener; + private WindowManagerFuncs mWindowManagerFuncs; + private InputManagerFuncs mInputManagerFuncs; + + // The synchronization root for the display manager. + // This lock guards most of the display manager's state. + // NOTE: This is synchronized on while holding WindowManagerService.mWindowMap so never call + // into WindowManagerService methods that require mWindowMap while holding this unless you are + // very very sure that no deadlock can occur. + private final SyncRoot mSyncRoot = new SyncRoot(); + + // True if in safe mode. + // This option may disable certain display adapters. + public 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. + public boolean mOnlyCore; + + // True if the display manager service should pretend there is only one display + // and only tell applications about the existence of the default logical display. + // The display manager can still mirror content to secondary displays but applications + // cannot present unique content on those displays. + // Used for demonstration purposes only. + private final boolean mSingleDisplayDemoMode; + + // All callback records indexed by calling process id. + public 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; + + // List of all display transaction listeners. + private final CopyOnWriteArrayList<DisplayTransactionListener> mDisplayTransactionListeners = + new CopyOnWriteArrayList<DisplayTransactionListener>(); + + // Set to true if all displays have been blanked by the power manager. + private int mAllDisplayBlankStateFromPowerManager = DISPLAY_BLANK_STATE_UNKNOWN; + + // Set to true when there are pending display changes that have yet to be applied + // to the surface flinger state. + private boolean mPendingTraversal; + + // The Wifi display adapter, or null if not registered. + private WifiDisplayAdapter mWifiDisplayAdapter; + + // The number of active wifi display scan requests. + private int mWifiDisplayScanRequestCount; + + // The virtual display adapter, or null if not registered. + private VirtualDisplayAdapter mVirtualDisplayAdapter; + + // Viewports of the default display and the display that should receive touch + // input from an external source. Used by the input system. + private final DisplayViewport mDefaultViewport = new DisplayViewport(); + private final DisplayViewport mExternalTouchViewport = new DisplayViewport(); + + // Persistent data store for all internal settings maintained by the display manager service. + private final PersistentDataStore mPersistentDataStore = new PersistentDataStore(); + + // Temporary callback list, used when sending display events to applications. + // May be used outside of the lock but only on the handler thread. + private final ArrayList<CallbackRecord> mTempCallbacks = new ArrayList<CallbackRecord>(); + + // Temporary display info, used for comparing display configurations. + private final DisplayInfo mTempDisplayInfo = new DisplayInfo(); + + // Temporary viewports, used when sending new viewport information to the + // input system. May be used outside of the lock but only on the handler thread. + private final DisplayViewport mTempDefaultViewport = new DisplayViewport(); + private final DisplayViewport mTempExternalTouchViewport = new DisplayViewport(); + + public DisplayManagerService(Context context, Handler mainHandler) { + mContext = context; + mHandler = new DisplayManagerHandler(mainHandler.getLooper()); + mUiHandler = UiThread.getHandler(); + mDisplayAdapterListener = new DisplayAdapterListener(); + mSingleDisplayDemoMode = SystemProperties.getBoolean("persist.demo.singledisplay", false); + + mHandler.sendEmptyMessage(MSG_REGISTER_DEFAULT_DISPLAY_ADAPTER); + } + + /** + * Pauses the boot process to wait for the first display to be initialized. + */ + public boolean waitForDefaultDisplay() { + synchronized (mSyncRoot) { + 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 { + mSyncRoot.wait(delay); + } catch (InterruptedException ex) { + } + } + } + return true; + } + + /** + * Called during initialization to associate the display manager with the + * window manager. + */ + public void setWindowManager(WindowManagerFuncs windowManagerFuncs) { + synchronized (mSyncRoot) { + mWindowManagerFuncs = windowManagerFuncs; + scheduleTraversalLocked(false); + } + } + + /** + * Called during initialization to associate the display manager with the + * input manager. + */ + public void setInputManager(InputManagerFuncs inputManagerFuncs) { + synchronized (mSyncRoot) { + mInputManagerFuncs = inputManagerFuncs; + scheduleTraversalLocked(false); + } + } + + /** + * Called when the system is ready to go. + */ + public void systemReady(boolean safeMode, boolean onlyCore) { + synchronized (mSyncRoot) { + mSafeMode = safeMode; + mOnlyCore = onlyCore; + } + + mHandler.sendEmptyMessage(MSG_REGISTER_ADDITIONAL_DISPLAY_ADAPTERS); + } + + /** + * Registers a display transaction listener to provide the client a chance to + * update its surfaces within the same transaction as any display layout updates. + * + * @param listener The listener to register. + */ + public void registerDisplayTransactionListener(DisplayTransactionListener listener) { + if (listener == null) { + throw new IllegalArgumentException("listener must not be null"); + } + + // List is self-synchronized copy-on-write. + mDisplayTransactionListeners.add(listener); + } + + /** + * Unregisters a display transaction listener to provide the client a chance to + * update its surfaces within the same transaction as any display layout updates. + * + * @param listener The listener to unregister. + */ + public void unregisterDisplayTransactionListener(DisplayTransactionListener listener) { + if (listener == null) { + throw new IllegalArgumentException("listener must not be null"); + } + + // List is self-synchronized copy-on-write. + mDisplayTransactionListeners.remove(listener); + } + + /** + * 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. It is expected to apply the requested change + * to the display information synchronously so that applications will immediately + * observe the new state. + * + * NOTE: This method must be the only entry point by which the window manager + * influences the logical configuration of displays. + * + * @param displayId The logical display id. + * @param info The new data to be stored. + */ + public void setDisplayInfoOverrideFromWindowManager( + int displayId, DisplayInfo info) { + synchronized (mSyncRoot) { + LogicalDisplay display = mLogicalDisplays.get(displayId); + if (display != null) { + if (display.setDisplayInfoOverrideFromWindowManagerLocked(info)) { + sendDisplayEventLocked(displayId, DisplayManagerGlobal.EVENT_DISPLAY_CHANGED); + scheduleTraversalLocked(false); + } + } + } + } + + /** + * Called by the window manager to perform traversals while holding a + * surface flinger transaction. + */ + public void performTraversalInTransactionFromWindowManager() { + synchronized (mSyncRoot) { + if (!mPendingTraversal) { + return; + } + mPendingTraversal = false; + + performTraversalInTransactionLocked(); + } + + // List is self-synchronized copy-on-write. + for (DisplayTransactionListener listener : mDisplayTransactionListeners) { + listener.onDisplayTransaction(); + } + } + + /** + * Called by the power manager to blank all displays. + */ + public void blankAllDisplaysFromPowerManager() { + synchronized (mSyncRoot) { + if (mAllDisplayBlankStateFromPowerManager != DISPLAY_BLANK_STATE_BLANKED) { + mAllDisplayBlankStateFromPowerManager = DISPLAY_BLANK_STATE_BLANKED; + updateAllDisplayBlankingLocked(); + scheduleTraversalLocked(false); + } + } + } + + /** + * Called by the power manager to unblank all displays. + */ + public void unblankAllDisplaysFromPowerManager() { + synchronized (mSyncRoot) { + if (mAllDisplayBlankStateFromPowerManager != DISPLAY_BLANK_STATE_UNBLANKED) { + mAllDisplayBlankStateFromPowerManager = DISPLAY_BLANK_STATE_UNBLANKED; + updateAllDisplayBlankingLocked(); + scheduleTraversalLocked(false); + } + } + } + + /** + * Returns information about the specified logical display. + * + * @param displayId The logical display id. + * @return The logical display info, or null if the display does not exist. The + * returned object must be treated as immutable. + */ + @Override // Binder call + public DisplayInfo getDisplayInfo(int displayId) { + final int callingUid = Binder.getCallingUid(); + final long token = Binder.clearCallingIdentity(); + try { + synchronized (mSyncRoot) { + LogicalDisplay display = mLogicalDisplays.get(displayId); + if (display != null) { + DisplayInfo info = display.getDisplayInfoLocked(); + if (info.hasAccess(callingUid)) { + return info; + } + } + return null; + } + } finally { + Binder.restoreCallingIdentity(token); + } + } + + /** + * Returns the list of all display ids. + */ + @Override // Binder call + public int[] getDisplayIds() { + final int callingUid = Binder.getCallingUid(); + final long token = Binder.clearCallingIdentity(); + try { + synchronized (mSyncRoot) { + final int count = mLogicalDisplays.size(); + int[] displayIds = new int[count]; + int n = 0; + for (int i = 0; i < count; i++) { + LogicalDisplay display = mLogicalDisplays.valueAt(i); + DisplayInfo info = display.getDisplayInfoLocked(); + if (info.hasAccess(callingUid)) { + displayIds[n++] = mLogicalDisplays.keyAt(i); + } + } + if (n != count) { + displayIds = Arrays.copyOfRange(displayIds, 0, n); + } + return displayIds; + } + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override // Binder call + public void registerCallback(IDisplayManagerCallback callback) { + if (callback == null) { + throw new IllegalArgumentException("listener must not be null"); + } + + synchronized (mSyncRoot) { + 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(CallbackRecord record) { + synchronized (mSyncRoot) { + mCallbacks.remove(record.mPid); + stopWifiDisplayScanLocked(record); + } + } + + @Override // Binder call + public void startWifiDisplayScan() { + mContext.enforceCallingOrSelfPermission(Manifest.permission.CONFIGURE_WIFI_DISPLAY, + "Permission required to start wifi display scans"); + + final int callingPid = Binder.getCallingPid(); + final long token = Binder.clearCallingIdentity(); + try { + synchronized (mSyncRoot) { + CallbackRecord record = mCallbacks.get(callingPid); + if (record == null) { + throw new IllegalStateException("The calling process has not " + + "registered an IDisplayManagerCallback."); + } + startWifiDisplayScanLocked(record); + } + } finally { + Binder.restoreCallingIdentity(token); + } + } + + private void startWifiDisplayScanLocked(CallbackRecord record) { + if (!record.mWifiDisplayScanRequested) { + record.mWifiDisplayScanRequested = true; + if (mWifiDisplayScanRequestCount++ == 0) { + if (mWifiDisplayAdapter != null) { + mWifiDisplayAdapter.requestStartScanLocked(); + } + } + } + } + + @Override // Binder call + public void stopWifiDisplayScan() { + mContext.enforceCallingOrSelfPermission(Manifest.permission.CONFIGURE_WIFI_DISPLAY, + "Permission required to stop wifi display scans"); + + final int callingPid = Binder.getCallingPid(); + final long token = Binder.clearCallingIdentity(); + try { + synchronized (mSyncRoot) { + CallbackRecord record = mCallbacks.get(callingPid); + if (record == null) { + throw new IllegalStateException("The calling process has not " + + "registered an IDisplayManagerCallback."); + } + stopWifiDisplayScanLocked(record); + } + } finally { + Binder.restoreCallingIdentity(token); + } + } + + private void stopWifiDisplayScanLocked(CallbackRecord record) { + if (record.mWifiDisplayScanRequested) { + record.mWifiDisplayScanRequested = false; + if (--mWifiDisplayScanRequestCount == 0) { + if (mWifiDisplayAdapter != null) { + mWifiDisplayAdapter.requestStopScanLocked(); + } + } else if (mWifiDisplayScanRequestCount < 0) { + Log.wtf(TAG, "mWifiDisplayScanRequestCount became negative: " + + mWifiDisplayScanRequestCount); + mWifiDisplayScanRequestCount = 0; + } + } + } + + @Override // Binder call + public void connectWifiDisplay(String address) { + if (address == null) { + throw new IllegalArgumentException("address must not be null"); + } + mContext.enforceCallingOrSelfPermission(Manifest.permission.CONFIGURE_WIFI_DISPLAY, + "Permission required to connect to a wifi display"); + + final long token = Binder.clearCallingIdentity(); + try { + synchronized (mSyncRoot) { + if (mWifiDisplayAdapter != null) { + mWifiDisplayAdapter.requestConnectLocked(address); + } + } + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override + public void pauseWifiDisplay() { + mContext.enforceCallingOrSelfPermission(Manifest.permission.CONFIGURE_WIFI_DISPLAY, + "Permission required to pause a wifi display session"); + + final long token = Binder.clearCallingIdentity(); + try { + synchronized (mSyncRoot) { + if (mWifiDisplayAdapter != null) { + mWifiDisplayAdapter.requestPauseLocked(); + } + } + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override + public void resumeWifiDisplay() { + mContext.enforceCallingOrSelfPermission(Manifest.permission.CONFIGURE_WIFI_DISPLAY, + "Permission required to resume a wifi display session"); + + final long token = Binder.clearCallingIdentity(); + try { + synchronized (mSyncRoot) { + if (mWifiDisplayAdapter != null) { + mWifiDisplayAdapter.requestResumeLocked(); + } + } + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override // Binder call + public void disconnectWifiDisplay() { + // This request does not require special permissions. + // Any app can request disconnection from the currently active wifi display. + // This exception should no longer be needed once wifi display control moves + // to the media router service. + + final long token = Binder.clearCallingIdentity(); + try { + synchronized (mSyncRoot) { + if (mWifiDisplayAdapter != null) { + mWifiDisplayAdapter.requestDisconnectLocked(); + } + } + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override // Binder call + public void renameWifiDisplay(String address, String alias) { + if (address == null) { + throw new IllegalArgumentException("address must not be null"); + } + mContext.enforceCallingOrSelfPermission(Manifest.permission.CONFIGURE_WIFI_DISPLAY, + "Permission required to rename to a wifi display"); + + final long token = Binder.clearCallingIdentity(); + try { + synchronized (mSyncRoot) { + if (mWifiDisplayAdapter != null) { + mWifiDisplayAdapter.requestRenameLocked(address, alias); + } + } + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override // Binder call + public void forgetWifiDisplay(String address) { + if (address == null) { + throw new IllegalArgumentException("address must not be null"); + } + mContext.enforceCallingOrSelfPermission(Manifest.permission.CONFIGURE_WIFI_DISPLAY, + "Permission required to forget to a wifi display"); + + final long token = Binder.clearCallingIdentity(); + try { + synchronized (mSyncRoot) { + if (mWifiDisplayAdapter != null) { + mWifiDisplayAdapter.requestForgetLocked(address); + } + } + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override // Binder call + public WifiDisplayStatus getWifiDisplayStatus() { + // This request does not require special permissions. + // Any app can get information about available wifi displays. + + final long token = Binder.clearCallingIdentity(); + try { + synchronized (mSyncRoot) { + if (mWifiDisplayAdapter != null) { + return mWifiDisplayAdapter.getWifiDisplayStatusLocked(); + } + return new WifiDisplayStatus(); + } + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override // Binder call + public int createVirtualDisplay(IBinder appToken, String packageName, + String name, int width, int height, int densityDpi, Surface surface, int flags) { + final int callingUid = Binder.getCallingUid(); + if (!validatePackageName(callingUid, packageName)) { + throw new SecurityException("packageName must match the calling uid"); + } + if (appToken == null) { + throw new IllegalArgumentException("appToken must not be null"); + } + if (TextUtils.isEmpty(name)) { + throw new IllegalArgumentException("name must be non-null and non-empty"); + } + if (width <= 0 || height <= 0 || densityDpi <= 0) { + throw new IllegalArgumentException("width, height, and densityDpi must be " + + "greater than 0"); + } + if (surface == null) { + throw new IllegalArgumentException("surface must not be null"); + } + if ((flags & DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC) != 0) { + if (mContext.checkCallingPermission(android.Manifest.permission.CAPTURE_VIDEO_OUTPUT) + != PackageManager.PERMISSION_GRANTED + && mContext.checkCallingPermission( + android.Manifest.permission.CAPTURE_SECURE_VIDEO_OUTPUT) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Requires CAPTURE_VIDEO_OUTPUT or " + + "CAPTURE_SECURE_VIDEO_OUTPUT permission to create a " + + "public virtual display."); + } + } + if ((flags & DisplayManager.VIRTUAL_DISPLAY_FLAG_SECURE) != 0) { + if (mContext.checkCallingPermission( + android.Manifest.permission.CAPTURE_SECURE_VIDEO_OUTPUT) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Requires CAPTURE_SECURE_VIDEO_OUTPUT " + + "to create a secure virtual display."); + } + } + + final long token = Binder.clearCallingIdentity(); + try { + synchronized (mSyncRoot) { + if (mVirtualDisplayAdapter == null) { + Slog.w(TAG, "Rejecting request to create private virtual display " + + "because the virtual display adapter is not available."); + return -1; + } + + DisplayDevice device = mVirtualDisplayAdapter.createVirtualDisplayLocked( + appToken, callingUid, packageName, name, width, height, densityDpi, + surface, flags); + if (device == null) { + return -1; + } + + handleDisplayDeviceAddedLocked(device); + LogicalDisplay display = findLogicalDisplayForDeviceLocked(device); + if (display != null) { + return display.getDisplayIdLocked(); + } + + // Something weird happened and the logical display was not created. + Slog.w(TAG, "Rejecting request to create virtual display " + + "because the logical display was not created."); + mVirtualDisplayAdapter.releaseVirtualDisplayLocked(appToken); + handleDisplayDeviceRemovedLocked(device); + } + } finally { + Binder.restoreCallingIdentity(token); + } + return -1; + } + + @Override // Binder call + public void releaseVirtualDisplay(IBinder appToken) { + final long token = Binder.clearCallingIdentity(); + try { + synchronized (mSyncRoot) { + if (mVirtualDisplayAdapter == null) { + return; + } + + DisplayDevice device = + mVirtualDisplayAdapter.releaseVirtualDisplayLocked(appToken); + if (device != null) { + handleDisplayDeviceRemovedLocked(device); + } + } + } finally { + Binder.restoreCallingIdentity(token); + } + } + + private boolean validatePackageName(int uid, String packageName) { + if (packageName != null) { + String[] packageNames = mContext.getPackageManager().getPackagesForUid(uid); + if (packageNames != null) { + for (String n : packageNames) { + if (n.equals(packageName)) { + return true; + } + } + } + } + return false; + } + + private void registerDefaultDisplayAdapter() { + // Register default display adapter. + synchronized (mSyncRoot) { + registerDisplayAdapterLocked(new LocalDisplayAdapter( + mSyncRoot, mContext, mHandler, mDisplayAdapterListener)); + } + } + + private void registerAdditionalDisplayAdapters() { + synchronized (mSyncRoot) { + if (shouldRegisterNonEssentialDisplayAdaptersLocked()) { + registerOverlayDisplayAdapterLocked(); + registerWifiDisplayAdapterLocked(); + registerVirtualDisplayAdapterLocked(); + } + } + } + + private void registerOverlayDisplayAdapterLocked() { + registerDisplayAdapterLocked(new OverlayDisplayAdapter( + mSyncRoot, mContext, mHandler, mDisplayAdapterListener, mUiHandler)); + } + + private void registerWifiDisplayAdapterLocked() { + if (mContext.getResources().getBoolean( + com.android.internal.R.bool.config_enableWifiDisplay) + || SystemProperties.getInt(FORCE_WIFI_DISPLAY_ENABLE, -1) == 1) { + mWifiDisplayAdapter = new WifiDisplayAdapter( + mSyncRoot, mContext, mHandler, mDisplayAdapterListener, + mPersistentDataStore); + registerDisplayAdapterLocked(mWifiDisplayAdapter); + } + } + + private void registerVirtualDisplayAdapterLocked() { + mVirtualDisplayAdapter = new VirtualDisplayAdapter( + mSyncRoot, mContext, mHandler, mDisplayAdapterListener); + registerDisplayAdapterLocked(mVirtualDisplayAdapter); + } + + private boolean shouldRegisterNonEssentialDisplayAdaptersLocked() { + // 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. + return !mSafeMode && !mOnlyCore; + } + + private void registerDisplayAdapterLocked(DisplayAdapter adapter) { + mDisplayAdapters.add(adapter); + adapter.registerLocked(); + } + + private void handleDisplayDeviceAdded(DisplayDevice device) { + synchronized (mSyncRoot) { + handleDisplayDeviceAddedLocked(device); + } + } + + private void handleDisplayDeviceAddedLocked(DisplayDevice device) { + if (mDisplayDevices.contains(device)) { + Slog.w(TAG, "Attempted to add already added display device: " + + device.getDisplayDeviceInfoLocked()); + return; + } + + Slog.i(TAG, "Display device added: " + device.getDisplayDeviceInfoLocked()); + + mDisplayDevices.add(device); + addLogicalDisplayLocked(device); + updateDisplayBlankingLocked(device); + scheduleTraversalLocked(false); + } + + private void handleDisplayDeviceChanged(DisplayDevice device) { + synchronized (mSyncRoot) { + if (!mDisplayDevices.contains(device)) { + Slog.w(TAG, "Attempted to change non-existent display device: " + + device.getDisplayDeviceInfoLocked()); + return; + } + + Slog.i(TAG, "Display device changed: " + device.getDisplayDeviceInfoLocked()); + + device.applyPendingDisplayDeviceInfoChangesLocked(); + if (updateLogicalDisplaysLocked()) { + scheduleTraversalLocked(false); + } + } + } + + private void handleDisplayDeviceRemoved(DisplayDevice device) { + synchronized (mSyncRoot) { + handleDisplayDeviceRemovedLocked(device); + } + } + private void handleDisplayDeviceRemovedLocked(DisplayDevice device) { + if (!mDisplayDevices.remove(device)) { + Slog.w(TAG, "Attempted to remove non-existent display device: " + + device.getDisplayDeviceInfoLocked()); + return; + } + + Slog.i(TAG, "Display device removed: " + device.getDisplayDeviceInfoLocked()); + + updateLogicalDisplaysLocked(); + scheduleTraversalLocked(false); + } + + private void updateAllDisplayBlankingLocked() { + final int count = mDisplayDevices.size(); + for (int i = 0; i < count; i++) { + DisplayDevice device = mDisplayDevices.get(i); + updateDisplayBlankingLocked(device); + } + } + + private void updateDisplayBlankingLocked(DisplayDevice device) { + // Blank or unblank the display immediately to match the state requested + // by the power manager (if known). + DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked(); + if ((info.flags & DisplayDeviceInfo.FLAG_NEVER_BLANK) == 0) { + switch (mAllDisplayBlankStateFromPowerManager) { + case DISPLAY_BLANK_STATE_BLANKED: + device.blankLocked(); + break; + case DISPLAY_BLANK_STATE_UNBLANKED: + device.unblankLocked(); + break; + } + } + } + + // Adds a new logical display based on the given display device. + // Sends notifications if needed. + private void addLogicalDisplayLocked(DisplayDevice device) { + DisplayDeviceInfo deviceInfo = device.getDisplayDeviceInfoLocked(); + boolean isDefault = (deviceInfo.flags + & DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY) != 0; + if (isDefault && mLogicalDisplays.get(Display.DEFAULT_DISPLAY) != null) { + Slog.w(TAG, "Ignoring attempt to add a second default display: " + deviceInfo); + isDefault = false; + } + + if (!isDefault && mSingleDisplayDemoMode) { + Slog.i(TAG, "Not creating a logical display for a secondary display " + + " because single display demo mode is enabled: " + deviceInfo); + return; + } + + final int displayId = assignDisplayIdLocked(isDefault); + final int layerStack = assignLayerStackLocked(displayId); + + LogicalDisplay display = new LogicalDisplay(displayId, layerStack, device); + display.updateLocked(mDisplayDevices); + if (!display.isValidLocked()) { + // This should never happen currently. + Slog.w(TAG, "Ignoring display device because the logical display " + + "created from it was not considered valid: " + deviceInfo); + return; + } + + mLogicalDisplays.put(displayId, display); + + // Wake up waitForDefaultDisplay. + if (isDefault) { + mSyncRoot.notifyAll(); + } + + sendDisplayEventLocked(displayId, DisplayManagerGlobal.EVENT_DISPLAY_ADDED); + } + + private int assignDisplayIdLocked(boolean isDefault) { + return isDefault ? Display.DEFAULT_DISPLAY : mNextNonDefaultDisplayId++; + } + + private int assignLayerStackLocked(int displayId) { + // Currently layer stacks and display ids are the same. + // This need not be the case. + return displayId; + } + + // Updates all existing logical displays given the current set of display devices. + // Removes invalid logical displays. + // Sends notifications if needed. + private boolean updateLogicalDisplaysLocked() { + boolean changed = false; + for (int i = mLogicalDisplays.size(); i-- > 0; ) { + final int displayId = mLogicalDisplays.keyAt(i); + LogicalDisplay display = mLogicalDisplays.valueAt(i); + + mTempDisplayInfo.copyFrom(display.getDisplayInfoLocked()); + display.updateLocked(mDisplayDevices); + if (!display.isValidLocked()) { + mLogicalDisplays.removeAt(i); + sendDisplayEventLocked(displayId, DisplayManagerGlobal.EVENT_DISPLAY_REMOVED); + changed = true; + } else if (!mTempDisplayInfo.equals(display.getDisplayInfoLocked())) { + sendDisplayEventLocked(displayId, DisplayManagerGlobal.EVENT_DISPLAY_CHANGED); + changed = true; + } + } + return changed; + } + + private void performTraversalInTransactionLocked() { + // Clear all viewports before configuring displays so that we can keep + // track of which ones we have configured. + clearViewportsLocked(); + + // Configure each display device. + final int count = mDisplayDevices.size(); + for (int i = 0; i < count; i++) { + DisplayDevice device = mDisplayDevices.get(i); + configureDisplayInTransactionLocked(device); + device.performTraversalInTransactionLocked(); + } + + // Tell the input system about these new viewports. + if (mInputManagerFuncs != null) { + mHandler.sendEmptyMessage(MSG_UPDATE_VIEWPORT); + } + } + + /** + * Tells the display manager whether there is interesting unique content on the + * specified logical display. This is used to control automatic mirroring. + * <p> + * If the display has unique content, then the display manager arranges for it + * to be presented on a physical display if appropriate. Otherwise, the display manager + * may choose to make the physical display mirror some other logical display. + * </p> + * + * @param displayId The logical display id to update. + * @param hasContent True if the logical display has content. + * @param inTraversal True if called from WindowManagerService during a window traversal prior + * to call to performTraversalInTransactionFromWindowManager. + */ + public void setDisplayHasContent(int displayId, boolean hasContent, boolean inTraversal) { + synchronized (mSyncRoot) { + LogicalDisplay display = mLogicalDisplays.get(displayId); + if (display != null && display.hasContentLocked() != hasContent) { + if (DEBUG) { + Slog.d(TAG, "Display " + displayId + " hasContent flag changed: " + + "hasContent=" + hasContent + ", inTraversal=" + inTraversal); + } + + display.setHasContentLocked(hasContent); + scheduleTraversalLocked(inTraversal); + } + } + } + + private void clearViewportsLocked() { + mDefaultViewport.valid = false; + mExternalTouchViewport.valid = false; + } + + private void configureDisplayInTransactionLocked(DisplayDevice device) { + DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked(); + boolean isPrivate = (info.flags & DisplayDeviceInfo.FLAG_PRIVATE) != 0; + + // Find the logical display that the display device is showing. + // Private displays never mirror other displays. + LogicalDisplay display = findLogicalDisplayForDeviceLocked(device); + if (!isPrivate) { + if (display != null && !display.hasContentLocked()) { + // If the display does not have any content of its own, then + // automatically mirror the default logical display contents. + display = null; + } + if (display == null) { + display = mLogicalDisplays.get(Display.DEFAULT_DISPLAY); + } + } + + // Apply the logical display configuration to the display device. + if (display == null) { + // TODO: no logical display for the device, blank it + Slog.w(TAG, "Missing logical display to use for physical display device: " + + device.getDisplayDeviceInfoLocked()); + return; + } + boolean isBlanked = (mAllDisplayBlankStateFromPowerManager == DISPLAY_BLANK_STATE_BLANKED) + && (info.flags & DisplayDeviceInfo.FLAG_NEVER_BLANK) == 0; + display.configureDisplayInTransactionLocked(device, isBlanked); + + // Update the viewports if needed. + if (!mDefaultViewport.valid + && (info.flags & DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY) != 0) { + setViewportLocked(mDefaultViewport, display, device); + } + if (!mExternalTouchViewport.valid + && info.touch == DisplayDeviceInfo.TOUCH_EXTERNAL) { + setViewportLocked(mExternalTouchViewport, display, device); + } + } + + private static void setViewportLocked(DisplayViewport viewport, + LogicalDisplay display, DisplayDevice device) { + viewport.valid = true; + viewport.displayId = display.getDisplayIdLocked(); + device.populateViewportLocked(viewport); + } + + private LogicalDisplay findLogicalDisplayForDeviceLocked(DisplayDevice device) { + final int count = mLogicalDisplays.size(); + for (int i = 0; i < count; i++) { + LogicalDisplay display = mLogicalDisplays.valueAt(i); + if (display.getPrimaryDisplayDeviceLocked() == device) { + return display; + } + } + return null; + } + + private void sendDisplayEventLocked(int displayId, int event) { + Message msg = mHandler.obtainMessage(MSG_DELIVER_DISPLAY_EVENT, displayId, event); + mHandler.sendMessage(msg); + } + + // Requests that performTraversalsInTransactionFromWindowManager be called at a + // later time to apply changes to surfaces and displays. + private void scheduleTraversalLocked(boolean inTraversal) { + if (!mPendingTraversal && mWindowManagerFuncs != null) { + mPendingTraversal = true; + if (!inTraversal) { + mHandler.sendEmptyMessage(MSG_REQUEST_TRAVERSAL); + } + } + } + + // Runs on Handler thread. + // Delivers display event notifications to callbacks. + private void deliverDisplayEvent(int displayId, int event) { + if (DEBUG) { + Slog.d(TAG, "Delivering display event: displayId=" + + displayId + ", event=" + event); + } + + // Grab the lock and copy the callbacks. + final int count; + synchronized (mSyncRoot) { + count = mCallbacks.size(); + mTempCallbacks.clear(); + for (int i = 0; i < count; i++) { + mTempCallbacks.add(mCallbacks.valueAt(i)); + } + } + + // After releasing the lock, send the notifications out. + 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)"); + + synchronized (mSyncRoot) { + pw.println(" mOnlyCode=" + mOnlyCore); + pw.println(" mSafeMode=" + mSafeMode); + pw.println(" mPendingTraversal=" + mPendingTraversal); + pw.println(" mAllDisplayBlankStateFromPowerManager=" + + mAllDisplayBlankStateFromPowerManager); + pw.println(" mNextNonDefaultDisplayId=" + mNextNonDefaultDisplayId); + pw.println(" mDefaultViewport=" + mDefaultViewport); + pw.println(" mExternalTouchViewport=" + mExternalTouchViewport); + pw.println(" mSingleDisplayDemoMode=" + mSingleDisplayDemoMode); + pw.println(" mWifiDisplayScanRequestCount=" + mWifiDisplayScanRequestCount); + + IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); + ipw.increaseIndent(); + + pw.println(); + pw.println("Display Adapters: size=" + mDisplayAdapters.size()); + for (DisplayAdapter adapter : mDisplayAdapters) { + pw.println(" " + adapter.getName()); + adapter.dumpLocked(ipw); + } + + pw.println(); + pw.println("Display Devices: size=" + mDisplayDevices.size()); + for (DisplayDevice device : mDisplayDevices) { + pw.println(" " + device.getDisplayDeviceInfoLocked()); + device.dumpLocked(ipw); + } + + 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 + ":"); + display.dumpLocked(ipw); + } + + final int callbackCount = mCallbacks.size(); + pw.println(); + pw.println("Callbacks: size=" + callbackCount); + for (int i = 0; i < callbackCount; i++) { + CallbackRecord callback = mCallbacks.valueAt(i); + pw.println(" " + i + ": mPid=" + callback.mPid + + ", mWifiDisplayScanRequested=" + callback.mWifiDisplayScanRequested); + } + } + } + + /** + * This is the object that everything in the display manager locks on. + * We make it an inner class within the {@link DisplayManagerService} to so that it is + * clear that the object belongs to the display manager service and that it is + * a unique object with a special purpose. + */ + public static final class SyncRoot { + } + + /** + * Private interface to the window manager. + */ + public interface WindowManagerFuncs { + /** + * Request that the window manager call + * {@link #performTraversalInTransactionFromWindowManager} within a surface + * transaction at a later time. + */ + void requestTraversal(); + } + + /** + * Private interface to the input manager. + */ + public interface InputManagerFuncs { + /** + * Sets information about the displays as needed by the input system. + * The input system should copy this information if required. + */ + void setDisplayViewports(DisplayViewport defaultViewport, + DisplayViewport externalTouchViewport); + } + + 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; + + case MSG_REQUEST_TRAVERSAL: + mWindowManagerFuncs.requestTraversal(); + break; + + case MSG_UPDATE_VIEWPORT: { + synchronized (mSyncRoot) { + mTempDefaultViewport.copyFrom(mDefaultViewport); + mTempExternalTouchViewport.copyFrom(mExternalTouchViewport); + } + mInputManagerFuncs.setDisplayViewports( + mTempDefaultViewport, mTempExternalTouchViewport); + 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; + } + } + + @Override + public void onTraversalRequested() { + synchronized (mSyncRoot) { + scheduleTraversalLocked(false); + } + } + } + + private final class CallbackRecord implements DeathRecipient { + public final int mPid; + private final IDisplayManagerCallback mCallback; + + public boolean mWifiDisplayScanRequested; + + 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(this); + } + + 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(); + } + } + } +} |