diff options
19 files changed, 770 insertions, 319 deletions
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 @@ public final class DisplayManager { /** * 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 @@ public final class DisplayManager { } /** + * 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 final class DisplayManagerGlobal { } } + 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 @@ interface IDisplayManager { 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 @@ package android.hardware.display; import android.os.Parcel; import android.os.Parcelable; +import libcore.util.Objects; + /** * Describes the properties of a Wifi display. * <p> @@ -30,6 +32,7 @@ import android.os.Parcelable; 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 final class WifiDisplay implements Parcelable { 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 final class WifiDisplay implements Parcelable { } }; - 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 @@ public final class WifiDisplay implements Parcelable { mDeviceAddress = deviceAddress; mDeviceName = deviceName; + mDeviceAlias = deviceAlias; } /** @@ -71,6 +76,25 @@ public final class WifiDisplay implements Parcelable { 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 final class WifiDisplay implements Parcelable { 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 final class WifiDisplay implements Parcelable { public void writeToParcel(Parcel dest, int flags) { dest.writeString(mDeviceAddress); dest.writeString(mDeviceName); + dest.writeString(mDeviceAlias); } @Override @@ -102,6 +128,10 @@ public final class WifiDisplay implements Parcelable { // 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 @@ import java.util.Arrays; /** * 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 @@ import java.util.Arrays; * @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 @@ public final class WifiDisplayStatus implements Parcelable { 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); + } + + WifiDisplay[] rememberedDisplays = WifiDisplay.CREATOR.newArray(in.readInt()); + for (int i = 0; i < rememberedDisplays.length; i++) { + rememberedDisplays[i] = WifiDisplay.CREATOR.createFromParcel(in); } - return new WifiDisplayStatus(enabled, scanState, activeDisplayState, - activeDisplay, knownDisplays); + return new WifiDisplayStatus(featureState, scanState, activeDisplayState, + activeDisplay, availableDisplays, rememberedDisplays); } public WifiDisplayStatus[] newArray(int size) { @@ -70,33 +90,38 @@ public final class WifiDisplayStatus implements Parcelable { }; 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 @@ public final class WifiDisplayStatus implements Parcelable { } /** - * 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 @@ public final class WifiDisplayStatus implements Parcelable { 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 @@ public final class WifiDisplayStatus implements Parcelable { // 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 @@ public final class Settings { "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 @@ public final class Settings { "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..ab8e961 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 @@ public class DatabaseHelper extends SQLiteOpenHelper { 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 @@ class QuickSettings { private DisplayManager mDisplayManager; private WifiDisplayStatus mWifiDisplayStatus; - private WifiDisplayListAdapter mWifiDisplayListAdapter; - + private BrightnessController mBrightnessController; private BluetoothController mBluetoothController; private Dialog mBrightnessDialog; @@ -111,7 +110,6 @@ class QuickSettings { 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 @@ class QuickSettings { 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 @@ class QuickSettings { } } - // 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 @@ class QuickSettings { if (intent.getAction().equals(DisplayManager.ACTION_WIFI_DISPLAY_STATUS_CHANGED)) { WifiDisplayStatus status = (WifiDisplayStatus)intent.getParcelableExtra( DisplayManager.EXTRA_WIFI_DISPLAY_STATUS); - applyWifiDisplayStatus(status); - } - } - }; - - 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()); + mWifiDisplayStatus = status; + applyWifiDisplayStatus(); } - 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 @@ class QuickSettingsModel implements BluetoothStateChangeCallback, 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 @@ class ServerThread extends Thread { // 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 @@ public final class DisplayManagerService extends IDisplayManager.Stub { 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 @@ public final class DisplayManagerService extends IDisplayManager.Stub { } @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 @@ public final class DisplayManagerService extends IDisplayManager.Stub { 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 @@ import java.util.Arrays; 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 @@ final class WifiDisplayAdapter extends DisplayAdapter { 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 @@ final class WifiDisplayAdapter extends DisplayAdapter { public void registerLocked() { super.registerLocked(); + updateRememberedDisplaysLocked(); + getHandler().post(new Runnable() { @Override public void run() { @@ -135,18 +143,58 @@ final class WifiDisplayAdapter extends DisplayAdapter { }); } + 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 @@ final class WifiDisplayAdapter extends DisplayAdapter { 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 @@ final class WifiDisplayAdapter extends DisplayAdapter { } private void scheduleStatusChangedBroadcastLocked() { + mCurrentStatus = null; if (!mPendingStatusChangeBroadcast) { mPendingStatusChangeBroadcast = true; getHandler().post(mStatusChangeBroadcast); @@ -202,11 +251,10 @@ final class WifiDisplayAdapter extends DisplayAdapter { 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 @@ final class WifiDisplayAdapter extends DisplayAdapter { 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 @@ final class WifiDisplayAdapter extends DisplayAdapter { @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 @@ final class WifiDisplayAdapter extends DisplayAdapter { 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 @@ final class WifiDisplayAdapter extends DisplayAdapter { 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 @@ final class WifiDisplayAdapter extends DisplayAdapter { 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 @@ package com.android.server.display; 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.Channel; 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 @@ final class WifiDisplayController implements DumpUtils.Dump { 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 @@ final class WifiDisplayController implements DumpUtils.Dump { 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 @@ final class WifiDisplayController implements DumpUtils.Dump { 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 @@ final class WifiDisplayController implements DumpUtils.Dump { } public void requestConnect(String address) { - for (WifiP2pDevice device : mKnownWifiDisplayPeers) { + for (WifiP2pDevice device : mAvailableWifiDisplayPeers) { if (device.deviceAddress.equals(address)) { connect(device); } @@ -187,49 +216,65 @@ final class WifiDisplayController implements DumpUtils.Dump { disconnect(); } - private void enableWfd() { - 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."); - } - if (mWfdEnabling) { - mWfdEnabling = false; - setWfdEnabled(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."); + } + if (mWfdEnabling) { + mWfdEnabling = false; + mWfdEnabled = true; + reportFeatureState(); + } } - } - @Override - public void onFailure(int reason) { - if (DEBUG) { - Slog.d(TAG, "Failed to set WFD info with reason " + reason + "."); + @Override + public void onFailure(int reason) { + if (DEBUG) { + Slog.d(TAG, "Failed to set WFD info with reason " + reason + "."); + } + mWfdEnabling = false; } - 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 @@ final class WifiDisplayController implements DumpUtils.Dump { 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 @@ final class WifiDisplayController implements DumpUtils.Dump { } 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 @@ final class WifiDisplayController implements DumpUtils.Dump { } 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 @@ final class WifiDisplayController implements DumpUtils.Dump { 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 @@ final class WifiDisplayController implements DumpUtils.Dump { } 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 @@ final class WifiDisplayController implements DumpUtils.Dump { 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 @@ final class WifiDisplayController implements DumpUtils.Dump { 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 @@ final class WifiDisplayController implements DumpUtils.Dump { } 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 @@ final class WifiDisplayController implements DumpUtils.Dump { 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 @@ final class WifiDisplayController implements DumpUtils.Dump { * 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(); |
