Add support for remembering Wifi display devices.
Add a setting to globally disable Wifi display.
Fixed a bug where the wifi display broadcast receiver
was running on the wrong thread.
Removed the wifi-display QuickSettings dialog, all functionality
has been moved to Settings.
Bug: 7178216
Bug: 7192799
Change-Id: I9796baac8245d664cf28fa147b9ed978d81d8ab9
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 4347e75..58a0f13 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -160,6 +160,10 @@
/**
* Connects to a Wifi display.
* The results are sent as a {@link #ACTION_WIFI_DISPLAY_STATUS_CHANGED} broadcast.
+ * <p>
+ * Automatically remembers the display after a successful connection, if not
+ * already remembered.
+ * </p>
*
* @param deviceAddress The MAC address of the device to which we should connect.
* @hide
@@ -178,6 +182,36 @@
}
/**
+ * Renames a Wifi display.
+ * <p>
+ * The display must already be remembered for this call to succeed. In other words,
+ * we must already have successfully connected to the display at least once and then
+ * not forgotten it.
+ * </p>
+ *
+ * @param deviceAddress The MAC address of the device to rename.
+ * @param alias The alias name by which to remember the device, or null
+ * or empty if no alias should be used.
+ * @hide
+ */
+ public void renameWifiDisplay(String deviceAddress, String alias) {
+ mGlobal.renameWifiDisplay(deviceAddress, alias);
+ }
+
+ /**
+ * Forgets a previously remembered Wifi display.
+ * <p>
+ * Automatically disconnects from the display if currently connected to it.
+ * </p>
+ *
+ * @param deviceAddress The MAC address of the device to forget.
+ * @hide
+ */
+ public void forgetWifiDisplay(String deviceAddress) {
+ mGlobal.forgetWifiDisplay(deviceAddress);
+ }
+
+ /**
* Gets the current Wifi display status.
* Watch for changes in the status by registering a broadcast receiver for
* {@link #ACTION_WIFI_DISPLAY_STATUS_CHANGED}.
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index 14b5440..a858681 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -281,6 +281,31 @@
}
}
+ public void renameWifiDisplay(String deviceAddress, String alias) {
+ if (deviceAddress == null) {
+ throw new IllegalArgumentException("deviceAddress must not be null");
+ }
+
+ try {
+ mDm.renameWifiDisplay(deviceAddress, alias);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Failed to rename Wifi display " + deviceAddress
+ + " with alias " + alias + ".", ex);
+ }
+ }
+
+ public void forgetWifiDisplay(String deviceAddress) {
+ if (deviceAddress == null) {
+ throw new IllegalArgumentException("deviceAddress must not be null");
+ }
+
+ try {
+ mDm.forgetWifiDisplay(deviceAddress);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Failed to forget Wifi display.", ex);
+ }
+ }
+
public WifiDisplayStatus getWifiDisplayStatus() {
try {
return mDm.getWifiDisplayStatus();
diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl
index 36a9a7f..4b6fb53 100644
--- a/core/java/android/hardware/display/IDisplayManager.aidl
+++ b/core/java/android/hardware/display/IDisplayManager.aidl
@@ -38,5 +38,11 @@
void disconnectWifiDisplay();
// Requires CONFIGURE_WIFI_DISPLAY permission.
+ void renameWifiDisplay(String address, String alias);
+
+ // Requires CONFIGURE_WIFI_DISPLAY permission.
+ void forgetWifiDisplay(String address);
+
+ // Requires CONFIGURE_WIFI_DISPLAY permission.
WifiDisplayStatus getWifiDisplayStatus();
}
diff --git a/core/java/android/hardware/display/WifiDisplay.java b/core/java/android/hardware/display/WifiDisplay.java
index e51e97e..0138b1c 100644
--- a/core/java/android/hardware/display/WifiDisplay.java
+++ b/core/java/android/hardware/display/WifiDisplay.java
@@ -19,6 +19,8 @@
import android.os.Parcel;
import android.os.Parcelable;
+import libcore.util.Objects;
+
/**
* Describes the properties of a Wifi display.
* <p>
@@ -30,6 +32,7 @@
public final class WifiDisplay implements Parcelable {
private final String mDeviceAddress;
private final String mDeviceName;
+ private final String mDeviceAlias;
public static final WifiDisplay[] EMPTY_ARRAY = new WifiDisplay[0];
@@ -37,7 +40,8 @@
public WifiDisplay createFromParcel(Parcel in) {
String deviceAddress = in.readString();
String deviceName = in.readString();
- return new WifiDisplay(deviceAddress, deviceName);
+ String deviceAlias = in.readString();
+ return new WifiDisplay(deviceAddress, deviceName, deviceAlias);
}
public WifiDisplay[] newArray(int size) {
@@ -45,7 +49,7 @@
}
};
- public WifiDisplay(String deviceAddress, String deviceName) {
+ public WifiDisplay(String deviceAddress, String deviceName, String deviceAlias) {
if (deviceAddress == null) {
throw new IllegalArgumentException("deviceAddress must not be null");
}
@@ -55,6 +59,7 @@
mDeviceAddress = deviceAddress;
mDeviceName = deviceName;
+ mDeviceAlias = deviceAlias;
}
/**
@@ -71,6 +76,25 @@
return mDeviceName;
}
+ /**
+ * Gets the user-specified alias of the Wifi display device, or null if none.
+ * <p>
+ * The alias should be used in the UI whenever available. It is the value
+ * provided by the user when renaming the device.
+ * </p>
+ */
+ public String getDeviceAlias() {
+ return mDeviceAlias;
+ }
+
+ /**
+ * Gets the name to show in the UI.
+ * Uses the device alias if available, otherwise uses the device name.
+ */
+ public String getFriendlyDisplayName() {
+ return mDeviceAlias != null ? mDeviceAlias : mDeviceName;
+ }
+
@Override
public boolean equals(Object o) {
return o instanceof WifiDisplay && equals((WifiDisplay)o);
@@ -79,7 +103,8 @@
public boolean equals(WifiDisplay other) {
return other != null
&& mDeviceAddress.equals(other.mDeviceAddress)
- && mDeviceName.equals(other.mDeviceName);
+ && mDeviceName.equals(other.mDeviceName)
+ && Objects.equal(mDeviceAlias, other.mDeviceAlias);
}
@Override
@@ -92,6 +117,7 @@
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(mDeviceAddress);
dest.writeString(mDeviceName);
+ dest.writeString(mDeviceAlias);
}
@Override
@@ -102,6 +128,10 @@
// For debugging purposes only.
@Override
public String toString() {
- return mDeviceName + " (" + mDeviceAddress + ")";
+ String result = mDeviceName + " (" + mDeviceAddress + ")";
+ if (mDeviceAlias != null) {
+ result += ", alias " + mDeviceAlias;
+ }
+ return result;
}
}
diff --git a/core/java/android/hardware/display/WifiDisplayStatus.java b/core/java/android/hardware/display/WifiDisplayStatus.java
index d5fe45d..f7e72c4 100644
--- a/core/java/android/hardware/display/WifiDisplayStatus.java
+++ b/core/java/android/hardware/display/WifiDisplayStatus.java
@@ -23,7 +23,7 @@
/**
* Describes the current global state of Wifi display connectivity, including the
- * currently connected display and all known displays.
+ * currently connected display and all available or remembered displays.
* <p>
* This object is immutable.
* </p>
@@ -31,22 +31,37 @@
* @hide
*/
public final class WifiDisplayStatus implements Parcelable {
- private final boolean mEnabled;
+ private final int mFeatureState;
private final int mScanState;
private final int mActiveDisplayState;
private final WifiDisplay mActiveDisplay;
- private final WifiDisplay[] mKnownDisplays;
+ private final WifiDisplay[] mAvailableDisplays;
+ private final WifiDisplay[] mRememberedDisplays;
+ /** Feature state: Wifi display is not available on this device. */
+ public static final int FEATURE_STATE_UNAVAILABLE = 0;
+ /** Feature state: Wifi display is disabled, probably because Wifi is disabled. */
+ public static final int FEATURE_STATE_DISABLED = 1;
+ /** Feature state: Wifi display is turned off in settings. */
+ public static final int FEATURE_STATE_OFF = 2;
+ /** Feature state: Wifi display is turned on in settings. */
+ public static final int FEATURE_STATE_ON = 3;
+
+ /** Scan state: Not currently scanning. */
public static final int SCAN_STATE_NOT_SCANNING = 0;
+ /** Scan state: Currently scanning. */
public static final int SCAN_STATE_SCANNING = 1;
+ /** Display state: Not connected. */
public static final int DISPLAY_STATE_NOT_CONNECTED = 0;
+ /** Display state: Connecting to active display. */
public static final int DISPLAY_STATE_CONNECTING = 1;
+ /** Display state: Connected to active display. */
public static final int DISPLAY_STATE_CONNECTED = 2;
public static final Creator<WifiDisplayStatus> CREATOR = new Creator<WifiDisplayStatus>() {
public WifiDisplayStatus createFromParcel(Parcel in) {
- boolean enabled = (in.readInt() != 0);
+ int featureState = in.readInt();
int scanState = in.readInt();
int activeDisplayState= in.readInt();
@@ -55,13 +70,18 @@
activeDisplay = 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);
+ WifiDisplay[] availableDisplays = WifiDisplay.CREATOR.newArray(in.readInt());
+ for (int i = 0; i < availableDisplays.length; i++) {
+ availableDisplays[i] = WifiDisplay.CREATOR.createFromParcel(in);
}
- return new WifiDisplayStatus(enabled, scanState, activeDisplayState,
- activeDisplay, knownDisplays);
+ WifiDisplay[] rememberedDisplays = WifiDisplay.CREATOR.newArray(in.readInt());
+ for (int i = 0; i < rememberedDisplays.length; i++) {
+ rememberedDisplays[i] = WifiDisplay.CREATOR.createFromParcel(in);
+ }
+
+ return new WifiDisplayStatus(featureState, scanState, activeDisplayState,
+ activeDisplay, availableDisplays, rememberedDisplays);
}
public WifiDisplayStatus[] newArray(int size) {
@@ -70,33 +90,38 @@
};
public WifiDisplayStatus() {
- this(false, SCAN_STATE_NOT_SCANNING, DISPLAY_STATE_NOT_CONNECTED,
- null, WifiDisplay.EMPTY_ARRAY);
+ this(FEATURE_STATE_UNAVAILABLE, SCAN_STATE_NOT_SCANNING, DISPLAY_STATE_NOT_CONNECTED,
+ null, WifiDisplay.EMPTY_ARRAY, WifiDisplay.EMPTY_ARRAY);
}
- public WifiDisplayStatus(boolean enabled, int scanState, int activeDisplayState,
- WifiDisplay activeDisplay, WifiDisplay[] knownDisplays) {
- if (knownDisplays == null) {
- throw new IllegalArgumentException("knownDisplays must not be null");
+ public WifiDisplayStatus(int featureState, int scanState,
+ int activeDisplayState, WifiDisplay activeDisplay,
+ WifiDisplay[] availableDisplays, WifiDisplay[] rememberedDisplays) {
+ if (availableDisplays == null) {
+ throw new IllegalArgumentException("availableDisplays must not be null");
+ }
+ if (rememberedDisplays == null) {
+ throw new IllegalArgumentException("rememberedDisplays must not be null");
}
- mEnabled = enabled;
+ mFeatureState = featureState;
mScanState = scanState;
mActiveDisplayState = activeDisplayState;
mActiveDisplay = activeDisplay;
- mKnownDisplays = knownDisplays;
+ mAvailableDisplays = availableDisplays;
+ mRememberedDisplays = rememberedDisplays;
}
/**
- * Returns true if the Wifi display feature is enabled and available for use.
+ * Returns the state of the Wifi display feature on this device.
* <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.
+ * The value of this property reflects whether the device supports the Wifi display,
+ * whether it has been enabled by the user and whether the prerequisites for
+ * connecting to displays have been met.
* </p>
*/
- public boolean isEnabled() {
- return mEnabled;
+ public int getFeatureState() {
+ return mFeatureState;
}
/**
@@ -127,15 +152,29 @@
}
/**
- * Gets the list of all known Wifi displays, never null.
+ * Gets the list of all available Wifi displays as reported by the most recent
+ * scan, never null.
+ * <p>
+ * Some of these displays may already be remembered, others may be unknown.
+ * </p>
*/
- public WifiDisplay[] getKnownDisplays() {
- return mKnownDisplays;
+ public WifiDisplay[] getAvailableDisplays() {
+ return mAvailableDisplays;
+ }
+
+ /**
+ * Gets the list of all remembered Wifi displays, never null.
+ * <p>
+ * Not all remembered displays will necessarily be available.
+ * </p>
+ */
+ public WifiDisplay[] getRememberedDisplays() {
+ return mRememberedDisplays;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(mEnabled ? 1 : 0);
+ dest.writeInt(mFeatureState);
dest.writeInt(mScanState);
dest.writeInt(mActiveDisplayState);
@@ -146,8 +185,13 @@
dest.writeInt(0);
}
- dest.writeInt(mKnownDisplays.length);
- for (WifiDisplay display : mKnownDisplays) {
+ dest.writeInt(mAvailableDisplays.length);
+ for (WifiDisplay display : mAvailableDisplays) {
+ display.writeToParcel(dest, flags);
+ }
+
+ dest.writeInt(mRememberedDisplays.length);
+ for (WifiDisplay display : mRememberedDisplays) {
display.writeToParcel(dest, flags);
}
}
@@ -160,11 +204,12 @@
// For debugging purposes only.
@Override
public String toString() {
- return "WifiDisplayStatus{enabled=" + mEnabled
+ return "WifiDisplayStatus{featureState=" + mFeatureState
+ ", scanState=" + mScanState
+ ", activeDisplayState=" + mActiveDisplayState
+ ", activeDisplay=" + mActiveDisplay
- + ", knownDisplays=" + Arrays.toString(mKnownDisplays)
+ + ", availableDisplays=" + Arrays.toString(mAvailableDisplays)
+ + ", rememberedDisplays=" + Arrays.toString(mRememberedDisplays)
+ "}";
}
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 2cda5a8..bb118b2 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -214,6 +214,21 @@
"android.settings.BLUETOOTH_SETTINGS";
/**
+ * Activity Action: Show settings to allow configuration of Wifi Displays.
+ * <p>
+ * In some cases, a matching Activity may not exist, so ensure you
+ * safeguard against this.
+ * <p>
+ * Input: Nothing.
+ * <p>
+ * Output: Nothing.
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_WIFI_DISPLAY_SETTINGS =
+ "android.settings.WIFI_DISPLAY_SETTINGS";
+
+ /**
* Activity Action: Show settings to allow configuration of date and time.
* <p>
* In some cases, a matching Activity may not exist, so ensure you
@@ -5540,6 +5555,13 @@
"web_autofill_query_url";
/**
+ * Whether Wifi display is enabled/disabled
+ * 0=disabled. 1=enabled.
+ * @hide
+ */
+ public static final String WIFI_DISPLAY_ON = "wifi_display_on";
+
+ /**
* Whether to notify the user of open networks.
* <p>
* If not connected and the scan results have an open network, we will
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 21e8d76..ab183a3 100755
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -946,4 +946,18 @@
players. -->
<integer name="config_safe_media_volume_index">10</integer>
+ <!-- Whether WiFi display is supported by this device.
+ There are many prerequisites for this feature to work correctly.
+ Here are a few of them:
+ * The WiFi radio must support WiFi P2P.
+ * The WiFi radio must support concurrent connections to the WiFi display and
+ to an access point.
+ * The Audio Flinger audio_policy.conf file must specify a rule for the "r_submix"
+ remote submix module. This module is used to record and stream system
+ audio output to the WiFi display encoder in the media server.
+ * The remote submix module "audio.r_submix.default" must be installed on the device.
+ * The device must be provisioned with HDCP keys (for protected content).
+ -->
+ <bool name="config_enableWifiDisplay">false</bool>
+
</resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 5612360..54e3b06 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -268,6 +268,7 @@
<java-symbol type="bool" name="config_sendAudioBecomingNoisy" />
<java-symbol type="bool" name="config_enableScreenshotChord" />
<java-symbol type="bool" name="config_bluetooth_default_profiles" />
+ <java-symbol type="bool" name="config_enableWifiDisplay" />
<java-symbol type="integer" name="config_cursorWindowSize" />
<java-symbol type="integer" name="config_longPressOnPowerBehavior" />
diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index 92261da..ab8e961b 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -34,6 +34,7 @@
<bool name="def_haptic_feedback">true</bool>
<bool name="def_bluetooth_on">false</bool>
+ <bool name="def_wifi_display_on">false</bool>
<bool name="def_install_non_market_apps">false</bool>
<bool name="def_package_verifier_enable">true</bool>
<!-- Comma-separated list of location providers.
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index 8a847e1..8275b25 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -1995,6 +1995,9 @@
loadIntegerSetting(stmt, Settings.Global.WIFI_MAX_DHCP_RETRY_COUNT,
R.integer.def_max_dhcp_retries);
+ loadBooleanSetting(stmt, Settings.Global.WIFI_DISPLAY_ON,
+ R.bool.def_wifi_display_on);
+
// --- New global settings start here
} finally {
if (stmt != null) stmt.close();
diff --git a/packages/SystemUI/res/layout/wifi_display_dialog.xml b/packages/SystemUI/res/layout/wifi_display_dialog.xml
deleted file mode 100644
index a78096e..0000000
--- a/packages/SystemUI/res/layout/wifi_display_dialog.xml
+++ /dev/null
@@ -1,38 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <ListView android:id="@+id/list"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_weight="2" />
-
- <Button android:id="@+id/scan"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:text="@string/wifi_display_scan" />
-
- <Button android:id="@+id/disconnect"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:text="@string/wifi_display_disconnect" />
-</LinearLayout>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index cbd9957..4545706 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -445,22 +445,4 @@
<string name="quick_settings_brightness_dialog_title">Brightness</string>
<!-- QuickSettings: Brightness dialog auto brightness button [CHAR LIMIT=NONE] -->
<string name="quick_settings_brightness_dialog_auto_brightness_label">AUTO</string>
-
- <!-- Wifi display: Scan button text [CHAR LIMIT=15] -->
- <string name="wifi_display_scan">Scan</string>
-
- <!-- Wifi display: Disconnect button text [CHAR LIMIT=15] -->
- <string name="wifi_display_disconnect">Disconnect</string>
-
- <!-- Wifi display: Quick setting dialog title [CHAR LIMIT=30] -->
- <string name="wifi_display_dialog_title">Wifi Display</string>
-
- <!-- Wifi display: Subtitle text shown to indicate that a display is available [CHAR LIMIT=30] -->
- <string name="wifi_display_state_available">Available</string>
-
- <!-- Wifi display: Subtitle text shown to indicate that a display is connecting [CHAR LIMIT=30] -->
- <string name="wifi_display_state_connecting">Connecting</string>
-
- <!-- Wifi display: Subtitle text shown to indicate that a display is connected [CHAR LIMIT=30] -->
- <string name="wifi_display_state_connected">Connected</string>
</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java
index 9fb6d7c..1b28045 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java
@@ -81,8 +81,7 @@
private DisplayManager mDisplayManager;
private WifiDisplayStatus mWifiDisplayStatus;
- private WifiDisplayListAdapter mWifiDisplayListAdapter;
-
+
private BrightnessController mBrightnessController;
private BluetoothController mBluetoothController;
private Dialog mBrightnessDialog;
@@ -111,7 +110,6 @@
mContainerView = container;
mModel = new QuickSettingsModel(context);
mWifiDisplayStatus = new WifiDisplayStatus();
- mWifiDisplayListAdapter = new WifiDisplayListAdapter(context);
Resources r = mContext.getResources();
mBatteryLevels = (LevelListDrawable) r.getDrawable(R.drawable.qs_sys_battery);
@@ -483,8 +481,7 @@
wifiDisplayTile.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
- mBar.collapseAllPanels(true);
- showWifiDisplayDialog();
+ startSettingsActivity(android.provider.Settings.ACTION_WIFI_DISPLAY_SETTINGS);
}
});
mModel.addWifiDisplayTile(wifiDisplayTile, new QuickSettingsModel.RefreshCallback() {
@@ -578,71 +575,13 @@
}
}
- // Wifi Display
- private void showWifiDisplayDialog() {
- mDisplayManager.scanWifiDisplays();
- updateWifiDisplayStatus();
-
- Dialog dialog = new Dialog(mContext);
- dialog.setContentView(R.layout.wifi_display_dialog);
- dialog.setCanceledOnTouchOutside(true);
- dialog.setTitle(R.string.wifi_display_dialog_title);
-
- Button scanButton = (Button)dialog.findViewById(R.id.scan);
- scanButton.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- mDisplayManager.scanWifiDisplays();
- }
- });
-
- Button disconnectButton = (Button)dialog.findViewById(R.id.disconnect);
- disconnectButton.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- mDisplayManager.disconnectWifiDisplay();
- }
- });
-
- ListView list = (ListView)dialog.findViewById(R.id.list);
- list.setAdapter(mWifiDisplayListAdapter);
- list.setOnItemClickListener(new AdapterView.OnItemClickListener() {
- @Override
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- WifiDisplay display = mWifiDisplayListAdapter.getItem(position);
- mDisplayManager.connectWifiDisplay(display.getDeviceAddress());
- }
- });
-
- dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
- dialog.show();
- }
-
private void updateWifiDisplayStatus() {
- applyWifiDisplayStatus(mDisplayManager.getWifiDisplayStatus());
+ mWifiDisplayStatus = mDisplayManager.getWifiDisplayStatus();
+ applyWifiDisplayStatus();
}
- private void applyWifiDisplayStatus(WifiDisplayStatus status) {
- mWifiDisplayStatus = status;
-
- mWifiDisplayListAdapter.clear();
- mWifiDisplayListAdapter.addAll(status.getKnownDisplays());
- if (status.getActiveDisplay() != null
- && !contains(status.getKnownDisplays(), status.getActiveDisplay())) {
- mWifiDisplayListAdapter.add(status.getActiveDisplay());
- }
- mWifiDisplayListAdapter.sort(mWifiDisplayComparator);
-
- mModel.onWifiDisplayStateChanged(status);
- }
-
- private static boolean contains(WifiDisplay[] displays, WifiDisplay display) {
- for (WifiDisplay d : displays) {
- if (d.equals(display)) {
- return true;
- }
- }
- return false;
+ private void applyWifiDisplayStatus() {
+ mModel.onWifiDisplayStateChanged(mWifiDisplayStatus);
}
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@@ -651,59 +590,9 @@
if (intent.getAction().equals(DisplayManager.ACTION_WIFI_DISPLAY_STATUS_CHANGED)) {
WifiDisplayStatus status = (WifiDisplayStatus)intent.getParcelableExtra(
DisplayManager.EXTRA_WIFI_DISPLAY_STATUS);
- applyWifiDisplayStatus(status);
+ mWifiDisplayStatus = status;
+ applyWifiDisplayStatus();
}
}
};
-
- private final class WifiDisplayListAdapter extends ArrayAdapter<WifiDisplay> {
- private final LayoutInflater mInflater;
-
- public WifiDisplayListAdapter(Context context) {
- super(context, android.R.layout.simple_list_item_2);
- mInflater = LayoutInflater.from(context);
- }
-
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- WifiDisplay item = getItem(position);
- View view = convertView;
- if (view == null) {
- view = mInflater.inflate(android.R.layout.simple_list_item_2,
- parent, false);
- }
- TextView headline = (TextView) view.findViewById(android.R.id.text1);
- TextView subText = (TextView) view.findViewById(android.R.id.text2);
- headline.setText(item.getDeviceName());
-
- int state = WifiDisplayStatus.DISPLAY_STATE_NOT_CONNECTED;
- if (item.equals(mWifiDisplayStatus.getActiveDisplay())) {
- state = mWifiDisplayStatus.getActiveDisplayState();
- }
- switch (state) {
- case WifiDisplayStatus.DISPLAY_STATE_CONNECTING:
- subText.setText(R.string.wifi_display_state_connecting);
- break;
- case WifiDisplayStatus.DISPLAY_STATE_CONNECTED:
- subText.setText(R.string.wifi_display_state_connected);
- break;
- case WifiDisplayStatus.DISPLAY_STATE_NOT_CONNECTED:
- default:
- subText.setText(R.string.wifi_display_state_available);
- break;
- }
- return view;
- }
- }
-
- private final Comparator<WifiDisplay> mWifiDisplayComparator = new Comparator<WifiDisplay>() {
- @Override
- public int compare(WifiDisplay lhs, WifiDisplay rhs) {
- int c = lhs.getDeviceName().compareToIgnoreCase(rhs.getDeviceName());
- if (c == 0) {
- c = lhs.getDeviceAddress().compareToIgnoreCase(rhs.getDeviceAddress());
- }
- return c;
- }
- };
}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java
index 485b3e5..724df34 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java
@@ -347,9 +347,10 @@
mWifiDisplayCallback = cb;
}
public void onWifiDisplayStateChanged(WifiDisplayStatus status) {
- mWifiDisplayState.enabled = status.isEnabled();
+ mWifiDisplayState.enabled =
+ (status.getFeatureState() != WifiDisplayStatus.FEATURE_STATE_UNAVAILABLE);
if (status.getActiveDisplay() != null) {
- mWifiDisplayState.label = status.getActiveDisplay().getDeviceName();
+ mWifiDisplayState.label = status.getActiveDisplay().getFriendlyDisplayName();
} else {
mWifiDisplayState.label = mContext.getString(
R.string.quick_settings_wifi_display_no_connection_label);
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 90783b7..1396d8b 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -189,7 +189,7 @@
// For debug builds, log event loop stalls to dropbox for analysis.
if (StrictMode.conditionallyEnableDebugLogging()) {
- Slog.i(TAG, "Enabled StrictMode logging for UI Looper");
+ Slog.i(TAG, "Enabled StrictMode logging for WM Looper");
}
}
});
diff --git a/services/java/com/android/server/display/DisplayManagerService.java b/services/java/com/android/server/display/DisplayManagerService.java
index 39f2418..02fc6b1 100644
--- a/services/java/com/android/server/display/DisplayManagerService.java
+++ b/services/java/com/android/server/display/DisplayManagerService.java
@@ -148,6 +148,9 @@
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>();
@@ -403,6 +406,50 @@
}
@Override // Binder call
+ public void renameWifiDisplay(String address, String alias) {
+ 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) {
+ if (mWifiDisplayAdapter != null) {
+ mWifiDisplayAdapter.requestRenameLocked(address, alias);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override // Binder call
+ public void forgetWifiDisplay(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) {
+ if (mWifiDisplayAdapter != null) {
+ mWifiDisplayAdapter.requestForgetLocked(address);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override // Binder call
public WifiDisplayStatus getWifiDisplayStatus() {
if (mContext.checkCallingPermission(android.Manifest.permission.CONFIGURE_WIFI_DISPLAY)
!= PackageManager.PERMISSION_GRANTED) {
@@ -439,15 +486,27 @@
private void registerAdditionalDisplayAdapters() {
synchronized (mSyncRoot) {
if (shouldRegisterNonEssentialDisplayAdaptersLocked()) {
- registerDisplayAdapterLocked(new OverlayDisplayAdapter(
- mSyncRoot, mContext, mHandler, mDisplayAdapterListener, mUiHandler));
- mWifiDisplayAdapter = new WifiDisplayAdapter(
- mSyncRoot, mContext, mHandler, mDisplayAdapterListener);
- registerDisplayAdapterLocked(mWifiDisplayAdapter);
+ registerOverlayDisplayAdapterLocked();
+ registerWifiDisplayAdapterLocked();
}
}
}
+ 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)) {
+ mWifiDisplayAdapter = new WifiDisplayAdapter(
+ mSyncRoot, mContext, mHandler, mDisplayAdapterListener,
+ mPersistentDataStore);
+ registerDisplayAdapterLocked(mWifiDisplayAdapter);
+ }
+ }
+
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
diff --git a/services/java/com/android/server/display/PersistentDataStore.java b/services/java/com/android/server/display/PersistentDataStore.java
new file mode 100644
index 0000000..6e7717e
--- /dev/null
+++ b/services/java/com/android/server/display/PersistentDataStore.java
@@ -0,0 +1,288 @@
+/*
+ * 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.FastXmlSerializer;
+import com.android.internal.util.XmlUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import android.hardware.display.WifiDisplay;
+import android.util.AtomicFile;
+import android.util.Slog;
+import android.util.Xml;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+
+import libcore.io.IoUtils;
+import libcore.util.Objects;
+
+/**
+ * Manages persistent state recorded by the display manager service as an XML file.
+ * Caller must acquire lock on the data store before accessing it.
+ *
+ * File format:
+ * <code>
+ * <display-manager-state>
+ * <remembered-wifi-displays>
+ * <wifi-display deviceAddress="00:00:00:00:00:00" deviceName="XXXX" deviceAlias="YYYY" />
+ * >remembered-wifi-displays>
+ * >/display-manager-state>
+ * </code>
+ *
+ * TODO: refactor this to extract common code shared with the input manager's data store
+ */
+final class PersistentDataStore {
+ static final String TAG = "DisplayManager";
+
+ // Remembered Wifi display devices.
+ private ArrayList<WifiDisplay> mRememberedWifiDisplays = new ArrayList<WifiDisplay>();
+
+ // The atomic file used to safely read or write the file.
+ private final AtomicFile mAtomicFile;
+
+ // True if the data has been loaded.
+ private boolean mLoaded;
+
+ // True if there are changes to be saved.
+ private boolean mDirty;
+
+ public PersistentDataStore() {
+ mAtomicFile = new AtomicFile(new File("/data/system/display-manager-state.xml"));
+ }
+
+ public void saveIfNeeded() {
+ if (mDirty) {
+ save();
+ mDirty = false;
+ }
+ }
+
+ public WifiDisplay[] getRememberedWifiDisplays() {
+ loadIfNeeded();
+ return mRememberedWifiDisplays.toArray(new WifiDisplay[mRememberedWifiDisplays.size()]);
+ }
+
+ public WifiDisplay applyWifiDisplayAlias(WifiDisplay display) {
+ if (display != null) {
+ loadIfNeeded();
+
+ int index = findRememberedWifiDisplay(display.getDeviceAddress());
+ if (index >= 0) {
+ return mRememberedWifiDisplays.get(index);
+ }
+ }
+ return display;
+ }
+
+ public WifiDisplay[] applyWifiDisplayAliases(WifiDisplay[] displays) {
+ WifiDisplay[] results = displays;
+ if (results != null) {
+ int count = displays.length;
+ for (int i = 0; i < count; i++) {
+ WifiDisplay result = applyWifiDisplayAlias(displays[i]);
+ if (result != displays[i]) {
+ if (results == displays) {
+ results = new WifiDisplay[count];
+ System.arraycopy(displays, 0, results, 0, count);
+ }
+ results[i] = result;
+ }
+ }
+ }
+ return results;
+ }
+
+ public boolean rememberWifiDisplay(WifiDisplay display) {
+ loadIfNeeded();
+
+ int index = findRememberedWifiDisplay(display.getDeviceAddress());
+ if (index >= 0) {
+ WifiDisplay other = mRememberedWifiDisplays.get(index);
+ if (other.equals(display)) {
+ return false; // already remembered without change
+ }
+ mRememberedWifiDisplays.set(index, display);
+ } else {
+ mRememberedWifiDisplays.add(display);
+ }
+ setDirty();
+ return true;
+ }
+
+ public boolean renameWifiDisplay(String deviceAddress, String alias) {
+ int index = findRememberedWifiDisplay(deviceAddress);
+ if (index >= 0) {
+ WifiDisplay display = mRememberedWifiDisplays.get(index);
+ if (Objects.equal(display.getDeviceAlias(), alias)) {
+ return false; // already has this alias
+ }
+ WifiDisplay renamedDisplay = new WifiDisplay(deviceAddress,
+ display.getDeviceName(), alias);
+ mRememberedWifiDisplays.set(index, renamedDisplay);
+ setDirty();
+ return true;
+ }
+ return false;
+ }
+
+ public boolean forgetWifiDisplay(String deviceAddress) {
+ int index = findRememberedWifiDisplay(deviceAddress);
+ if (index >= 0) {
+ mRememberedWifiDisplays.remove(index);
+ setDirty();
+ return true;
+ }
+ return false;
+ }
+
+ private int findRememberedWifiDisplay(String deviceAddress) {
+ int count = mRememberedWifiDisplays.size();
+ for (int i = 0; i < count; i++) {
+ if (mRememberedWifiDisplays.get(i).getDeviceAddress().equals(deviceAddress)) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ private void loadIfNeeded() {
+ if (!mLoaded) {
+ load();
+ mLoaded = true;
+ }
+ }
+
+ private void setDirty() {
+ mDirty = true;
+ }
+
+ private void clearState() {
+ mRememberedWifiDisplays.clear();
+ }
+
+ private void load() {
+ clearState();
+
+ final InputStream is;
+ try {
+ is = mAtomicFile.openRead();
+ } catch (FileNotFoundException ex) {
+ return;
+ }
+
+ XmlPullParser parser;
+ try {
+ parser = Xml.newPullParser();
+ parser.setInput(new BufferedInputStream(is), null);
+ loadFromXml(parser);
+ } catch (IOException ex) {
+ Slog.w(TAG, "Failed to load display manager persistent store data.", ex);
+ clearState();
+ } catch (XmlPullParserException ex) {
+ Slog.w(TAG, "Failed to load display manager persistent store data.", ex);
+ clearState();
+ } finally {
+ IoUtils.closeQuietly(is);
+ }
+ }
+
+ private void save() {
+ final FileOutputStream os;
+ try {
+ os = mAtomicFile.startWrite();
+ boolean success = false;
+ try {
+ XmlSerializer serializer = new FastXmlSerializer();
+ serializer.setOutput(new BufferedOutputStream(os), "utf-8");
+ saveToXml(serializer);
+ serializer.flush();
+ success = true;
+ } finally {
+ if (success) {
+ mAtomicFile.finishWrite(os);
+ } else {
+ mAtomicFile.failWrite(os);
+ }
+ }
+ } catch (IOException ex) {
+ Slog.w(TAG, "Failed to save display manager persistent store data.", ex);
+ }
+ }
+
+ private void loadFromXml(XmlPullParser parser)
+ throws IOException, XmlPullParserException {
+ XmlUtils.beginDocument(parser, "display-manager-state");
+ final int outerDepth = parser.getDepth();
+ while (XmlUtils.nextElementWithin(parser, outerDepth)) {
+ if (parser.getName().equals("remembered-wifi-displays")) {
+ loadRememberedWifiDisplaysFromXml(parser);
+ }
+ }
+ }
+
+ private void loadRememberedWifiDisplaysFromXml(XmlPullParser parser)
+ throws IOException, XmlPullParserException {
+ final int outerDepth = parser.getDepth();
+ while (XmlUtils.nextElementWithin(parser, outerDepth)) {
+ if (parser.getName().equals("wifi-display")) {
+ String deviceAddress = parser.getAttributeValue(null, "deviceAddress");
+ String deviceName = parser.getAttributeValue(null, "deviceName");
+ String deviceAlias = parser.getAttributeValue(null, "deviceAlias");
+ if (deviceAddress == null || deviceName == null) {
+ throw new XmlPullParserException(
+ "Missing deviceAddress or deviceName attribute on wifi-display.");
+ }
+ if (findRememberedWifiDisplay(deviceAddress) >= 0) {
+ throw new XmlPullParserException(
+ "Found duplicate wifi display device address.");
+ }
+
+ mRememberedWifiDisplays.add(
+ new WifiDisplay(deviceAddress, deviceName, deviceAlias));
+ }
+ }
+ }
+
+ private void saveToXml(XmlSerializer serializer) throws IOException {
+ serializer.startDocument(null, true);
+ serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
+ serializer.startTag(null, "display-manager-state");
+ serializer.startTag(null, "remembered-wifi-displays");
+ for (WifiDisplay display : mRememberedWifiDisplays) {
+ serializer.startTag(null, "wifi-display");
+ serializer.attribute(null, "deviceAddress", display.getDeviceAddress());
+ serializer.attribute(null, "deviceName", display.getDeviceName());
+ if (display.getDeviceAlias() != null) {
+ serializer.attribute(null, "deviceAlias", display.getDeviceAlias());
+ }
+ serializer.endTag(null, "wifi-display");
+ }
+ serializer.endTag(null, "remembered-wifi-displays");
+ serializer.endTag(null, "display-manager-state");
+ serializer.endDocument();
+ }
+}
\ No newline at end of file
diff --git a/services/java/com/android/server/display/WifiDisplayAdapter.java b/services/java/com/android/server/display/WifiDisplayAdapter.java
index b57d3dc..1d50ded 100644
--- a/services/java/com/android/server/display/WifiDisplayAdapter.java
+++ b/services/java/com/android/server/display/WifiDisplayAdapter.java
@@ -49,21 +49,26 @@
final class WifiDisplayAdapter extends DisplayAdapter {
private static final String TAG = "WifiDisplayAdapter";
+ private PersistentDataStore mPersistentDataStore;
+
private WifiDisplayController mDisplayController;
private WifiDisplayDevice mDisplayDevice;
private WifiDisplayStatus mCurrentStatus;
- private boolean mEnabled;
+ private int mFeatureState;
private int mScanState;
private int mActiveDisplayState;
private WifiDisplay mActiveDisplay;
- private WifiDisplay[] mKnownDisplays = WifiDisplay.EMPTY_ARRAY;
+ private WifiDisplay[] mAvailableDisplays = WifiDisplay.EMPTY_ARRAY;
+ private WifiDisplay[] mRememberedDisplays = WifiDisplay.EMPTY_ARRAY;
private boolean mPendingStatusChangeBroadcast;
public WifiDisplayAdapter(DisplayManagerService.SyncRoot syncRoot,
- Context context, Handler handler, Listener listener) {
+ Context context, Handler handler, Listener listener,
+ PersistentDataStore persistentDataStore) {
super(syncRoot, context, handler, listener, TAG);
+ mPersistentDataStore = persistentDataStore;
}
@Override
@@ -71,11 +76,12 @@
super.dumpLocked(pw);
pw.println("mCurrentStatus=" + getWifiDisplayStatusLocked());
- pw.println("mEnabled=" + mEnabled);
+ pw.println("mFeatureState=" + mFeatureState);
pw.println("mScanState=" + mScanState);
pw.println("mActiveDisplayState=" + mActiveDisplayState);
pw.println("mActiveDisplay=" + mActiveDisplay);
- pw.println("mKnownDisplays=" + Arrays.toString(mKnownDisplays));
+ pw.println("mAvailableDisplays=" + Arrays.toString(mAvailableDisplays));
+ pw.println("mRememberedDisplays=" + Arrays.toString(mRememberedDisplays));
pw.println("mPendingStatusChangeBroadcast=" + mPendingStatusChangeBroadcast);
// Try to dump the controller state.
@@ -93,6 +99,8 @@
public void registerLocked() {
super.registerLocked();
+ updateRememberedDisplaysLocked();
+
getHandler().post(new Runnable() {
@Override
public void run() {
@@ -135,18 +143,58 @@
});
}
+ public void requestRenameLocked(String address, String alias) {
+ if (alias != null) {
+ alias = alias.trim();
+ if (alias.isEmpty()) {
+ alias = null;
+ }
+ }
+
+ if (mPersistentDataStore.renameWifiDisplay(address, alias)) {
+ mPersistentDataStore.saveIfNeeded();
+ updateRememberedDisplaysLocked();
+ scheduleStatusChangedBroadcastLocked();
+ }
+ }
+
+ public void requestForgetLocked(String address) {
+ if (mPersistentDataStore.forgetWifiDisplay(address)) {
+ mPersistentDataStore.saveIfNeeded();
+ updateRememberedDisplaysLocked();
+ scheduleStatusChangedBroadcastLocked();
+ }
+
+ if (mActiveDisplay != null && mActiveDisplay.getDeviceAddress().equals(address)) {
+ requestDisconnectLocked();
+ }
+ }
+
public WifiDisplayStatus getWifiDisplayStatusLocked() {
if (mCurrentStatus == null) {
- mCurrentStatus = new WifiDisplayStatus(mEnabled, mScanState, mActiveDisplayState,
- mActiveDisplay, mKnownDisplays);
+ mCurrentStatus = new WifiDisplayStatus(
+ mFeatureState, mScanState, mActiveDisplayState,
+ mActiveDisplay, mAvailableDisplays, mRememberedDisplays);
}
return mCurrentStatus;
}
+ private void updateRememberedDisplaysLocked() {
+ mRememberedDisplays = mPersistentDataStore.getRememberedWifiDisplays();
+ mActiveDisplay = mPersistentDataStore.applyWifiDisplayAlias(mActiveDisplay);
+ mAvailableDisplays = mPersistentDataStore.applyWifiDisplayAliases(mAvailableDisplays);
+ }
+
private void handleConnectLocked(WifiDisplay display,
Surface surface, int width, int height, int flags) {
handleDisconnectLocked();
+ if (mPersistentDataStore.rememberWifiDisplay(display)) {
+ mPersistentDataStore.saveIfNeeded();
+ updateRememberedDisplaysLocked();
+ scheduleStatusChangedBroadcastLocked();
+ }
+
int deviceFlags = 0;
if ((flags & RemoteDisplay.DISPLAY_FLAG_SECURE) != 0) {
deviceFlags |= DisplayDeviceInfo.FLAG_SUPPORTS_SECURE_VIDEO_OUTPUT;
@@ -154,7 +202,7 @@
float refreshRate = 60.0f; // TODO: get this for real
- String name = display.getDeviceName();
+ String name = display.getFriendlyDisplayName();
IBinder displayToken = Surface.createDisplay(name);
mDisplayDevice = new WifiDisplayDevice(displayToken, name, width, height,
refreshRate, deviceFlags, surface);
@@ -170,6 +218,7 @@
}
private void scheduleStatusChangedBroadcastLocked() {
+ mCurrentStatus = null;
if (!mPendingStatusChangeBroadcast) {
mPendingStatusChangeBroadcast = true;
getHandler().post(mStatusChangeBroadcast);
@@ -202,11 +251,10 @@
private final WifiDisplayController.Listener mWifiDisplayListener =
new WifiDisplayController.Listener() {
@Override
- public void onEnablementChanged(boolean enabled) {
+ public void onFeatureStateChanged(int featureState) {
synchronized (getSyncRoot()) {
- if (mEnabled != enabled) {
- mCurrentStatus = null;
- mEnabled = enabled;
+ if (mFeatureState != featureState) {
+ mFeatureState = featureState;
scheduleStatusChangedBroadcastLocked();
}
}
@@ -216,20 +264,21 @@
public void onScanStarted() {
synchronized (getSyncRoot()) {
if (mScanState != WifiDisplayStatus.SCAN_STATE_SCANNING) {
- mCurrentStatus = null;
mScanState = WifiDisplayStatus.SCAN_STATE_SCANNING;
scheduleStatusChangedBroadcastLocked();
}
}
}
- public void onScanFinished(WifiDisplay[] knownDisplays) {
+ public void onScanFinished(WifiDisplay[] availableDisplays) {
synchronized (getSyncRoot()) {
+ availableDisplays = mPersistentDataStore.applyWifiDisplayAliases(
+ availableDisplays);
+
if (mScanState != WifiDisplayStatus.SCAN_STATE_NOT_SCANNING
- || !Arrays.equals(mKnownDisplays, knownDisplays)) {
- mCurrentStatus = null;
+ || !Arrays.equals(mAvailableDisplays, availableDisplays)) {
mScanState = WifiDisplayStatus.SCAN_STATE_NOT_SCANNING;
- mKnownDisplays = knownDisplays;
+ mAvailableDisplays = availableDisplays;
scheduleStatusChangedBroadcastLocked();
}
}
@@ -238,10 +287,11 @@
@Override
public void onDisplayConnecting(WifiDisplay display) {
synchronized (getSyncRoot()) {
+ display = mPersistentDataStore.applyWifiDisplayAlias(display);
+
if (mActiveDisplayState != WifiDisplayStatus.DISPLAY_STATE_CONNECTING
|| mActiveDisplay == null
|| !mActiveDisplay.equals(display)) {
- mCurrentStatus = null;
mActiveDisplayState = WifiDisplayStatus.DISPLAY_STATE_CONNECTING;
mActiveDisplay = display;
scheduleStatusChangedBroadcastLocked();
@@ -254,7 +304,6 @@
synchronized (getSyncRoot()) {
if (mActiveDisplayState != WifiDisplayStatus.DISPLAY_STATE_NOT_CONNECTED
|| mActiveDisplay != null) {
- mCurrentStatus = null;
mActiveDisplayState = WifiDisplayStatus.DISPLAY_STATE_NOT_CONNECTED;
mActiveDisplay = null;
scheduleStatusChangedBroadcastLocked();
@@ -266,12 +315,12 @@
public void onDisplayConnected(WifiDisplay display, Surface surface,
int width, int height, int flags) {
synchronized (getSyncRoot()) {
+ display = mPersistentDataStore.applyWifiDisplayAlias(display);
handleConnectLocked(display, surface, width, height, flags);
if (mActiveDisplayState != WifiDisplayStatus.DISPLAY_STATE_CONNECTED
|| mActiveDisplay == null
|| !mActiveDisplay.equals(display)) {
- mCurrentStatus = null;
mActiveDisplayState = WifiDisplayStatus.DISPLAY_STATE_CONNECTED;
mActiveDisplay = display;
scheduleStatusChangedBroadcastLocked();
@@ -287,7 +336,6 @@
if (mActiveDisplayState != WifiDisplayStatus.DISPLAY_STATE_NOT_CONNECTED
|| mActiveDisplay != null) {
- mCurrentStatus = null;
mActiveDisplayState = WifiDisplayStatus.DISPLAY_STATE_NOT_CONNECTED;
mActiveDisplay = null;
scheduleStatusChangedBroadcastLocked();
diff --git a/services/java/com/android/server/display/WifiDisplayController.java b/services/java/com/android/server/display/WifiDisplayController.java
index 328f687..fd0fcc5 100644
--- a/services/java/com/android/server/display/WifiDisplayController.java
+++ b/services/java/com/android/server/display/WifiDisplayController.java
@@ -19,13 +19,17 @@
import com.android.internal.util.DumpUtils;
import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.database.ContentObserver;
import android.hardware.display.WifiDisplay;
+import android.hardware.display.WifiDisplayStatus;
import android.media.AudioManager;
import android.media.RemoteDisplay;
import android.net.NetworkInfo;
+import android.net.Uri;
import android.net.wifi.p2p.WifiP2pConfig;
import android.net.wifi.p2p.WifiP2pDevice;
import android.net.wifi.p2p.WifiP2pDeviceList;
@@ -37,6 +41,7 @@
import android.net.wifi.p2p.WifiP2pManager.GroupInfoListener;
import android.net.wifi.p2p.WifiP2pManager.PeerListListener;
import android.os.Handler;
+import android.provider.Settings;
import android.util.Slog;
import android.view.Surface;
@@ -94,9 +99,12 @@
private boolean mWfdEnabling;
private NetworkInfo mNetworkInfo;
- private final ArrayList<WifiP2pDevice> mKnownWifiDisplayPeers =
+ private final ArrayList<WifiP2pDevice> mAvailableWifiDisplayPeers =
new ArrayList<WifiP2pDevice>();
+ // True if Wifi display is enabled by the user.
+ private boolean mWifiDisplayOnSetting;
+
// True if there is a call to discoverPeers in progress.
private boolean mDiscoverPeersInProgress;
@@ -146,10 +154,31 @@
intentFilter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION);
intentFilter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION);
intentFilter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
- context.registerReceiver(mWifiP2pReceiver, intentFilter);
+ context.registerReceiver(mWifiP2pReceiver, intentFilter, null, mHandler);
+
+ ContentObserver settingsObserver = new ContentObserver(mHandler) {
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ updateSettings();
+ }
+ };
+
+ final ContentResolver resolver = mContext.getContentResolver();
+ resolver.registerContentObserver(Settings.Global.getUriFor(
+ Settings.Global.WIFI_DISPLAY_ON), false, settingsObserver);
+ updateSettings();
+ }
+
+ private void updateSettings() {
+ final ContentResolver resolver = mContext.getContentResolver();
+ mWifiDisplayOnSetting = Settings.Global.getInt(resolver,
+ Settings.Global.WIFI_DISPLAY_ON, 0) != 0;
+
+ updateWfdEnableState();
}
public void dump(PrintWriter pw) {
+ pw.println("mWifiDisplayOnSetting=" + mWifiDisplayOnSetting);
pw.println("mWifiP2pEnabled=" + mWifiP2pEnabled);
pw.println("mWfdEnabled=" + mWfdEnabled);
pw.println("mWfdEnabling=" + mWfdEnabling);
@@ -165,8 +194,8 @@
pw.println("mRemoteDisplayConnected=" + mRemoteDisplayConnected);
pw.println("mRemoteSubmixOn=" + mRemoteSubmixOn);
- pw.println("mKnownWifiDisplayPeers: size=" + mKnownWifiDisplayPeers.size());
- for (WifiP2pDevice device : mKnownWifiDisplayPeers) {
+ pw.println("mAvailableWifiDisplayPeers: size=" + mAvailableWifiDisplayPeers.size());
+ for (WifiP2pDevice device : mAvailableWifiDisplayPeers) {
pw.println(" " + describeWifiP2pDevice(device));
}
}
@@ -176,7 +205,7 @@
}
public void requestConnect(String address) {
- for (WifiP2pDevice device : mKnownWifiDisplayPeers) {
+ for (WifiP2pDevice device : mAvailableWifiDisplayPeers) {
if (device.deviceAddress.equals(address)) {
connect(device);
}
@@ -187,49 +216,65 @@
disconnect();
}
- private void enableWfd() {
- if (!mWfdEnabled && !mWfdEnabling) {
- mWfdEnabling = true;
+ private void updateWfdEnableState() {
+ if (mWifiDisplayOnSetting && mWifiP2pEnabled) {
+ // WFD should be enabled.
+ if (!mWfdEnabled && !mWfdEnabling) {
+ mWfdEnabling = true;
- WifiP2pWfdInfo wfdInfo = new WifiP2pWfdInfo();
- wfdInfo.setWfdEnabled(true);
- wfdInfo.setDeviceType(WifiP2pWfdInfo.WFD_SOURCE);
- wfdInfo.setSessionAvailable(true);
- wfdInfo.setControlPort(DEFAULT_CONTROL_PORT);
- wfdInfo.setMaxThroughput(MAX_THROUGHPUT);
- mWifiP2pManager.setWFDInfo(mWifiP2pChannel, wfdInfo, new ActionListener() {
- @Override
- public void onSuccess() {
- if (DEBUG) {
- Slog.d(TAG, "Successfully set WFD info.");
+ WifiP2pWfdInfo wfdInfo = new WifiP2pWfdInfo();
+ wfdInfo.setWfdEnabled(true);
+ wfdInfo.setDeviceType(WifiP2pWfdInfo.WFD_SOURCE);
+ wfdInfo.setSessionAvailable(true);
+ wfdInfo.setControlPort(DEFAULT_CONTROL_PORT);
+ wfdInfo.setMaxThroughput(MAX_THROUGHPUT);
+ mWifiP2pManager.setWFDInfo(mWifiP2pChannel, wfdInfo, new ActionListener() {
+ @Override
+ public void onSuccess() {
+ if (DEBUG) {
+ Slog.d(TAG, "Successfully set WFD info.");
+ }
+ if (mWfdEnabling) {
+ mWfdEnabling = false;
+ mWfdEnabled = true;
+ reportFeatureState();
+ }
}
- if (mWfdEnabling) {
+
+ @Override
+ public void onFailure(int reason) {
+ if (DEBUG) {
+ Slog.d(TAG, "Failed to set WFD info with reason " + reason + ".");
+ }
mWfdEnabling = false;
- setWfdEnabled(true);
}
- }
-
- @Override
- public void onFailure(int reason) {
- if (DEBUG) {
- Slog.d(TAG, "Failed to set WFD info with reason " + reason + ".");
- }
- mWfdEnabling = false;
- }
- });
+ });
+ }
+ } else {
+ // WFD should be disabled.
+ mWfdEnabling = false;
+ mWfdEnabled = false;
+ reportFeatureState();
+ disconnect();
}
}
- private void setWfdEnabled(final boolean enabled) {
- if (mWfdEnabled != enabled) {
- mWfdEnabled = enabled;
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- mListener.onEnablementChanged(enabled);
- }
- });
+ private void reportFeatureState() {
+ final int featureState = computeFeatureState();
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mListener.onFeatureStateChanged(featureState);
+ }
+ });
+ }
+
+ private int computeFeatureState() {
+ if (!mWifiP2pEnabled) {
+ return WifiDisplayStatus.FEATURE_STATE_DISABLED;
}
+ return mWifiDisplayOnSetting ? WifiDisplayStatus.FEATURE_STATE_ON :
+ WifiDisplayStatus.FEATURE_STATE_OFF;
}
private void discoverPeers() {
@@ -296,14 +341,14 @@
Slog.d(TAG, "Received list of peers.");
}
- mKnownWifiDisplayPeers.clear();
+ mAvailableWifiDisplayPeers.clear();
for (WifiP2pDevice device : peers.getDeviceList()) {
if (DEBUG) {
Slog.d(TAG, " " + describeWifiP2pDevice(device));
}
if (isWifiDisplay(device)) {
- mKnownWifiDisplayPeers.add(device);
+ mAvailableWifiDisplayPeers.add(device);
}
}
@@ -322,10 +367,10 @@
}
private void handleScanFinished() {
- final int count = mKnownWifiDisplayPeers.size();
+ final int count = mAvailableWifiDisplayPeers.size();
final WifiDisplay[] displays = WifiDisplay.CREATOR.newArray(count);
for (int i = 0; i < count; i++) {
- displays[i] = createWifiDisplay(mKnownWifiDisplayPeers.get(i));
+ displays[i] = createWifiDisplay(mAvailableWifiDisplayPeers.get(i));
}
mHandler.post(new Runnable() {
@@ -368,18 +413,11 @@
}
private void retryConnection() {
- if (mDesiredDevice != null && mConnectedDevice != 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();
- }
+ // 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();
}
/**
@@ -513,6 +551,13 @@
if (mConnectingDevice == newDevice) {
Slog.i(TAG, "Failed to initiate connection to Wifi display: "
+ newDevice.deviceName + ", reason=" + reason);
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mListener.onDisplayDisconnected();
+ }
+ });
+
mConnectingDevice = null;
handleConnectionFailure(false);
}
@@ -595,26 +640,13 @@
}
private void handleStateChanged(boolean enabled) {
- if (mWifiP2pEnabled != enabled) {
- mWifiP2pEnabled = enabled;
- if (enabled) {
- if (!mWfdEnabled) {
- enableWfd();
- }
- } else {
- setWfdEnabled(false);
- disconnect();
- }
- }
+ mWifiP2pEnabled = enabled;
+ updateWfdEnableState();
}
private void handlePeersChanged() {
- if (mWifiP2pEnabled) {
- if (mWfdEnabled) {
- requestPeers();
- } else {
- enableWfd();
- }
+ if (mWfdEnabled) {
+ requestPeers();
}
}
@@ -632,7 +664,8 @@
if (mConnectingDevice != null && !info.contains(mConnectingDevice)) {
Slog.i(TAG, "Aborting connection to Wifi display because "
+ "the current P2P group does not contain the device "
- + "we expected to find: " + mConnectingDevice.deviceName);
+ + "we expected to find: " + mConnectingDevice.deviceName
+ + ", group info was: " + describeWifiP2pGroup(info));
handleConnectionFailure(false);
return;
}
@@ -704,10 +737,16 @@
if (mDesiredDevice != null) {
if (mConnectionRetriesLeft > 0) {
+ final WifiP2pDevice oldDevice = mDesiredDevice;
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
- retryConnection();
+ if (mDesiredDevice == oldDevice && mConnectionRetriesLeft > 0) {
+ mConnectionRetriesLeft -= 1;
+ Slog.i(TAG, "Retrying Wifi display connection. Retries left: "
+ + mConnectionRetriesLeft);
+ retryConnection();
+ }
}
}, timeoutOccurred ? 0 : CONNECT_RETRY_DELAY_MILLIS);
} else {
@@ -768,7 +807,7 @@
}
private static WifiDisplay createWifiDisplay(WifiP2pDevice device) {
- return new WifiDisplay(device.deviceAddress, device.deviceName);
+ return new WifiDisplay(device.deviceAddress, device.deviceName, null);
}
private final BroadcastReceiver mWifiP2pReceiver = new BroadcastReceiver() {
@@ -776,6 +815,8 @@
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
if (action.equals(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION)) {
+ // This broadcast is sticky so we'll always get the initial Wifi P2P state
+ // on startup.
boolean enabled = (intent.getIntExtra(WifiP2pManager.EXTRA_WIFI_STATE,
WifiP2pManager.WIFI_P2P_STATE_DISABLED)) ==
WifiP2pManager.WIFI_P2P_STATE_ENABLED;
@@ -808,10 +849,10 @@
* Called on the handler thread when displays are connected or disconnected.
*/
public interface Listener {
- void onEnablementChanged(boolean enabled);
+ void onFeatureStateChanged(int featureState);
void onScanStarted();
- void onScanFinished(WifiDisplay[] knownDisplays);
+ void onScanFinished(WifiDisplay[] availableDisplays);
void onDisplayConnecting(WifiDisplay display);
void onDisplayConnectionFailed();