WifiDisplaySource.cpp revision 0224bf170a3904576bba81593eaab113c5d3a4e7
1/*
2 * Copyright 2012, The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17//#define LOG_NDEBUG 0
18#define LOG_TAG "WifiDisplaySource"
19#include <utils/Log.h>
20
21#include "WifiDisplaySource.h"
22#include "PlaybackSession.h"
23#include "Parameters.h"
24#include "ParsedMessage.h"
25#include "Sender.h"
26
27#include <binder/IServiceManager.h>
28#include <gui/ISurfaceTexture.h>
29#include <media/IHDCP.h>
30#include <media/IMediaPlayerService.h>
31#include <media/IRemoteDisplayClient.h>
32#include <media/stagefright/foundation/ABuffer.h>
33#include <media/stagefright/foundation/ADebug.h>
34#include <media/stagefright/foundation/AMessage.h>
35#include <media/stagefright/MediaErrors.h>
36
37#include <arpa/inet.h>
38#include <cutils/properties.h>
39
40#include <ctype.h>
41
42namespace android {
43
44WifiDisplaySource::WifiDisplaySource(
45        const sp<ANetworkSession> &netSession,
46        const sp<IRemoteDisplayClient> &client)
47    : mState(INITIALIZED),
48      mNetSession(netSession),
49      mClient(client),
50      mSessionID(0),
51      mStopReplyID(0),
52      mChosenRTPPort(-1),
53      mUsingPCMAudio(false),
54      mClientSessionID(0),
55      mReaperPending(false),
56      mNextCSeq(1),
57      mUsingHDCP(false),
58      mIsHDCP2_0(false),
59      mHDCPPort(0),
60      mHDCPInitializationComplete(false),
61      mSetupTriggerDeferred(false)
62{
63}
64
65WifiDisplaySource::~WifiDisplaySource() {
66}
67
68status_t WifiDisplaySource::start(const char *iface) {
69    CHECK_EQ(mState, INITIALIZED);
70
71    sp<AMessage> msg = new AMessage(kWhatStart, id());
72    msg->setString("iface", iface);
73
74    sp<AMessage> response;
75    status_t err = msg->postAndAwaitResponse(&response);
76
77    if (err != OK) {
78        return err;
79    }
80
81    if (!response->findInt32("err", &err)) {
82        err = OK;
83    }
84
85    return err;
86}
87
88status_t WifiDisplaySource::stop() {
89    sp<AMessage> msg = new AMessage(kWhatStop, id());
90
91    sp<AMessage> response;
92    status_t err = msg->postAndAwaitResponse(&response);
93
94    if (err != OK) {
95        return err;
96    }
97
98    if (!response->findInt32("err", &err)) {
99        err = OK;
100    }
101
102    return err;
103}
104
105void WifiDisplaySource::onMessageReceived(const sp<AMessage> &msg) {
106    switch (msg->what()) {
107        case kWhatStart:
108        {
109            uint32_t replyID;
110            CHECK(msg->senderAwaitsResponse(&replyID));
111
112            AString iface;
113            CHECK(msg->findString("iface", &iface));
114
115            status_t err = OK;
116
117            ssize_t colonPos = iface.find(":");
118
119            unsigned long port;
120
121            if (colonPos >= 0) {
122                const char *s = iface.c_str() + colonPos + 1;
123
124                char *end;
125                port = strtoul(s, &end, 10);
126
127                if (end == s || *end != '\0' || port > 65535) {
128                    err = -EINVAL;
129                } else {
130                    iface.erase(colonPos, iface.size() - colonPos);
131                }
132            } else {
133                port = kWifiDisplayDefaultPort;
134            }
135
136            if (err == OK) {
137                if (inet_aton(iface.c_str(), &mInterfaceAddr) != 0) {
138                    sp<AMessage> notify = new AMessage(kWhatRTSPNotify, id());
139
140                    err = mNetSession->createRTSPServer(
141                            mInterfaceAddr, port, notify, &mSessionID);
142                } else {
143                    err = -EINVAL;
144                }
145            }
146
147            if (err == OK) {
148                mState = AWAITING_CLIENT_CONNECTION;
149            }
150
151            sp<AMessage> response = new AMessage;
152            response->setInt32("err", err);
153            response->postReply(replyID);
154            break;
155        }
156
157        case kWhatRTSPNotify:
158        {
159            int32_t reason;
160            CHECK(msg->findInt32("reason", &reason));
161
162            switch (reason) {
163                case ANetworkSession::kWhatError:
164                {
165                    int32_t sessionID;
166                    CHECK(msg->findInt32("sessionID", &sessionID));
167
168                    int32_t err;
169                    CHECK(msg->findInt32("err", &err));
170
171                    AString detail;
172                    CHECK(msg->findString("detail", &detail));
173
174                    ALOGE("An error occurred in session %d (%d, '%s/%s').",
175                          sessionID,
176                          err,
177                          detail.c_str(),
178                          strerror(-err));
179
180                    mNetSession->destroySession(sessionID);
181
182                    if (sessionID == mClientSessionID) {
183                        mClientSessionID = 0;
184
185                        mClient->onDisplayError(
186                                IRemoteDisplayClient::kDisplayErrorUnknown);
187                    }
188                    break;
189                }
190
191                case ANetworkSession::kWhatClientConnected:
192                {
193                    int32_t sessionID;
194                    CHECK(msg->findInt32("sessionID", &sessionID));
195
196                    if (mClientSessionID > 0) {
197                        ALOGW("A client tried to connect, but we already "
198                              "have one.");
199
200                        mNetSession->destroySession(sessionID);
201                        break;
202                    }
203
204                    CHECK_EQ(mState, AWAITING_CLIENT_CONNECTION);
205
206                    CHECK(msg->findString("client-ip", &mClientInfo.mRemoteIP));
207                    CHECK(msg->findString("server-ip", &mClientInfo.mLocalIP));
208
209                    if (mClientInfo.mRemoteIP == mClientInfo.mLocalIP) {
210                        // Disallow connections from the local interface
211                        // for security reasons.
212                        mNetSession->destroySession(sessionID);
213                        break;
214                    }
215
216                    CHECK(msg->findInt32(
217                                "server-port", &mClientInfo.mLocalPort));
218                    mClientInfo.mPlaybackSessionID = -1;
219
220                    mClientSessionID = sessionID;
221
222                    ALOGI("We now have a client (%d) connected.", sessionID);
223
224                    mState = AWAITING_CLIENT_SETUP;
225
226                    status_t err = sendM1(sessionID);
227                    CHECK_EQ(err, (status_t)OK);
228                    break;
229                }
230
231                case ANetworkSession::kWhatData:
232                {
233                    status_t err = onReceiveClientData(msg);
234
235                    if (err != OK) {
236                        mClient->onDisplayError(
237                                IRemoteDisplayClient::kDisplayErrorUnknown);
238                    }
239                    break;
240                }
241
242                default:
243                    TRESPASS();
244            }
245            break;
246        }
247
248        case kWhatStop:
249        {
250            CHECK(msg->senderAwaitsResponse(&mStopReplyID));
251
252            CHECK_LT(mState, AWAITING_CLIENT_TEARDOWN);
253
254            if (mState >= AWAITING_CLIENT_PLAY) {
255                // We have a session, i.e. a previous SETUP succeeded.
256
257                status_t err = sendM5(
258                        mClientSessionID, true /* requestShutdown */);
259
260                if (err == OK) {
261                    mState = AWAITING_CLIENT_TEARDOWN;
262
263                    (new AMessage(kWhatTeardownTriggerTimedOut, id()))->post(
264                            kTeardownTriggerTimeouSecs * 1000000ll);
265
266                    break;
267                }
268
269                // fall through.
270            }
271
272            finishStop();
273            break;
274        }
275
276        case kWhatReapDeadClients:
277        {
278            mReaperPending = false;
279
280            if (mClientSessionID == 0
281                    || mClientInfo.mPlaybackSession == NULL) {
282                break;
283            }
284
285            if (mClientInfo.mPlaybackSession->getLastLifesignUs()
286                    + kPlaybackSessionTimeoutUs < ALooper::GetNowUs()) {
287                ALOGI("playback session timed out, reaping.");
288
289                mNetSession->destroySession(mClientSessionID);
290                mClientSessionID = 0;
291
292                mClient->onDisplayError(
293                        IRemoteDisplayClient::kDisplayErrorUnknown);
294            } else {
295                scheduleReaper();
296            }
297            break;
298        }
299
300        case kWhatPlaybackSessionNotify:
301        {
302            int32_t playbackSessionID;
303            CHECK(msg->findInt32("playbackSessionID", &playbackSessionID));
304
305            int32_t what;
306            CHECK(msg->findInt32("what", &what));
307
308            if (what == PlaybackSession::kWhatSessionDead) {
309                ALOGI("playback session wants to quit.");
310
311                mClient->onDisplayError(
312                        IRemoteDisplayClient::kDisplayErrorUnknown);
313            } else if (what == PlaybackSession::kWhatSessionEstablished) {
314                if (mClient != NULL) {
315                    mClient->onDisplayConnected(
316                            mClientInfo.mPlaybackSession->getSurfaceTexture(),
317                            mClientInfo.mPlaybackSession->width(),
318                            mClientInfo.mPlaybackSession->height(),
319                            mUsingHDCP
320                                ? IRemoteDisplayClient::kDisplayFlagSecure
321                                : 0);
322                }
323
324                if (mState == ABOUT_TO_PLAY) {
325                    mState = PLAYING;
326                }
327            } else if (what == PlaybackSession::kWhatSessionDestroyed) {
328                disconnectClient2();
329            } else {
330                CHECK_EQ(what, PlaybackSession::kWhatBinaryData);
331
332                int32_t channel;
333                CHECK(msg->findInt32("channel", &channel));
334
335                sp<ABuffer> data;
336                CHECK(msg->findBuffer("data", &data));
337
338                CHECK_LE(channel, 0xffu);
339                CHECK_LE(data->size(), 0xffffu);
340
341                int32_t sessionID;
342                CHECK(msg->findInt32("sessionID", &sessionID));
343
344                char header[4];
345                header[0] = '$';
346                header[1] = channel;
347                header[2] = data->size() >> 8;
348                header[3] = data->size() & 0xff;
349
350                mNetSession->sendRequest(
351                        sessionID, header, sizeof(header));
352
353                mNetSession->sendRequest(
354                        sessionID, data->data(), data->size());
355            }
356            break;
357        }
358
359        case kWhatKeepAlive:
360        {
361            int32_t sessionID;
362            CHECK(msg->findInt32("sessionID", &sessionID));
363
364            if (mClientSessionID != sessionID) {
365                // Obsolete event, client is already gone.
366                break;
367            }
368
369            sendM16(sessionID);
370            break;
371        }
372
373        case kWhatTeardownTriggerTimedOut:
374        {
375            if (mState == AWAITING_CLIENT_TEARDOWN) {
376                ALOGI("TEARDOWN trigger timed out, forcing disconnection.");
377
378                CHECK_NE(mStopReplyID, 0);
379                finishStop();
380                break;
381            }
382            break;
383        }
384
385        case kWhatHDCPNotify:
386        {
387            int32_t msgCode, ext1, ext2;
388            CHECK(msg->findInt32("msg", &msgCode));
389            CHECK(msg->findInt32("ext1", &ext1));
390            CHECK(msg->findInt32("ext2", &ext2));
391
392            ALOGI("Saw HDCP notification code %d, ext1 %d, ext2 %d",
393                    msgCode, ext1, ext2);
394
395            switch (msgCode) {
396                case HDCPModule::HDCP_INITIALIZATION_COMPLETE:
397                {
398                    mHDCPInitializationComplete = true;
399
400                    if (mSetupTriggerDeferred) {
401                        mSetupTriggerDeferred = false;
402
403                        sendM5(mClientSessionID, false /* requestShutdown */);
404                    }
405                    break;
406                }
407
408                case HDCPModule::HDCP_SHUTDOWN_COMPLETE:
409                case HDCPModule::HDCP_SHUTDOWN_FAILED:
410                {
411                    // Ugly hack to make sure that the call to
412                    // HDCPObserver::notify is completely handled before
413                    // we clear the HDCP instance and unload the shared
414                    // library :(
415                    (new AMessage(kWhatFinishStop2, id()))->post(300000ll);
416                    break;
417                }
418
419                default:
420                {
421                    ALOGE("HDCP failure, shutting down.");
422
423                    mClient->onDisplayError(
424                            IRemoteDisplayClient::kDisplayErrorUnknown);
425                    break;
426                }
427            }
428            break;
429        }
430
431        case kWhatFinishStop2:
432        {
433            finishStop2();
434            break;
435        }
436
437        default:
438            TRESPASS();
439    }
440}
441
442void WifiDisplaySource::registerResponseHandler(
443        int32_t sessionID, int32_t cseq, HandleRTSPResponseFunc func) {
444    ResponseID id;
445    id.mSessionID = sessionID;
446    id.mCSeq = cseq;
447    mResponseHandlers.add(id, func);
448}
449
450status_t WifiDisplaySource::sendM1(int32_t sessionID) {
451    AString request = "OPTIONS * RTSP/1.0\r\n";
452    AppendCommonResponse(&request, mNextCSeq);
453
454    request.append(
455            "Require: org.wfa.wfd1.0\r\n"
456            "\r\n");
457
458    status_t err =
459        mNetSession->sendRequest(sessionID, request.c_str(), request.size());
460
461    if (err != OK) {
462        return err;
463    }
464
465    registerResponseHandler(
466            sessionID, mNextCSeq, &WifiDisplaySource::onReceiveM1Response);
467
468    ++mNextCSeq;
469
470    return OK;
471}
472
473status_t WifiDisplaySource::sendM3(int32_t sessionID) {
474    AString body =
475        "wfd_content_protection\r\n"
476        "wfd_video_formats\r\n"
477        "wfd_audio_codecs\r\n"
478        "wfd_client_rtp_ports\r\n";
479
480    AString request = "GET_PARAMETER rtsp://localhost/wfd1.0 RTSP/1.0\r\n";
481    AppendCommonResponse(&request, mNextCSeq);
482
483    request.append("Content-Type: text/parameters\r\n");
484    request.append(StringPrintf("Content-Length: %d\r\n", body.size()));
485    request.append("\r\n");
486    request.append(body);
487
488    status_t err =
489        mNetSession->sendRequest(sessionID, request.c_str(), request.size());
490
491    if (err != OK) {
492        return err;
493    }
494
495    registerResponseHandler(
496            sessionID, mNextCSeq, &WifiDisplaySource::onReceiveM3Response);
497
498    ++mNextCSeq;
499
500    return OK;
501}
502
503status_t WifiDisplaySource::sendM4(int32_t sessionID) {
504    // wfd_video_formats:
505    // 1 byte "native"
506    // 1 byte "preferred-display-mode-supported" 0 or 1
507    // one or more avc codec structures
508    //   1 byte profile
509    //   1 byte level
510    //   4 byte CEA mask
511    //   4 byte VESA mask
512    //   4 byte HH mask
513    //   1 byte latency
514    //   2 byte min-slice-slice
515    //   2 byte slice-enc-params
516    //   1 byte framerate-control-support
517    //   max-hres (none or 2 byte)
518    //   max-vres (none or 2 byte)
519
520    CHECK_EQ(sessionID, mClientSessionID);
521
522    AString transportString = "UDP";
523
524    char val[PROPERTY_VALUE_MAX];
525    if (property_get("media.wfd.enable-tcp", val, NULL)
526            && (!strcasecmp("true", val) || !strcmp("1", val))) {
527        ALOGI("Using TCP transport.");
528        transportString = "TCP";
529    }
530
531    // For 720p60:
532    //   use "30 00 02 02 00000040 00000000 00000000 00 0000 0000 00 none none\r\n"
533    // For 720p30:
534    //   use "28 00 02 02 00000020 00000000 00000000 00 0000 0000 00 none none\r\n"
535    // For 720p24:
536    //   use "78 00 02 02 00008000 00000000 00000000 00 0000 0000 00 none none\r\n"
537    // For 1080p30:
538    //   use "38 00 02 02 00000080 00000000 00000000 00 0000 0000 00 none none\r\n"
539    AString body = StringPrintf(
540        "wfd_video_formats: "
541#if USE_1080P
542        "38 00 02 02 00000080 00000000 00000000 00 0000 0000 00 none none\r\n"
543#else
544        "28 00 02 02 00000020 00000000 00000000 00 0000 0000 00 none none\r\n"
545#endif
546        "wfd_audio_codecs: %s\r\n"
547        "wfd_presentation_URL: rtsp://%s/wfd1.0/streamid=0 none\r\n"
548        "wfd_client_rtp_ports: RTP/AVP/%s;unicast %d 0 mode=play\r\n",
549        (mUsingPCMAudio
550            ? "LPCM 00000002 00" // 2 ch PCM 48kHz
551            : "AAC 00000001 00"),  // 2 ch AAC 48kHz
552        mClientInfo.mLocalIP.c_str(), transportString.c_str(), mChosenRTPPort);
553
554    AString request = "SET_PARAMETER rtsp://localhost/wfd1.0 RTSP/1.0\r\n";
555    AppendCommonResponse(&request, mNextCSeq);
556
557    request.append("Content-Type: text/parameters\r\n");
558    request.append(StringPrintf("Content-Length: %d\r\n", body.size()));
559    request.append("\r\n");
560    request.append(body);
561
562    status_t err =
563        mNetSession->sendRequest(sessionID, request.c_str(), request.size());
564
565    if (err != OK) {
566        return err;
567    }
568
569    registerResponseHandler(
570            sessionID, mNextCSeq, &WifiDisplaySource::onReceiveM4Response);
571
572    ++mNextCSeq;
573
574    return OK;
575}
576
577status_t WifiDisplaySource::sendM5(int32_t sessionID, bool requestShutdown) {
578    AString body = "wfd_trigger_method: ";
579    if (requestShutdown) {
580        ALOGI("Sending TEARDOWN trigger.");
581        body.append("TEARDOWN");
582    } else {
583        body.append("SETUP");
584    }
585
586    body.append("\r\n");
587
588    AString request = "SET_PARAMETER rtsp://localhost/wfd1.0 RTSP/1.0\r\n";
589    AppendCommonResponse(&request, mNextCSeq);
590
591    request.append("Content-Type: text/parameters\r\n");
592    request.append(StringPrintf("Content-Length: %d\r\n", body.size()));
593    request.append("\r\n");
594    request.append(body);
595
596    status_t err =
597        mNetSession->sendRequest(sessionID, request.c_str(), request.size());
598
599    if (err != OK) {
600        return err;
601    }
602
603    registerResponseHandler(
604            sessionID, mNextCSeq, &WifiDisplaySource::onReceiveM5Response);
605
606    ++mNextCSeq;
607
608    return OK;
609}
610
611status_t WifiDisplaySource::sendM16(int32_t sessionID) {
612    AString request = "GET_PARAMETER rtsp://localhost/wfd1.0 RTSP/1.0\r\n";
613    AppendCommonResponse(&request, mNextCSeq);
614
615    CHECK_EQ(sessionID, mClientSessionID);
616    request.append(
617            StringPrintf("Session: %d\r\n", mClientInfo.mPlaybackSessionID));
618    request.append("\r\n");  // Empty body
619
620    status_t err =
621        mNetSession->sendRequest(sessionID, request.c_str(), request.size());
622
623    if (err != OK) {
624        return err;
625    }
626
627    registerResponseHandler(
628            sessionID, mNextCSeq, &WifiDisplaySource::onReceiveM16Response);
629
630    ++mNextCSeq;
631
632    return OK;
633}
634
635status_t WifiDisplaySource::onReceiveM1Response(
636        int32_t sessionID, const sp<ParsedMessage> &msg) {
637    int32_t statusCode;
638    if (!msg->getStatusCode(&statusCode)) {
639        return ERROR_MALFORMED;
640    }
641
642    if (statusCode != 200) {
643        return ERROR_UNSUPPORTED;
644    }
645
646    return OK;
647}
648
649// sink_audio_list := ("LPCM"|"AAC"|"AC3" HEXDIGIT*8 HEXDIGIT*2)
650//                       (", " sink_audio_list)*
651static void GetAudioModes(const char *s, const char *prefix, uint32_t *modes) {
652    *modes = 0;
653
654    size_t prefixLen = strlen(prefix);
655
656    while (*s != '0') {
657        if (!strncmp(s, prefix, prefixLen) && s[prefixLen] == ' ') {
658            unsigned latency;
659            if (sscanf(&s[prefixLen + 1], "%08x %02x", modes, &latency) != 2) {
660                *modes = 0;
661            }
662
663            return;
664        }
665
666        char *commaPos = strchr(s, ',');
667        if (commaPos != NULL) {
668            s = commaPos + 1;
669
670            while (isspace(*s)) {
671                ++s;
672            }
673        } else {
674            break;
675        }
676    }
677}
678
679status_t WifiDisplaySource::onReceiveM3Response(
680        int32_t sessionID, const sp<ParsedMessage> &msg) {
681    int32_t statusCode;
682    if (!msg->getStatusCode(&statusCode)) {
683        return ERROR_MALFORMED;
684    }
685
686    if (statusCode != 200) {
687        return ERROR_UNSUPPORTED;
688    }
689
690    sp<Parameters> params =
691        Parameters::Parse(msg->getContent(), strlen(msg->getContent()));
692
693    if (params == NULL) {
694        return ERROR_MALFORMED;
695    }
696
697    AString value;
698    if (!params->findParameter("wfd_client_rtp_ports", &value)) {
699        ALOGE("Sink doesn't report its choice of wfd_client_rtp_ports.");
700        return ERROR_MALFORMED;
701    }
702
703    unsigned port0, port1;
704    if (sscanf(value.c_str(),
705               "RTP/AVP/UDP;unicast %u %u mode=play",
706               &port0,
707               &port1) != 2
708        || port0 == 0 || port0 > 65535 || port1 != 0) {
709        ALOGE("Sink chose its wfd_client_rtp_ports poorly (%s)",
710              value.c_str());
711
712        return ERROR_MALFORMED;
713    }
714
715    mChosenRTPPort = port0;
716
717    if (!params->findParameter("wfd_audio_codecs", &value)) {
718        ALOGE("Sink doesn't report its choice of wfd_audio_codecs.");
719        return ERROR_MALFORMED;
720    }
721
722    if  (value == "none") {
723        ALOGE("Sink doesn't support audio at all.");
724        return ERROR_UNSUPPORTED;
725    }
726
727    uint32_t modes;
728    GetAudioModes(value.c_str(), "AAC", &modes);
729
730    bool supportsAAC = (modes & 1) != 0;  // AAC 2ch 48kHz
731
732    GetAudioModes(value.c_str(), "LPCM", &modes);
733
734    bool supportsPCM = (modes & 2) != 0;  // LPCM 2ch 48kHz
735
736    char val[PROPERTY_VALUE_MAX];
737    if (supportsPCM
738            && property_get("media.wfd.use-pcm-audio", val, NULL)
739            && (!strcasecmp("true", val) || !strcmp("1", val))) {
740        ALOGI("Using PCM audio.");
741        mUsingPCMAudio = true;
742    } else if (supportsAAC) {
743        ALOGI("Using AAC audio.");
744        mUsingPCMAudio = false;
745    } else if (supportsPCM) {
746        ALOGI("Using PCM audio.");
747        mUsingPCMAudio = true;
748    } else {
749        ALOGI("Sink doesn't support an audio format we do.");
750        return ERROR_UNSUPPORTED;
751    }
752
753    mUsingHDCP = false;
754    if (!params->findParameter("wfd_content_protection", &value)) {
755        ALOGI("Sink doesn't appear to support content protection.");
756    } else if (value == "none") {
757        ALOGI("Sink does not support content protection.");
758    } else {
759        mUsingHDCP = true;
760
761        bool isHDCP2_0 = false;
762        if (value.startsWith("HDCP2.0 ")) {
763            isHDCP2_0 = true;
764        } else if (!value.startsWith("HDCP2.1 ")) {
765            ALOGE("malformed wfd_content_protection: '%s'", value.c_str());
766
767            return ERROR_MALFORMED;
768        }
769
770        int32_t hdcpPort;
771        if (!ParsedMessage::GetInt32Attribute(
772                    value.c_str() + 8, "port", &hdcpPort)
773                || hdcpPort < 1 || hdcpPort > 65535) {
774            return ERROR_MALFORMED;
775        }
776
777        mIsHDCP2_0 = isHDCP2_0;
778        mHDCPPort = hdcpPort;
779
780        status_t err = makeHDCP();
781        if (err != OK) {
782            ALOGE("Unable to instantiate HDCP component. "
783                  "Not using HDCP after all.");
784
785            mUsingHDCP = false;
786        }
787    }
788
789    return sendM4(sessionID);
790}
791
792status_t WifiDisplaySource::onReceiveM4Response(
793        int32_t sessionID, const sp<ParsedMessage> &msg) {
794    int32_t statusCode;
795    if (!msg->getStatusCode(&statusCode)) {
796        return ERROR_MALFORMED;
797    }
798
799    if (statusCode != 200) {
800        return ERROR_UNSUPPORTED;
801    }
802
803    if (mUsingHDCP && !mHDCPInitializationComplete) {
804        ALOGI("Deferring SETUP trigger until HDCP initialization completes.");
805
806        mSetupTriggerDeferred = true;
807        return OK;
808    }
809
810    return sendM5(sessionID, false /* requestShutdown */);
811}
812
813status_t WifiDisplaySource::onReceiveM5Response(
814        int32_t sessionID, const sp<ParsedMessage> &msg) {
815    int32_t statusCode;
816    if (!msg->getStatusCode(&statusCode)) {
817        return ERROR_MALFORMED;
818    }
819
820    if (statusCode != 200) {
821        return ERROR_UNSUPPORTED;
822    }
823
824    return OK;
825}
826
827status_t WifiDisplaySource::onReceiveM16Response(
828        int32_t sessionID, const sp<ParsedMessage> &msg) {
829    // If only the response was required to include a "Session:" header...
830
831    CHECK_EQ(sessionID, mClientSessionID);
832
833    if (mClientInfo.mPlaybackSession != NULL) {
834        mClientInfo.mPlaybackSession->updateLiveness();
835
836        scheduleKeepAlive(sessionID);
837    }
838
839    return OK;
840}
841
842void WifiDisplaySource::scheduleReaper() {
843    if (mReaperPending) {
844        return;
845    }
846
847    mReaperPending = true;
848    (new AMessage(kWhatReapDeadClients, id()))->post(kReaperIntervalUs);
849}
850
851void WifiDisplaySource::scheduleKeepAlive(int32_t sessionID) {
852    // We need to send updates at least 5 secs before the timeout is set to
853    // expire, make sure the timeout is greater than 5 secs to begin with.
854    CHECK_GT(kPlaybackSessionTimeoutUs, 5000000ll);
855
856    sp<AMessage> msg = new AMessage(kWhatKeepAlive, id());
857    msg->setInt32("sessionID", sessionID);
858    msg->post(kPlaybackSessionTimeoutUs - 5000000ll);
859}
860
861status_t WifiDisplaySource::onReceiveClientData(const sp<AMessage> &msg) {
862    int32_t sessionID;
863    CHECK(msg->findInt32("sessionID", &sessionID));
864
865    sp<RefBase> obj;
866    CHECK(msg->findObject("data", &obj));
867
868    sp<ParsedMessage> data =
869        static_cast<ParsedMessage *>(obj.get());
870
871    ALOGV("session %d received '%s'",
872          sessionID, data->debugString().c_str());
873
874    AString method;
875    AString uri;
876    data->getRequestField(0, &method);
877
878    int32_t cseq;
879    if (!data->findInt32("cseq", &cseq)) {
880        sendErrorResponse(sessionID, "400 Bad Request", -1 /* cseq */);
881        return ERROR_MALFORMED;
882    }
883
884    if (method.startsWith("RTSP/")) {
885        // This is a response.
886
887        ResponseID id;
888        id.mSessionID = sessionID;
889        id.mCSeq = cseq;
890
891        ssize_t index = mResponseHandlers.indexOfKey(id);
892
893        if (index < 0) {
894            ALOGW("Received unsolicited server response, cseq %d", cseq);
895            return ERROR_MALFORMED;
896        }
897
898        HandleRTSPResponseFunc func = mResponseHandlers.valueAt(index);
899        mResponseHandlers.removeItemsAt(index);
900
901        status_t err = (this->*func)(sessionID, data);
902
903        if (err != OK) {
904            ALOGW("Response handler for session %d, cseq %d returned "
905                  "err %d (%s)",
906                  sessionID, cseq, err, strerror(-err));
907
908            return err;
909        }
910
911        return OK;
912    }
913
914    AString version;
915    data->getRequestField(2, &version);
916    if (!(version == AString("RTSP/1.0"))) {
917        sendErrorResponse(sessionID, "505 RTSP Version not supported", cseq);
918        return ERROR_UNSUPPORTED;
919    }
920
921    status_t err;
922    if (method == "OPTIONS") {
923        err = onOptionsRequest(sessionID, cseq, data);
924    } else if (method == "SETUP") {
925        err = onSetupRequest(sessionID, cseq, data);
926    } else if (method == "PLAY") {
927        err = onPlayRequest(sessionID, cseq, data);
928    } else if (method == "PAUSE") {
929        err = onPauseRequest(sessionID, cseq, data);
930    } else if (method == "TEARDOWN") {
931        err = onTeardownRequest(sessionID, cseq, data);
932    } else if (method == "GET_PARAMETER") {
933        err = onGetParameterRequest(sessionID, cseq, data);
934    } else if (method == "SET_PARAMETER") {
935        err = onSetParameterRequest(sessionID, cseq, data);
936    } else {
937        sendErrorResponse(sessionID, "405 Method Not Allowed", cseq);
938
939        err = ERROR_UNSUPPORTED;
940    }
941
942    return err;
943}
944
945status_t WifiDisplaySource::onOptionsRequest(
946        int32_t sessionID,
947        int32_t cseq,
948        const sp<ParsedMessage> &data) {
949    int32_t playbackSessionID;
950    sp<PlaybackSession> playbackSession =
951        findPlaybackSession(data, &playbackSessionID);
952
953    if (playbackSession != NULL) {
954        playbackSession->updateLiveness();
955    }
956
957    AString response = "RTSP/1.0 200 OK\r\n";
958    AppendCommonResponse(&response, cseq);
959
960    response.append(
961            "Public: org.wfa.wfd1.0, SETUP, TEARDOWN, PLAY, PAUSE, "
962            "GET_PARAMETER, SET_PARAMETER\r\n");
963
964    response.append("\r\n");
965
966    status_t err = mNetSession->sendRequest(sessionID, response.c_str());
967
968    if (err == OK) {
969        err = sendM3(sessionID);
970    }
971
972    return err;
973}
974
975status_t WifiDisplaySource::onSetupRequest(
976        int32_t sessionID,
977        int32_t cseq,
978        const sp<ParsedMessage> &data) {
979    CHECK_EQ(sessionID, mClientSessionID);
980    if (mClientInfo.mPlaybackSessionID != -1) {
981        // We only support a single playback session per client.
982        // This is due to the reversed keep-alive design in the wfd specs...
983        sendErrorResponse(sessionID, "400 Bad Request", cseq);
984        return ERROR_MALFORMED;
985    }
986
987    AString transport;
988    if (!data->findString("transport", &transport)) {
989        sendErrorResponse(sessionID, "400 Bad Request", cseq);
990        return ERROR_MALFORMED;
991    }
992
993    Sender::TransportMode transportMode = Sender::TRANSPORT_UDP;
994
995    int clientRtp, clientRtcp;
996    if (transport.startsWith("RTP/AVP/TCP;")) {
997        AString interleaved;
998        if (ParsedMessage::GetAttribute(
999                    transport.c_str(), "interleaved", &interleaved)
1000                && sscanf(interleaved.c_str(), "%d-%d",
1001                          &clientRtp, &clientRtcp) == 2) {
1002            transportMode = Sender::TRANSPORT_TCP_INTERLEAVED;
1003        } else {
1004            bool badRequest = false;
1005
1006            AString clientPort;
1007            if (!ParsedMessage::GetAttribute(
1008                        transport.c_str(), "client_port", &clientPort)) {
1009                badRequest = true;
1010            } else if (sscanf(clientPort.c_str(), "%d-%d",
1011                              &clientRtp, &clientRtcp) == 2) {
1012            } else if (sscanf(clientPort.c_str(), "%d", &clientRtp) == 1) {
1013                // No RTCP.
1014                clientRtcp = -1;
1015            } else {
1016                badRequest = true;
1017            }
1018
1019            if (badRequest) {
1020                sendErrorResponse(sessionID, "400 Bad Request", cseq);
1021                return ERROR_MALFORMED;
1022            }
1023
1024            transportMode = Sender::TRANSPORT_TCP;
1025        }
1026    } else if (transport.startsWith("RTP/AVP;unicast;")
1027            || transport.startsWith("RTP/AVP/UDP;unicast;")) {
1028        bool badRequest = false;
1029
1030        AString clientPort;
1031        if (!ParsedMessage::GetAttribute(
1032                    transport.c_str(), "client_port", &clientPort)) {
1033            badRequest = true;
1034        } else if (sscanf(clientPort.c_str(), "%d-%d",
1035                          &clientRtp, &clientRtcp) == 2) {
1036        } else if (sscanf(clientPort.c_str(), "%d", &clientRtp) == 1) {
1037            // No RTCP.
1038            clientRtcp = -1;
1039        } else {
1040            badRequest = true;
1041        }
1042
1043        if (badRequest) {
1044            sendErrorResponse(sessionID, "400 Bad Request", cseq);
1045            return ERROR_MALFORMED;
1046        }
1047#if 1
1048    // The older LG dongles doesn't specify client_port=xxx apparently.
1049    } else if (transport == "RTP/AVP/UDP;unicast") {
1050        clientRtp = 19000;
1051        clientRtcp = -1;
1052#endif
1053    } else {
1054        sendErrorResponse(sessionID, "461 Unsupported Transport", cseq);
1055        return ERROR_UNSUPPORTED;
1056    }
1057
1058    int32_t playbackSessionID = makeUniquePlaybackSessionID();
1059
1060    sp<AMessage> notify = new AMessage(kWhatPlaybackSessionNotify, id());
1061    notify->setInt32("playbackSessionID", playbackSessionID);
1062    notify->setInt32("sessionID", sessionID);
1063
1064    sp<PlaybackSession> playbackSession =
1065        new PlaybackSession(
1066                mNetSession, notify, mInterfaceAddr, mHDCP);
1067
1068    looper()->registerHandler(playbackSession);
1069
1070    AString uri;
1071    data->getRequestField(1, &uri);
1072
1073    if (strncasecmp("rtsp://", uri.c_str(), 7)) {
1074        sendErrorResponse(sessionID, "400 Bad Request", cseq);
1075        return ERROR_MALFORMED;
1076    }
1077
1078    if (!(uri.startsWith("rtsp://") && uri.endsWith("/wfd1.0/streamid=0"))) {
1079        sendErrorResponse(sessionID, "404 Not found", cseq);
1080        return ERROR_MALFORMED;
1081    }
1082
1083    status_t err = playbackSession->init(
1084            mClientInfo.mRemoteIP.c_str(),
1085            clientRtp,
1086            clientRtcp,
1087            transportMode,
1088            mUsingPCMAudio);
1089
1090    if (err != OK) {
1091        looper()->unregisterHandler(playbackSession->id());
1092        playbackSession.clear();
1093    }
1094
1095    switch (err) {
1096        case OK:
1097            break;
1098        case -ENOENT:
1099            sendErrorResponse(sessionID, "404 Not Found", cseq);
1100            return err;
1101        default:
1102            sendErrorResponse(sessionID, "403 Forbidden", cseq);
1103            return err;
1104    }
1105
1106    mClientInfo.mPlaybackSessionID = playbackSessionID;
1107    mClientInfo.mPlaybackSession = playbackSession;
1108
1109    AString response = "RTSP/1.0 200 OK\r\n";
1110    AppendCommonResponse(&response, cseq, playbackSessionID);
1111
1112    if (transportMode == Sender::TRANSPORT_TCP_INTERLEAVED) {
1113        response.append(
1114                StringPrintf(
1115                    "Transport: RTP/AVP/TCP;interleaved=%d-%d;",
1116                    clientRtp, clientRtcp));
1117    } else {
1118        int32_t serverRtp = playbackSession->getRTPPort();
1119
1120        AString transportString = "UDP";
1121        if (transportMode == Sender::TRANSPORT_TCP) {
1122            transportString = "TCP";
1123        }
1124
1125        if (clientRtcp >= 0) {
1126            response.append(
1127                    StringPrintf(
1128                        "Transport: RTP/AVP/%s;unicast;client_port=%d-%d;"
1129                        "server_port=%d-%d\r\n",
1130                        transportString.c_str(),
1131                        clientRtp, clientRtcp, serverRtp, serverRtp + 1));
1132        } else {
1133            response.append(
1134                    StringPrintf(
1135                        "Transport: RTP/AVP/%s;unicast;client_port=%d;"
1136                        "server_port=%d\r\n",
1137                        transportString.c_str(),
1138                        clientRtp, serverRtp));
1139        }
1140    }
1141
1142    response.append("\r\n");
1143
1144    err = mNetSession->sendRequest(sessionID, response.c_str());
1145
1146    if (err != OK) {
1147        return err;
1148    }
1149
1150    mState = AWAITING_CLIENT_PLAY;
1151
1152    scheduleReaper();
1153    scheduleKeepAlive(sessionID);
1154
1155    return OK;
1156}
1157
1158status_t WifiDisplaySource::onPlayRequest(
1159        int32_t sessionID,
1160        int32_t cseq,
1161        const sp<ParsedMessage> &data) {
1162    int32_t playbackSessionID;
1163    sp<PlaybackSession> playbackSession =
1164        findPlaybackSession(data, &playbackSessionID);
1165
1166    if (playbackSession == NULL) {
1167        sendErrorResponse(sessionID, "454 Session Not Found", cseq);
1168        return ERROR_MALFORMED;
1169    }
1170
1171    ALOGI("Received PLAY request.");
1172
1173    status_t err = playbackSession->play();
1174    CHECK_EQ(err, (status_t)OK);
1175
1176    AString response = "RTSP/1.0 200 OK\r\n";
1177    AppendCommonResponse(&response, cseq, playbackSessionID);
1178    response.append("Range: npt=now-\r\n");
1179    response.append("\r\n");
1180
1181    err = mNetSession->sendRequest(sessionID, response.c_str());
1182
1183    if (err != OK) {
1184        return err;
1185    }
1186
1187    playbackSession->finishPlay();
1188
1189    CHECK_EQ(mState, AWAITING_CLIENT_PLAY);
1190    mState = ABOUT_TO_PLAY;
1191
1192    return OK;
1193}
1194
1195status_t WifiDisplaySource::onPauseRequest(
1196        int32_t sessionID,
1197        int32_t cseq,
1198        const sp<ParsedMessage> &data) {
1199    int32_t playbackSessionID;
1200    sp<PlaybackSession> playbackSession =
1201        findPlaybackSession(data, &playbackSessionID);
1202
1203    if (playbackSession == NULL) {
1204        sendErrorResponse(sessionID, "454 Session Not Found", cseq);
1205        return ERROR_MALFORMED;
1206    }
1207
1208    status_t err = playbackSession->pause();
1209    CHECK_EQ(err, (status_t)OK);
1210
1211    AString response = "RTSP/1.0 200 OK\r\n";
1212    AppendCommonResponse(&response, cseq, playbackSessionID);
1213    response.append("\r\n");
1214
1215    err = mNetSession->sendRequest(sessionID, response.c_str());
1216
1217    return err;
1218}
1219
1220status_t WifiDisplaySource::onTeardownRequest(
1221        int32_t sessionID,
1222        int32_t cseq,
1223        const sp<ParsedMessage> &data) {
1224    ALOGI("Received TEARDOWN request.");
1225
1226    int32_t playbackSessionID;
1227    sp<PlaybackSession> playbackSession =
1228        findPlaybackSession(data, &playbackSessionID);
1229
1230    if (playbackSession == NULL) {
1231        sendErrorResponse(sessionID, "454 Session Not Found", cseq);
1232        return ERROR_MALFORMED;
1233    }
1234
1235    AString response = "RTSP/1.0 200 OK\r\n";
1236    AppendCommonResponse(&response, cseq, playbackSessionID);
1237    response.append("Connection: close\r\n");
1238    response.append("\r\n");
1239
1240    mNetSession->sendRequest(sessionID, response.c_str());
1241
1242    if (mState == AWAITING_CLIENT_TEARDOWN) {
1243        CHECK_NE(mStopReplyID, 0);
1244        finishStop();
1245    } else {
1246        mClient->onDisplayError(IRemoteDisplayClient::kDisplayErrorUnknown);
1247    }
1248
1249    return OK;
1250}
1251
1252void WifiDisplaySource::finishStop() {
1253    ALOGV("finishStop");
1254
1255    mState = STOPPING;
1256
1257    disconnectClientAsync();
1258}
1259
1260void WifiDisplaySource::finishStopAfterDisconnectingClient() {
1261    ALOGV("finishStopAfterDisconnectingClient");
1262
1263    if (mHDCP != NULL) {
1264        ALOGI("Initiating HDCP shutdown.");
1265        mHDCP->shutdownAsync();
1266        return;
1267    }
1268
1269    finishStop2();
1270}
1271
1272void WifiDisplaySource::finishStop2() {
1273    ALOGV("finishStop2");
1274
1275    if (mHDCP != NULL) {
1276        mHDCP->setObserver(NULL);
1277        mHDCPObserver.clear();
1278        mHDCP.clear();
1279    }
1280
1281    if (mSessionID != 0) {
1282        mNetSession->destroySession(mSessionID);
1283        mSessionID = 0;
1284    }
1285
1286    ALOGI("We're stopped.");
1287    mState = STOPPED;
1288
1289    status_t err = OK;
1290
1291    sp<AMessage> response = new AMessage;
1292    response->setInt32("err", err);
1293    response->postReply(mStopReplyID);
1294}
1295
1296status_t WifiDisplaySource::onGetParameterRequest(
1297        int32_t sessionID,
1298        int32_t cseq,
1299        const sp<ParsedMessage> &data) {
1300    int32_t playbackSessionID;
1301    sp<PlaybackSession> playbackSession =
1302        findPlaybackSession(data, &playbackSessionID);
1303
1304    if (playbackSession == NULL) {
1305        sendErrorResponse(sessionID, "454 Session Not Found", cseq);
1306        return ERROR_MALFORMED;
1307    }
1308
1309    playbackSession->updateLiveness();
1310
1311    AString response = "RTSP/1.0 200 OK\r\n";
1312    AppendCommonResponse(&response, cseq, playbackSessionID);
1313    response.append("\r\n");
1314
1315    status_t err = mNetSession->sendRequest(sessionID, response.c_str());
1316    return err;
1317}
1318
1319status_t WifiDisplaySource::onSetParameterRequest(
1320        int32_t sessionID,
1321        int32_t cseq,
1322        const sp<ParsedMessage> &data) {
1323    int32_t playbackSessionID;
1324    sp<PlaybackSession> playbackSession =
1325        findPlaybackSession(data, &playbackSessionID);
1326
1327    if (playbackSession == NULL) {
1328        sendErrorResponse(sessionID, "454 Session Not Found", cseq);
1329        return ERROR_MALFORMED;
1330    }
1331
1332    if (strstr(data->getContent(), "wfd_idr_request\r\n")) {
1333        playbackSession->requestIDRFrame();
1334    }
1335
1336    playbackSession->updateLiveness();
1337
1338    AString response = "RTSP/1.0 200 OK\r\n";
1339    AppendCommonResponse(&response, cseq, playbackSessionID);
1340    response.append("\r\n");
1341
1342    status_t err = mNetSession->sendRequest(sessionID, response.c_str());
1343    return err;
1344}
1345
1346// static
1347void WifiDisplaySource::AppendCommonResponse(
1348        AString *response, int32_t cseq, int32_t playbackSessionID) {
1349    time_t now = time(NULL);
1350    struct tm *now2 = gmtime(&now);
1351    char buf[128];
1352    strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S %z", now2);
1353
1354    response->append("Date: ");
1355    response->append(buf);
1356    response->append("\r\n");
1357
1358    response->append("Server: Mine/1.0\r\n");
1359
1360    if (cseq >= 0) {
1361        response->append(StringPrintf("CSeq: %d\r\n", cseq));
1362    }
1363
1364    if (playbackSessionID >= 0ll) {
1365        response->append(
1366                StringPrintf(
1367                    "Session: %d;timeout=%lld\r\n",
1368                    playbackSessionID, kPlaybackSessionTimeoutSecs));
1369    }
1370}
1371
1372void WifiDisplaySource::sendErrorResponse(
1373        int32_t sessionID,
1374        const char *errorDetail,
1375        int32_t cseq) {
1376    AString response;
1377    response.append("RTSP/1.0 ");
1378    response.append(errorDetail);
1379    response.append("\r\n");
1380
1381    AppendCommonResponse(&response, cseq);
1382
1383    response.append("\r\n");
1384
1385    mNetSession->sendRequest(sessionID, response.c_str());
1386}
1387
1388int32_t WifiDisplaySource::makeUniquePlaybackSessionID() const {
1389    return rand();
1390}
1391
1392sp<WifiDisplaySource::PlaybackSession> WifiDisplaySource::findPlaybackSession(
1393        const sp<ParsedMessage> &data, int32_t *playbackSessionID) const {
1394    if (!data->findInt32("session", playbackSessionID)) {
1395        // XXX the older dongles do not always include a "Session:" header.
1396        *playbackSessionID = mClientInfo.mPlaybackSessionID;
1397        return mClientInfo.mPlaybackSession;
1398    }
1399
1400    if (*playbackSessionID != mClientInfo.mPlaybackSessionID) {
1401        return NULL;
1402    }
1403
1404    return mClientInfo.mPlaybackSession;
1405}
1406
1407void WifiDisplaySource::disconnectClientAsync() {
1408    ALOGV("disconnectClient");
1409
1410    if (mClientInfo.mPlaybackSession == NULL) {
1411        disconnectClient2();
1412        return;
1413    }
1414
1415    if (mClientInfo.mPlaybackSession != NULL) {
1416        ALOGV("Destroying PlaybackSession");
1417        mClientInfo.mPlaybackSession->destroyAsync();
1418    }
1419}
1420
1421void WifiDisplaySource::disconnectClient2() {
1422    ALOGV("disconnectClient2");
1423
1424    if (mClientInfo.mPlaybackSession != NULL) {
1425        looper()->unregisterHandler(mClientInfo.mPlaybackSession->id());
1426        mClientInfo.mPlaybackSession.clear();
1427    }
1428
1429    if (mClientSessionID != 0) {
1430        mNetSession->destroySession(mClientSessionID);
1431        mClientSessionID = 0;
1432    }
1433
1434    mClient->onDisplayDisconnected();
1435
1436    finishStopAfterDisconnectingClient();
1437}
1438
1439struct WifiDisplaySource::HDCPObserver : public BnHDCPObserver {
1440    HDCPObserver(const sp<AMessage> &notify);
1441
1442    virtual void notify(
1443            int msg, int ext1, int ext2, const Parcel *obj);
1444
1445private:
1446    sp<AMessage> mNotify;
1447
1448    DISALLOW_EVIL_CONSTRUCTORS(HDCPObserver);
1449};
1450
1451WifiDisplaySource::HDCPObserver::HDCPObserver(
1452        const sp<AMessage> &notify)
1453    : mNotify(notify) {
1454}
1455
1456void WifiDisplaySource::HDCPObserver::notify(
1457        int msg, int ext1, int ext2, const Parcel *obj) {
1458    sp<AMessage> notify = mNotify->dup();
1459    notify->setInt32("msg", msg);
1460    notify->setInt32("ext1", ext1);
1461    notify->setInt32("ext2", ext2);
1462    notify->post();
1463}
1464
1465status_t WifiDisplaySource::makeHDCP() {
1466    sp<IServiceManager> sm = defaultServiceManager();
1467    sp<IBinder> binder = sm->getService(String16("media.player"));
1468    sp<IMediaPlayerService> service = interface_cast<IMediaPlayerService>(binder);
1469    CHECK(service != NULL);
1470
1471    mHDCP = service->makeHDCP();
1472
1473    if (mHDCP == NULL) {
1474        return ERROR_UNSUPPORTED;
1475    }
1476
1477    sp<AMessage> notify = new AMessage(kWhatHDCPNotify, id());
1478    mHDCPObserver = new HDCPObserver(notify);
1479
1480    status_t err = mHDCP->setObserver(mHDCPObserver);
1481
1482    if (err != OK) {
1483        ALOGE("Failed to set HDCP observer.");
1484
1485        mHDCPObserver.clear();
1486        mHDCP.clear();
1487
1488        return err;
1489    }
1490
1491    ALOGI("Initiating HDCP negotiation w/ host %s:%d",
1492            mClientInfo.mRemoteIP.c_str(), mHDCPPort);
1493
1494    err = mHDCP->initAsync(mClientInfo.mRemoteIP.c_str(), mHDCPPort);
1495
1496    if (err != OK) {
1497        return err;
1498    }
1499
1500    return OK;
1501}
1502
1503}  // namespace android
1504
1505