When certification mode is enabled: - Pass wfd session info to wifi display settings - Allow sink to connect to source - Add interface in display manager for pausing/resuming session - Add interface in WifiP2pManager for setting lc, oc and starting autonomous GO Note that we're compliant regardless of certification mode, but some confusing options (eg. allowing incoming connection from sink) we want to hide when not being tested. Bug: 9371882 Change-Id: Icc7dcae4e046453796cfa03f5f197055fabf234b
733 lines
27 KiB
Java
733 lines
27 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 requestScanLocked() {
|
|
if (DEBUG) {
|
|
Slog.d(TAG, "requestScanLocked");
|
|
}
|
|
|
|
getHandler().post(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
if (mDisplayController != null) {
|
|
mDisplayController.requestScan();
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
public void requestConnectLocked(final String address, final boolean trusted) {
|
|
if (DEBUG) {
|
|
Slog.d(TAG, "requestConnectLocked: address=" + address + ", trusted=" + trusted);
|
|
}
|
|
|
|
if (!trusted) {
|
|
synchronized (getSyncRoot()) {
|
|
if (!isRememberedDisplayLocked(address)) {
|
|
Slog.w(TAG, "Ignoring request by an untrusted client to connect to "
|
|
+ "an unknown wifi display: " + address);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
getHandler().post(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
if (mDisplayController != null) {
|
|
mDisplayController.requestConnect(address);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
private boolean isRememberedDisplayLocked(String address) {
|
|
for (WifiDisplay display : mRememberedDisplays) {
|
|
if (display.getDeviceAddress().equals(address)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
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);
|
|
|
|
scheduleUpdateNotificationLocked();
|
|
}
|
|
|
|
private void removeDisplayDeviceLocked() {
|
|
if (mDisplayDevice != null) {
|
|
mDisplayDevice.destroyLocked();
|
|
sendDisplayDeviceEventLocked(mDisplayDevice, DISPLAY_DEVICE_EVENT_REMOVED);
|
|
mDisplayDevice = null;
|
|
|
|
scheduleUpdateNotificationLocked();
|
|
}
|
|
}
|
|
|
|
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 boolean isConnected;
|
|
synchronized (getSyncRoot()) {
|
|
if (!mPendingNotificationUpdate) {
|
|
return;
|
|
}
|
|
|
|
mPendingNotificationUpdate = false;
|
|
isConnected = (mDisplayDevice != null);
|
|
}
|
|
|
|
// Cancel the old notification if there is one.
|
|
mNotificationManager.cancelAsUser(null,
|
|
R.string.wifi_display_notification_title, UserHandle.ALL);
|
|
|
|
if (isConnected) {
|
|
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 = new Notification.Builder(context)
|
|
.setContentTitle(r.getString(
|
|
R.string.wifi_display_notification_title))
|
|
.setContentText(r.getString(
|
|
R.string.wifi_display_notification_message))
|
|
.setContentIntent(mSettingsPendingIntent)
|
|
.setSmallIcon(R.drawable.ic_notify_wifidisplay)
|
|
.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_title,
|
|
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 onScanFinished(WifiDisplay[] availableDisplays) {
|
|
synchronized (getSyncRoot()) {
|
|
availableDisplays = mPersistentDataStore.applyWifiDisplayAliases(
|
|
availableDisplays);
|
|
|
|
// check if any of the available displays changed canConnect status
|
|
boolean changed = !Arrays.equals(mAvailableDisplays, availableDisplays);
|
|
for (int i = 0; !changed && i<availableDisplays.length; i++) {
|
|
changed = availableDisplays[i].canConnect()
|
|
!= mAvailableDisplays[i].canConnect();
|
|
}
|
|
|
|
if (mScanState != WifiDisplayStatus.SCAN_STATE_NOT_SCANNING || changed) {
|
|
mScanState = WifiDisplayStatus.SCAN_STATE_NOT_SCANNING;
|
|
mAvailableDisplays = availableDisplays;
|
|
fixRememberedDisplayNamesFromAvailableDisplaysLocked();
|
|
updateDisplaysLocked();
|
|
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();
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onDisplayConnectionFailed() {
|
|
synchronized (getSyncRoot()) {
|
|
if (mActiveDisplayState != WifiDisplayStatus.DISPLAY_STATE_NOT_CONNECTED
|
|
|| mActiveDisplay != null) {
|
|
mActiveDisplayState = WifiDisplayStatus.DISPLAY_STATE_NOT_CONNECTED;
|
|
mActiveDisplay = null;
|
|
scheduleStatusChangedBroadcastLocked();
|
|
}
|
|
}
|
|
}
|
|
|
|
@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();
|
|
}
|
|
}
|
|
}
|
|
|
|
@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();
|
|
}
|
|
}
|
|
}
|
|
|
|
@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();
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
}
|