这一章中我们来看Wifi Display连接过程的建立,包含P2P的部分和RTSP的部分,首先来大致看一下Wifi Display规范相关的东西。
HIDC: Human Interface Device Class (遵循HID标准的设备类)
UIBC: User Input Back Channel (UIBC分为两种,一种是Generic,包含鼠标、键盘等;另一种是HIDC,HID是一个规范,只有遵循HID的标准,都可以叫做HID设备,包含USB鼠标、键盘、蓝牙、红外等)
PES: Packetized Elementary Stream (数字电视基本码流)
HDCP: High-bandwidth Digital Content Protection (加密方式,用于加密传输的MPEG2-TS流)
MPEG2-TS: Moving Picture Experts Group 2 Transport Stream (Wifi display之间传输的是MPEG2-TS流)
RTSP: Real-Time Streaming Protocol (Wifi display通过RTSP协议来交互两边的能力)
RTP: Real-time Transport Protocol (Wifi display通过RTP来传输MPEG2-TS流)
Wi-Fi P2P: Wi-Fi Direct
TDLS: Tunneled Direct Link Setup (另一种方式建立两台设备之间的直连,与P2P类似,但要借助一台AP)
另一种比较重要的概念是在Wifi Display中分为Source和Sink两种角色,如下图。Source是用于encode并输出TS流;Sink用于decode并显示TS流。相当于Server/Client架构中,Source就是Server,用于提供服务;Sink就是Client。当然,我们这篇文章主要介绍在Android上Wifi display Source的流程。
从上面的架构图我们可以看到,Wifi display是建立在TCP/UDP上面的应用层协议,L2链路层是通过P2P和TDLS两种方式建立,TDLS是optional的。在L2层建立连接后,Source就会在一个特定的port上listen,等待client的TCP连接。当与Client建立了TCP连接后,就会有M1~M7七个消息的交互,用户获取对方设备的能力,包括视频编码能力、Audio输出能力、是否支持HDCP加密等等。在获取这些能力之后,Source就会选择一种视频编码格式以及Audio格式用于这次会话当中。当一个RTSP会话建立后,双方就会决定出用于传输TS流的RTP port,RTP协议是基于UDP的。当这些都准备好后,Sink设备就会发送M7消息,也就是Play给Source,双方就可以开始传输数据了。
关于M1~M7是什么,我们后面再来介绍。首先我们来介绍在Android WifiDisplay中如何建立P2P的连接。
private void pairWifiDisplay(WifiDisplay display) {
if (display.canConnect()) {
mDisplayManager.connectWifiDisplay(display.getDeviceAddress());
}
}
public void connectWifiDisplay(String address) {
if (address == null) {
throw new IllegalArgumentException("address must not be null");
}
mContext.enforceCallingOrSelfPermission(Manifest.permission.CONFIGURE_WIFI_DISPLAY,
"Permission required to connect to a wifi display");
final long token = Binder.clearCallingIdentity();
try {
synchronized (mSyncRoot) {
if (mWifiDisplayAdapter != null) {
mWifiDisplayAdapter.requestConnectLocked(address);
}
}
} finally {
Binder.restoreCallingIdentity(token);
}
}
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 requestConnect(String address) {
for (WifiP2pDevice device : mAvailableWifiDisplayPeers) {
if (device.deviceAddress.equals(address)) {
connect(device);
}
}
}
private void connect(final WifiP2pDevice device) {
if (mDesiredDevice != null
&& !mDesiredDevice.deviceAddress.equals(device.deviceAddress)) {
if (DEBUG) {
Slog.d(TAG, "connect: nothing to do, already connecting to "
+ describeWifiP2pDevice(device));
}
return;
}
if (mConnectedDevice != null
&& !mConnectedDevice.deviceAddress.equals(device.deviceAddress)
&& mDesiredDevice == null) {
if (DEBUG) {
Slog.d(TAG, "connect: nothing to do, already connected to "
+ describeWifiP2pDevice(device) + " and not part way through "
+ "connecting to a different device.");
}
return;
}
if (!mWfdEnabled) {
Slog.i(TAG, "Ignoring request to connect to Wifi display because the "
+" feature is currently disabled: " + device.deviceName);
return;
}
mDesiredDevice = device;
mConnectionRetriesLeft = CONNECT_MAX_RETRIES;
updateConnection();
}
private void updateConnection() {
//更新是否需要scan或者停止scan
updateScanState();
//如果有已经连接上的RemoteDisplay,先断开。这里先不看
if (mRemoteDisplay != null && mConnectedDevice != mDesiredDevice) {
}
// 接上面的一步,段开这个group
if (mDisconnectingDevice != null) {
return; // wait for asynchronous callback
}
if (mConnectedDevice != null && mConnectedDevice != mDesiredDevice) {
}
// 如果有正在连接的设备,先停止连接之前的设备
if (mCancelingDevice != null) {
return; // wait for asynchronous callback
}
if (mConnectingDevice != null && mConnectingDevice != mDesiredDevice) {
}
// 当断开之前的连接或者启动匿名GROUP时,这里就结束了
if (mDesiredDevice == null) {
}
// 开始连接,这是我们要看的重点
if (mConnectedDevice == null && mConnectingDevice == null) {
Slog.i(TAG, "Connecting to Wifi display: " + mDesiredDevice.deviceName);
mConnectingDevice = mDesiredDevice;
WifiP2pConfig config = new WifiP2pConfig();
WpsInfo wps = new WpsInfo();
if (mWifiDisplayWpsConfig != WpsInfo.INVALID) {
wps.setup = mWifiDisplayWpsConfig;
} else if (mConnectingDevice.wpsPbcSupported()) {
wps.setup = WpsInfo.PBC;
} else if (mConnectingDevice.wpsDisplaySupported()) {
wps.setup = WpsInfo.KEYPAD;
} else {
wps.setup = WpsInfo.DISPLAY;
}
config.wps = wps;
config.deviceAddress = mConnectingDevice.deviceAddress;
config.groupOwnerIntent = WifiP2pConfig.MIN_GROUP_OWNER_INTENT;
WifiDisplay display = createWifiDisplay(mConnectingDevice);
advertiseDisplay(display, null, 0, 0, 0);
final WifiP2pDevice newDevice = mDesiredDevice;
mWifiP2pManager.connect(mWifiP2pChannel, config, new ActionListener() {
@Override
public void onSuccess() {
Slog.i(TAG, "Initiated connection to Wifi display: " + newDevice.deviceName);
mHandler.postDelayed(mConnectionTimeout, CONNECTION_TIMEOUT_SECONDS * 1000);
}
@Override
public void onFailure(int reason) {
if (mConnectingDevice == newDevice) {
Slog.i(TAG, "Failed to initiate connection to Wifi display: "
+ newDevice.deviceName + ", reason=" + reason);
mConnectingDevice = null;
handleConnectionFailure(false);
}
}
});
return;
}
} else if (action.equals(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION)) {
NetworkInfo networkInfo = (NetworkInfo)intent.getParcelableExtra(
WifiP2pManager.EXTRA_NETWORK_INFO);
if (DEBUG) {
Slog.d(TAG, "Received WIFI_P2P_CONNECTION_CHANGED_ACTION: networkInfo="
+ networkInfo);
}
handleConnectionChanged(networkInfo);
private void handleConnectionChanged(NetworkInfo networkInfo) {
mNetworkInfo = networkInfo;
if (mWfdEnabled && networkInfo.isConnected()) {
if (mDesiredDevice != null || mWifiDisplayCertMode) {
mWifiP2pManager.requestGroupInfo(mWifiP2pChannel, new GroupInfoListener() {
@Override
public void onGroupInfoAvailable(WifiP2pGroup info) {
if (DEBUG) {
Slog.d(TAG, "Received group info: " + describeWifiP2pGroup(info));
}
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
+ ", group info was: " + describeWifiP2pGroup(info));
handleConnectionFailure(false);
return;
}
if (mDesiredDevice != null && !info.contains(mDesiredDevice)) {
disconnect();
return;
}
if (mConnectingDevice != null && mConnectingDevice == mDesiredDevice) {
Slog.i(TAG, "Connected to Wifi display: "
+ mConnectingDevice.deviceName);
mHandler.removeCallbacks(mConnectionTimeout);
mConnectedDeviceGroupInfo = info;
mConnectedDevice = mConnectingDevice;
mConnectingDevice = null;
updateConnection();
}
}
});
}
}
private void updateConnection() {
// 更新是否需要scan或者停止scan
updateScanState();
// 如果有连接上的RemoteDisplay,这里先断开
if (mRemoteDisplay != null && mConnectedDevice != mDesiredDevice) {
}
// 接着上面的一步,先断开之前连接的设备
if (mDisconnectingDevice != null) {
return; // wait for asynchronous callback
}
if (mConnectedDevice != null && mConnectedDevice != mDesiredDevice) {
}
// 如果有正在连接的设备,先断开之前连接的设备
if (mCancelingDevice != null) {
return; // wait for asynchronous callback
}
if (mConnectingDevice != null && mConnectingDevice != mDesiredDevice) {
}
// 当断开之前的连接或者匿名GO时,这里就结束了
if (mDesiredDevice == null) {
}
// 如果有连接请求,则进入此
if (mConnectedDevice == null && mConnectingDevice == null) {
}
// 当连接上P2P后,就进入到此
if (mConnectedDevice != null && mRemoteDisplay == null) {
Inet4Address addr = getInterfaceAddress(mConnectedDeviceGroupInfo);
if (addr == null) {
Slog.i(TAG, "Failed to get local interface address for communicating "
+ "with Wifi display: " + mConnectedDevice.deviceName);
handleConnectionFailure(false);
return; // done
}
mWifiP2pManager.setMiracastMode(WifiP2pManager.MIRACAST_SOURCE);
final WifiP2pDevice oldDevice = mConnectedDevice;
final int port = getPortNumber(mConnectedDevice);
final String iface = addr.getHostAddress() + ":" + port;
mRemoteDisplayInterface = iface;
Slog.i(TAG, "Listening for RTSP connection on " + iface
+ " from Wifi display: " + mConnectedDevice.deviceName);
mRemoteDisplay = RemoteDisplay.listen(iface, new RemoteDisplay.Listener() {
@Override
public void onDisplayConnected(Surface surface,
int width, int height, int flags, int session) {
if (mConnectedDevice == oldDevice && !mRemoteDisplayConnected) {
Slog.i(TAG, "Opened RTSP connection with Wifi display: "
+ mConnectedDevice.deviceName);
mRemoteDisplayConnected = true;
mHandler.removeCallbacks(mRtspTimeout);
if (mWifiDisplayCertMode) {
mListener.onDisplaySessionInfo(
getSessionInfo(mConnectedDeviceGroupInfo, session));
}
final WifiDisplay display = createWifiDisplay(mConnectedDevice);
advertiseDisplay(display, surface, width, height, flags);
}
}
@Override
public void onDisplayDisconnected() {
if (mConnectedDevice == oldDevice) {
Slog.i(TAG, "Closed RTSP connection with Wifi display: "
+ mConnectedDevice.deviceName);
mHandler.removeCallbacks(mRtspTimeout);
disconnect();
}
}
@Override
public void onDisplayError(int error) {
if (mConnectedDevice == oldDevice) {
Slog.i(TAG, "Lost RTSP connection with Wifi display due to error "
+ error + ": " + mConnectedDevice.deviceName);
mHandler.removeCallbacks(mRtspTimeout);
handleConnectionFailure(false);
}
}
}, mHandler);
// Use extended timeout value for certification, as some tests require user inputs
int rtspTimeout = mWifiDisplayCertMode ?
RTSP_TIMEOUT_SECONDS_CERT_MODE : RTSP_TIMEOUT_SECONDS;
mHandler.postDelayed(mRtspTimeout, rtspTimeout * 1000);
}
}
public static RemoteDisplay listen(String iface, Listener listener, Handler handler) {
if (iface == null) {
throw new IllegalArgumentException("iface must not be null");
}
if (listener == null) {
throw new IllegalArgumentException("listener must not be null");
}
if (handler == null) {
throw new IllegalArgumentException("handler must not be null");
}
RemoteDisplay display = new RemoteDisplay(listener, handler);
display.startListening(iface);
return display;
}
private void startListening(String iface) {
mPtr = nativeListen(iface);
if (mPtr == 0) {
throw new IllegalStateException("Could not start listening for "
+ "remote display connection on \"" + iface + "\"");
}
mGuard.open("dispose");
}
static jint nativeListen(JNIEnv* env, jobject remoteDisplayObj, jstring ifaceStr) {
ScopedUtfChars iface(env, ifaceStr);
sp sm = defaultServiceManager();
sp service = interface_cast(
sm->getService(String16("media.player")));
if (service == NULL) {
ALOGE("Could not obtain IMediaPlayerService from service manager");
return 0;
}
sp client(new NativeRemoteDisplayClient(env, remoteDisplayObj));
sp display = service->listenForRemoteDisplay(
client, String8(iface.c_str()));
if (display == NULL) {
ALOGE("Media player service rejected request to listen for remote display '%s'.",
iface.c_str());
return 0;
}
NativeRemoteDisplay* wrapper = new NativeRemoteDisplay(display, client);
return reinterpret_cast(wrapper);
}
class NativeRemoteDisplayClient : public BnRemoteDisplayClient {
public:
NativeRemoteDisplayClient(JNIEnv* env, jobject remoteDisplayObj) :
mRemoteDisplayObjGlobal(env->NewGlobalRef(remoteDisplayObj)) {
}
protected:
~NativeRemoteDisplayClient() {
JNIEnv* env = AndroidRuntime::getJNIEnv();
env->DeleteGlobalRef(mRemoteDisplayObjGlobal);
}
public:
virtual void onDisplayConnected(const sp& bufferProducer,
uint32_t width, uint32_t height, uint32_t flags, uint32_t session) {
env->CallVoidMethod(mRemoteDisplayObjGlobal,
gRemoteDisplayClassInfo.notifyDisplayConnected,
surfaceObj, width, height, flags, session);
}
virtual void onDisplayDisconnected() {
}
virtual void onDisplayError(int32_t error) {
}
private:
jobject mRemoteDisplayObjGlobal;
static void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) {
}
}
};
class NativeRemoteDisplay {
public:
NativeRemoteDisplay(const sp& display,
const sp& client) :
mDisplay(display), mClient(client) {
}
~NativeRemoteDisplay() {
mDisplay->dispose();
}
void pause() {
mDisplay->pause();
}
void resume() {
mDisplay->resume();
}
private:
sp mDisplay;
sp mClient;
};
sp MediaPlayerService::listenForRemoteDisplay(
const sp& client, const String8& iface) {
if (!checkPermission("android.permission.CONTROL_WIFI_DISPLAY")) {
return NULL;
}
return new RemoteDisplay(client, iface.string());
}
RemoteDisplay::RemoteDisplay(
const sp &client,
const char *iface)
: mLooper(new ALooper),
mNetSession(new ANetworkSession) {
mLooper->setName("wfd_looper");
mSource = new WifiDisplaySource(mNetSession, client);
mLooper->registerHandler(mSource);
mNetSession->start();
mLooper->start();
mSource->start(iface);
}
ALooper::ALooper()
: mRunningLocally(false) {
}
void ALooper::setName(const char *name) {
mName = name;
}
ALooper::handler_id ALooper::registerHandler(const sp &handler) {
return gLooperRoster.registerHandler(this, handler);
}
ALooper::handler_id ALooperRoster::registerHandler(
const sp looper, const sp &handler) {
Mutex::Autolock autoLock(mLock);
if (handler->id() != 0) {
CHECK(!"A handler must only be registered once.");
return INVALID_OPERATION;
}
HandlerInfo info;
info.mLooper = looper;
info.mHandler = handler;
ALooper::handler_id handlerID = mNextHandlerID++;
mHandlers.add(handlerID, info);
handler->setID(handlerID);
return handlerID;
}
status_t ALooper::start(
bool runOnCallingThread, bool canCallJava, int32_t priority) {
if (runOnCallingThread) {
}
Mutex::Autolock autoLock(mLock);
mThread = new LooperThread(this, canCallJava);
status_t err = mThread->run(
mName.empty() ? "ALooper" : mName.c_str(), priority);
if (err != OK) {
mThread.clear();
}
return err;
}
virtual bool threadLoop() {
return mLooper->loop();
}
bool ALooper::loop() {
Event event;
{
Mutex::Autolock autoLock(mLock);
if (mEventQueue.empty()) {
mQueueChangedCondition.wait(mLock);
return true;
}
int64_t whenUs = (*mEventQueue.begin()).mWhenUs;
int64_t nowUs = GetNowUs();
if (whenUs > nowUs) {
int64_t delayUs = whenUs - nowUs;
mQueueChangedCondition.waitRelative(mLock, delayUs * 1000ll);
return true;
}
event = *mEventQueue.begin();
mEventQueue.erase(mEventQueue.begin());
}
gLooperRoster.deliverMessage(event.mMessage);
return true;
}
void ALooperRoster::deliverMessage(const sp &msg) {
sp handler;
{
Mutex::Autolock autoLock(mLock);
ssize_t index = mHandlers.indexOfKey(msg->target());
if (index < 0) {
ALOGW("failed to deliver message. Target handler not registered.");
return;
}
const HandlerInfo &info = mHandlers.valueAt(index);
handler = info.mHandler.promote();
if (handler == NULL) {
ALOGW("failed to deliver message. "
"Target handler %d registered, but object gone.",
msg->target());
mHandlers.removeItemsAt(index);
return;
}
}
handler->onMessageReceived(msg);
}
ANetworkSession::ANetworkSession()
: mNextSessionID(1) {
mPipeFd[0] = mPipeFd[1] = -1;
}
status_t ANetworkSession::start() {
if (mThread != NULL) {
return INVALID_OPERATION;
}
int res = pipe(mPipeFd);
if (res != 0) {
mPipeFd[0] = mPipeFd[1] = -1;
return -errno;
}
mThread = new NetworkThread(this);
status_t err = mThread->run("ANetworkSession", ANDROID_PRIORITY_AUDIO);
if (err != OK) {
mThread.clear();
close(mPipeFd[0]);
close(mPipeFd[1]);
mPipeFd[0] = mPipeFd[1] = -1;
return err;
}
return OK;
}
void ANetworkSession::threadLoop() {
fd_set rs, ws;
FD_ZERO(&rs);
FD_ZERO(&ws);
FD_SET(mPipeFd[0], &rs);
int maxFd = mPipeFd[0];
{
Mutex::Autolock autoLock(mLock);
for (size_t i = 0; i < mSessions.size(); ++i) {
const sp &session = mSessions.valueAt(i);
int s = session->socket();
if (s < 0) {
continue;
}
if (session->wantsToRead()) {
FD_SET(s, &rs);
if (s > maxFd) {
maxFd = s;
}
}
if (session->wantsToWrite()) {
FD_SET(s, &ws);
if (s > maxFd) {
maxFd = s;
}
}
}
}
int res = select(maxFd + 1, &rs, &ws, NULL, NULL /* tv */);
if (res == 0) {
return;
}
if (res < 0) {
if (errno == EINTR) {
return;
}
ALOGE("select failed w/ error %d (%s)", errno, strerror(errno));
return;
}
}
WifiDisplaySource::WifiDisplaySource(
const sp &netSession,
const sp &client,
const char *path)
: mState(INITIALIZED),
mNetSession(netSession),
mClient(client),
mSessionID(0),
mStopReplyID(0),
mChosenRTPPort(-1),
mUsingPCMAudio(false),
mClientSessionID(0),
mReaperPending(false),
mNextCSeq(1),
mUsingHDCP(false),
mIsHDCP2_0(false),
mHDCPPort(0),
mHDCPInitializationComplete(false),
mSetupTriggerDeferred(false),
mPlaybackSessionEstablished(false) {
if (path != NULL) {
mMediaPath.setTo(path);
}
mSupportedSourceVideoFormats.disableAll();
mSupportedSourceVideoFormats.setNativeResolution(
VideoFormats::RESOLUTION_CEA, 5); // 1280x720 p30
// Enable all resolutions up to 1280x720p30
mSupportedSourceVideoFormats.enableResolutionUpto(
VideoFormats::RESOLUTION_CEA, 5,
VideoFormats::PROFILE_CHP, // Constrained High Profile
VideoFormats::LEVEL_32); // Level 3.2
}
VideoFormats::VideoFormats() {
memcpy(mConfigs, mResolutionTable, sizeof(mConfigs));
for (size_t i = 0; i < kNumResolutionTypes; ++i) {
mResolutionEnabled[i] = 0;
}
setNativeResolution(RESOLUTION_CEA, 0); // default to 640x480 p60
}
struct config_t {
size_t width, height, framesPerSecond;
bool interlaced;
unsigned char profile, level;
};
void VideoFormats::setNativeResolution(ResolutionType type, size_t index) {
CHECK_LT(type, kNumResolutionTypes);
CHECK(GetConfiguration(type, index, NULL, NULL, NULL, NULL));
mNativeType = type;
mNativeIndex = index;
setResolutionEnabled(type, index);
}
void VideoFormats::setResolutionEnabled(
ResolutionType type, size_t index, bool enabled) {
CHECK_LT(type, kNumResolutionTypes);
CHECK(GetConfiguration(type, index, NULL, NULL, NULL, NULL));
if (enabled) {
mResolutionEnabled[type] |= (1ul << index);
mConfigs[type][index].profile = (1ul << PROFILE_CBP);
mConfigs[type][index].level = (1ul << LEVEL_31);
} else {
mResolutionEnabled[type] &= ~(1ul << index);
mConfigs[type][index].profile = 0;
mConfigs[type][index].level = 0;
}
}
void VideoFormats::enableResolutionUpto(
ResolutionType type, size_t index,
ProfileType profile, LevelType level) {
size_t width, height, fps, score;
bool interlaced;
if (!GetConfiguration(type, index, &width, &height,
&fps, &interlaced)) {
ALOGE("Maximum resolution not found!");
return;
}
score = width * height * fps * (!interlaced + 1);
for (size_t i = 0; i < kNumResolutionTypes; ++i) {
for (size_t j = 0; j < 32; j++) {
if (GetConfiguration((ResolutionType)i, j,
&width, &height, &fps, &interlaced)
&& score >= width * height * fps * (!interlaced + 1)) {
setResolutionEnabled((ResolutionType)i, j);
setProfileLevel((ResolutionType)i, j, profile, level);
}
}
}
}
status_t WifiDisplaySource::start(const char *iface) {
CHECK_EQ(mState, INITIALIZED);
sp msg = new AMessage(kWhatStart, id());
msg->setString("iface", iface);
sp response;
return PostAndAwaitResponse(msg, &response);
}
static status_t PostAndAwaitResponse(
const sp &msg, sp *response) {
status_t err = msg->postAndAwaitResponse(response);
if (err != OK) {
return err;
}
if (response == NULL || !(*response)->findInt32("err", &err)) {
err = OK;
}
return err;
}
status_t AMessage::postAndAwaitResponse(sp *response) {
return gLooperRoster.postAndAwaitResponse(this, response);
}
status_t ALooperRoster::postAndAwaitResponse(
const sp &msg, sp *response) {
Mutex::Autolock autoLock(mLock);
uint32_t replyID = mNextReplyID++;
msg->setInt32("replyID", replyID);
status_t err = postMessage_l(msg, 0 /* delayUs */);
if (err != OK) {
response->clear();
return err;
}
ssize_t index;
while ((index = mReplies.indexOfKey(replyID)) < 0) {
mRepliesCondition.wait(mLock);
}
*response = mReplies.valueAt(index);
mReplies.removeItemsAt(index);
return OK;
}
status_t ALooperRoster::postMessage_l(
const sp &msg, int64_t delayUs) {
ssize_t index = mHandlers.indexOfKey(msg->target());
if (index < 0) {
ALOGW("failed to post message '%s'. Target handler not registered.",
msg->debugString().c_str());
return -ENOENT;
}
const HandlerInfo &info = mHandlers.valueAt(index);
sp looper = info.mLooper.promote();
if (looper == NULL) {
ALOGW("failed to post message. "
"Target handler %d still registered, but object gone.",
msg->target());
mHandlers.removeItemsAt(index);
return -ENOENT;
}
looper->post(msg, delayUs);
return OK;
}
void ALooper::post(const sp &msg, int64_t delayUs) {
Mutex::Autolock autoLock(mLock);
int64_t whenUs;
if (delayUs > 0) {
whenUs = GetNowUs() + delayUs;
} else {
whenUs = GetNowUs();
}
List::iterator it = mEventQueue.begin();
while (it != mEventQueue.end() && (*it).mWhenUs <= whenUs) {
++it;
}
Event event;
event.mWhenUs = whenUs;
event.mMessage = msg;
if (it == mEventQueue.begin()) {
mQueueChangedCondition.signal();
}
mEventQueue.insert(it, event);
}
void WifiDisplaySource::onMessageReceived(const sp &msg) {
switch (msg->what()) {
case kWhatStart:
{
uint32_t replyID;
CHECK(msg->senderAwaitsResponse(&replyID));
AString iface;
CHECK(msg->findString("iface", &iface));
status_t err = OK;
ssize_t colonPos = iface.find(":");
unsigned long port;
if (colonPos >= 0) {
const char *s = iface.c_str() + colonPos + 1;
char *end;
port = strtoul(s, &end, 10);
if (end == s || *end != '\0' || port > 65535) {
err = -EINVAL;
} else {
iface.erase(colonPos, iface.size() - colonPos);
}
} else {
port = kWifiDisplayDefaultPort;
}
if (err == OK) {
if (inet_aton(iface.c_str(), &mInterfaceAddr) != 0) {
sp notify = new AMessage(kWhatRTSPNotify, id());
err = mNetSession->createRTSPServer(
mInterfaceAddr, port, notify, &mSessionID);
} else {
err = -EINVAL;
}
}
mState = AWAITING_CLIENT_CONNECTION;
sp response = new AMessage;
response->setInt32("err", err);
response->postReply(replyID);
break;
}
status_t ANetworkSession::createRTSPServer(
const struct in_addr &addr, unsigned port,
const sp ¬ify, int32_t *sessionID) {
return createClientOrServer(
kModeCreateRTSPServer,
&addr,
port,
NULL /* remoteHost */,
0 /* remotePort */,
notify,
sessionID);
}
status_t ANetworkSession::createClientOrServer(
Mode mode,
const struct in_addr *localAddr,
unsigned port,
const char *remoteHost,
unsigned remotePort,
const sp ¬ify,
int32_t *sessionID) {
Mutex::Autolock autoLock(mLock);
*sessionID = 0;
status_t err = OK;
int s, res;
sp session;
s = socket(
AF_INET,
(mode == kModeCreateUDPSession) ? SOCK_DGRAM : SOCK_STREAM,
0);
if (s < 0) {
err = -errno;
goto bail;
}
if (mode == kModeCreateRTSPServer
|| mode == kModeCreateTCPDatagramSessionPassive) {
const int yes = 1;
res = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));
if (res < 0) {
err = -errno;
goto bail2;
}
}
err = MakeSocketNonBlocking(s);
if (err != OK) {
goto bail2;
}
struct sockaddr_in addr;
memset(addr.sin_zero, 0, sizeof(addr.sin_zero));
addr.sin_family = AF_INET;
} else if (localAddr != NULL) {
addr.sin_addr = *localAddr;
addr.sin_port = htons(port);
res = bind(s, (const struct sockaddr *)&addr, sizeof(addr));
if (res == 0) {
if (mode == kModeCreateRTSPServer
|| mode == kModeCreateTCPDatagramSessionPassive) {
res = listen(s, 4);
} else {
if (res < 0) {
err = -errno;
goto bail2;
}
Session::State state;
switch (mode) {
case kModeCreateRTSPServer:
state = Session::LISTENING_RTSP;
break;
default:
CHECK_EQ(mode, kModeCreateUDPSession);
state = Session::DATAGRAM;
break;
}
session = new Session(
mNextSessionID++,
state,
s,
notify);
mSessions.add(session->sessionID(), session);
interrupt();
*sessionID = session->sessionID();
goto bail;
bail2:
close(s);
s = -1;
bail:
return err;
}
void ANetworkSession::interrupt() {
static const char dummy = 0;
ssize_t n;
do {
n = write(mPipeFd[1], &dummy, 1);
} while (n < 0 && errno == EINTR);
if (n < 0) {
ALOGW("Error writing to pipe (%s)", strerror(errno));
}
}