这一章中我们来看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<IServiceManager> sm = defaultServiceManager(); sp<IMediaPlayerService> service = interface_cast<IMediaPlayerService>( sm->getService(String16("media.player"))); if (service == NULL) { ALOGE("Could not obtain IMediaPlayerService from service manager"); return 0; } sp<NativeRemoteDisplayClient> client(new NativeRemoteDisplayClient(env, remoteDisplayObj)); sp<IRemoteDisplay> 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<jint>(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<IGraphicBufferProducer>& 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<IRemoteDisplay>& display, const sp<NativeRemoteDisplayClient>& client) : mDisplay(display), mClient(client) { } ~NativeRemoteDisplay() { mDisplay->dispose(); } void pause() { mDisplay->pause(); } void resume() { mDisplay->resume(); } private: sp<IRemoteDisplay> mDisplay; sp<NativeRemoteDisplayClient> mClient; };
sp<IRemoteDisplay> MediaPlayerService::listenForRemoteDisplay( const sp<IRemoteDisplayClient>& client, const String8& iface) { if (!checkPermission("android.permission.CONTROL_WIFI_DISPLAY")) { return NULL; } return new RemoteDisplay(client, iface.string()); }
RemoteDisplay::RemoteDisplay( const sp<IRemoteDisplayClient> &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<AHandler> &handler) { return gLooperRoster.registerHandler(this, handler); }
ALooper::handler_id ALooperRoster::registerHandler( const sp<ALooper> looper, const sp<AHandler> &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<AMessage> &msg) { sp<AHandler> 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> &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<ANetworkSession> &netSession, const sp<IRemoteDisplayClient> &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<AMessage> msg = new AMessage(kWhatStart, id()); msg->setString("iface", iface); sp<AMessage> response; return PostAndAwaitResponse(msg, &response); } static status_t PostAndAwaitResponse( const sp<AMessage> &msg, sp<AMessage> *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<AMessage> *response) { return gLooperRoster.postAndAwaitResponse(this, response); }
status_t ALooperRoster::postAndAwaitResponse( const sp<AMessage> &msg, sp<AMessage> *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<AMessage> &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<ALooper> 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<AMessage> &msg, int64_t delayUs) { Mutex::Autolock autoLock(mLock); int64_t whenUs; if (delayUs > 0) { whenUs = GetNowUs() + delayUs; } else { whenUs = GetNowUs(); } List<Event>::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<AMessage> &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<AMessage> notify = new AMessage(kWhatRTSPNotify, id()); err = mNetSession->createRTSPServer( mInterfaceAddr, port, notify, &mSessionID); } else { err = -EINVAL; } } mState = AWAITING_CLIENT_CONNECTION; sp<AMessage> response = new AMessage; response->setInt32("err", err); response->postReply(replyID); break; }
status_t ANetworkSession::createRTSPServer( const struct in_addr &addr, unsigned port, const sp<AMessage> ¬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<AMessage> ¬ify, int32_t *sessionID) { Mutex::Autolock autoLock(mLock); *sessionID = 0; status_t err = OK; int s, res; sp<Session> 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)); } }