NuPlayerDriver.cpp revision d0c8913702815fb0e86c4e183e6dbb4bab1016c2
1/* 2 * Copyright (C) 2010 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 "NuPlayerDriver" 19#include <inttypes.h> 20#include <utils/Log.h> 21 22#include "NuPlayerDriver.h" 23 24#include "NuPlayer.h" 25#include "NuPlayerSource.h" 26 27#include <media/stagefright/foundation/ADebug.h> 28#include <media/stagefright/foundation/ALooper.h> 29#include <media/stagefright/MetaData.h> 30#include <media/stagefright/Utils.h> 31 32namespace android { 33 34NuPlayerDriver::NuPlayerDriver() 35 : mState(STATE_IDLE), 36 mIsAsyncPrepare(false), 37 mAsyncResult(UNKNOWN_ERROR), 38 mSetSurfaceInProgress(false), 39 mDurationUs(-1), 40 mPositionUs(-1), 41 mNotifyTimeRealUs(-1), 42 mPauseStartedTimeUs(-1), 43 mNumFramesTotal(0), 44 mNumFramesDropped(0), 45 mLooper(new ALooper), 46 mPlayerFlags(0), 47 mAtEOS(false), 48 mLooping(false), 49 mAutoLoop(false), 50 mStartupSeekTimeUs(-1) { 51 ALOGV("NuPlayerDriver(%p)", this); 52 mLooper->setName("NuPlayerDriver Looper"); 53 54 mLooper->start( 55 false, /* runOnCallingThread */ 56 true, /* canCallJava */ 57 PRIORITY_AUDIO); 58 59 mPlayer = new NuPlayer; 60 mLooper->registerHandler(mPlayer); 61 62 mPlayer->setDriver(this); 63} 64 65NuPlayerDriver::~NuPlayerDriver() { 66 ALOGV("~NuPlayerDriver(%p)", this); 67 mLooper->stop(); 68} 69 70status_t NuPlayerDriver::initCheck() { 71 return OK; 72} 73 74status_t NuPlayerDriver::setUID(uid_t uid) { 75 mPlayer->setUID(uid); 76 77 return OK; 78} 79 80status_t NuPlayerDriver::setDataSource( 81 const sp<IMediaHTTPService> &httpService, 82 const char *url, 83 const KeyedVector<String8, String8> *headers) { 84 ALOGV("setDataSource(%p) url(%s)", this, uriDebugString(url, false).c_str()); 85 Mutex::Autolock autoLock(mLock); 86 87 if (mState != STATE_IDLE) { 88 return INVALID_OPERATION; 89 } 90 91 mState = STATE_SET_DATASOURCE_PENDING; 92 93 mPlayer->setDataSourceAsync(httpService, url, headers); 94 95 while (mState == STATE_SET_DATASOURCE_PENDING) { 96 mCondition.wait(mLock); 97 } 98 99 return mAsyncResult; 100} 101 102status_t NuPlayerDriver::setDataSource(int fd, int64_t offset, int64_t length) { 103 ALOGV("setDataSource(%p) file(%d)", this, fd); 104 Mutex::Autolock autoLock(mLock); 105 106 if (mState != STATE_IDLE) { 107 return INVALID_OPERATION; 108 } 109 110 mState = STATE_SET_DATASOURCE_PENDING; 111 112 mPlayer->setDataSourceAsync(fd, offset, length); 113 114 while (mState == STATE_SET_DATASOURCE_PENDING) { 115 mCondition.wait(mLock); 116 } 117 118 return mAsyncResult; 119} 120 121status_t NuPlayerDriver::setDataSource(const sp<IStreamSource> &source) { 122 ALOGV("setDataSource(%p) stream source", this); 123 Mutex::Autolock autoLock(mLock); 124 125 if (mState != STATE_IDLE) { 126 return INVALID_OPERATION; 127 } 128 129 mState = STATE_SET_DATASOURCE_PENDING; 130 131 mPlayer->setDataSourceAsync(source); 132 133 while (mState == STATE_SET_DATASOURCE_PENDING) { 134 mCondition.wait(mLock); 135 } 136 137 return mAsyncResult; 138} 139 140status_t NuPlayerDriver::setVideoSurfaceTexture( 141 const sp<IGraphicBufferProducer> &bufferProducer) { 142 ALOGV("setVideoSurfaceTexture(%p)", this); 143 Mutex::Autolock autoLock(mLock); 144 145 if (mSetSurfaceInProgress) { 146 return INVALID_OPERATION; 147 } 148 149 switch (mState) { 150 case STATE_SET_DATASOURCE_PENDING: 151 case STATE_RESET_IN_PROGRESS: 152 return INVALID_OPERATION; 153 154 default: 155 break; 156 } 157 158 mSetSurfaceInProgress = true; 159 160 mPlayer->setVideoSurfaceTextureAsync(bufferProducer); 161 162 while (mSetSurfaceInProgress) { 163 mCondition.wait(mLock); 164 } 165 166 return OK; 167} 168 169status_t NuPlayerDriver::prepare() { 170 ALOGV("prepare(%p)", this); 171 Mutex::Autolock autoLock(mLock); 172 return prepare_l(); 173} 174 175status_t NuPlayerDriver::prepare_l() { 176 switch (mState) { 177 case STATE_UNPREPARED: 178 mState = STATE_PREPARING; 179 180 // Make sure we're not posting any notifications, success or 181 // failure information is only communicated through our result 182 // code. 183 mIsAsyncPrepare = false; 184 mPlayer->prepareAsync(); 185 while (mState == STATE_PREPARING) { 186 mCondition.wait(mLock); 187 } 188 return (mState == STATE_PREPARED) ? OK : UNKNOWN_ERROR; 189 case STATE_STOPPED: 190 // this is really just paused. handle as seek to start 191 mAtEOS = false; 192 mState = STATE_STOPPED_AND_PREPARING; 193 mIsAsyncPrepare = false; 194 mPlayer->seekToAsync(0, true /* needNotify */); 195 while (mState == STATE_STOPPED_AND_PREPARING) { 196 mCondition.wait(mLock); 197 } 198 return (mState == STATE_STOPPED_AND_PREPARED) ? OK : UNKNOWN_ERROR; 199 default: 200 return INVALID_OPERATION; 201 }; 202} 203 204status_t NuPlayerDriver::prepareAsync() { 205 ALOGV("prepareAsync(%p)", this); 206 Mutex::Autolock autoLock(mLock); 207 208 switch (mState) { 209 case STATE_UNPREPARED: 210 mState = STATE_PREPARING; 211 mIsAsyncPrepare = true; 212 mPlayer->prepareAsync(); 213 return OK; 214 case STATE_STOPPED: 215 // this is really just paused. handle as seek to start 216 mAtEOS = false; 217 mState = STATE_STOPPED_AND_PREPARING; 218 mIsAsyncPrepare = true; 219 mPlayer->seekToAsync(0, true /* needNotify */); 220 return OK; 221 default: 222 return INVALID_OPERATION; 223 }; 224} 225 226status_t NuPlayerDriver::start() { 227 ALOGD("start(%p)", this); 228 Mutex::Autolock autoLock(mLock); 229 230 switch (mState) { 231 case STATE_UNPREPARED: 232 { 233 status_t err = prepare_l(); 234 235 if (err != OK) { 236 return err; 237 } 238 239 CHECK_EQ(mState, STATE_PREPARED); 240 241 // fall through 242 } 243 244 case STATE_PREPARED: 245 { 246 mAtEOS = false; 247 mPlayer->start(); 248 249 if (mStartupSeekTimeUs >= 0) { 250 if (mStartupSeekTimeUs > 0) { 251 mPlayer->seekToAsync(mStartupSeekTimeUs); 252 } 253 254 mStartupSeekTimeUs = -1; 255 } 256 break; 257 } 258 259 case STATE_RUNNING: 260 { 261 if (mAtEOS) { 262 mPlayer->seekToAsync(0); 263 mAtEOS = false; 264 mPositionUs = -1; 265 } 266 break; 267 } 268 269 case STATE_PAUSED: 270 case STATE_STOPPED_AND_PREPARED: 271 { 272 if (mAtEOS) { 273 mPlayer->seekToAsync(0); 274 mAtEOS = false; 275 mPlayer->resume(); 276 mPositionUs = -1; 277 } else { 278 mPlayer->resume(); 279 if (mNotifyTimeRealUs != -1) { 280 // Pause time must be set if here by setPauseStartedTimeIfNeeded(). 281 //CHECK(mPauseStartedTimeUs != -1); 282 283 // if no seek occurs, adjust our notify time so that getCurrentPosition() 284 // is continuous if read immediately after calling start(). 285 mNotifyTimeRealUs += ALooper::GetNowUs() - mPauseStartedTimeUs; 286 } 287 } 288 break; 289 } 290 291 default: 292 return INVALID_OPERATION; 293 } 294 295 mState = STATE_RUNNING; 296 mPauseStartedTimeUs = -1; 297 298 return OK; 299} 300 301status_t NuPlayerDriver::stop() { 302 ALOGD("stop(%p)", this); 303 Mutex::Autolock autoLock(mLock); 304 305 switch (mState) { 306 case STATE_RUNNING: 307 mPlayer->pause(); 308 // fall through 309 310 case STATE_PAUSED: 311 mState = STATE_STOPPED; 312 notifyListener_l(MEDIA_STOPPED); 313 break; 314 315 case STATE_PREPARED: 316 case STATE_STOPPED: 317 case STATE_STOPPED_AND_PREPARING: 318 case STATE_STOPPED_AND_PREPARED: 319 mState = STATE_STOPPED; 320 break; 321 322 default: 323 return INVALID_OPERATION; 324 } 325 setPauseStartedTimeIfNeeded(); 326 327 return OK; 328} 329 330status_t NuPlayerDriver::pause() { 331 Mutex::Autolock autoLock(mLock); 332 333 switch (mState) { 334 case STATE_PAUSED: 335 case STATE_PREPARED: 336 return OK; 337 338 case STATE_RUNNING: 339 setPauseStartedTimeIfNeeded(); 340 mState = STATE_PAUSED; 341 notifyListener_l(MEDIA_PAUSED); 342 mPlayer->pause(); 343 break; 344 345 default: 346 return INVALID_OPERATION; 347 } 348 349 return OK; 350} 351 352bool NuPlayerDriver::isPlaying() { 353 return mState == STATE_RUNNING && !mAtEOS; 354} 355 356status_t NuPlayerDriver::seekTo(int msec) { 357 ALOGD("seekTo(%p) %d ms", this, msec); 358 Mutex::Autolock autoLock(mLock); 359 360 int64_t seekTimeUs = msec * 1000ll; 361 362 switch (mState) { 363 case STATE_PREPARED: 364 { 365 int curpos = 0; 366 if (mPositionUs > 0) { 367 curpos = (mPositionUs + 500ll) / 1000; 368 } 369 if (curpos == msec) { 370 // nothing to do, and doing something anyway could result in deadlock (b/15323063) 371 break; 372 } 373 mStartupSeekTimeUs = seekTimeUs; 374 // pretend that the seek completed. It will actually happen when starting playback. 375 // TODO: actually perform the seek here, so the player is ready to go at the new 376 // location 377 notifySeekComplete_l(); 378 break; 379 } 380 381 case STATE_RUNNING: 382 case STATE_PAUSED: 383 { 384 mAtEOS = false; 385 // seeks can take a while, so we essentially paused 386 notifyListener_l(MEDIA_PAUSED); 387 mPlayer->seekToAsync(seekTimeUs, true /* needNotify */); 388 break; 389 } 390 391 default: 392 return INVALID_OPERATION; 393 } 394 395 mPositionUs = seekTimeUs; 396 mNotifyTimeRealUs = -1; 397 return OK; 398} 399 400status_t NuPlayerDriver::getCurrentPosition(int *msec) { 401 Mutex::Autolock autoLock(mLock); 402 403 if (mPositionUs < 0) { 404 // mPositionUs is the media time. 405 // It is negative under these cases 406 // (1) == -1 after reset, or very first playback, no stream notification yet. 407 // (2) == -1 start after end of stream, no stream notification yet. 408 // (3) == large negative # after ~292,471 years of continuous playback. 409 410 //CHECK_EQ(mPositionUs, -1); 411 *msec = 0; 412 } else if (mNotifyTimeRealUs == -1) { 413 // A seek has occurred just occurred, no stream notification yet. 414 // mPositionUs (>= 0) is the new media position. 415 *msec = mPositionUs / 1000; 416 } else { 417 // mPosition must be valid (i.e. >= 0) by the first check above. 418 // We're either playing or have pause time set: mPauseStartedTimeUs is >= 0 419 //LOG_ALWAYS_FATAL_IF( 420 // !isPlaying() && mPauseStartedTimeUs < 0, 421 // "Player in non-playing mState(%d) and mPauseStartedTimeUs(%lld) < 0", 422 // mState, (long long)mPauseStartedTimeUs); 423 ALOG_ASSERT(mNotifyTimeRealUs >= 0); 424 int64_t nowUs = 425 (isPlaying() ? ALooper::GetNowUs() : mPauseStartedTimeUs); 426 *msec = (mPositionUs + nowUs - mNotifyTimeRealUs + 500ll) / 1000; 427 // It is possible for *msec to be negative if the media position is > 596 hours. 428 // but we turn on this checking in NDEBUG == 0 mode. 429 ALOG_ASSERT(*msec >= 0); 430 ALOGV("getCurrentPosition nowUs(%lld)", (long long)nowUs); 431 } 432 ALOGV("getCurrentPosition returning(%d) mPositionUs(%lld) mNotifyRealTimeUs(%lld)", 433 *msec, (long long)mPositionUs, (long long)mNotifyTimeRealUs); 434 return OK; 435} 436 437status_t NuPlayerDriver::getDuration(int *msec) { 438 Mutex::Autolock autoLock(mLock); 439 440 if (mDurationUs < 0) { 441 return UNKNOWN_ERROR; 442 } 443 444 *msec = (mDurationUs + 500ll) / 1000; 445 446 return OK; 447} 448 449status_t NuPlayerDriver::reset() { 450 ALOGD("reset(%p)", this); 451 Mutex::Autolock autoLock(mLock); 452 453 switch (mState) { 454 case STATE_IDLE: 455 return OK; 456 457 case STATE_SET_DATASOURCE_PENDING: 458 case STATE_RESET_IN_PROGRESS: 459 return INVALID_OPERATION; 460 461 case STATE_PREPARING: 462 { 463 CHECK(mIsAsyncPrepare); 464 465 notifyListener_l(MEDIA_PREPARED); 466 break; 467 } 468 469 default: 470 break; 471 } 472 473 if (mState != STATE_STOPPED) { 474 notifyListener_l(MEDIA_STOPPED); 475 } 476 477 mState = STATE_RESET_IN_PROGRESS; 478 mPlayer->resetAsync(); 479 480 while (mState == STATE_RESET_IN_PROGRESS) { 481 mCondition.wait(mLock); 482 } 483 484 mDurationUs = -1; 485 mPositionUs = -1; 486 mStartupSeekTimeUs = -1; 487 mLooping = false; 488 489 return OK; 490} 491 492status_t NuPlayerDriver::setLooping(int loop) { 493 mLooping = loop != 0; 494 return OK; 495} 496 497player_type NuPlayerDriver::playerType() { 498 return NU_PLAYER; 499} 500 501status_t NuPlayerDriver::invoke(const Parcel &request, Parcel *reply) { 502 if (reply == NULL) { 503 ALOGE("reply is a NULL pointer"); 504 return BAD_VALUE; 505 } 506 507 int32_t methodId; 508 status_t ret = request.readInt32(&methodId); 509 if (ret != OK) { 510 ALOGE("Failed to retrieve the requested method to invoke"); 511 return ret; 512 } 513 514 switch (methodId) { 515 case INVOKE_ID_SET_VIDEO_SCALING_MODE: 516 { 517 int mode = request.readInt32(); 518 return mPlayer->setVideoScalingMode(mode); 519 } 520 521 case INVOKE_ID_GET_TRACK_INFO: 522 { 523 return mPlayer->getTrackInfo(reply); 524 } 525 526 case INVOKE_ID_SELECT_TRACK: 527 { 528 int trackIndex = request.readInt32(); 529 return mPlayer->selectTrack(trackIndex, true /* select */); 530 } 531 532 case INVOKE_ID_UNSELECT_TRACK: 533 { 534 int trackIndex = request.readInt32(); 535 return mPlayer->selectTrack(trackIndex, false /* select */); 536 } 537 538 case INVOKE_ID_GET_SELECTED_TRACK: 539 { 540 int32_t type = request.readInt32(); 541 return mPlayer->getSelectedTrack(type, reply); 542 } 543 544 default: 545 { 546 return INVALID_OPERATION; 547 } 548 } 549} 550 551void NuPlayerDriver::setAudioSink(const sp<AudioSink> &audioSink) { 552 mPlayer->setAudioSink(audioSink); 553 mAudioSink = audioSink; 554} 555 556status_t NuPlayerDriver::setParameter( 557 int /* key */, const Parcel & /* request */) { 558 return INVALID_OPERATION; 559} 560 561status_t NuPlayerDriver::getParameter(int /* key */, Parcel * /* reply */) { 562 return INVALID_OPERATION; 563} 564 565status_t NuPlayerDriver::getMetadata( 566 const media::Metadata::Filter& /* ids */, Parcel *records) { 567 Mutex::Autolock autoLock(mLock); 568 569 using media::Metadata; 570 571 Metadata meta(records); 572 573 meta.appendBool( 574 Metadata::kPauseAvailable, 575 mPlayerFlags & NuPlayer::Source::FLAG_CAN_PAUSE); 576 577 meta.appendBool( 578 Metadata::kSeekBackwardAvailable, 579 mPlayerFlags & NuPlayer::Source::FLAG_CAN_SEEK_BACKWARD); 580 581 meta.appendBool( 582 Metadata::kSeekForwardAvailable, 583 mPlayerFlags & NuPlayer::Source::FLAG_CAN_SEEK_FORWARD); 584 585 meta.appendBool( 586 Metadata::kSeekAvailable, 587 mPlayerFlags & NuPlayer::Source::FLAG_CAN_SEEK); 588 589 return OK; 590} 591 592void NuPlayerDriver::notifyResetComplete() { 593 ALOGD("notifyResetComplete(%p)", this); 594 Mutex::Autolock autoLock(mLock); 595 596 CHECK_EQ(mState, STATE_RESET_IN_PROGRESS); 597 mState = STATE_IDLE; 598 mCondition.broadcast(); 599} 600 601void NuPlayerDriver::notifySetSurfaceComplete() { 602 ALOGV("notifySetSurfaceComplete(%p)", this); 603 Mutex::Autolock autoLock(mLock); 604 605 CHECK(mSetSurfaceInProgress); 606 mSetSurfaceInProgress = false; 607 608 mCondition.broadcast(); 609} 610 611void NuPlayerDriver::notifyDuration(int64_t durationUs) { 612 Mutex::Autolock autoLock(mLock); 613 mDurationUs = durationUs; 614} 615 616void NuPlayerDriver::notifyPosition(int64_t positionUs) { 617 Mutex::Autolock autoLock(mLock); 618 if (isPlaying()) { 619 mPositionUs = positionUs; 620 mNotifyTimeRealUs = ALooper::GetNowUs(); 621 } 622} 623 624void NuPlayerDriver::notifySeekComplete() { 625 ALOGV("notifySeekComplete(%p)", this); 626 Mutex::Autolock autoLock(mLock); 627 notifySeekComplete_l(); 628} 629 630void NuPlayerDriver::notifySeekComplete_l() { 631 bool wasSeeking = true; 632 if (mState == STATE_STOPPED_AND_PREPARING) { 633 wasSeeking = false; 634 mState = STATE_STOPPED_AND_PREPARED; 635 mCondition.broadcast(); 636 if (!mIsAsyncPrepare) { 637 // if we are preparing synchronously, no need to notify listener 638 return; 639 } 640 } else if (mState == STATE_STOPPED) { 641 // no need to notify listener 642 return; 643 } 644 notifyListener_l(wasSeeking ? MEDIA_SEEK_COMPLETE : MEDIA_PREPARED); 645} 646 647void NuPlayerDriver::notifyFrameStats( 648 int64_t numFramesTotal, int64_t numFramesDropped) { 649 Mutex::Autolock autoLock(mLock); 650 mNumFramesTotal = numFramesTotal; 651 mNumFramesDropped = numFramesDropped; 652} 653 654status_t NuPlayerDriver::dump( 655 int fd, const Vector<String16> & /* args */) const { 656 Mutex::Autolock autoLock(mLock); 657 658 FILE *out = fdopen(dup(fd), "w"); 659 660 fprintf(out, " NuPlayer\n"); 661 fprintf(out, " numFramesTotal(%" PRId64 "), numFramesDropped(%" PRId64 "), " 662 "percentageDropped(%.2f)\n", 663 mNumFramesTotal, 664 mNumFramesDropped, 665 mNumFramesTotal == 0 666 ? 0.0 : (double)mNumFramesDropped / mNumFramesTotal); 667 668 fclose(out); 669 out = NULL; 670 671 return OK; 672} 673 674void NuPlayerDriver::notifyListener( 675 int msg, int ext1, int ext2, const Parcel *in) { 676 Mutex::Autolock autoLock(mLock); 677 notifyListener_l(msg, ext1, ext2, in); 678} 679 680void NuPlayerDriver::notifyListener_l( 681 int msg, int ext1, int ext2, const Parcel *in) { 682 switch (msg) { 683 case MEDIA_PLAYBACK_COMPLETE: 684 { 685 if (mState != STATE_RESET_IN_PROGRESS) { 686 if (mLooping || (mAutoLoop 687 && (mAudioSink == NULL || mAudioSink->realtime()))) { 688 mPlayer->seekToAsync(0); 689 break; 690 } 691 692 mPlayer->pause(); 693 mState = STATE_PAUSED; 694 } 695 // fall through 696 } 697 698 case MEDIA_ERROR: 699 { 700 mAtEOS = true; 701 setPauseStartedTimeIfNeeded(); 702 break; 703 } 704 705 default: 706 break; 707 } 708 709 mLock.unlock(); 710 sendEvent(msg, ext1, ext2, in); 711 mLock.lock(); 712} 713 714void NuPlayerDriver::notifySetDataSourceCompleted(status_t err) { 715 Mutex::Autolock autoLock(mLock); 716 717 CHECK_EQ(mState, STATE_SET_DATASOURCE_PENDING); 718 719 mAsyncResult = err; 720 mState = (err == OK) ? STATE_UNPREPARED : STATE_IDLE; 721 mCondition.broadcast(); 722} 723 724void NuPlayerDriver::notifyPrepareCompleted(status_t err) { 725 Mutex::Autolock autoLock(mLock); 726 727 if (mState != STATE_PREPARING) { 728 // We were preparing asynchronously when the client called 729 // reset(), we sent a premature "prepared" notification and 730 // then initiated the reset. This notification is stale. 731 CHECK(mState == STATE_RESET_IN_PROGRESS || mState == STATE_IDLE); 732 return; 733 } 734 735 CHECK_EQ(mState, STATE_PREPARING); 736 737 mAsyncResult = err; 738 739 if (err == OK) { 740 // update state before notifying client, so that if client calls back into NuPlayerDriver 741 // in response, NuPlayerDriver has the right state 742 mState = STATE_PREPARED; 743 if (mIsAsyncPrepare) { 744 notifyListener_l(MEDIA_PREPARED); 745 } 746 } else { 747 mState = STATE_UNPREPARED; 748 if (mIsAsyncPrepare) { 749 notifyListener_l(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, err); 750 } 751 } 752 753 sp<MetaData> meta = mPlayer->getFileMeta(); 754 int32_t loop; 755 if (meta != NULL 756 && meta->findInt32(kKeyAutoLoop, &loop) && loop != 0) { 757 mAutoLoop = true; 758 } 759 760 mCondition.broadcast(); 761} 762 763void NuPlayerDriver::notifyFlagsChanged(uint32_t flags) { 764 Mutex::Autolock autoLock(mLock); 765 766 mPlayerFlags = flags; 767} 768 769void NuPlayerDriver::setPauseStartedTimeIfNeeded() { 770 if (mPauseStartedTimeUs == -1) { 771 mPauseStartedTimeUs = ALooper::GetNowUs(); 772 } 773} 774 775} // namespace android 776