Keep track of how many clients are requesting scans and scan continuously until all of them are gone then explicitly terminate the scan instead of letting it time out as before. Suspend wifi display scans while connecting or connected to a remote display. This is handled by both the display manager and media router since neither has complete information about what is happening. Much of this code will no longer be needed once wifi display support is integrated directly into the media router service. Ensure that we don't attempt to scan or connect to wifi displays while the wifi display feature is off. Infer when a connection attempt fails and unselect the wifi display route automatically so it doesn't appear to be connecting forever. Fix issues around correctly canceling and retrying connection attempts. Often we would cancel but not retry. Improved connection reliability somewhat. It seems that discovery must already be in progress in order for a connection attempt to succeed. Ensure QuickSettings uses exactly the same logic as the MediaRouteButton to determine when the remote display tile should be made visible. Bug: 11717053 Change-Id: I18afc977b0e8c26204b8c96adaa79f05225f7b6e
761 lines
29 KiB
Java
761 lines
29 KiB
Java
/*
|
|
* Copyright (C) 2012 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package com.android.server.display;
|
|
|
|
import com.android.internal.R;
|
|
import com.android.internal.util.DumpUtils;
|
|
import com.android.internal.util.IndentingPrintWriter;
|
|
|
|
import android.app.Notification;
|
|
import android.app.NotificationManager;
|
|
import android.app.PendingIntent;
|
|
import android.content.BroadcastReceiver;
|
|
import android.content.Context;
|
|
import android.content.Intent;
|
|
import android.content.IntentFilter;
|
|
import android.content.res.Resources;
|
|
import android.hardware.display.DisplayManager;
|
|
import android.hardware.display.WifiDisplay;
|
|
import android.hardware.display.WifiDisplaySessionInfo;
|
|
import android.hardware.display.WifiDisplayStatus;
|
|
import android.media.RemoteDisplay;
|
|
import android.os.Handler;
|
|
import android.os.IBinder;
|
|
import android.os.Looper;
|
|
import android.os.Message;
|
|
import android.os.UserHandle;
|
|
import android.provider.Settings;
|
|
import android.util.Slog;
|
|
import android.view.Display;
|
|
import android.view.Surface;
|
|
import android.view.SurfaceControl;
|
|
|
|
import java.io.PrintWriter;
|
|
import java.util.Arrays;
|
|
import java.util.List;
|
|
import java.util.ArrayList;
|
|
|
|
import libcore.util.Objects;
|
|
|
|
/**
|
|
* Connects to Wifi displays that implement the Miracast protocol.
|
|
* <p>
|
|
* The Wifi display protocol relies on Wifi direct for discovering and pairing
|
|
* with the display. Once connected, the Media Server opens an RTSP socket and accepts
|
|
* a connection from the display. After session negotiation, the Media Server
|
|
* streams encoded buffers to the display.
|
|
* </p><p>
|
|
* This class is responsible for connecting to Wifi displays and mediating
|
|
* the interactions between Media Server, Surface Flinger and the Display Manager Service.
|
|
* </p><p>
|
|
* Display adapters are guarded by the {@link DisplayManagerService.SyncRoot} lock.
|
|
* </p>
|
|
*/
|
|
final class WifiDisplayAdapter extends DisplayAdapter {
|
|
private static final String TAG = "WifiDisplayAdapter";
|
|
|
|
private static final boolean DEBUG = false;
|
|
|
|
private static final int MSG_SEND_STATUS_CHANGE_BROADCAST = 1;
|
|
private static final int MSG_UPDATE_NOTIFICATION = 2;
|
|
|
|
private static final String ACTION_DISCONNECT = "android.server.display.wfd.DISCONNECT";
|
|
|
|
private final WifiDisplayHandler mHandler;
|
|
private final PersistentDataStore mPersistentDataStore;
|
|
private final boolean mSupportsProtectedBuffers;
|
|
private final NotificationManager mNotificationManager;
|
|
|
|
private PendingIntent mSettingsPendingIntent;
|
|
private PendingIntent mDisconnectPendingIntent;
|
|
|
|
private WifiDisplayController mDisplayController;
|
|
private WifiDisplayDevice mDisplayDevice;
|
|
|
|
private WifiDisplayStatus mCurrentStatus;
|
|
private int mFeatureState;
|
|
private int mScanState;
|
|
private int mActiveDisplayState;
|
|
private WifiDisplay mActiveDisplay;
|
|
private WifiDisplay[] mDisplays = WifiDisplay.EMPTY_ARRAY;
|
|
private WifiDisplay[] mAvailableDisplays = WifiDisplay.EMPTY_ARRAY;
|
|
private WifiDisplay[] mRememberedDisplays = WifiDisplay.EMPTY_ARRAY;
|
|
private WifiDisplaySessionInfo mSessionInfo;
|
|
|
|
private boolean mPendingStatusChangeBroadcast;
|
|
private boolean mPendingNotificationUpdate;
|
|
|
|
// Called with SyncRoot lock held.
|
|
public WifiDisplayAdapter(DisplayManagerService.SyncRoot syncRoot,
|
|
Context context, Handler handler, Listener listener,
|
|
PersistentDataStore persistentDataStore) {
|
|
super(syncRoot, context, handler, listener, TAG);
|
|
mHandler = new WifiDisplayHandler(handler.getLooper());
|
|
mPersistentDataStore = persistentDataStore;
|
|
mSupportsProtectedBuffers = context.getResources().getBoolean(
|
|
com.android.internal.R.bool.config_wifiDisplaySupportsProtectedBuffers);
|
|
mNotificationManager = (NotificationManager)context.getSystemService(
|
|
Context.NOTIFICATION_SERVICE);
|
|
}
|
|
|
|
@Override
|
|
public void dumpLocked(PrintWriter pw) {
|
|
super.dumpLocked(pw);
|
|
|
|
pw.println("mCurrentStatus=" + getWifiDisplayStatusLocked());
|
|
pw.println("mFeatureState=" + mFeatureState);
|
|
pw.println("mScanState=" + mScanState);
|
|
pw.println("mActiveDisplayState=" + mActiveDisplayState);
|
|
pw.println("mActiveDisplay=" + mActiveDisplay);
|
|
pw.println("mDisplays=" + Arrays.toString(mDisplays));
|
|
pw.println("mAvailableDisplays=" + Arrays.toString(mAvailableDisplays));
|
|
pw.println("mRememberedDisplays=" + Arrays.toString(mRememberedDisplays));
|
|
pw.println("mPendingStatusChangeBroadcast=" + mPendingStatusChangeBroadcast);
|
|
pw.println("mPendingNotificationUpdate=" + mPendingNotificationUpdate);
|
|
pw.println("mSupportsProtectedBuffers=" + mSupportsProtectedBuffers);
|
|
|
|
// Try to dump the controller state.
|
|
if (mDisplayController == null) {
|
|
pw.println("mDisplayController=null");
|
|
} else {
|
|
pw.println("mDisplayController:");
|
|
final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
|
|
ipw.increaseIndent();
|
|
DumpUtils.dumpAsync(getHandler(), mDisplayController, ipw, 200);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void registerLocked() {
|
|
super.registerLocked();
|
|
|
|
updateRememberedDisplaysLocked();
|
|
|
|
getHandler().post(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
mDisplayController = new WifiDisplayController(
|
|
getContext(), getHandler(), mWifiDisplayListener);
|
|
|
|
getContext().registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL,
|
|
new IntentFilter(ACTION_DISCONNECT), null, mHandler);
|
|
}
|
|
});
|
|
}
|
|
|
|
public void requestStartScanLocked() {
|
|
if (DEBUG) {
|
|
Slog.d(TAG, "requestStartScanLocked");
|
|
}
|
|
|
|
getHandler().post(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
if (mDisplayController != null) {
|
|
mDisplayController.requestStartScan();
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
public void requestStopScanLocked() {
|
|
if (DEBUG) {
|
|
Slog.d(TAG, "requestStopScanLocked");
|
|
}
|
|
|
|
getHandler().post(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
if (mDisplayController != null) {
|
|
mDisplayController.requestStopScan();
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
public void requestConnectLocked(final String address) {
|
|
if (DEBUG) {
|
|
Slog.d(TAG, "requestConnectLocked: address=" + address);
|
|
}
|
|
|
|
getHandler().post(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
if (mDisplayController != null) {
|
|
mDisplayController.requestConnect(address);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
public void requestPauseLocked() {
|
|
if (DEBUG) {
|
|
Slog.d(TAG, "requestPauseLocked");
|
|
}
|
|
|
|
getHandler().post(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
if (mDisplayController != null) {
|
|
mDisplayController.requestPause();
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
public void requestResumeLocked() {
|
|
if (DEBUG) {
|
|
Slog.d(TAG, "requestResumeLocked");
|
|
}
|
|
|
|
getHandler().post(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
if (mDisplayController != null) {
|
|
mDisplayController.requestResume();
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
public void requestDisconnectLocked() {
|
|
if (DEBUG) {
|
|
Slog.d(TAG, "requestDisconnectedLocked");
|
|
}
|
|
|
|
getHandler().post(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
if (mDisplayController != null) {
|
|
mDisplayController.requestDisconnect();
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
public void requestRenameLocked(String address, String alias) {
|
|
if (DEBUG) {
|
|
Slog.d(TAG, "requestRenameLocked: address=" + address + ", alias=" + alias);
|
|
}
|
|
|
|
if (alias != null) {
|
|
alias = alias.trim();
|
|
if (alias.isEmpty() || alias.equals(address)) {
|
|
alias = null;
|
|
}
|
|
}
|
|
|
|
WifiDisplay display = mPersistentDataStore.getRememberedWifiDisplay(address);
|
|
if (display != null && !Objects.equal(display.getDeviceAlias(), alias)) {
|
|
display = new WifiDisplay(address, display.getDeviceName(), alias,
|
|
false, false, false);
|
|
if (mPersistentDataStore.rememberWifiDisplay(display)) {
|
|
mPersistentDataStore.saveIfNeeded();
|
|
updateRememberedDisplaysLocked();
|
|
scheduleStatusChangedBroadcastLocked();
|
|
}
|
|
}
|
|
|
|
if (mActiveDisplay != null && mActiveDisplay.getDeviceAddress().equals(address)) {
|
|
renameDisplayDeviceLocked(mActiveDisplay.getFriendlyDisplayName());
|
|
}
|
|
}
|
|
|
|
public void requestForgetLocked(String address) {
|
|
if (DEBUG) {
|
|
Slog.d(TAG, "requestForgetLocked: address=" + 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(
|
|
mFeatureState, mScanState, mActiveDisplayState,
|
|
mActiveDisplay, mDisplays, mSessionInfo);
|
|
}
|
|
|
|
if (DEBUG) {
|
|
Slog.d(TAG, "getWifiDisplayStatusLocked: result=" + mCurrentStatus);
|
|
}
|
|
return mCurrentStatus;
|
|
}
|
|
|
|
private void updateDisplaysLocked() {
|
|
List<WifiDisplay> displays = new ArrayList<WifiDisplay>(
|
|
mAvailableDisplays.length + mRememberedDisplays.length);
|
|
boolean[] remembered = new boolean[mAvailableDisplays.length];
|
|
for (WifiDisplay d : mRememberedDisplays) {
|
|
boolean available = false;
|
|
for (int i = 0; i < mAvailableDisplays.length; i++) {
|
|
if (d.equals(mAvailableDisplays[i])) {
|
|
remembered[i] = available = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!available) {
|
|
displays.add(new WifiDisplay(d.getDeviceAddress(), d.getDeviceName(),
|
|
d.getDeviceAlias(), false, false, true));
|
|
}
|
|
}
|
|
for (int i = 0; i < mAvailableDisplays.length; i++) {
|
|
WifiDisplay d = mAvailableDisplays[i];
|
|
displays.add(new WifiDisplay(d.getDeviceAddress(), d.getDeviceName(),
|
|
d.getDeviceAlias(), true, d.canConnect(), remembered[i]));
|
|
}
|
|
mDisplays = displays.toArray(WifiDisplay.EMPTY_ARRAY);
|
|
}
|
|
|
|
private void updateRememberedDisplaysLocked() {
|
|
mRememberedDisplays = mPersistentDataStore.getRememberedWifiDisplays();
|
|
mActiveDisplay = mPersistentDataStore.applyWifiDisplayAlias(mActiveDisplay);
|
|
mAvailableDisplays = mPersistentDataStore.applyWifiDisplayAliases(mAvailableDisplays);
|
|
updateDisplaysLocked();
|
|
}
|
|
|
|
private void fixRememberedDisplayNamesFromAvailableDisplaysLocked() {
|
|
// It may happen that a display name has changed since it was remembered.
|
|
// Consult the list of available displays and update the name if needed.
|
|
// We don't do anything special for the active display here. The display
|
|
// controller will send a separate event when it needs to be updates.
|
|
boolean changed = false;
|
|
for (int i = 0; i < mRememberedDisplays.length; i++) {
|
|
WifiDisplay rememberedDisplay = mRememberedDisplays[i];
|
|
WifiDisplay availableDisplay = findAvailableDisplayLocked(
|
|
rememberedDisplay.getDeviceAddress());
|
|
if (availableDisplay != null && !rememberedDisplay.equals(availableDisplay)) {
|
|
if (DEBUG) {
|
|
Slog.d(TAG, "fixRememberedDisplayNamesFromAvailableDisplaysLocked: "
|
|
+ "updating remembered display to " + availableDisplay);
|
|
}
|
|
mRememberedDisplays[i] = availableDisplay;
|
|
changed |= mPersistentDataStore.rememberWifiDisplay(availableDisplay);
|
|
}
|
|
}
|
|
if (changed) {
|
|
mPersistentDataStore.saveIfNeeded();
|
|
}
|
|
}
|
|
|
|
private WifiDisplay findAvailableDisplayLocked(String address) {
|
|
for (WifiDisplay display : mAvailableDisplays) {
|
|
if (display.getDeviceAddress().equals(address)) {
|
|
return display;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
private void addDisplayDeviceLocked(WifiDisplay display,
|
|
Surface surface, int width, int height, int flags) {
|
|
removeDisplayDeviceLocked();
|
|
|
|
if (mPersistentDataStore.rememberWifiDisplay(display)) {
|
|
mPersistentDataStore.saveIfNeeded();
|
|
updateRememberedDisplaysLocked();
|
|
scheduleStatusChangedBroadcastLocked();
|
|
}
|
|
|
|
boolean secure = (flags & RemoteDisplay.DISPLAY_FLAG_SECURE) != 0;
|
|
int deviceFlags = DisplayDeviceInfo.FLAG_PRESENTATION;
|
|
if (secure) {
|
|
deviceFlags |= DisplayDeviceInfo.FLAG_SECURE;
|
|
if (mSupportsProtectedBuffers) {
|
|
deviceFlags |= DisplayDeviceInfo.FLAG_SUPPORTS_PROTECTED_BUFFERS;
|
|
}
|
|
}
|
|
|
|
float refreshRate = 60.0f; // TODO: get this for real
|
|
|
|
String name = display.getFriendlyDisplayName();
|
|
String address = display.getDeviceAddress();
|
|
IBinder displayToken = SurfaceControl.createDisplay(name, secure);
|
|
mDisplayDevice = new WifiDisplayDevice(displayToken, name, width, height,
|
|
refreshRate, deviceFlags, address, surface);
|
|
sendDisplayDeviceEventLocked(mDisplayDevice, DISPLAY_DEVICE_EVENT_ADDED);
|
|
}
|
|
|
|
private void removeDisplayDeviceLocked() {
|
|
if (mDisplayDevice != null) {
|
|
mDisplayDevice.destroyLocked();
|
|
sendDisplayDeviceEventLocked(mDisplayDevice, DISPLAY_DEVICE_EVENT_REMOVED);
|
|
mDisplayDevice = null;
|
|
}
|
|
}
|
|
|
|
private void renameDisplayDeviceLocked(String name) {
|
|
if (mDisplayDevice != null && !mDisplayDevice.getNameLocked().equals(name)) {
|
|
mDisplayDevice.setNameLocked(name);
|
|
sendDisplayDeviceEventLocked(mDisplayDevice, DISPLAY_DEVICE_EVENT_CHANGED);
|
|
}
|
|
}
|
|
|
|
private void scheduleStatusChangedBroadcastLocked() {
|
|
mCurrentStatus = null;
|
|
if (!mPendingStatusChangeBroadcast) {
|
|
mPendingStatusChangeBroadcast = true;
|
|
mHandler.sendEmptyMessage(MSG_SEND_STATUS_CHANGE_BROADCAST);
|
|
}
|
|
}
|
|
|
|
private void scheduleUpdateNotificationLocked() {
|
|
if (!mPendingNotificationUpdate) {
|
|
mPendingNotificationUpdate = true;
|
|
mHandler.sendEmptyMessage(MSG_UPDATE_NOTIFICATION);
|
|
}
|
|
}
|
|
|
|
// Runs on the handler.
|
|
private void handleSendStatusChangeBroadcast() {
|
|
final Intent intent;
|
|
synchronized (getSyncRoot()) {
|
|
if (!mPendingStatusChangeBroadcast) {
|
|
return;
|
|
}
|
|
|
|
mPendingStatusChangeBroadcast = false;
|
|
intent = new Intent(DisplayManager.ACTION_WIFI_DISPLAY_STATUS_CHANGED);
|
|
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
|
|
intent.putExtra(DisplayManager.EXTRA_WIFI_DISPLAY_STATUS,
|
|
getWifiDisplayStatusLocked());
|
|
}
|
|
|
|
// Send protected broadcast about wifi display status to registered receivers.
|
|
getContext().sendBroadcastAsUser(intent, UserHandle.ALL);
|
|
}
|
|
|
|
// Runs on the handler.
|
|
private void handleUpdateNotification() {
|
|
final int state;
|
|
final WifiDisplay display;
|
|
synchronized (getSyncRoot()) {
|
|
if (!mPendingNotificationUpdate) {
|
|
return;
|
|
}
|
|
|
|
mPendingNotificationUpdate = false;
|
|
state = mActiveDisplayState;
|
|
display = mActiveDisplay;
|
|
}
|
|
|
|
// Cancel the old notification if there is one.
|
|
mNotificationManager.cancelAsUser(null,
|
|
R.string.wifi_display_notification_disconnect, UserHandle.ALL);
|
|
|
|
if (state == WifiDisplayStatus.DISPLAY_STATE_CONNECTING
|
|
|| state == WifiDisplayStatus.DISPLAY_STATE_CONNECTED) {
|
|
Context context = getContext();
|
|
|
|
// Initialize pending intents for the notification outside of the lock because
|
|
// creating a pending intent requires a call into the activity manager.
|
|
if (mSettingsPendingIntent == null) {
|
|
Intent settingsIntent = new Intent(Settings.ACTION_WIFI_DISPLAY_SETTINGS);
|
|
settingsIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
|
|
| Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
|
|
| Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
|
mSettingsPendingIntent = PendingIntent.getActivityAsUser(
|
|
context, 0, settingsIntent, 0, null, UserHandle.CURRENT);
|
|
}
|
|
|
|
if (mDisconnectPendingIntent == null) {
|
|
Intent disconnectIntent = new Intent(ACTION_DISCONNECT);
|
|
mDisconnectPendingIntent = PendingIntent.getBroadcastAsUser(
|
|
context, 0, disconnectIntent, 0, UserHandle.CURRENT);
|
|
}
|
|
|
|
// Post the notification.
|
|
Resources r = context.getResources();
|
|
Notification notification;
|
|
if (state == WifiDisplayStatus.DISPLAY_STATE_CONNECTING) {
|
|
notification = new Notification.Builder(context)
|
|
.setContentTitle(r.getString(
|
|
R.string.wifi_display_notification_connecting_title))
|
|
.setContentText(r.getString(
|
|
R.string.wifi_display_notification_connecting_message,
|
|
display.getFriendlyDisplayName()))
|
|
.setContentIntent(mSettingsPendingIntent)
|
|
.setSmallIcon(R.drawable.ic_notification_cast_connecting)
|
|
.setOngoing(true)
|
|
.addAction(android.R.drawable.ic_menu_close_clear_cancel,
|
|
r.getString(R.string.wifi_display_notification_disconnect),
|
|
mDisconnectPendingIntent)
|
|
.build();
|
|
} else {
|
|
notification = new Notification.Builder(context)
|
|
.setContentTitle(r.getString(
|
|
R.string.wifi_display_notification_connected_title))
|
|
.setContentText(r.getString(
|
|
R.string.wifi_display_notification_connected_message,
|
|
display.getFriendlyDisplayName()))
|
|
.setContentIntent(mSettingsPendingIntent)
|
|
.setSmallIcon(R.drawable.ic_notification_cast_on)
|
|
.setOngoing(true)
|
|
.addAction(android.R.drawable.ic_menu_close_clear_cancel,
|
|
r.getString(R.string.wifi_display_notification_disconnect),
|
|
mDisconnectPendingIntent)
|
|
.build();
|
|
}
|
|
mNotificationManager.notifyAsUser(null,
|
|
R.string.wifi_display_notification_disconnect,
|
|
notification, UserHandle.ALL);
|
|
}
|
|
}
|
|
|
|
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
|
|
@Override
|
|
public void onReceive(Context context, Intent intent) {
|
|
if (intent.getAction().equals(ACTION_DISCONNECT)) {
|
|
synchronized (getSyncRoot()) {
|
|
requestDisconnectLocked();
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
private final WifiDisplayController.Listener mWifiDisplayListener =
|
|
new WifiDisplayController.Listener() {
|
|
@Override
|
|
public void onFeatureStateChanged(int featureState) {
|
|
synchronized (getSyncRoot()) {
|
|
if (mFeatureState != featureState) {
|
|
mFeatureState = featureState;
|
|
scheduleStatusChangedBroadcastLocked();
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onScanStarted() {
|
|
synchronized (getSyncRoot()) {
|
|
if (mScanState != WifiDisplayStatus.SCAN_STATE_SCANNING) {
|
|
mScanState = WifiDisplayStatus.SCAN_STATE_SCANNING;
|
|
scheduleStatusChangedBroadcastLocked();
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onScanResults(WifiDisplay[] availableDisplays) {
|
|
synchronized (getSyncRoot()) {
|
|
availableDisplays = mPersistentDataStore.applyWifiDisplayAliases(
|
|
availableDisplays);
|
|
|
|
boolean changed = !Arrays.equals(mAvailableDisplays, availableDisplays);
|
|
|
|
// Check whether any of the available displays changed canConnect status.
|
|
for (int i = 0; !changed && i<availableDisplays.length; i++) {
|
|
changed = availableDisplays[i].canConnect()
|
|
!= mAvailableDisplays[i].canConnect();
|
|
}
|
|
|
|
if (changed) {
|
|
mAvailableDisplays = availableDisplays;
|
|
fixRememberedDisplayNamesFromAvailableDisplaysLocked();
|
|
updateDisplaysLocked();
|
|
scheduleStatusChangedBroadcastLocked();
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onScanFinished() {
|
|
synchronized (getSyncRoot()) {
|
|
if (mScanState != WifiDisplayStatus.SCAN_STATE_NOT_SCANNING) {
|
|
mScanState = WifiDisplayStatus.SCAN_STATE_NOT_SCANNING;
|
|
scheduleStatusChangedBroadcastLocked();
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onDisplayConnecting(WifiDisplay display) {
|
|
synchronized (getSyncRoot()) {
|
|
display = mPersistentDataStore.applyWifiDisplayAlias(display);
|
|
|
|
if (mActiveDisplayState != WifiDisplayStatus.DISPLAY_STATE_CONNECTING
|
|
|| mActiveDisplay == null
|
|
|| !mActiveDisplay.equals(display)) {
|
|
mActiveDisplayState = WifiDisplayStatus.DISPLAY_STATE_CONNECTING;
|
|
mActiveDisplay = display;
|
|
scheduleStatusChangedBroadcastLocked();
|
|
scheduleUpdateNotificationLocked();
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onDisplayConnectionFailed() {
|
|
synchronized (getSyncRoot()) {
|
|
if (mActiveDisplayState != WifiDisplayStatus.DISPLAY_STATE_NOT_CONNECTED
|
|
|| mActiveDisplay != null) {
|
|
mActiveDisplayState = WifiDisplayStatus.DISPLAY_STATE_NOT_CONNECTED;
|
|
mActiveDisplay = null;
|
|
scheduleStatusChangedBroadcastLocked();
|
|
scheduleUpdateNotificationLocked();
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onDisplayConnected(WifiDisplay display, Surface surface,
|
|
int width, int height, int flags) {
|
|
synchronized (getSyncRoot()) {
|
|
display = mPersistentDataStore.applyWifiDisplayAlias(display);
|
|
addDisplayDeviceLocked(display, surface, width, height, flags);
|
|
|
|
if (mActiveDisplayState != WifiDisplayStatus.DISPLAY_STATE_CONNECTED
|
|
|| mActiveDisplay == null
|
|
|| !mActiveDisplay.equals(display)) {
|
|
mActiveDisplayState = WifiDisplayStatus.DISPLAY_STATE_CONNECTED;
|
|
mActiveDisplay = display;
|
|
scheduleStatusChangedBroadcastLocked();
|
|
scheduleUpdateNotificationLocked();
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onDisplaySessionInfo(WifiDisplaySessionInfo sessionInfo) {
|
|
synchronized (getSyncRoot()) {
|
|
mSessionInfo = sessionInfo;
|
|
scheduleStatusChangedBroadcastLocked();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onDisplayChanged(WifiDisplay display) {
|
|
synchronized (getSyncRoot()) {
|
|
display = mPersistentDataStore.applyWifiDisplayAlias(display);
|
|
if (mActiveDisplay != null
|
|
&& mActiveDisplay.hasSameAddress(display)
|
|
&& !mActiveDisplay.equals(display)) {
|
|
mActiveDisplay = display;
|
|
renameDisplayDeviceLocked(display.getFriendlyDisplayName());
|
|
scheduleStatusChangedBroadcastLocked();
|
|
scheduleUpdateNotificationLocked();
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onDisplayDisconnected() {
|
|
// Stop listening.
|
|
synchronized (getSyncRoot()) {
|
|
removeDisplayDeviceLocked();
|
|
|
|
if (mActiveDisplayState != WifiDisplayStatus.DISPLAY_STATE_NOT_CONNECTED
|
|
|| mActiveDisplay != null) {
|
|
mActiveDisplayState = WifiDisplayStatus.DISPLAY_STATE_NOT_CONNECTED;
|
|
mActiveDisplay = null;
|
|
scheduleStatusChangedBroadcastLocked();
|
|
scheduleUpdateNotificationLocked();
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
private final class WifiDisplayDevice extends DisplayDevice {
|
|
private String mName;
|
|
private final int mWidth;
|
|
private final int mHeight;
|
|
private final float mRefreshRate;
|
|
private final int mFlags;
|
|
private final String mAddress;
|
|
|
|
private Surface mSurface;
|
|
private DisplayDeviceInfo mInfo;
|
|
|
|
public WifiDisplayDevice(IBinder displayToken, String name,
|
|
int width, int height, float refreshRate, int flags, String address,
|
|
Surface surface) {
|
|
super(WifiDisplayAdapter.this, displayToken);
|
|
mName = name;
|
|
mWidth = width;
|
|
mHeight = height;
|
|
mRefreshRate = refreshRate;
|
|
mFlags = flags;
|
|
mAddress = address;
|
|
mSurface = surface;
|
|
}
|
|
|
|
public void destroyLocked() {
|
|
if (mSurface != null) {
|
|
mSurface.release();
|
|
mSurface = null;
|
|
}
|
|
SurfaceControl.destroyDisplay(getDisplayTokenLocked());
|
|
}
|
|
|
|
public void setNameLocked(String name) {
|
|
mName = name;
|
|
mInfo = null;
|
|
}
|
|
|
|
@Override
|
|
public void performTraversalInTransactionLocked() {
|
|
if (mSurface != null) {
|
|
setSurfaceInTransactionLocked(mSurface);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public DisplayDeviceInfo getDisplayDeviceInfoLocked() {
|
|
if (mInfo == null) {
|
|
mInfo = new DisplayDeviceInfo();
|
|
mInfo.name = mName;
|
|
mInfo.width = mWidth;
|
|
mInfo.height = mHeight;
|
|
mInfo.refreshRate = mRefreshRate;
|
|
mInfo.flags = mFlags;
|
|
mInfo.type = Display.TYPE_WIFI;
|
|
mInfo.address = mAddress;
|
|
mInfo.touch = DisplayDeviceInfo.TOUCH_EXTERNAL;
|
|
mInfo.setAssumedDensityForExternalDisplay(mWidth, mHeight);
|
|
}
|
|
return mInfo;
|
|
}
|
|
}
|
|
|
|
private final class WifiDisplayHandler extends Handler {
|
|
public WifiDisplayHandler(Looper looper) {
|
|
super(looper, null, true /*async*/);
|
|
}
|
|
|
|
@Override
|
|
public void handleMessage(Message msg) {
|
|
switch (msg.what) {
|
|
case MSG_SEND_STATUS_CHANGE_BROADCAST:
|
|
handleSendStatusChangeBroadcast();
|
|
break;
|
|
|
|
case MSG_UPDATE_NOTIFICATION:
|
|
handleUpdateNotification();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|