WifiDisplaySource.cpp revision 90a92053219ae50ddf4bb54e3d54db2d309e2b8d
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 AString body = StringPrintf( 538 "wfd_video_formats: " 539 "28 00 02 02 00000020 00000000 00000000 00 0000 0000 00 none none\r\n" 540 "wfd_audio_codecs: %s\r\n" 541 "wfd_presentation_URL: rtsp://%s/wfd1.0/streamid=0 none\r\n" 542 "wfd_client_rtp_ports: RTP/AVP/%s;unicast %d 0 mode=play\r\n", 543 (mUsingPCMAudio 544 ? "LPCM 00000002 00" // 2 ch PCM 48kHz 545 : "AAC 00000001 00"), // 2 ch AAC 48kHz 546 mClientInfo.mLocalIP.c_str(), transportString.c_str(), mChosenRTPPort); 547 548 AString request = "SET_PARAMETER rtsp://localhost/wfd1.0 RTSP/1.0\r\n"; 549 AppendCommonResponse(&request, mNextCSeq); 550 551 request.append("Content-Type: text/parameters\r\n"); 552 request.append(StringPrintf("Content-Length: %d\r\n", body.size())); 553 request.append("\r\n"); 554 request.append(body); 555 556 status_t err = 557 mNetSession->sendRequest(sessionID, request.c_str(), request.size()); 558 559 if (err != OK) { 560 return err; 561 } 562 563 registerResponseHandler( 564 sessionID, mNextCSeq, &WifiDisplaySource::onReceiveM4Response); 565 566 ++mNextCSeq; 567 568 return OK; 569} 570 571status_t WifiDisplaySource::sendM5(int32_t sessionID, bool requestShutdown) { 572 AString body = "wfd_trigger_method: "; 573 if (requestShutdown) { 574 ALOGI("Sending TEARDOWN trigger."); 575 body.append("TEARDOWN"); 576 } else { 577 body.append("SETUP"); 578 } 579 580 body.append("\r\n"); 581 582 AString request = "SET_PARAMETER rtsp://localhost/wfd1.0 RTSP/1.0\r\n"; 583 AppendCommonResponse(&request, mNextCSeq); 584 585 request.append("Content-Type: text/parameters\r\n"); 586 request.append(StringPrintf("Content-Length: %d\r\n", body.size())); 587 request.append("\r\n"); 588 request.append(body); 589 590 status_t err = 591 mNetSession->sendRequest(sessionID, request.c_str(), request.size()); 592 593 if (err != OK) { 594 return err; 595 } 596 597 registerResponseHandler( 598 sessionID, mNextCSeq, &WifiDisplaySource::onReceiveM5Response); 599 600 ++mNextCSeq; 601 602 return OK; 603} 604 605status_t WifiDisplaySource::sendM16(int32_t sessionID) { 606 AString request = "GET_PARAMETER rtsp://localhost/wfd1.0 RTSP/1.0\r\n"; 607 AppendCommonResponse(&request, mNextCSeq); 608 609 CHECK_EQ(sessionID, mClientSessionID); 610 request.append( 611 StringPrintf("Session: %d\r\n", mClientInfo.mPlaybackSessionID)); 612 request.append("\r\n"); // Empty body 613 614 status_t err = 615 mNetSession->sendRequest(sessionID, request.c_str(), request.size()); 616 617 if (err != OK) { 618 return err; 619 } 620 621 registerResponseHandler( 622 sessionID, mNextCSeq, &WifiDisplaySource::onReceiveM16Response); 623 624 ++mNextCSeq; 625 626 return OK; 627} 628 629status_t WifiDisplaySource::onReceiveM1Response( 630 int32_t sessionID, const sp<ParsedMessage> &msg) { 631 int32_t statusCode; 632 if (!msg->getStatusCode(&statusCode)) { 633 return ERROR_MALFORMED; 634 } 635 636 if (statusCode != 200) { 637 return ERROR_UNSUPPORTED; 638 } 639 640 return OK; 641} 642 643// sink_audio_list := ("LPCM"|"AAC"|"AC3" HEXDIGIT*8 HEXDIGIT*2) 644// (", " sink_audio_list)* 645static void GetAudioModes(const char *s, const char *prefix, uint32_t *modes) { 646 *modes = 0; 647 648 size_t prefixLen = strlen(prefix); 649 650 while (*s != '0') { 651 if (!strncmp(s, prefix, prefixLen) && s[prefixLen] == ' ') { 652 unsigned latency; 653 if (sscanf(&s[prefixLen + 1], "%08x %02x", modes, &latency) != 2) { 654 *modes = 0; 655 } 656 657 return; 658 } 659 660 char *commaPos = strchr(s, ','); 661 if (commaPos != NULL) { 662 s = commaPos + 1; 663 664 while (isspace(*s)) { 665 ++s; 666 } 667 } else { 668 break; 669 } 670 } 671} 672 673status_t WifiDisplaySource::onReceiveM3Response( 674 int32_t sessionID, const sp<ParsedMessage> &msg) { 675 int32_t statusCode; 676 if (!msg->getStatusCode(&statusCode)) { 677 return ERROR_MALFORMED; 678 } 679 680 if (statusCode != 200) { 681 return ERROR_UNSUPPORTED; 682 } 683 684 sp<Parameters> params = 685 Parameters::Parse(msg->getContent(), strlen(msg->getContent())); 686 687 if (params == NULL) { 688 return ERROR_MALFORMED; 689 } 690 691 AString value; 692 if (!params->findParameter("wfd_client_rtp_ports", &value)) { 693 ALOGE("Sink doesn't report its choice of wfd_client_rtp_ports."); 694 return ERROR_MALFORMED; 695 } 696 697 unsigned port0, port1; 698 if (sscanf(value.c_str(), 699 "RTP/AVP/UDP;unicast %u %u mode=play", 700 &port0, 701 &port1) != 2 702 || port0 == 0 || port0 > 65535 || port1 != 0) { 703 ALOGE("Sink chose its wfd_client_rtp_ports poorly (%s)", 704 value.c_str()); 705 706 return ERROR_MALFORMED; 707 } 708 709 mChosenRTPPort = port0; 710 711 if (!params->findParameter("wfd_audio_codecs", &value)) { 712 ALOGE("Sink doesn't report its choice of wfd_audio_codecs."); 713 return ERROR_MALFORMED; 714 } 715 716 if (value == "none") { 717 ALOGE("Sink doesn't support audio at all."); 718 return ERROR_UNSUPPORTED; 719 } 720 721 uint32_t modes; 722 GetAudioModes(value.c_str(), "AAC", &modes); 723 724 bool supportsAAC = (modes & 1) != 0; // AAC 2ch 48kHz 725 726 GetAudioModes(value.c_str(), "LPCM", &modes); 727 728 bool supportsPCM = (modes & 2) != 0; // LPCM 2ch 48kHz 729 730 char val[PROPERTY_VALUE_MAX]; 731 if (supportsPCM 732 && property_get("media.wfd.use-pcm-audio", val, NULL) 733 && (!strcasecmp("true", val) || !strcmp("1", val))) { 734 ALOGI("Using PCM audio."); 735 mUsingPCMAudio = true; 736 } else if (supportsAAC) { 737 ALOGI("Using AAC audio."); 738 mUsingPCMAudio = false; 739 } else if (supportsPCM) { 740 ALOGI("Using PCM audio."); 741 mUsingPCMAudio = true; 742 } else { 743 ALOGI("Sink doesn't support an audio format we do."); 744 return ERROR_UNSUPPORTED; 745 } 746 747 mUsingHDCP = false; 748 if (!params->findParameter("wfd_content_protection", &value)) { 749 ALOGI("Sink doesn't appear to support content protection."); 750 } else if (value == "none") { 751 ALOGI("Sink does not support content protection."); 752 } else { 753 mUsingHDCP = true; 754 755 bool isHDCP2_0 = false; 756 if (value.startsWith("HDCP2.0 ")) { 757 isHDCP2_0 = true; 758 } else if (!value.startsWith("HDCP2.1 ")) { 759 ALOGE("malformed wfd_content_protection: '%s'", value.c_str()); 760 761 return ERROR_MALFORMED; 762 } 763 764 int32_t hdcpPort; 765 if (!ParsedMessage::GetInt32Attribute( 766 value.c_str() + 8, "port", &hdcpPort) 767 || hdcpPort < 1 || hdcpPort > 65535) { 768 return ERROR_MALFORMED; 769 } 770 771 mIsHDCP2_0 = isHDCP2_0; 772 mHDCPPort = hdcpPort; 773 774 status_t err = makeHDCP(); 775 if (err != OK) { 776 ALOGE("Unable to instantiate HDCP component."); 777 return err; 778 } 779 } 780 781 return sendM4(sessionID); 782} 783 784status_t WifiDisplaySource::onReceiveM4Response( 785 int32_t sessionID, const sp<ParsedMessage> &msg) { 786 int32_t statusCode; 787 if (!msg->getStatusCode(&statusCode)) { 788 return ERROR_MALFORMED; 789 } 790 791 if (statusCode != 200) { 792 return ERROR_UNSUPPORTED; 793 } 794 795 if (mUsingHDCP && !mHDCPInitializationComplete) { 796 ALOGI("Deferring SETUP trigger until HDCP initialization completes."); 797 798 mSetupTriggerDeferred = true; 799 return OK; 800 } 801 802 return sendM5(sessionID, false /* requestShutdown */); 803} 804 805status_t WifiDisplaySource::onReceiveM5Response( 806 int32_t sessionID, const sp<ParsedMessage> &msg) { 807 int32_t statusCode; 808 if (!msg->getStatusCode(&statusCode)) { 809 return ERROR_MALFORMED; 810 } 811 812 if (statusCode != 200) { 813 return ERROR_UNSUPPORTED; 814 } 815 816 return OK; 817} 818 819status_t WifiDisplaySource::onReceiveM16Response( 820 int32_t sessionID, const sp<ParsedMessage> &msg) { 821 // If only the response was required to include a "Session:" header... 822 823 CHECK_EQ(sessionID, mClientSessionID); 824 825 if (mClientInfo.mPlaybackSession != NULL) { 826 mClientInfo.mPlaybackSession->updateLiveness(); 827 828 scheduleKeepAlive(sessionID); 829 } 830 831 return OK; 832} 833 834void WifiDisplaySource::scheduleReaper() { 835 if (mReaperPending) { 836 return; 837 } 838 839 mReaperPending = true; 840 (new AMessage(kWhatReapDeadClients, id()))->post(kReaperIntervalUs); 841} 842 843void WifiDisplaySource::scheduleKeepAlive(int32_t sessionID) { 844 // We need to send updates at least 5 secs before the timeout is set to 845 // expire, make sure the timeout is greater than 5 secs to begin with. 846 CHECK_GT(kPlaybackSessionTimeoutUs, 5000000ll); 847 848 sp<AMessage> msg = new AMessage(kWhatKeepAlive, id()); 849 msg->setInt32("sessionID", sessionID); 850 msg->post(kPlaybackSessionTimeoutUs - 5000000ll); 851} 852 853status_t WifiDisplaySource::onReceiveClientData(const sp<AMessage> &msg) { 854 int32_t sessionID; 855 CHECK(msg->findInt32("sessionID", &sessionID)); 856 857 sp<RefBase> obj; 858 CHECK(msg->findObject("data", &obj)); 859 860 sp<ParsedMessage> data = 861 static_cast<ParsedMessage *>(obj.get()); 862 863 ALOGV("session %d received '%s'", 864 sessionID, data->debugString().c_str()); 865 866 AString method; 867 AString uri; 868 data->getRequestField(0, &method); 869 870 int32_t cseq; 871 if (!data->findInt32("cseq", &cseq)) { 872 sendErrorResponse(sessionID, "400 Bad Request", -1 /* cseq */); 873 return ERROR_MALFORMED; 874 } 875 876 if (method.startsWith("RTSP/")) { 877 // This is a response. 878 879 ResponseID id; 880 id.mSessionID = sessionID; 881 id.mCSeq = cseq; 882 883 ssize_t index = mResponseHandlers.indexOfKey(id); 884 885 if (index < 0) { 886 ALOGW("Received unsolicited server response, cseq %d", cseq); 887 return ERROR_MALFORMED; 888 } 889 890 HandleRTSPResponseFunc func = mResponseHandlers.valueAt(index); 891 mResponseHandlers.removeItemsAt(index); 892 893 status_t err = (this->*func)(sessionID, data); 894 895 if (err != OK) { 896 ALOGW("Response handler for session %d, cseq %d returned " 897 "err %d (%s)", 898 sessionID, cseq, err, strerror(-err)); 899 900 return err; 901 } 902 903 return OK; 904 } 905 906 AString version; 907 data->getRequestField(2, &version); 908 if (!(version == AString("RTSP/1.0"))) { 909 sendErrorResponse(sessionID, "505 RTSP Version not supported", cseq); 910 return ERROR_UNSUPPORTED; 911 } 912 913 status_t err; 914 if (method == "OPTIONS") { 915 err = onOptionsRequest(sessionID, cseq, data); 916 } else if (method == "SETUP") { 917 err = onSetupRequest(sessionID, cseq, data); 918 } else if (method == "PLAY") { 919 err = onPlayRequest(sessionID, cseq, data); 920 } else if (method == "PAUSE") { 921 err = onPauseRequest(sessionID, cseq, data); 922 } else if (method == "TEARDOWN") { 923 err = onTeardownRequest(sessionID, cseq, data); 924 } else if (method == "GET_PARAMETER") { 925 err = onGetParameterRequest(sessionID, cseq, data); 926 } else if (method == "SET_PARAMETER") { 927 err = onSetParameterRequest(sessionID, cseq, data); 928 } else { 929 sendErrorResponse(sessionID, "405 Method Not Allowed", cseq); 930 931 err = ERROR_UNSUPPORTED; 932 } 933 934 return err; 935} 936 937status_t WifiDisplaySource::onOptionsRequest( 938 int32_t sessionID, 939 int32_t cseq, 940 const sp<ParsedMessage> &data) { 941 int32_t playbackSessionID; 942 sp<PlaybackSession> playbackSession = 943 findPlaybackSession(data, &playbackSessionID); 944 945 if (playbackSession != NULL) { 946 playbackSession->updateLiveness(); 947 } 948 949 AString response = "RTSP/1.0 200 OK\r\n"; 950 AppendCommonResponse(&response, cseq); 951 952 response.append( 953 "Public: org.wfa.wfd1.0, SETUP, TEARDOWN, PLAY, PAUSE, " 954 "GET_PARAMETER, SET_PARAMETER\r\n"); 955 956 response.append("\r\n"); 957 958 status_t err = mNetSession->sendRequest(sessionID, response.c_str()); 959 960 if (err == OK) { 961 err = sendM3(sessionID); 962 } 963 964 return err; 965} 966 967status_t WifiDisplaySource::onSetupRequest( 968 int32_t sessionID, 969 int32_t cseq, 970 const sp<ParsedMessage> &data) { 971 CHECK_EQ(sessionID, mClientSessionID); 972 if (mClientInfo.mPlaybackSessionID != -1) { 973 // We only support a single playback session per client. 974 // This is due to the reversed keep-alive design in the wfd specs... 975 sendErrorResponse(sessionID, "400 Bad Request", cseq); 976 return ERROR_MALFORMED; 977 } 978 979 AString transport; 980 if (!data->findString("transport", &transport)) { 981 sendErrorResponse(sessionID, "400 Bad Request", cseq); 982 return ERROR_MALFORMED; 983 } 984 985 Sender::TransportMode transportMode = Sender::TRANSPORT_UDP; 986 987 int clientRtp, clientRtcp; 988 if (transport.startsWith("RTP/AVP/TCP;")) { 989 AString interleaved; 990 if (ParsedMessage::GetAttribute( 991 transport.c_str(), "interleaved", &interleaved) 992 && sscanf(interleaved.c_str(), "%d-%d", 993 &clientRtp, &clientRtcp) == 2) { 994 transportMode = Sender::TRANSPORT_TCP_INTERLEAVED; 995 } else { 996 bool badRequest = false; 997 998 AString clientPort; 999 if (!ParsedMessage::GetAttribute( 1000 transport.c_str(), "client_port", &clientPort)) { 1001 badRequest = true; 1002 } else if (sscanf(clientPort.c_str(), "%d-%d", 1003 &clientRtp, &clientRtcp) == 2) { 1004 } else if (sscanf(clientPort.c_str(), "%d", &clientRtp) == 1) { 1005 // No RTCP. 1006 clientRtcp = -1; 1007 } else { 1008 badRequest = true; 1009 } 1010 1011 if (badRequest) { 1012 sendErrorResponse(sessionID, "400 Bad Request", cseq); 1013 return ERROR_MALFORMED; 1014 } 1015 1016 transportMode = Sender::TRANSPORT_TCP; 1017 } 1018 } else if (transport.startsWith("RTP/AVP;unicast;") 1019 || transport.startsWith("RTP/AVP/UDP;unicast;")) { 1020 bool badRequest = false; 1021 1022 AString clientPort; 1023 if (!ParsedMessage::GetAttribute( 1024 transport.c_str(), "client_port", &clientPort)) { 1025 badRequest = true; 1026 } else if (sscanf(clientPort.c_str(), "%d-%d", 1027 &clientRtp, &clientRtcp) == 2) { 1028 } else if (sscanf(clientPort.c_str(), "%d", &clientRtp) == 1) { 1029 // No RTCP. 1030 clientRtcp = -1; 1031 } else { 1032 badRequest = true; 1033 } 1034 1035 if (badRequest) { 1036 sendErrorResponse(sessionID, "400 Bad Request", cseq); 1037 return ERROR_MALFORMED; 1038 } 1039#if 1 1040 // The older LG dongles doesn't specify client_port=xxx apparently. 1041 } else if (transport == "RTP/AVP/UDP;unicast") { 1042 clientRtp = 19000; 1043 clientRtcp = -1; 1044#endif 1045 } else { 1046 sendErrorResponse(sessionID, "461 Unsupported Transport", cseq); 1047 return ERROR_UNSUPPORTED; 1048 } 1049 1050 int32_t playbackSessionID = makeUniquePlaybackSessionID(); 1051 1052 sp<AMessage> notify = new AMessage(kWhatPlaybackSessionNotify, id()); 1053 notify->setInt32("playbackSessionID", playbackSessionID); 1054 notify->setInt32("sessionID", sessionID); 1055 1056 sp<PlaybackSession> playbackSession = 1057 new PlaybackSession( 1058 mNetSession, notify, mInterfaceAddr, mHDCP); 1059 1060 looper()->registerHandler(playbackSession); 1061 1062 AString uri; 1063 data->getRequestField(1, &uri); 1064 1065 if (strncasecmp("rtsp://", uri.c_str(), 7)) { 1066 sendErrorResponse(sessionID, "400 Bad Request", cseq); 1067 return ERROR_MALFORMED; 1068 } 1069 1070 if (!(uri.startsWith("rtsp://") && uri.endsWith("/wfd1.0/streamid=0"))) { 1071 sendErrorResponse(sessionID, "404 Not found", cseq); 1072 return ERROR_MALFORMED; 1073 } 1074 1075 status_t err = playbackSession->init( 1076 mClientInfo.mRemoteIP.c_str(), 1077 clientRtp, 1078 clientRtcp, 1079 transportMode, 1080 mUsingPCMAudio); 1081 1082 if (err != OK) { 1083 looper()->unregisterHandler(playbackSession->id()); 1084 playbackSession.clear(); 1085 } 1086 1087 switch (err) { 1088 case OK: 1089 break; 1090 case -ENOENT: 1091 sendErrorResponse(sessionID, "404 Not Found", cseq); 1092 return err; 1093 default: 1094 sendErrorResponse(sessionID, "403 Forbidden", cseq); 1095 return err; 1096 } 1097 1098 mClientInfo.mPlaybackSessionID = playbackSessionID; 1099 mClientInfo.mPlaybackSession = playbackSession; 1100 1101 AString response = "RTSP/1.0 200 OK\r\n"; 1102 AppendCommonResponse(&response, cseq, playbackSessionID); 1103 1104 if (transportMode == Sender::TRANSPORT_TCP_INTERLEAVED) { 1105 response.append( 1106 StringPrintf( 1107 "Transport: RTP/AVP/TCP;interleaved=%d-%d;", 1108 clientRtp, clientRtcp)); 1109 } else { 1110 int32_t serverRtp = playbackSession->getRTPPort(); 1111 1112 AString transportString = "UDP"; 1113 if (transportMode == Sender::TRANSPORT_TCP) { 1114 transportString = "TCP"; 1115 } 1116 1117 if (clientRtcp >= 0) { 1118 response.append( 1119 StringPrintf( 1120 "Transport: RTP/AVP/%s;unicast;client_port=%d-%d;" 1121 "server_port=%d-%d\r\n", 1122 transportString.c_str(), 1123 clientRtp, clientRtcp, serverRtp, serverRtp + 1)); 1124 } else { 1125 response.append( 1126 StringPrintf( 1127 "Transport: RTP/AVP/%s;unicast;client_port=%d;" 1128 "server_port=%d\r\n", 1129 transportString.c_str(), 1130 clientRtp, serverRtp)); 1131 } 1132 } 1133 1134 response.append("\r\n"); 1135 1136 err = mNetSession->sendRequest(sessionID, response.c_str()); 1137 1138 if (err != OK) { 1139 return err; 1140 } 1141 1142 mState = AWAITING_CLIENT_PLAY; 1143 1144 scheduleReaper(); 1145 scheduleKeepAlive(sessionID); 1146 1147 return OK; 1148} 1149 1150status_t WifiDisplaySource::onPlayRequest( 1151 int32_t sessionID, 1152 int32_t cseq, 1153 const sp<ParsedMessage> &data) { 1154 int32_t playbackSessionID; 1155 sp<PlaybackSession> playbackSession = 1156 findPlaybackSession(data, &playbackSessionID); 1157 1158 if (playbackSession == NULL) { 1159 sendErrorResponse(sessionID, "454 Session Not Found", cseq); 1160 return ERROR_MALFORMED; 1161 } 1162 1163 ALOGI("Received PLAY request."); 1164 1165 status_t err = playbackSession->play(); 1166 CHECK_EQ(err, (status_t)OK); 1167 1168 AString response = "RTSP/1.0 200 OK\r\n"; 1169 AppendCommonResponse(&response, cseq, playbackSessionID); 1170 response.append("Range: npt=now-\r\n"); 1171 response.append("\r\n"); 1172 1173 err = mNetSession->sendRequest(sessionID, response.c_str()); 1174 1175 if (err != OK) { 1176 return err; 1177 } 1178 1179 playbackSession->finishPlay(); 1180 1181 CHECK_EQ(mState, AWAITING_CLIENT_PLAY); 1182 mState = ABOUT_TO_PLAY; 1183 1184 return OK; 1185} 1186 1187status_t WifiDisplaySource::onPauseRequest( 1188 int32_t sessionID, 1189 int32_t cseq, 1190 const sp<ParsedMessage> &data) { 1191 int32_t playbackSessionID; 1192 sp<PlaybackSession> playbackSession = 1193 findPlaybackSession(data, &playbackSessionID); 1194 1195 if (playbackSession == NULL) { 1196 sendErrorResponse(sessionID, "454 Session Not Found", cseq); 1197 return ERROR_MALFORMED; 1198 } 1199 1200 status_t err = playbackSession->pause(); 1201 CHECK_EQ(err, (status_t)OK); 1202 1203 AString response = "RTSP/1.0 200 OK\r\n"; 1204 AppendCommonResponse(&response, cseq, playbackSessionID); 1205 response.append("\r\n"); 1206 1207 err = mNetSession->sendRequest(sessionID, response.c_str()); 1208 1209 return err; 1210} 1211 1212status_t WifiDisplaySource::onTeardownRequest( 1213 int32_t sessionID, 1214 int32_t cseq, 1215 const sp<ParsedMessage> &data) { 1216 ALOGI("Received TEARDOWN request."); 1217 1218 int32_t playbackSessionID; 1219 sp<PlaybackSession> playbackSession = 1220 findPlaybackSession(data, &playbackSessionID); 1221 1222 if (playbackSession == NULL) { 1223 sendErrorResponse(sessionID, "454 Session Not Found", cseq); 1224 return ERROR_MALFORMED; 1225 } 1226 1227 AString response = "RTSP/1.0 200 OK\r\n"; 1228 AppendCommonResponse(&response, cseq, playbackSessionID); 1229 response.append("Connection: close\r\n"); 1230 response.append("\r\n"); 1231 1232 mNetSession->sendRequest(sessionID, response.c_str()); 1233 1234 if (mState == AWAITING_CLIENT_TEARDOWN) { 1235 CHECK_NE(mStopReplyID, 0); 1236 finishStop(); 1237 } else { 1238 mClient->onDisplayError(IRemoteDisplayClient::kDisplayErrorUnknown); 1239 } 1240 1241 return OK; 1242} 1243 1244void WifiDisplaySource::finishStop() { 1245 ALOGV("finishStop"); 1246 1247 mState = STOPPING; 1248 1249 disconnectClientAsync(); 1250} 1251 1252void WifiDisplaySource::finishStopAfterDisconnectingClient() { 1253 ALOGV("finishStopAfterDisconnectingClient"); 1254 1255 if (mHDCP != NULL) { 1256 ALOGI("Initiating HDCP shutdown."); 1257 mHDCP->shutdownAsync(); 1258 return; 1259 } 1260 1261 finishStop2(); 1262} 1263 1264void WifiDisplaySource::finishStop2() { 1265 ALOGV("finishStop2"); 1266 1267 if (mHDCP != NULL) { 1268 mHDCP->setObserver(NULL); 1269 mHDCPObserver.clear(); 1270 mHDCP.clear(); 1271 } 1272 1273 if (mSessionID != 0) { 1274 mNetSession->destroySession(mSessionID); 1275 mSessionID = 0; 1276 } 1277 1278 ALOGI("We're stopped."); 1279 mState = STOPPED; 1280 1281 status_t err = OK; 1282 1283 sp<AMessage> response = new AMessage; 1284 response->setInt32("err", err); 1285 response->postReply(mStopReplyID); 1286} 1287 1288status_t WifiDisplaySource::onGetParameterRequest( 1289 int32_t sessionID, 1290 int32_t cseq, 1291 const sp<ParsedMessage> &data) { 1292 int32_t playbackSessionID; 1293 sp<PlaybackSession> playbackSession = 1294 findPlaybackSession(data, &playbackSessionID); 1295 1296 if (playbackSession == NULL) { 1297 sendErrorResponse(sessionID, "454 Session Not Found", cseq); 1298 return ERROR_MALFORMED; 1299 } 1300 1301 playbackSession->updateLiveness(); 1302 1303 AString response = "RTSP/1.0 200 OK\r\n"; 1304 AppendCommonResponse(&response, cseq, playbackSessionID); 1305 response.append("\r\n"); 1306 1307 status_t err = mNetSession->sendRequest(sessionID, response.c_str()); 1308 return err; 1309} 1310 1311status_t WifiDisplaySource::onSetParameterRequest( 1312 int32_t sessionID, 1313 int32_t cseq, 1314 const sp<ParsedMessage> &data) { 1315 int32_t playbackSessionID; 1316 sp<PlaybackSession> playbackSession = 1317 findPlaybackSession(data, &playbackSessionID); 1318 1319 if (playbackSession == NULL) { 1320 sendErrorResponse(sessionID, "454 Session Not Found", cseq); 1321 return ERROR_MALFORMED; 1322 } 1323 1324 if (strstr(data->getContent(), "wfd_idr_request\r\n")) { 1325 playbackSession->requestIDRFrame(); 1326 } 1327 1328 playbackSession->updateLiveness(); 1329 1330 AString response = "RTSP/1.0 200 OK\r\n"; 1331 AppendCommonResponse(&response, cseq, playbackSessionID); 1332 response.append("\r\n"); 1333 1334 status_t err = mNetSession->sendRequest(sessionID, response.c_str()); 1335 return err; 1336} 1337 1338// static 1339void WifiDisplaySource::AppendCommonResponse( 1340 AString *response, int32_t cseq, int32_t playbackSessionID) { 1341 time_t now = time(NULL); 1342 struct tm *now2 = gmtime(&now); 1343 char buf[128]; 1344 strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S %z", now2); 1345 1346 response->append("Date: "); 1347 response->append(buf); 1348 response->append("\r\n"); 1349 1350 response->append("Server: Mine/1.0\r\n"); 1351 1352 if (cseq >= 0) { 1353 response->append(StringPrintf("CSeq: %d\r\n", cseq)); 1354 } 1355 1356 if (playbackSessionID >= 0ll) { 1357 response->append( 1358 StringPrintf( 1359 "Session: %d;timeout=%lld\r\n", 1360 playbackSessionID, kPlaybackSessionTimeoutSecs)); 1361 } 1362} 1363 1364void WifiDisplaySource::sendErrorResponse( 1365 int32_t sessionID, 1366 const char *errorDetail, 1367 int32_t cseq) { 1368 AString response; 1369 response.append("RTSP/1.0 "); 1370 response.append(errorDetail); 1371 response.append("\r\n"); 1372 1373 AppendCommonResponse(&response, cseq); 1374 1375 response.append("\r\n"); 1376 1377 mNetSession->sendRequest(sessionID, response.c_str()); 1378} 1379 1380int32_t WifiDisplaySource::makeUniquePlaybackSessionID() const { 1381 return rand(); 1382} 1383 1384sp<WifiDisplaySource::PlaybackSession> WifiDisplaySource::findPlaybackSession( 1385 const sp<ParsedMessage> &data, int32_t *playbackSessionID) const { 1386 if (!data->findInt32("session", playbackSessionID)) { 1387 // XXX the older dongles do not always include a "Session:" header. 1388 *playbackSessionID = mClientInfo.mPlaybackSessionID; 1389 return mClientInfo.mPlaybackSession; 1390 } 1391 1392 if (*playbackSessionID != mClientInfo.mPlaybackSessionID) { 1393 return NULL; 1394 } 1395 1396 return mClientInfo.mPlaybackSession; 1397} 1398 1399void WifiDisplaySource::disconnectClientAsync() { 1400 ALOGV("disconnectClient"); 1401 1402 if (mClientInfo.mPlaybackSession == NULL) { 1403 disconnectClient2(); 1404 return; 1405 } 1406 1407 if (mClientInfo.mPlaybackSession != NULL) { 1408 ALOGV("Destroying PlaybackSession"); 1409 mClientInfo.mPlaybackSession->destroyAsync(); 1410 } 1411} 1412 1413void WifiDisplaySource::disconnectClient2() { 1414 ALOGV("disconnectClient2"); 1415 1416 if (mClientInfo.mPlaybackSession != NULL) { 1417 looper()->unregisterHandler(mClientInfo.mPlaybackSession->id()); 1418 mClientInfo.mPlaybackSession.clear(); 1419 } 1420 1421 if (mClientSessionID != 0) { 1422 mNetSession->destroySession(mClientSessionID); 1423 mClientSessionID = 0; 1424 } 1425 1426 mClient->onDisplayDisconnected(); 1427 1428 finishStopAfterDisconnectingClient(); 1429} 1430 1431struct WifiDisplaySource::HDCPObserver : public BnHDCPObserver { 1432 HDCPObserver(const sp<AMessage> ¬ify); 1433 1434 virtual void notify( 1435 int msg, int ext1, int ext2, const Parcel *obj); 1436 1437private: 1438 sp<AMessage> mNotify; 1439 1440 DISALLOW_EVIL_CONSTRUCTORS(HDCPObserver); 1441}; 1442 1443WifiDisplaySource::HDCPObserver::HDCPObserver( 1444 const sp<AMessage> ¬ify) 1445 : mNotify(notify) { 1446} 1447 1448void WifiDisplaySource::HDCPObserver::notify( 1449 int msg, int ext1, int ext2, const Parcel *obj) { 1450 sp<AMessage> notify = mNotify->dup(); 1451 notify->setInt32("msg", msg); 1452 notify->setInt32("ext1", ext1); 1453 notify->setInt32("ext2", ext2); 1454 notify->post(); 1455} 1456 1457status_t WifiDisplaySource::makeHDCP() { 1458 sp<IServiceManager> sm = defaultServiceManager(); 1459 sp<IBinder> binder = sm->getService(String16("media.player")); 1460 sp<IMediaPlayerService> service = interface_cast<IMediaPlayerService>(binder); 1461 CHECK(service != NULL); 1462 1463 mHDCP = service->makeHDCP(); 1464 1465 if (mHDCP == NULL) { 1466 return ERROR_UNSUPPORTED; 1467 } 1468 1469 sp<AMessage> notify = new AMessage(kWhatHDCPNotify, id()); 1470 mHDCPObserver = new HDCPObserver(notify); 1471 1472 status_t err = mHDCP->setObserver(mHDCPObserver); 1473 1474 if (err != OK) { 1475 ALOGE("Failed to set HDCP observer."); 1476 1477 mHDCPObserver.clear(); 1478 mHDCP.clear(); 1479 1480 return err; 1481 } 1482 1483 ALOGI("Initiating HDCP negotiation w/ host %s:%d", 1484 mClientInfo.mRemoteIP.c_str(), mHDCPPort); 1485 1486 err = mHDCP->initAsync(mClientInfo.mRemoteIP.c_str(), mHDCPPort); 1487 1488 if (err != OK) { 1489 return err; 1490 } 1491 1492 return OK; 1493} 1494 1495} // namespace android 1496 1497