Merge "Initial changes to add QuickSettings." into jb-mr1-dev
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 2814301..4347e75 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -40,6 +40,28 @@
private final Object mLock = new Object();
private final SparseArray<Display> mDisplays = new SparseArray<Display>();
+ /**
+ * Broadcast receiver that indicates when the Wifi display status changes.
+ * <p>
+ * The status is provided as a {@link WifiDisplayStatus} object in the
+ * {@link #EXTRA_WIFI_DISPLAY_STATUS} extra.
+ * </p><p>
+ * This broadcast is only sent to registered receivers with the
+ * {@link android.Manifest.permission#CONFIGURE_WIFI_DISPLAY} permission and can
+ * only be sent by the system.
+ * </p>
+ * @hide
+ */
+ public static final String ACTION_WIFI_DISPLAY_STATUS_CHANGED =
+ "android.hardware.display.action.WIFI_DISPLAY_STATUS_CHANGED";
+
+ /**
+ * Contains a {@link WifiDisplayStatus} object.
+ * @hide
+ */
+ public static final String EXTRA_WIFI_DISPLAY_STATUS =
+ "android.hardware.display.extra.WIFI_DISPLAY_STATUS";
+
/** @hide */
public DisplayManager(Context context) {
mContext = context;
@@ -127,6 +149,47 @@
}
/**
+ * Initiates a fresh scan of availble Wifi displays.
+ * The results are sent as a {@link #ACTION_WIFI_DISPLAY_STATUS_CHANGED} broadcast.
+ * @hide
+ */
+ public void scanWifiDisplays() {
+ mGlobal.scanWifiDisplays();
+ }
+
+ /**
+ * Connects to a Wifi display.
+ * The results are sent as a {@link #ACTION_WIFI_DISPLAY_STATUS_CHANGED} broadcast.
+ *
+ * @param deviceAddress The MAC address of the device to which we should connect.
+ * @hide
+ */
+ public void connectWifiDisplay(String deviceAddress) {
+ mGlobal.connectWifiDisplay(deviceAddress);
+ }
+
+ /**
+ * Disconnects from the current Wifi display.
+ * The results are sent as a {@link #ACTION_WIFI_DISPLAY_STATUS_CHANGED} broadcast.
+ * @hide
+ */
+ public void disconnectWifiDisplay() {
+ mGlobal.disconnectWifiDisplay();
+ }
+
+ /**
+ * Gets the current Wifi display status.
+ * Watch for changes in the status by registering a broadcast receiver for
+ * {@link #ACTION_WIFI_DISPLAY_STATUS_CHANGED}.
+ *
+ * @return The current Wifi display status.
+ * @hide
+ */
+ public WifiDisplayStatus getWifiDisplayStatus() {
+ return mGlobal.getWifiDisplayStatus();
+ }
+
+ /**
* Listens for changes in available display devices.
*/
public interface DisplayListener {
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index 4077964..14b5440 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -253,6 +253,43 @@
}
}
+ public void scanWifiDisplays() {
+ try {
+ mDm.scanWifiDisplays();
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Failed to scan for Wifi displays.", ex);
+ }
+ }
+
+ public void connectWifiDisplay(String deviceAddress) {
+ if (deviceAddress == null) {
+ throw new IllegalArgumentException("deviceAddress must not be null");
+ }
+
+ try {
+ mDm.connectWifiDisplay(deviceAddress);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Failed to connect to Wifi display " + deviceAddress + ".", ex);
+ }
+ }
+
+ public void disconnectWifiDisplay() {
+ try {
+ mDm.disconnectWifiDisplay();
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Failed to disconnect from Wifi display.", ex);
+ }
+ }
+
+ public WifiDisplayStatus getWifiDisplayStatus() {
+ try {
+ return mDm.getWifiDisplayStatus();
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Failed to get Wifi display status.", ex);
+ return new WifiDisplayStatus();
+ }
+ }
+
private final class DisplayManagerCallback extends IDisplayManagerCallback.Stub {
@Override
public void onDisplayEvent(int displayId, int event) {
diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl
index d802aa1..36a9a7f 100644
--- a/core/java/android/hardware/display/IDisplayManager.aidl
+++ b/core/java/android/hardware/display/IDisplayManager.aidl
@@ -17,6 +17,8 @@
package android.hardware.display;
import android.hardware.display.IDisplayManagerCallback;
+import android.hardware.display.WifiDisplay;
+import android.hardware.display.WifiDisplayStatus;
import android.view.DisplayInfo;
/** @hide */
@@ -25,4 +27,16 @@
int[] getDisplayIds();
void registerCallback(in IDisplayManagerCallback callback);
+
+ // Requires CONFIGURE_WIFI_DISPLAY permission.
+ void scanWifiDisplays();
+
+ // Requires CONFIGURE_WIFI_DISPLAY permission.
+ void connectWifiDisplay(String address);
+
+ // Requires CONFIGURE_WIFI_DISPLAY permission.
+ void disconnectWifiDisplay();
+
+ // Requires CONFIGURE_WIFI_DISPLAY permission.
+ WifiDisplayStatus getWifiDisplayStatus();
}
diff --git a/core/java/android/hardware/display/WifiDisplay.aidl b/core/java/android/hardware/display/WifiDisplay.aidl
new file mode 100644
index 0000000..7733075
--- /dev/null
+++ b/core/java/android/hardware/display/WifiDisplay.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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 android.hardware.display;
+
+parcelable WifiDisplay;
diff --git a/core/java/android/hardware/display/WifiDisplay.java b/core/java/android/hardware/display/WifiDisplay.java
new file mode 100644
index 0000000..e51e97e
--- /dev/null
+++ b/core/java/android/hardware/display/WifiDisplay.java
@@ -0,0 +1,107 @@
+/*
+ * 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 android.hardware.display;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Describes the properties of a Wifi display.
+ * <p>
+ * This object is immutable.
+ * </p>
+ *
+ * @hide
+ */
+public final class WifiDisplay implements Parcelable {
+ private final String mDeviceAddress;
+ private final String mDeviceName;
+
+ public static final WifiDisplay[] EMPTY_ARRAY = new WifiDisplay[0];
+
+ public static final Creator<WifiDisplay> CREATOR = new Creator<WifiDisplay>() {
+ public WifiDisplay createFromParcel(Parcel in) {
+ String deviceAddress = in.readString();
+ String deviceName = in.readString();
+ return new WifiDisplay(deviceAddress, deviceName);
+ }
+
+ public WifiDisplay[] newArray(int size) {
+ return size == 0 ? EMPTY_ARRAY : new WifiDisplay[size];
+ }
+ };
+
+ public WifiDisplay(String deviceAddress, String deviceName) {
+ if (deviceAddress == null) {
+ throw new IllegalArgumentException("deviceAddress must not be null");
+ }
+ if (deviceName == null) {
+ throw new IllegalArgumentException("deviceName must not be null");
+ }
+
+ mDeviceAddress = deviceAddress;
+ mDeviceName = deviceName;
+ }
+
+ /**
+ * Gets the MAC address of the Wifi display device.
+ */
+ public String getDeviceAddress() {
+ return mDeviceAddress;
+ }
+
+ /**
+ * Gets the name of the Wifi display device.
+ */
+ public String getDeviceName() {
+ return mDeviceName;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ return o instanceof WifiDisplay && equals((WifiDisplay)o);
+ }
+
+ public boolean equals(WifiDisplay other) {
+ return other != null
+ && mDeviceAddress.equals(other.mDeviceAddress)
+ && mDeviceName.equals(other.mDeviceName);
+ }
+
+ @Override
+ public int hashCode() {
+ // The address on its own should be sufficiently unique for hashing purposes.
+ return mDeviceAddress.hashCode();
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(mDeviceAddress);
+ dest.writeString(mDeviceName);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ // For debugging purposes only.
+ @Override
+ public String toString() {
+ return mDeviceName + " (" + mDeviceAddress + ")";
+ }
+}
diff --git a/core/java/android/hardware/display/WifiDisplayStatus.aidl b/core/java/android/hardware/display/WifiDisplayStatus.aidl
new file mode 100644
index 0000000..35c633e
--- /dev/null
+++ b/core/java/android/hardware/display/WifiDisplayStatus.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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 android.hardware.display;
+
+parcelable WifiDisplayStatus;
diff --git a/core/java/android/hardware/display/WifiDisplayStatus.java b/core/java/android/hardware/display/WifiDisplayStatus.java
new file mode 100644
index 0000000..542d1b3
--- /dev/null
+++ b/core/java/android/hardware/display/WifiDisplayStatus.java
@@ -0,0 +1,159 @@
+/*
+ * 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 android.hardware.display;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Arrays;
+
+/**
+ * Describes the current global state of Wifi display connectivity, including the
+ * currently connected display and all known displays.
+ * <p>
+ * This object is immutable.
+ * </p>
+ *
+ * @hide
+ */
+public final class WifiDisplayStatus implements Parcelable {
+ private final boolean mEnabled;
+ private final WifiDisplay mConnectedDisplay;
+ private final WifiDisplay[] mKnownDisplays;
+ private final boolean mScanInProgress;
+ private final boolean mConnectionInProgress;
+
+ public static final Creator<WifiDisplayStatus> CREATOR = new Creator<WifiDisplayStatus>() {
+ public WifiDisplayStatus createFromParcel(Parcel in) {
+ boolean enabled = (in.readInt() != 0);
+
+ WifiDisplay connectedDisplay = null;
+ if (in.readInt() != 0) {
+ connectedDisplay = WifiDisplay.CREATOR.createFromParcel(in);
+ }
+
+ WifiDisplay[] knownDisplays = WifiDisplay.CREATOR.newArray(in.readInt());
+ for (int i = 0; i < knownDisplays.length; i++) {
+ knownDisplays[i] = WifiDisplay.CREATOR.createFromParcel(in);
+ }
+
+ boolean scanInProgress = (in.readInt() != 0);
+ boolean connectionInProgress = (in.readInt() != 0);
+
+ return new WifiDisplayStatus(enabled, connectedDisplay, knownDisplays,
+ scanInProgress, connectionInProgress);
+ }
+
+ public WifiDisplayStatus[] newArray(int size) {
+ return new WifiDisplayStatus[size];
+ }
+ };
+
+ public WifiDisplayStatus() {
+ this(false, null, WifiDisplay.EMPTY_ARRAY, false, false);
+ }
+
+ public WifiDisplayStatus(boolean enabled,
+ WifiDisplay connectedDisplay, WifiDisplay[] knownDisplays,
+ boolean scanInProgress, boolean connectionInProgress) {
+ if (knownDisplays == null) {
+ throw new IllegalArgumentException("knownDisplays must not be null");
+ }
+
+ mEnabled = enabled;
+ mConnectedDisplay = connectedDisplay;
+ mKnownDisplays = knownDisplays;
+ mScanInProgress = scanInProgress;
+ mConnectionInProgress = connectionInProgress;
+ }
+
+ /**
+ * Returns true if the Wifi display feature is enabled and available for use.
+ * <p>
+ * The value of this property reflects whether Wifi and Wifi P2P functions
+ * are enabled. Enablement is not directly controllable by the user at this
+ * time, except indirectly such as by turning off Wifi altogether.
+ * </p>
+ */
+ public boolean isEnabled() {
+ return mEnabled;
+ }
+
+ /**
+ * Gets the currently connected Wifi display or null if none.
+ */
+ public WifiDisplay getConnectedDisplay() {
+ return mConnectedDisplay;
+ }
+
+ /**
+ * Gets the list of all known Wifi displays, never null.
+ */
+ public WifiDisplay[] getKnownDisplays() {
+ return mKnownDisplays;
+ }
+
+ /**
+ * Returns true if there is currently a Wifi display scan in progress.
+ */
+ public boolean isScanInProgress() {
+ return mScanInProgress;
+ }
+
+ /**
+ * Returns true if there is currently a Wifi display connection in progress.
+ */
+ public boolean isConnectionInProgress() {
+ return mConnectionInProgress;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mEnabled ? 1 : 0);
+
+ if (mConnectedDisplay != null) {
+ dest.writeInt(1);
+ mConnectedDisplay.writeToParcel(dest, flags);
+ } else {
+ dest.writeInt(0);
+ }
+
+ dest.writeInt(mKnownDisplays.length);
+ for (WifiDisplay display : mKnownDisplays) {
+ display.writeToParcel(dest, flags);
+ }
+
+ dest.writeInt(mScanInProgress ? 1 : 0);
+ dest.writeInt(mConnectionInProgress ? 1 : 0);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ // For debugging purposes only.
+ @Override
+ public String toString() {
+ return "WifiDisplayStatus{enabled=" + mEnabled
+ + ", connectedDisplay=" + mConnectedDisplay
+ + ", knownDisplays=" + Arrays.toString(mKnownDisplays)
+ + ", scanInProgress=" + mScanInProgress
+ + ", connectionInProgress=" + mConnectionInProgress
+ + "}";
+ }
+}
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index a1901a5..cc2c002 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -331,6 +331,15 @@
}
/**
+ * Returns true if the screen auto-brightness adjustment setting should
+ * be available in the UI. This setting is experimental and disabled by default.
+ * @hide
+ */
+ public static boolean useScreenAutoBrightnessAdjustmentFeature() {
+ return SystemProperties.getBoolean("persist.power.useautobrightadj", false);
+ }
+
+ /**
* Creates a new wake lock with the specified level and flags.
* <p>
* The {@code levelAndFlags} parameter specifies a wake lock level and optional flags
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 23a412f..560021d 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -108,6 +108,8 @@
<protected-broadcast
android:name="android.bluetooth.pan.profile.action.CONNECTION_STATE_CHANGED" />
+ <protected-broadcast android:name="android.hardware.display.action.WIFI_DISPLAY_STATUS_CHANGED" />
+
<protected-broadcast android:name="android.hardware.usb.action.USB_STATE" />
<protected-broadcast android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED" />
<protected-broadcast android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED" />
@@ -1446,6 +1448,22 @@
android:description="@string/permdesc_readFrameBuffer"
android:protectionLevel="signature|system" />
+ <!-- Allows an application to configure and connect to Wifi displays
+ @hide -->
+ <permission android:name="android.permission.CONFIGURE_WIFI_DISPLAY"
+ android:label="@string/permlab_configureWifiDisplay"
+ android:description="@string/permdesc_configureWifiDisplay"
+ android:protectionLevel="signature" />
+
+ <!-- Allows an application to control low-level features of Wifi displays
+ such as opening an RTSP socket. This permission should only be used
+ by the display manager.
+ @hide -->
+ <permission android:name="android.permission.CONTROL_WIFI_DISPLAY"
+ android:label="@string/permlab_controlWifiDisplay"
+ android:description="@string/permdesc_controlWifiDisplay"
+ android:protectionLevel="signature" />
+
<!-- Required to be able to disable the device (very dangerous!). -->
<permission android:name="android.permission.BRICK"
android:label="@string/permlab_brick"
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 9a8e712..139715c 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -486,7 +486,6 @@
<java-symbol type="string" name="display_manager_hdmi_display_name" />
<java-symbol type="string" name="display_manager_overlay_display_name" />
<java-symbol type="string" name="display_manager_overlay_display_title" />
- <java-symbol type="string" name="display_manager_wifi_display_name" />
<java-symbol type="string" name="double_tap_toast" />
<java-symbol type="string" name="elapsed_time_short_format_h_mm_ss" />
<java-symbol type="string" name="elapsed_time_short_format_mm_ss" />
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 1e7e9fb..f989e4e 100755
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1191,6 +1191,16 @@
<string name="permdesc_readFrameBuffer">Allows the app to read the content of the frame buffer.</string>
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permlab_configureWifiDisplay">configure Wifi displays</string>
+ <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permdesc_configureWifiDisplay">Allows the app to configure and connect to Wifi displays.</string>
+
+ <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permlab_controlWifiDisplay">control Wifi displays</string>
+ <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permdesc_controlWifiDisplay">Allows the app to control low-level features of Wifi displays.</string>
+
+ <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permlab_modifyAudioSettings">change your audio settings</string>
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permdesc_modifyAudioSettings">Allows the app to modify global audio settings such as volume and which speaker is used for output.</string>
@@ -3678,9 +3688,6 @@
<!-- Title text to show within the overlay. [CHAR LIMIT=50] -->
<string name="display_manager_overlay_display_title"><xliff:g id="name">%1$s</xliff:g>: <xliff:g id="width">%2$d</xliff:g>x<xliff:g id="height">%3$d</xliff:g>, <xliff:g id="dpi">%4$d</xliff:g> dpi</string>
- <!-- Name of a wifi display. [CHAR LIMIT=50] -->
- <string name="display_manager_wifi_display_name">Wifi display: <xliff:g id="device">%1$s</xliff:g></string>
-
<!-- Keyguard strings -->
<!-- Label shown on emergency call button in keyguard -->
<string name="kg_emergency_call_label">Emergency call</string>
diff --git a/services/java/com/android/server/display/DisplayManagerService.java b/services/java/com/android/server/display/DisplayManagerService.java
index dc85d3f..41a0c09 100644
--- a/services/java/com/android/server/display/DisplayManagerService.java
+++ b/services/java/com/android/server/display/DisplayManagerService.java
@@ -24,6 +24,7 @@
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;
@@ -137,6 +138,9 @@
// to the surface flinger state.
private boolean mPendingTraversal;
+ // The Wifi display adapter, or null if not registered.
+ private WifiDisplayAdapter mWifiDisplayAdapter;
+
// 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>();
@@ -315,6 +319,77 @@
}
}
+ @Override // Binder call
+ public void scanWifiDisplays() {
+ if (mContext.checkCallingPermission(android.Manifest.permission.CONFIGURE_WIFI_DISPLAY)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("Requires CONFIGURE_WIFI_DISPLAY permission");
+ }
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ synchronized (mSyncRoot) {
+ mWifiDisplayAdapter.requestScanLocked();
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override // Binder call
+ public void connectWifiDisplay(String address) {
+ if (mContext.checkCallingPermission(android.Manifest.permission.CONFIGURE_WIFI_DISPLAY)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("Requires CONFIGURE_WIFI_DISPLAY permission");
+ }
+ if (address == null) {
+ throw new IllegalArgumentException("address must not be null");
+ }
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ synchronized (mSyncRoot) {
+ mWifiDisplayAdapter.requestConnectLocked(address);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override // Binder call
+ public void disconnectWifiDisplay() {
+ if (mContext.checkCallingPermission(android.Manifest.permission.CONFIGURE_WIFI_DISPLAY)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("Requires CONFIGURE_WIFI_DISPLAY permission");
+ }
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ synchronized (mSyncRoot) {
+ mWifiDisplayAdapter.requestDisconnectLocked();
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override // Binder call
+ public WifiDisplayStatus getWifiDisplayStatus() {
+ if (mContext.checkCallingPermission(android.Manifest.permission.CONFIGURE_WIFI_DISPLAY)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("Requires CONFIGURE_WIFI_DISPLAY permission");
+ }
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ synchronized (mSyncRoot) {
+ return mWifiDisplayAdapter.getWifiDisplayStatusLocked();
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
private void registerDefaultDisplayAdapter() {
// Register default display adapter.
synchronized (mSyncRoot) {
@@ -333,8 +408,9 @@
if (shouldRegisterNonEssentialDisplayAdaptersLocked()) {
registerDisplayAdapterLocked(new OverlayDisplayAdapter(
mSyncRoot, mContext, mHandler, mDisplayAdapterListener, mUiHandler));
- registerDisplayAdapterLocked(new WifiDisplayAdapter(
- mSyncRoot, mContext, mHandler, mDisplayAdapterListener));
+ mWifiDisplayAdapter = new WifiDisplayAdapter(
+ mSyncRoot, mContext, mHandler, mDisplayAdapterListener);
+ registerDisplayAdapterLocked(mWifiDisplayAdapter);
}
}
}
diff --git a/services/java/com/android/server/display/WifiDisplayAdapter.java b/services/java/com/android/server/display/WifiDisplayAdapter.java
index 38007af..abf0d27 100644
--- a/services/java/com/android/server/display/WifiDisplayAdapter.java
+++ b/services/java/com/android/server/display/WifiDisplayAdapter.java
@@ -20,6 +20,10 @@
import com.android.internal.util.IndentingPrintWriter;
import android.content.Context;
+import android.content.Intent;
+import android.hardware.display.DisplayManager;
+import android.hardware.display.WifiDisplay;
+import android.hardware.display.WifiDisplayStatus;
import android.media.RemoteDisplay;
import android.os.Handler;
import android.os.IBinder;
@@ -27,6 +31,7 @@
import android.view.Surface;
import java.io.PrintWriter;
+import java.util.Arrays;
/**
* Connects to Wifi displays that implement the Miracast protocol.
@@ -48,6 +53,15 @@
private WifiDisplayHandle mDisplayHandle;
private WifiDisplayController mDisplayController;
+ private WifiDisplayStatus mCurrentStatus;
+ private boolean mEnabled;
+ private WifiDisplay mConnectedDisplay;
+ private WifiDisplay[] mKnownDisplays = WifiDisplay.EMPTY_ARRAY;
+ private boolean mScanInProgress;
+ private boolean mConnectionInProgress;
+
+ private boolean mPendingStatusChangeBroadcast;
+
public WifiDisplayAdapter(DisplayManagerService.SyncRoot syncRoot,
Context context, Handler handler, Listener listener) {
super(syncRoot, context, handler, listener, TAG);
@@ -64,6 +78,14 @@
mDisplayHandle.dumpLocked(pw);
}
+ pw.println("mCurrentStatus=" + getWifiDisplayStatusLocked());
+ pw.println("mEnabled=" + mEnabled);
+ pw.println("mConnectedDisplay=" + mConnectedDisplay);
+ pw.println("mKnownDisplays=" + Arrays.toString(mKnownDisplays));
+ pw.println("mScanInProgress=" + mScanInProgress);
+ pw.println("mConnectionInProgress=" + mConnectionInProgress);
+ pw.println("mPendingStatusChangeBroadcast=" + mPendingStatusChangeBroadcast);
+
// Try to dump the controller state.
if (mDisplayController == null) {
pw.println("mDisplayController=null");
@@ -88,28 +110,160 @@
});
}
- private void connectLocked(String deviceName, String iface) {
- disconnectLocked();
-
- String name = getContext().getResources().getString(
- com.android.internal.R.string.display_manager_wifi_display_name,
- deviceName);
- mDisplayHandle = new WifiDisplayHandle(name, iface);
+ public void requestScanLocked() {
+ getHandler().post(new Runnable() {
+ @Override
+ public void run() {
+ if (mDisplayController != null) {
+ mDisplayController.requestScan();
+ }
+ }
+ });
}
- private void disconnectLocked() {
+ public void requestConnectLocked(final String address) {
+ getHandler().post(new Runnable() {
+ @Override
+ public void run() {
+ if (mDisplayController != null) {
+ mDisplayController.requestConnect(address);
+ }
+ }
+ });
+ }
+
+ public void requestDisconnectLocked() {
+ getHandler().post(new Runnable() {
+ @Override
+ public void run() {
+ if (mDisplayController != null) {
+ mDisplayController.requestDisconnect();
+ }
+ }
+ });
+ }
+
+ public WifiDisplayStatus getWifiDisplayStatusLocked() {
+ if (mCurrentStatus == null) {
+ mCurrentStatus = new WifiDisplayStatus(mEnabled,
+ mConnectedDisplay, mKnownDisplays,
+ mScanInProgress, mConnectionInProgress);
+ }
+ return mCurrentStatus;
+ }
+
+ private void handleConnectLocked(WifiDisplay display, String iface) {
+ handleDisconnectLocked();
+
+ mDisplayHandle = new WifiDisplayHandle(display.getDeviceName(), iface);
+ }
+
+ private void handleDisconnectLocked() {
if (mDisplayHandle != null) {
mDisplayHandle.disposeLocked();
mDisplayHandle = null;
}
}
+ private void scheduleStatusChangedBroadcastLocked() {
+ if (!mPendingStatusChangeBroadcast) {
+ mPendingStatusChangeBroadcast = true;
+ getHandler().post(mStatusChangeBroadcast);
+ }
+ }
+
+ private final Runnable mStatusChangeBroadcast = new Runnable() {
+ @Override
+ public void run() {
+ final Intent intent;
+ synchronized (getSyncRoot()) {
+ if (!mPendingStatusChangeBroadcast) {
+ return;
+ }
+
+ mPendingStatusChangeBroadcast = false;
+ intent = new Intent(DisplayManager.ACTION_WIFI_DISPLAY_STATUS_CHANGED);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ intent.putExtra(DisplayManager.EXTRA_WIFI_DISPLAY_STATUS,
+ getWifiDisplayStatusLocked());
+ }
+
+ // Send protected broadcast about wifi display status to receivers that
+ // have the required permission.
+ getContext().sendBroadcast(intent,
+ android.Manifest.permission.CONFIGURE_WIFI_DISPLAY);
+ }
+ };
+
private final WifiDisplayController.Listener mWifiDisplayListener =
new WifiDisplayController.Listener() {
@Override
- public void onDisplayConnected(String deviceName, String iface) {
+ public void onEnablementChanged(boolean enabled) {
synchronized (getSyncRoot()) {
- connectLocked(deviceName, iface);
+ if (mEnabled != enabled) {
+ mCurrentStatus = null;
+ mEnabled = enabled;
+ scheduleStatusChangedBroadcastLocked();
+ }
+ }
+ }
+
+ @Override
+ public void onScanStarted() {
+ synchronized (getSyncRoot()) {
+ if (!mScanInProgress) {
+ mCurrentStatus = null;
+ mScanInProgress = true;
+ scheduleStatusChangedBroadcastLocked();
+ }
+ }
+ }
+
+ public void onScanFinished(WifiDisplay[] knownDisplays) {
+ synchronized (getSyncRoot()) {
+ if (!Arrays.equals(mKnownDisplays, knownDisplays) || mScanInProgress) {
+ mCurrentStatus = null;
+ mKnownDisplays = knownDisplays;
+ mScanInProgress = false;
+ scheduleStatusChangedBroadcastLocked();
+ }
+ }
+ }
+
+ @Override
+ public void onDisplayConnecting(WifiDisplay display) {
+ synchronized (getSyncRoot()) {
+ if (!mConnectionInProgress) {
+ mCurrentStatus = null;
+ mConnectionInProgress = true;
+ scheduleStatusChangedBroadcastLocked();
+ }
+ }
+ }
+
+ @Override
+ public void onDisplayConnectionFailed() {
+ synchronized (getSyncRoot()) {
+ if (mConnectionInProgress) {
+ mCurrentStatus = null;
+ mConnectionInProgress = false;
+ scheduleStatusChangedBroadcastLocked();
+ }
+ }
+ }
+
+ @Override
+ public void onDisplayConnected(WifiDisplay display, String iface) {
+ synchronized (getSyncRoot()) {
+ handleConnectLocked(display, iface);
+
+ if (mConnectedDisplay == null || !mConnectedDisplay.equals(display)
+ || mConnectionInProgress) {
+ mCurrentStatus = null;
+ mConnectedDisplay = display;
+ mConnectionInProgress = false;
+ scheduleStatusChangedBroadcastLocked();
+ }
}
}
@@ -117,7 +271,14 @@
public void onDisplayDisconnected() {
// Stop listening.
synchronized (getSyncRoot()) {
- disconnectLocked();
+ handleDisconnectLocked();
+
+ if (mConnectedDisplay != null || mConnectionInProgress) {
+ mCurrentStatus = null;
+ mConnectedDisplay = null;
+ mConnectionInProgress = false;
+ scheduleStatusChangedBroadcastLocked();
+ }
}
}
};
diff --git a/services/java/com/android/server/display/WifiDisplayController.java b/services/java/com/android/server/display/WifiDisplayController.java
index b446e66..131502f 100644
--- a/services/java/com/android/server/display/WifiDisplayController.java
+++ b/services/java/com/android/server/display/WifiDisplayController.java
@@ -22,6 +22,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.hardware.display.WifiDisplay;
import android.net.NetworkInfo;
import android.net.wifi.p2p.WifiP2pConfig;
import android.net.wifi.p2p.WifiP2pDevice;
@@ -64,6 +65,12 @@
private static final int MAX_THROUGHPUT = 50;
private static final int CONNECTION_TIMEOUT_SECONDS = 30;
+ private static final int DISCOVER_PEERS_MAX_RETRIES = 10;
+ private static final int DISCOVER_PEERS_RETRY_DELAY_MILLIS = 500;
+
+ private static final int CONNECT_MAX_RETRIES = 3;
+ private static final int CONNECT_RETRY_DELAY_MILLIS = 500;
+
private final Context mContext;
private final Handler mHandler;
private final Listener mListener;
@@ -78,6 +85,12 @@
private final ArrayList<WifiP2pDevice> mKnownWifiDisplayPeers =
new ArrayList<WifiP2pDevice>();
+ // True if there is a call to discoverPeers in progress.
+ private boolean mDiscoverPeersInProgress;
+
+ // Number of discover peers retries remaining.
+ private int mDiscoverPeersRetriesLeft;
+
// The device to which we want to connect, or null if we want to be disconnected.
private WifiP2pDevice mDesiredDevice;
@@ -94,6 +107,9 @@
// The device that we announced to the rest of the system.
private WifiP2pDevice mPublishedDevice;
+ // Number of connection retries remaining.
+ private int mConnectionRetriesLeft;
+
public WifiDisplayController(Context context, Handler handler, Listener listener) {
mContext = context;
mHandler = handler;
@@ -114,10 +130,13 @@
pw.println("mWfdEnabled=" + mWfdEnabled);
pw.println("mWfdEnabling=" + mWfdEnabling);
pw.println("mNetworkInfo=" + mNetworkInfo);
+ pw.println("mDiscoverPeersInProgress=" + mDiscoverPeersInProgress);
+ pw.println("mDiscoverPeersRetriesLeft=" + mDiscoverPeersRetriesLeft);
pw.println("mDesiredDevice=" + describeWifiP2pDevice(mDesiredDevice));
pw.println("mConnectingDisplay=" + describeWifiP2pDevice(mConnectingDevice));
pw.println("mConnectedDevice=" + describeWifiP2pDevice(mConnectedDevice));
pw.println("mPublishedDevice=" + describeWifiP2pDevice(mPublishedDevice));
+ pw.println("mConnectionRetriesLeft=" + mConnectionRetriesLeft);
pw.println("mKnownWifiDisplayPeers: size=" + mKnownWifiDisplayPeers.size());
for (WifiP2pDevice device : mKnownWifiDisplayPeers) {
@@ -125,6 +144,22 @@
}
}
+ public void requestScan() {
+ discoverPeers();
+ }
+
+ public void requestConnect(String address) {
+ for (WifiP2pDevice device : mKnownWifiDisplayPeers) {
+ if (device.deviceAddress.equals(address)) {
+ connect(device);
+ }
+ }
+ }
+
+ public void requestDisconnect() {
+ disconnect();
+ }
+
private void enableWfd() {
if (!mWfdEnabled && !mWfdEnabling) {
mWfdEnabling = true;
@@ -142,8 +177,8 @@
Slog.d(TAG, "Successfully set WFD info.");
}
if (mWfdEnabling) {
- mWfdEnabled = true;
mWfdEnabling = false;
+ setWfdEnabled(true);
discoverPeers();
}
}
@@ -159,7 +194,28 @@
}
}
+ private void setWfdEnabled(final boolean enabled) {
+ if (mWfdEnabled != enabled) {
+ mWfdEnabled = enabled;
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mListener.onEnablementChanged(enabled);
+ }
+ });
+ }
+ }
+
private void discoverPeers() {
+ if (!mDiscoverPeersInProgress) {
+ mDiscoverPeersInProgress = true;
+ mDiscoverPeersRetriesLeft = DISCOVER_PEERS_MAX_RETRIES;
+ handleScanStarted();
+ tryDiscoverPeers();
+ }
+ }
+
+ private void tryDiscoverPeers() {
mWifiP2pManager.discoverPeers(mWifiP2pChannel, new ActionListener() {
@Override
public void onSuccess() {
@@ -167,6 +223,7 @@
Slog.d(TAG, "Discover peers succeeded. Requesting peers now.");
}
+ mDiscoverPeersInProgress = false;
requestPeers();
}
@@ -175,6 +232,32 @@
if (DEBUG) {
Slog.d(TAG, "Discover peers failed with reason " + reason + ".");
}
+
+ if (mDiscoverPeersInProgress) {
+ if (reason == 0 && mDiscoverPeersRetriesLeft > 0 && mWfdEnabled) {
+ mHandler.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ if (mDiscoverPeersInProgress) {
+ if (mDiscoverPeersRetriesLeft > 0 && mWfdEnabled) {
+ mDiscoverPeersRetriesLeft -= 1;
+ if (DEBUG) {
+ Slog.d(TAG, "Retrying discovery. Retries left: "
+ + mDiscoverPeersRetriesLeft);
+ }
+ tryDiscoverPeers();
+ } else {
+ handleScanFinished();
+ mDiscoverPeersInProgress = false;
+ }
+ }
+ }
+ }, DISCOVER_PEERS_RETRY_DELAY_MILLIS);
+ } else {
+ handleScanFinished();
+ mDiscoverPeersInProgress = false;
+ }
+ }
}
});
}
@@ -198,16 +281,31 @@
}
}
- // TODO: shouldn't auto-connect like this, let UI do it explicitly
- if (!mKnownWifiDisplayPeers.isEmpty()) {
- final WifiP2pDevice device = mKnownWifiDisplayPeers.get(0);
+ handleScanFinished();
+ }
+ });
+ }
- if (device.status == WifiP2pDevice.AVAILABLE) {
- connect(device);
- }
- }
+ private void handleScanStarted() {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mListener.onScanStarted();
+ }
+ });
+ }
- // TODO: publish this information to applications
+ private void handleScanFinished() {
+ final int count = mKnownWifiDisplayPeers.size();
+ final WifiDisplay[] displays = WifiDisplay.CREATOR.newArray(count);
+ for (int i = 0; i < count; i++) {
+ displays[i] = createWifiDisplay(mKnownWifiDisplayPeers.get(i));
+ }
+
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mListener.onScanFinished(displays);
}
});
}
@@ -230,9 +328,11 @@
+ describeWifiP2pDevice(device) + " and not part way through "
+ "connecting to a different device.");
}
+ return;
}
mDesiredDevice = device;
+ mConnectionRetriesLeft = CONNECT_MAX_RETRIES;
updateConnection();
}
@@ -241,6 +341,21 @@
updateConnection();
}
+ private void retryConnection() {
+ if (mDesiredDevice != null && mPublishedDevice != mDesiredDevice
+ && mConnectionRetriesLeft > 0) {
+ mConnectionRetriesLeft -= 1;
+ Slog.i(TAG, "Retrying Wifi display connection. Retries left: "
+ + mConnectionRetriesLeft);
+
+ // Cheap hack. Make a new instance of the device object so that we
+ // can distinguish it from the previous connection attempt.
+ // This will cause us to tear everything down before we try again.
+ mDesiredDevice = new WifiP2pDevice(mDesiredDevice);
+ updateConnection();
+ }
+ }
+
/**
* This function is called repeatedly after each asynchronous operation
* until all preconditions for the connection have been satisfied and the
@@ -335,6 +450,14 @@
WifiP2pConfig config = new WifiP2pConfig();
config.deviceAddress = mConnectingDevice.deviceAddress;
+ final WifiDisplay display = createWifiDisplay(mConnectingDevice);
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mListener.onDisplayConnecting(display);
+ }
+ });
+
final WifiP2pDevice newDevice = mDesiredDevice;
mWifiP2pManager.connect(mWifiP2pChannel, config, new ActionListener() {
@Override
@@ -353,7 +476,7 @@
+ newDevice.deviceName + ", reason=" + reason);
if (mConnectingDevice == newDevice) {
mConnectingDevice = null;
- handleConnectionFailure();
+ handleConnectionFailure(false);
}
}
});
@@ -366,20 +489,20 @@
if (addr == null) {
Slog.i(TAG, "Failed to get local interface address for communicating "
+ "with Wifi display: " + mConnectedDevice.deviceName);
- handleConnectionFailure();
+ handleConnectionFailure(false);
return; // done
}
WifiP2pWfdInfo wfdInfo = mConnectedDevice.wfdInfo;
int port = (wfdInfo != null ? wfdInfo.getControlPort() : DEFAULT_CONTROL_PORT);
- final String name = mConnectedDevice.deviceName;
+ final WifiDisplay display = createWifiDisplay(mConnectedDevice);
final String iface = addr.getHostAddress() + ":" + port;
mPublishedDevice = mConnectedDevice;
mHandler.post(new Runnable() {
@Override
public void run() {
- mListener.onDisplayConnected(name, iface);
+ mListener.onDisplayConnected(display, iface);
}
});
}
@@ -395,7 +518,7 @@
enableWfd();
}
} else {
- mWfdEnabled = false;
+ setWfdEnabled(false);
disconnect();
}
}
@@ -426,7 +549,7 @@
Slog.i(TAG, "Aborting connection to Wifi display because "
+ "the current P2P group does not contain the device "
+ "we expected to find: " + mConnectingDevice.deviceName);
- handleConnectionFailure();
+ handleConnectionFailure(false);
return;
}
@@ -436,7 +559,8 @@
}
if (mConnectingDevice != null && mConnectingDevice == mDesiredDevice) {
- Slog.i(TAG, "Connected to Wifi display: " + mConnectingDevice.deviceName);
+ Slog.i(TAG, "Connected to Wifi display: "
+ + mConnectingDevice.deviceName);
mHandler.removeCallbacks(mConnectionTimeout);
mConnectedDeviceGroupInfo = info;
@@ -459,15 +583,32 @@
Slog.i(TAG, "Timed out waiting for Wifi display connection after "
+ CONNECTION_TIMEOUT_SECONDS + " seconds: "
+ mConnectingDevice.deviceName);
- handleConnectionFailure();
+ handleConnectionFailure(true);
}
}
};
- private void handleConnectionFailure() {
+ private void handleConnectionFailure(boolean timeoutOccurred) {
if (mDesiredDevice != null) {
Slog.i(TAG, "Wifi display connection failed!");
- disconnect();
+
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mListener.onDisplayConnectionFailed();
+ }
+ });
+
+ if (mConnectionRetriesLeft > 0) {
+ mHandler.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ retryConnection();
+ }
+ }, timeoutOccurred ? 0 : CONNECT_RETRY_DELAY_MILLIS);
+ } else {
+ disconnect();
+ }
}
}
@@ -496,12 +637,10 @@
private static boolean isWifiDisplay(WifiP2pDevice device) {
// FIXME: the wfdInfo API doesn't work yet
- return false;
- //return device.deviceName.equals("DWD-300-22ACC2");
- //return device.deviceName.startsWith("DWD-")
- // || device.deviceName.startsWith("DIRECT-")
- // || device.deviceName.startsWith("CAVM-");
- //return device.wfdInfo != null && device.wfdInfo.isWfdEnabled();
+ return device.deviceName.startsWith("DWD-")
+ || device.deviceName.startsWith("DIRECT-")
+ || device.deviceName.startsWith("CAVM-");
+ //device.wfdInfo != null && device.wfdInfo.isWfdEnabled();
}
private static String describeWifiP2pDevice(WifiP2pDevice device) {
@@ -512,6 +651,10 @@
return group != null ? group.toString().replace('\n', ',') : "null";
}
+ private static WifiDisplay createWifiDisplay(WifiP2pDevice device) {
+ return new WifiDisplay(device.deviceAddress, device.deviceName);
+ }
+
private final BroadcastReceiver mWifiP2pReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -549,7 +692,14 @@
* Called on the handler thread when displays are connected or disconnected.
*/
public interface Listener {
- void onDisplayConnected(String deviceName, String iface);
+ void onEnablementChanged(boolean enabled);
+
+ void onScanStarted();
+ void onScanFinished(WifiDisplay[] knownDisplays);
+
+ void onDisplayConnecting(WifiDisplay display);
+ void onDisplayConnectionFailed();
+ void onDisplayConnected(WifiDisplay display, String iface);
void onDisplayDisconnected();
}
}
diff --git a/services/java/com/android/server/power/DisplayPowerController.java b/services/java/com/android/server/power/DisplayPowerController.java
index 4f5561a..5f4a786 100644
--- a/services/java/com/android/server/power/DisplayPowerController.java
+++ b/services/java/com/android/server/power/DisplayPowerController.java
@@ -83,7 +83,8 @@
private static final boolean USE_ELECTRON_BEAM_ON_ANIMATION = false;
// If true, enables the use of the screen auto-brightness adjustment setting.
- private static final boolean USE_SCREEN_AUTO_BRIGHTNESS_ADJUSTMENT = false;
+ private static final boolean USE_SCREEN_AUTO_BRIGHTNESS_ADJUSTMENT =
+ PowerManager.useScreenAutoBrightnessAdjustmentFeature();
// The maximum range of gamma adjustment possible using the screen
// auto-brightness adjustment setting.