Merge "Add support for remembering Wifi display devices." into jb-mr1-dev

This commit is contained in:
Jeff Brown
2012-09-19 22:14:01 -07:00
committed by Android (Google) Code Review
19 changed files with 768 additions and 317 deletions

View File

@@ -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
@@ -177,6 +181,36 @@ public final class DisplayManager {
mGlobal.disconnectWifiDisplay();
}
/**
* 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

View File

@@ -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();

View File

@@ -37,6 +37,12 @@ interface IDisplayManager {
// Requires CONFIGURE_WIFI_DISPLAY permission.
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();
}

View File

@@ -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;
}
}

View File

@@ -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);
}
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 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)
+ "}";
}
}

View File

@@ -213,6 +213,21 @@ public final class Settings {
public static final String ACTION_BLUETOOTH_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>
@@ -5539,6 +5554,13 @@ public final class Settings {
public static final String WEB_AUTOFILL_QUERY_URL =
"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>

View File

@@ -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>

View File

@@ -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" />

View File

@@ -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.

View File

@@ -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();

View File

@@ -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>

View File

@@ -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>

View File

@@ -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);
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;
}
};
}

View File

@@ -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);

View File

@@ -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");
}
}
});

View File

@@ -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>();
@@ -402,6 +405,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)
@@ -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

View File

@@ -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>
* &lt;display-manager-state>
* &lt;remembered-wifi-displays>
* &lt;wifi-display deviceAddress="00:00:00:00:00:00" deviceName="XXXX" deviceAlias="YYYY" />
* &gt;remembered-wifi-displays>
* &gt;/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();
}
}

View File

@@ -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();

View File

@@ -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;
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 @@ 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();