/* * 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.DumpUtils; import com.android.internal.util.IndentingPrintWriter; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; 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.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. *
* 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. *
* This class is responsible for connecting to Wifi displays and mediating * the interactions between Media Server, Surface Flinger and the Display Manager Service. *
* Display adapters are guarded by the {@link DisplayManagerService.SyncRoot} lock. *
*/ 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 String ACTION_DISCONNECT = "android.server.display.wfd.DISCONNECT"; // Unique id prefix for wifi displays private static final String DISPLAY_NAME_PREFIX = "wifi:"; private final WifiDisplayHandler mHandler; private final PersistentDataStore mPersistentDataStore; private final boolean mSupportsProtectedBuffers; 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; // 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); } @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("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