Files
frameworks_base/services/java/com/android/server/display/DisplayManagerService.java
Chong Zhang 1f3ecaae63 wifi-display: add certification options
When certification mode is enabled:

- Pass wfd session info to wifi display settings

- Allow sink to connect to source

- Add interface in display manager for pausing/resuming session

- Add interface in WifiP2pManager for setting lc, oc and starting
  autonomous GO

Note that we're compliant regardless of certification mode, but
some confusing options (eg. allowing incoming connection from
sink) we want to hide when not being tested.

Bug: 9371882
Change-Id: Icc7dcae4e046453796cfa03f5f197055fabf234b
2013-08-23 16:02:09 -07:00

1269 lines
49 KiB
Java

/*
* 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.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 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 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 boolean mHeadless;
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 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;
mHeadless = SystemProperties.get(SYSTEM_HEADLESS).equals("1");
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);
}
/**
* Returns true if the device is headless.
*
* @return True if the device is headless.
*/
public boolean isHeadless() {
return mHeadless;
}
/**
* 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(int pid) {
synchronized (mSyncRoot) {
mCallbacks.remove(pid);
}
}
@Override // Binder call
public void scanWifiDisplays() {
final long token = Binder.clearCallingIdentity();
try {
synchronized (mSyncRoot) {
if (mWifiDisplayAdapter != null) {
mWifiDisplayAdapter.requestScanLocked();
}
}
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override // Binder call
public void connectWifiDisplay(String address) {
if (address == null) {
throw new IllegalArgumentException("address must not be null");
}
final boolean trusted = canCallerConfigureWifiDisplay();
final long token = Binder.clearCallingIdentity();
try {
synchronized (mSyncRoot) {
if (mWifiDisplayAdapter != null) {
mWifiDisplayAdapter.requestConnectLocked(address, trusted);
}
}
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override
public void pauseWifiDisplay() {
if (mContext.checkCallingPermission(
android.Manifest.permission.CONFIGURE_WIFI_DISPLAY)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Requires CONFIGURE_WIFI_DISPLAY"
+ "permission 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() {
if (mContext.checkCallingPermission(
android.Manifest.permission.CONFIGURE_WIFI_DISPLAY)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Requires CONFIGURE_WIFI_DISPLAY"
+ "permission 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() {
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");
}
if (!canCallerConfigureWifiDisplay()) {
throw new SecurityException("Requires CONFIGURE_WIFI_DISPLAY permission to "
+ "rename 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");
}
if (!canCallerConfigureWifiDisplay()) {
throw new SecurityException("Requires CONFIGURE_WIFI_DISPLAY permission to "
+ "forget 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() {
final long token = Binder.clearCallingIdentity();
try {
synchronized (mSyncRoot) {
if (mWifiDisplayAdapter != null) {
return mWifiDisplayAdapter.getWifiDisplayStatusLocked();
}
return new WifiDisplayStatus();
}
} finally {
Binder.restoreCallingIdentity(token);
}
}
private boolean canCallerConfigureWifiDisplay() {
return mContext.checkCallingPermission(android.Manifest.permission.CONFIGURE_WIFI_DISPLAY)
== PackageManager.PERMISSION_GRANTED;
}
@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) {
if (mHeadless) {
registerDisplayAdapterLocked(new HeadlessDisplayAdapter(
mSyncRoot, mContext, mHandler, mDisplayAdapterListener));
} else {
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(" mHeadless=" + mHeadless);
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);
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);
}
}
}
/**
* 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 {
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();
}
}
}
}