blob: 56e9471f2a07eaaa2b2068b04a2067eb315af369 [file] [log] [blame]
Wei Jia53692fa2017-12-11 10:33:46 -08001/*
2 * Copyright 2017 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 "NuPlayer2Driver"
19#include <inttypes.h>
Chih-Hung Hsiehc89566d2018-09-13 14:22:06 -070020#include <android-base/macros.h>
Wei Jia53692fa2017-12-11 10:33:46 -080021#include <utils/Log.h>
22#include <cutils/properties.h>
23
24#include "NuPlayer2Driver.h"
25
26#include "NuPlayer2.h"
27#include "NuPlayer2Source.h"
28
Wei Jiad2bb1bd2018-02-08 09:47:37 -080029#include <media/DataSourceDesc.h>
Wei Jia53692fa2017-12-11 10:33:46 -080030#include <media/stagefright/foundation/ADebug.h>
31#include <media/stagefright/foundation/ALooper.h>
32#include <media/stagefright/foundation/AUtils.h>
33#include <media/stagefright/foundation/ByteUtils.h>
34#include <media/stagefright/MediaClock.h>
35#include <media/stagefright/MetaData.h>
36#include <media/stagefright/Utils.h>
37
38#include <media/IMediaAnalyticsService.h>
39
Dongwon Kang9f631982018-07-10 12:34:41 -070040using google::protobuf::RepeatedPtrField;
41using android::media::MediaPlayer2Proto::Value;
42
Wei Jia53692fa2017-12-11 10:33:46 -080043static const int kDumpLockRetries = 50;
44static const int kDumpLockSleepUs = 20000;
45
46namespace android {
47
Dongwon Kang41929fb2018-09-09 08:29:56 -070048struct PlayerMessageWrapper : public RefBase {
49 static sp<PlayerMessageWrapper> Create(const PlayerMessage *p) {
Wei Jiac5c79da2017-12-21 18:03:05 -080050 if (p != NULL) {
Dongwon Kang41929fb2018-09-09 08:29:56 -070051 sp<PlayerMessageWrapper> pw = new PlayerMessageWrapper();
52 pw->copyFrom(p);
53 return pw;
Wei Jiac5c79da2017-12-21 18:03:05 -080054 }
55 return NULL;
56 }
57
Dongwon Kang41929fb2018-09-09 08:29:56 -070058 const PlayerMessage *getPlayerMessage() {
59 return mPlayerMessage;
Wei Jiac5c79da2017-12-21 18:03:05 -080060 }
61
62protected:
Dongwon Kang41929fb2018-09-09 08:29:56 -070063 virtual ~PlayerMessageWrapper() {
64 if (mPlayerMessage != NULL) {
65 delete mPlayerMessage;
Wei Jiac5c79da2017-12-21 18:03:05 -080066 }
67 }
68
69private:
Dongwon Kang41929fb2018-09-09 08:29:56 -070070 PlayerMessageWrapper()
71 : mPlayerMessage(NULL) { }
Wei Jiac5c79da2017-12-21 18:03:05 -080072
Dongwon Kang41929fb2018-09-09 08:29:56 -070073 void copyFrom(const PlayerMessage *p) {
74 if (mPlayerMessage == NULL) {
75 mPlayerMessage = new PlayerMessage;
Wei Jiac5c79da2017-12-21 18:03:05 -080076 }
Dongwon Kang41929fb2018-09-09 08:29:56 -070077 mPlayerMessage->CopyFrom(*p);
Wei Jiac5c79da2017-12-21 18:03:05 -080078 }
79
Dongwon Kang41929fb2018-09-09 08:29:56 -070080 PlayerMessage *mPlayerMessage;
Wei Jiac5c79da2017-12-21 18:03:05 -080081};
82
Wei Jia53692fa2017-12-11 10:33:46 -080083// key for media statistics
Ray Essickee54eef2018-01-24 11:16:54 -080084static const char *kKeyPlayer = "nuplayer";
Wei Jia53692fa2017-12-11 10:33:46 -080085// attrs for media statistics
Ray Essickde15b8c2018-01-30 16:35:56 -080086 // NB: these are matched with public Java API constants defined
87 // in frameworks/base/media/java/android/media/MediaPlayer2.java
88 // These must be kept synchronized with the constants there.
Wei Jia53692fa2017-12-11 10:33:46 -080089static const char *kPlayerVMime = "android.media.mediaplayer.video.mime";
90static const char *kPlayerVCodec = "android.media.mediaplayer.video.codec";
91static const char *kPlayerWidth = "android.media.mediaplayer.width";
92static const char *kPlayerHeight = "android.media.mediaplayer.height";
93static const char *kPlayerFrames = "android.media.mediaplayer.frames";
94static const char *kPlayerFramesDropped = "android.media.mediaplayer.dropped";
95static const char *kPlayerAMime = "android.media.mediaplayer.audio.mime";
96static const char *kPlayerACodec = "android.media.mediaplayer.audio.codec";
97static const char *kPlayerDuration = "android.media.mediaplayer.durationMs";
98static const char *kPlayerPlaying = "android.media.mediaplayer.playingMs";
99static const char *kPlayerError = "android.media.mediaplayer.err";
100static const char *kPlayerErrorCode = "android.media.mediaplayer.errcode";
Ray Essickde15b8c2018-01-30 16:35:56 -0800101
102// NB: These are not yet exposed as public Java API constants.
Ray Essick51f4c872017-12-15 12:27:56 -0800103static const char *kPlayerErrorState = "android.media.mediaplayer.errstate";
Wei Jia53692fa2017-12-11 10:33:46 -0800104static const char *kPlayerDataSourceType = "android.media.mediaplayer.dataSource";
105//
106static const char *kPlayerRebuffering = "android.media.mediaplayer.rebufferingMs";
107static const char *kPlayerRebufferingCount = "android.media.mediaplayer.rebuffers";
108static const char *kPlayerRebufferingAtExit = "android.media.mediaplayer.rebufferExit";
109
110
Dichen Zhangfc9f40f2019-01-04 14:15:28 -0800111NuPlayer2Driver::NuPlayer2Driver(pid_t pid, uid_t uid, const sp<JObjectHolder> &context)
Wei Jia53692fa2017-12-11 10:33:46 -0800112 : mState(STATE_IDLE),
Wei Jia53692fa2017-12-11 10:33:46 -0800113 mAsyncResult(UNKNOWN_ERROR),
Wei Jiad2bb1bd2018-02-08 09:47:37 -0800114 mSrcId(0),
Wei Jia53692fa2017-12-11 10:33:46 -0800115 mSetSurfaceInProgress(false),
116 mDurationUs(-1),
117 mPositionUs(-1),
118 mSeekInProgress(false),
119 mPlayingTimeUs(0),
120 mRebufferingTimeUs(0),
121 mRebufferingEvents(0),
122 mRebufferingAtExit(false),
123 mLooper(new ALooper),
Wei Jia12b9f4a2017-12-13 15:24:13 -0800124 mNuPlayer2Looper(new ALooper),
Wei Jia53692fa2017-12-11 10:33:46 -0800125 mMediaClock(new MediaClock),
Dichen Zhangfc9f40f2019-01-04 14:15:28 -0800126 mPlayer(new NuPlayer2(pid, uid, mMediaClock, context)),
Wei Jia53692fa2017-12-11 10:33:46 -0800127 mPlayerFlags(0),
128 mAnalyticsItem(NULL),
Wei Jia003fdb52018-02-06 14:44:32 -0800129 mClientUid(uid),
Wei Jia53692fa2017-12-11 10:33:46 -0800130 mAtEOS(false),
131 mLooping(false),
132 mAutoLoop(false) {
133 ALOGD("NuPlayer2Driver(%p) created, clientPid(%d)", this, pid);
134 mLooper->setName("NuPlayer2Driver Looper");
Wei Jia12b9f4a2017-12-13 15:24:13 -0800135 mNuPlayer2Looper->setName("NuPlayer2 Looper");
Wei Jia53692fa2017-12-11 10:33:46 -0800136
137 mMediaClock->init();
138
139 // set up an analytics record
140 mAnalyticsItem = new MediaAnalyticsItem(kKeyPlayer);
Wei Jia003fdb52018-02-06 14:44:32 -0800141 mAnalyticsItem->setUid(mClientUid);
Wei Jia53692fa2017-12-11 10:33:46 -0800142
Wei Jia12b9f4a2017-12-13 15:24:13 -0800143 mNuPlayer2Looper->start(
Wei Jia53692fa2017-12-11 10:33:46 -0800144 false, /* runOnCallingThread */
145 true, /* canCallJava */
146 PRIORITY_AUDIO);
147
Wei Jia12b9f4a2017-12-13 15:24:13 -0800148 mNuPlayer2Looper->registerHandler(mPlayer);
Wei Jia53692fa2017-12-11 10:33:46 -0800149
150 mPlayer->setDriver(this);
151}
152
153NuPlayer2Driver::~NuPlayer2Driver() {
154 ALOGV("~NuPlayer2Driver(%p)", this);
Wei Jia12b9f4a2017-12-13 15:24:13 -0800155 mNuPlayer2Looper->stop();
Wei Jia53692fa2017-12-11 10:33:46 -0800156 mLooper->stop();
157
158 // finalize any pending metrics, usually a no-op.
159 updateMetrics("destructor");
160 logMetrics("destructor");
161
162 if (mAnalyticsItem != NULL) {
163 delete mAnalyticsItem;
164 mAnalyticsItem = NULL;
165 }
166}
167
168status_t NuPlayer2Driver::initCheck() {
Wei Jia12b9f4a2017-12-13 15:24:13 -0800169 mLooper->start(
170 false, /* runOnCallingThread */
171 true, /* canCallJava */
172 PRIORITY_AUDIO);
173
174 mLooper->registerHandler(this);
Wei Jia53692fa2017-12-11 10:33:46 -0800175 return OK;
176}
177
Wei Jiac2636032018-02-01 09:15:25 -0800178status_t NuPlayer2Driver::setDataSource(const sp<DataSourceDesc> &dsd) {
Wei Jia57aeffd2018-02-15 16:01:14 -0800179 ALOGV("setDataSource(%p)", this);
Wei Jia53692fa2017-12-11 10:33:46 -0800180 Mutex::Autolock autoLock(mLock);
181
182 if (mState != STATE_IDLE) {
183 return INVALID_OPERATION;
184 }
185
Wei Jiad2bb1bd2018-02-08 09:47:37 -0800186 mSrcId = dsd->mId;
Wei Jia53692fa2017-12-11 10:33:46 -0800187 mState = STATE_SET_DATASOURCE_PENDING;
188
Wei Jiac2636032018-02-01 09:15:25 -0800189 mPlayer->setDataSourceAsync(dsd);
Wei Jia53692fa2017-12-11 10:33:46 -0800190
191 while (mState == STATE_SET_DATASOURCE_PENDING) {
192 mCondition.wait(mLock);
193 }
194
195 return mAsyncResult;
196}
197
Wei Jia57aeffd2018-02-15 16:01:14 -0800198status_t NuPlayer2Driver::prepareNextDataSource(const sp<DataSourceDesc> &dsd) {
199 ALOGV("prepareNextDataSource(%p)", this);
200 Mutex::Autolock autoLock(mLock);
201
202 mPlayer->prepareNextDataSourceAsync(dsd);
203
204 return OK;
205}
206
207status_t NuPlayer2Driver::playNextDataSource(int64_t srcId) {
208 ALOGV("playNextDataSource(%p)", this);
209 Mutex::Autolock autoLock(mLock);
210
211 mSrcId = srcId;
212 mPlayer->playNextDataSource(srcId);
213
214 return OK;
215}
216
Wei Jia28288fb2017-12-15 13:45:29 -0800217status_t NuPlayer2Driver::setVideoSurfaceTexture(const sp<ANativeWindowWrapper> &nww) {
Wei Jia53692fa2017-12-11 10:33:46 -0800218 ALOGV("setVideoSurfaceTexture(%p)", this);
219 Mutex::Autolock autoLock(mLock);
220
221 if (mSetSurfaceInProgress) {
222 return INVALID_OPERATION;
223 }
224
225 switch (mState) {
226 case STATE_SET_DATASOURCE_PENDING:
227 case STATE_RESET_IN_PROGRESS:
228 return INVALID_OPERATION;
229
230 default:
231 break;
232 }
233
234 mSetSurfaceInProgress = true;
235
Wei Jia28288fb2017-12-15 13:45:29 -0800236 mPlayer->setVideoSurfaceTextureAsync(nww);
Wei Jia53692fa2017-12-11 10:33:46 -0800237
238 while (mSetSurfaceInProgress) {
239 mCondition.wait(mLock);
240 }
241
242 return OK;
243}
244
245status_t NuPlayer2Driver::getBufferingSettings(BufferingSettings* buffering) {
246 ALOGV("getBufferingSettings(%p)", this);
247 {
248 Mutex::Autolock autoLock(mLock);
249 if (mState == STATE_IDLE) {
250 return INVALID_OPERATION;
251 }
252 }
253
254 return mPlayer->getBufferingSettings(buffering);
255}
256
257status_t NuPlayer2Driver::setBufferingSettings(const BufferingSettings& buffering) {
258 ALOGV("setBufferingSettings(%p)", this);
259 {
260 Mutex::Autolock autoLock(mLock);
261 if (mState == STATE_IDLE) {
262 return INVALID_OPERATION;
263 }
264 }
265
266 return mPlayer->setBufferingSettings(buffering);
267}
268
Wei Jia53692fa2017-12-11 10:33:46 -0800269status_t NuPlayer2Driver::prepareAsync() {
270 ALOGV("prepareAsync(%p)", this);
271 Mutex::Autolock autoLock(mLock);
272
273 switch (mState) {
274 case STATE_UNPREPARED:
275 mState = STATE_PREPARING;
Wei Jia53692fa2017-12-11 10:33:46 -0800276 mPlayer->prepareAsync();
277 return OK;
Wei Jia53692fa2017-12-11 10:33:46 -0800278 default:
279 return INVALID_OPERATION;
280 };
281}
282
283status_t NuPlayer2Driver::start() {
284 ALOGD("start(%p), state is %d, eos is %d", this, mState, mAtEOS);
285 Mutex::Autolock autoLock(mLock);
286 return start_l();
287}
288
289status_t NuPlayer2Driver::start_l() {
290 switch (mState) {
Wei Jia53692fa2017-12-11 10:33:46 -0800291 case STATE_PAUSED:
Wei Jia53692fa2017-12-11 10:33:46 -0800292 case STATE_PREPARED:
293 {
294 mPlayer->start();
Chih-Hung Hsiehc89566d2018-09-13 14:22:06 -0700295 FALLTHROUGH_INTENDED;
Wei Jia53692fa2017-12-11 10:33:46 -0800296 }
297
298 case STATE_RUNNING:
299 {
300 if (mAtEOS) {
Wei Jiad1864f92018-10-19 12:34:56 -0700301 mPlayer->rewind();
Wei Jia53692fa2017-12-11 10:33:46 -0800302 mAtEOS = false;
303 mPositionUs = -1;
304 }
305 break;
306 }
307
308 default:
309 return INVALID_OPERATION;
310 }
311
312 mState = STATE_RUNNING;
313
314 return OK;
315}
316
Wei Jia53692fa2017-12-11 10:33:46 -0800317status_t NuPlayer2Driver::pause() {
318 ALOGD("pause(%p)", this);
319 // The NuPlayerRenderer may get flushed if pause for long enough, e.g. the pause timeout tear
320 // down for audio offload mode. If that happens, the NuPlayerRenderer will no longer know the
321 // current position. So similar to seekTo, update |mPositionUs| to the pause position by calling
322 // getCurrentPosition here.
Wei Jia800fe372018-02-20 15:00:45 -0800323 int64_t unused;
Wei Jia53692fa2017-12-11 10:33:46 -0800324 getCurrentPosition(&unused);
325
326 Mutex::Autolock autoLock(mLock);
327
328 switch (mState) {
329 case STATE_PAUSED:
Wei Jia53692fa2017-12-11 10:33:46 -0800330 return OK;
331
Wei Jia6376cd52018-09-26 11:42:55 -0700332 case STATE_PREPARED:
Wei Jia53692fa2017-12-11 10:33:46 -0800333 case STATE_RUNNING:
334 mState = STATE_PAUSED;
Wei Jia53692fa2017-12-11 10:33:46 -0800335 mPlayer->pause();
336 break;
337
338 default:
339 return INVALID_OPERATION;
340 }
341
342 return OK;
343}
344
345bool NuPlayer2Driver::isPlaying() {
346 return mState == STATE_RUNNING && !mAtEOS;
347}
348
349status_t NuPlayer2Driver::setPlaybackSettings(const AudioPlaybackRate &rate) {
350 status_t err = mPlayer->setPlaybackSettings(rate);
351 if (err == OK) {
352 // try to update position
Wei Jia800fe372018-02-20 15:00:45 -0800353 int64_t unused;
Wei Jia53692fa2017-12-11 10:33:46 -0800354 getCurrentPosition(&unused);
Wei Jia53692fa2017-12-11 10:33:46 -0800355 }
356 return err;
357}
358
359status_t NuPlayer2Driver::getPlaybackSettings(AudioPlaybackRate *rate) {
360 return mPlayer->getPlaybackSettings(rate);
361}
362
363status_t NuPlayer2Driver::setSyncSettings(const AVSyncSettings &sync, float videoFpsHint) {
364 return mPlayer->setSyncSettings(sync, videoFpsHint);
365}
366
367status_t NuPlayer2Driver::getSyncSettings(AVSyncSettings *sync, float *videoFps) {
368 return mPlayer->getSyncSettings(sync, videoFps);
369}
370
Wei Jia800fe372018-02-20 15:00:45 -0800371status_t NuPlayer2Driver::seekTo(int64_t msec, MediaPlayer2SeekMode mode) {
372 ALOGD("seekTo(%p) (%lld ms, %d) at state %d", this, (long long)msec, mode, mState);
Wei Jia53692fa2017-12-11 10:33:46 -0800373 Mutex::Autolock autoLock(mLock);
374
Chih-Hung Hsiehd42529d2018-12-11 13:53:10 -0800375 int64_t seekTimeUs = msec * 1000LL;
Wei Jia53692fa2017-12-11 10:33:46 -0800376
377 switch (mState) {
378 case STATE_PREPARED:
Wei Jia53692fa2017-12-11 10:33:46 -0800379 case STATE_PAUSED:
380 case STATE_RUNNING:
381 {
382 mAtEOS = false;
383 mSeekInProgress = true;
Wei Jia53692fa2017-12-11 10:33:46 -0800384 mPlayer->seekToAsync(seekTimeUs, mode, true /* needNotify */);
385 break;
386 }
387
388 default:
389 return INVALID_OPERATION;
390 }
391
392 mPositionUs = seekTimeUs;
393 return OK;
394}
395
Wei Jia800fe372018-02-20 15:00:45 -0800396status_t NuPlayer2Driver::getCurrentPosition(int64_t *msec) {
Wei Jia53692fa2017-12-11 10:33:46 -0800397 int64_t tempUs = 0;
398 {
399 Mutex::Autolock autoLock(mLock);
400 if (mSeekInProgress || (mState == STATE_PAUSED && !mAtEOS)) {
401 tempUs = (mPositionUs <= 0) ? 0 : mPositionUs;
Wei Jia800fe372018-02-20 15:00:45 -0800402 *msec = divRound(tempUs, (int64_t)(1000));
Wei Jia53692fa2017-12-11 10:33:46 -0800403 return OK;
404 }
405 }
406
407 status_t ret = mPlayer->getCurrentPosition(&tempUs);
408
409 Mutex::Autolock autoLock(mLock);
410 // We need to check mSeekInProgress here because mPlayer->seekToAsync is an async call, which
411 // means getCurrentPosition can be called before seek is completed. Iow, renderer may return a
412 // position value that's different the seek to position.
413 if (ret != OK) {
414 tempUs = (mPositionUs <= 0) ? 0 : mPositionUs;
415 } else {
416 mPositionUs = tempUs;
417 }
Wei Jia800fe372018-02-20 15:00:45 -0800418 *msec = divRound(tempUs, (int64_t)(1000));
Wei Jia53692fa2017-12-11 10:33:46 -0800419 return OK;
420}
421
Wei Jia800fe372018-02-20 15:00:45 -0800422status_t NuPlayer2Driver::getDuration(int64_t *msec) {
Wei Jia53692fa2017-12-11 10:33:46 -0800423 Mutex::Autolock autoLock(mLock);
424
425 if (mDurationUs < 0) {
426 return UNKNOWN_ERROR;
427 }
428
Chih-Hung Hsiehd42529d2018-12-11 13:53:10 -0800429 *msec = (mDurationUs + 500LL) / 1000;
Wei Jia53692fa2017-12-11 10:33:46 -0800430
431 return OK;
432}
433
434void NuPlayer2Driver::updateMetrics(const char *where) {
435 if (where == NULL) {
436 where = "unknown";
437 }
438 ALOGV("updateMetrics(%p) from %s at state %d", this, where, mState);
439
440 // gather the final stats for this record
441 Vector<sp<AMessage>> trackStats;
442 mPlayer->getStats(&trackStats);
443
444 if (trackStats.size() > 0) {
445 for (size_t i = 0; i < trackStats.size(); ++i) {
446 const sp<AMessage> &stats = trackStats.itemAt(i);
447
448 AString mime;
449 stats->findString("mime", &mime);
450
451 AString name;
452 stats->findString("component-name", &name);
453
454 if (mime.startsWith("video/")) {
455 int32_t width, height;
456 mAnalyticsItem->setCString(kPlayerVMime, mime.c_str());
457 if (!name.empty()) {
458 mAnalyticsItem->setCString(kPlayerVCodec, name.c_str());
459 }
460
461 if (stats->findInt32("width", &width)
462 && stats->findInt32("height", &height)) {
463 mAnalyticsItem->setInt32(kPlayerWidth, width);
464 mAnalyticsItem->setInt32(kPlayerHeight, height);
465 }
466
467 int64_t numFramesTotal = 0;
468 int64_t numFramesDropped = 0;
469 stats->findInt64("frames-total", &numFramesTotal);
470 stats->findInt64("frames-dropped-output", &numFramesDropped);
471
472 mAnalyticsItem->setInt64(kPlayerFrames, numFramesTotal);
473 mAnalyticsItem->setInt64(kPlayerFramesDropped, numFramesDropped);
474
475
476 } else if (mime.startsWith("audio/")) {
477 mAnalyticsItem->setCString(kPlayerAMime, mime.c_str());
478 if (!name.empty()) {
479 mAnalyticsItem->setCString(kPlayerACodec, name.c_str());
480 }
481 }
482 }
483 }
484
485 // always provide duration and playing time, even if they have 0/unknown values.
486
487 // getDuration() uses mLock for mutex -- careful where we use it.
Wei Jia800fe372018-02-20 15:00:45 -0800488 int64_t duration_ms = -1;
Wei Jia53692fa2017-12-11 10:33:46 -0800489 getDuration(&duration_ms);
490 mAnalyticsItem->setInt64(kPlayerDuration, duration_ms);
491
492 mAnalyticsItem->setInt64(kPlayerPlaying, (mPlayingTimeUs+500)/1000 );
493
494 if (mRebufferingEvents != 0) {
495 mAnalyticsItem->setInt64(kPlayerRebuffering, (mRebufferingTimeUs+500)/1000 );
496 mAnalyticsItem->setInt32(kPlayerRebufferingCount, mRebufferingEvents);
497 mAnalyticsItem->setInt32(kPlayerRebufferingAtExit, mRebufferingAtExit);
498 }
499
500 mAnalyticsItem->setCString(kPlayerDataSourceType, mPlayer->getDataSourceType());
501}
502
503
504void NuPlayer2Driver::logMetrics(const char *where) {
505 if (where == NULL) {
506 where = "unknown";
507 }
508 ALOGV("logMetrics(%p) from %s at state %d", this, where, mState);
509
510 if (mAnalyticsItem == NULL || mAnalyticsItem->isEnabled() == false) {
511 return;
512 }
513
514 // log only non-empty records
515 // we always updateMetrics() before we get here
516 // and that always injects 3 fields (duration, playing time, and
517 // datasource) into the record.
518 // So the canonical "empty" record has 3 elements in it.
519 if (mAnalyticsItem->count() > 3) {
520
Wei Jia53692fa2017-12-11 10:33:46 -0800521 mAnalyticsItem->selfrecord();
522
523 // re-init in case we prepare() and start() again.
524 delete mAnalyticsItem ;
Ray Essickee54eef2018-01-24 11:16:54 -0800525 mAnalyticsItem = new MediaAnalyticsItem(kKeyPlayer);
Wei Jia53692fa2017-12-11 10:33:46 -0800526 if (mAnalyticsItem) {
Wei Jia53692fa2017-12-11 10:33:46 -0800527 mAnalyticsItem->setUid(mClientUid);
528 }
529 } else {
530 ALOGV("did not have anything to record");
531 }
532}
533
534status_t NuPlayer2Driver::reset() {
535 ALOGD("reset(%p) at state %d", this, mState);
536
537 updateMetrics("reset");
538 logMetrics("reset");
539
540 Mutex::Autolock autoLock(mLock);
541
542 switch (mState) {
543 case STATE_IDLE:
544 return OK;
545
546 case STATE_SET_DATASOURCE_PENDING:
547 case STATE_RESET_IN_PROGRESS:
548 return INVALID_OPERATION;
549
550 case STATE_PREPARING:
551 {
Wei Jiad2bb1bd2018-02-08 09:47:37 -0800552 notifyListener_l(mSrcId, MEDIA2_PREPARED);
Wei Jia53692fa2017-12-11 10:33:46 -0800553 break;
554 }
555
556 default:
557 break;
558 }
559
Wei Jia53692fa2017-12-11 10:33:46 -0800560 mState = STATE_RESET_IN_PROGRESS;
561 mPlayer->resetAsync();
562
563 while (mState == STATE_RESET_IN_PROGRESS) {
564 mCondition.wait(mLock);
565 }
566
567 mDurationUs = -1;
568 mPositionUs = -1;
569 mLooping = false;
570 mPlayingTimeUs = 0;
571 mRebufferingTimeUs = 0;
572 mRebufferingEvents = 0;
573 mRebufferingAtExit = false;
574
575 return OK;
576}
577
578status_t NuPlayer2Driver::notifyAt(int64_t mediaTimeUs) {
579 ALOGV("notifyAt(%p), time:%lld", this, (long long)mediaTimeUs);
580 return mPlayer->notifyAt(mediaTimeUs);
581}
582
583status_t NuPlayer2Driver::setLooping(int loop) {
584 mLooping = loop != 0;
585 return OK;
586}
587
Dongwon Kang9f631982018-07-10 12:34:41 -0700588status_t NuPlayer2Driver::invoke(const PlayerMessage &request, PlayerMessage *response) {
589 if (response == NULL) {
Wei Jia53692fa2017-12-11 10:33:46 -0800590 ALOGE("reply is a NULL pointer");
591 return BAD_VALUE;
592 }
593
Dongwon Kang9f631982018-07-10 12:34:41 -0700594 RepeatedPtrField<const Value>::const_iterator it = request.values().cbegin();
595 int32_t methodId = (it++)->int32_value();
Wei Jia53692fa2017-12-11 10:33:46 -0800596
597 switch (methodId) {
598 case MEDIA_PLAYER2_INVOKE_ID_SET_VIDEO_SCALING_MODE:
599 {
Dongwon Kang9f631982018-07-10 12:34:41 -0700600 int mode = (it++)->int32_value();
Wei Jia53692fa2017-12-11 10:33:46 -0800601 return mPlayer->setVideoScalingMode(mode);
602 }
603
604 case MEDIA_PLAYER2_INVOKE_ID_GET_TRACK_INFO:
605 {
Wei Jia17944af2018-12-13 18:13:10 -0800606 int64_t srcId = (it++)->int64_value();
607 return mPlayer->getTrackInfo(srcId, response);
Wei Jia53692fa2017-12-11 10:33:46 -0800608 }
609
610 case MEDIA_PLAYER2_INVOKE_ID_SELECT_TRACK:
611 {
Wei Jia17944af2018-12-13 18:13:10 -0800612 int64_t srcId = (it++)->int64_value();
Dongwon Kang9f631982018-07-10 12:34:41 -0700613 int trackIndex = (it++)->int32_value();
Wei Jia800fe372018-02-20 15:00:45 -0800614 int64_t msec = 0;
Wei Jia53692fa2017-12-11 10:33:46 -0800615 // getCurrentPosition should always return OK
616 getCurrentPosition(&msec);
Wei Jia17944af2018-12-13 18:13:10 -0800617 return mPlayer->selectTrack(srcId, trackIndex, true /* select */, msec * 1000LL);
Wei Jia53692fa2017-12-11 10:33:46 -0800618 }
619
620 case MEDIA_PLAYER2_INVOKE_ID_UNSELECT_TRACK:
621 {
Wei Jia17944af2018-12-13 18:13:10 -0800622 int64_t srcId = (it++)->int64_value();
Dongwon Kang9f631982018-07-10 12:34:41 -0700623 int trackIndex = (it++)->int32_value();
Wei Jia17944af2018-12-13 18:13:10 -0800624 return mPlayer->selectTrack(
625 srcId, trackIndex, false /* select */, 0xdeadbeef /* not used */);
Wei Jia53692fa2017-12-11 10:33:46 -0800626 }
627
628 case MEDIA_PLAYER2_INVOKE_ID_GET_SELECTED_TRACK:
629 {
Wei Jia17944af2018-12-13 18:13:10 -0800630 int64_t srcId = (it++)->int64_value();
Dongwon Kang9f631982018-07-10 12:34:41 -0700631 int32_t type = (it++)->int32_value();
Wei Jia17944af2018-12-13 18:13:10 -0800632 return mPlayer->getSelectedTrack(srcId, type, response);
Wei Jia53692fa2017-12-11 10:33:46 -0800633 }
634
635 default:
636 {
637 return INVALID_OPERATION;
638 }
639 }
640}
641
642void NuPlayer2Driver::setAudioSink(const sp<AudioSink> &audioSink) {
643 mPlayer->setAudioSink(audioSink);
644 mAudioSink = audioSink;
645}
646
647status_t NuPlayer2Driver::setParameter(
648 int /* key */, const Parcel & /* request */) {
649 return INVALID_OPERATION;
650}
651
652status_t NuPlayer2Driver::getParameter(int key, Parcel *reply) {
653
654 if (key == FOURCC('m','t','r','X')) {
655 // mtrX -- a play on 'metrics' (not matrix)
656 // gather current info all together, parcel it, and send it back
657 updateMetrics("api");
658 mAnalyticsItem->writeToParcel(reply);
659 return OK;
660 }
661
662 return INVALID_OPERATION;
663}
664
Wei Jiad2bb1bd2018-02-08 09:47:37 -0800665void NuPlayer2Driver::notifyResetComplete(int64_t /* srcId */) {
Wei Jia53692fa2017-12-11 10:33:46 -0800666 ALOGD("notifyResetComplete(%p)", this);
667 Mutex::Autolock autoLock(mLock);
668
669 CHECK_EQ(mState, STATE_RESET_IN_PROGRESS);
670 mState = STATE_IDLE;
671 mCondition.broadcast();
672}
673
Wei Jiad2bb1bd2018-02-08 09:47:37 -0800674void NuPlayer2Driver::notifySetSurfaceComplete(int64_t /* srcId */) {
Wei Jia53692fa2017-12-11 10:33:46 -0800675 ALOGV("notifySetSurfaceComplete(%p)", this);
676 Mutex::Autolock autoLock(mLock);
677
678 CHECK(mSetSurfaceInProgress);
679 mSetSurfaceInProgress = false;
680
681 mCondition.broadcast();
682}
683
Wei Jiad2bb1bd2018-02-08 09:47:37 -0800684void NuPlayer2Driver::notifyDuration(int64_t /* srcId */, int64_t durationUs) {
Wei Jia53692fa2017-12-11 10:33:46 -0800685 Mutex::Autolock autoLock(mLock);
686 mDurationUs = durationUs;
687}
688
Wei Jiad2bb1bd2018-02-08 09:47:37 -0800689void NuPlayer2Driver::notifyMorePlayingTimeUs(int64_t /* srcId */, int64_t playingUs) {
Wei Jia53692fa2017-12-11 10:33:46 -0800690 Mutex::Autolock autoLock(mLock);
691 mPlayingTimeUs += playingUs;
692}
693
Wei Jiad2bb1bd2018-02-08 09:47:37 -0800694void NuPlayer2Driver::notifyMoreRebufferingTimeUs(int64_t /* srcId */, int64_t rebufferingUs) {
Wei Jia53692fa2017-12-11 10:33:46 -0800695 Mutex::Autolock autoLock(mLock);
696 mRebufferingTimeUs += rebufferingUs;
697 mRebufferingEvents++;
698}
699
Wei Jiad2bb1bd2018-02-08 09:47:37 -0800700void NuPlayer2Driver::notifyRebufferingWhenExit(int64_t /* srcId */, bool status) {
Wei Jia53692fa2017-12-11 10:33:46 -0800701 Mutex::Autolock autoLock(mLock);
702 mRebufferingAtExit = status;
703}
704
Wei Jiad2bb1bd2018-02-08 09:47:37 -0800705void NuPlayer2Driver::notifySeekComplete(int64_t srcId) {
Wei Jia53692fa2017-12-11 10:33:46 -0800706 ALOGV("notifySeekComplete(%p)", this);
707 Mutex::Autolock autoLock(mLock);
708 mSeekInProgress = false;
Wei Jia0151ef42018-08-24 16:40:21 -0700709 notifyListener_l(srcId, MEDIA2_SEEK_COMPLETE);
Wei Jia53692fa2017-12-11 10:33:46 -0800710}
711
712status_t NuPlayer2Driver::dump(
713 int fd, const Vector<String16> & /* args */) const {
714
715 Vector<sp<AMessage> > trackStats;
716 mPlayer->getStats(&trackStats);
717
718 AString logString(" NuPlayer2\n");
719 char buf[256] = {0};
720
721 bool locked = false;
722 for (int i = 0; i < kDumpLockRetries; ++i) {
723 if (mLock.tryLock() == NO_ERROR) {
724 locked = true;
725 break;
726 }
727 usleep(kDumpLockSleepUs);
728 }
729
730 if (locked) {
731 snprintf(buf, sizeof(buf), " state(%d), atEOS(%d), looping(%d), autoLoop(%d)\n",
732 mState, mAtEOS, mLooping, mAutoLoop);
733 mLock.unlock();
734 } else {
735 snprintf(buf, sizeof(buf), " NPD(%p) lock is taken\n", this);
736 }
737 logString.append(buf);
738
739 for (size_t i = 0; i < trackStats.size(); ++i) {
740 const sp<AMessage> &stats = trackStats.itemAt(i);
741
742 AString mime;
743 if (stats->findString("mime", &mime)) {
744 snprintf(buf, sizeof(buf), " mime(%s)\n", mime.c_str());
745 logString.append(buf);
746 }
747
748 AString name;
749 if (stats->findString("component-name", &name)) {
750 snprintf(buf, sizeof(buf), " decoder(%s)\n", name.c_str());
751 logString.append(buf);
752 }
753
754 if (mime.startsWith("video/")) {
755 int32_t width, height;
756 if (stats->findInt32("width", &width)
757 && stats->findInt32("height", &height)) {
758 snprintf(buf, sizeof(buf), " resolution(%d x %d)\n", width, height);
759 logString.append(buf);
760 }
761
762 int64_t numFramesTotal = 0;
763 int64_t numFramesDropped = 0;
764
765 stats->findInt64("frames-total", &numFramesTotal);
766 stats->findInt64("frames-dropped-output", &numFramesDropped);
767 snprintf(buf, sizeof(buf), " numFramesTotal(%lld), numFramesDropped(%lld), "
768 "percentageDropped(%.2f%%)\n",
769 (long long)numFramesTotal,
770 (long long)numFramesDropped,
771 numFramesTotal == 0
772 ? 0.0 : (double)(numFramesDropped * 100) / numFramesTotal);
773 logString.append(buf);
774 }
775 }
776
777 ALOGI("%s", logString.c_str());
778
779 if (fd >= 0) {
780 FILE *out = fdopen(dup(fd), "w");
781 fprintf(out, "%s", logString.c_str());
782 fclose(out);
783 out = NULL;
784 }
785
786 return OK;
787}
788
Wei Jia12b9f4a2017-12-13 15:24:13 -0800789void NuPlayer2Driver::onMessageReceived(const sp<AMessage> &msg) {
790 switch (msg->what()) {
791 case kWhatNotifyListener: {
Wei Jiad2bb1bd2018-02-08 09:47:37 -0800792 int64_t srcId;
Wei Jia12b9f4a2017-12-13 15:24:13 -0800793 int32_t msgId;
Wei Jiac5c79da2017-12-21 18:03:05 -0800794 int32_t ext1 = 0;
795 int32_t ext2 = 0;
Wei Jiad2bb1bd2018-02-08 09:47:37 -0800796 CHECK(msg->findInt64("srcId", &srcId));
Wei Jia12b9f4a2017-12-13 15:24:13 -0800797 CHECK(msg->findInt32("messageId", &msgId));
Wei Jiac5c79da2017-12-21 18:03:05 -0800798 msg->findInt32("ext1", &ext1);
799 msg->findInt32("ext2", &ext2);
Dongwon Kang41929fb2018-09-09 08:29:56 -0700800 sp<PlayerMessageWrapper> in;
Wei Jiac5c79da2017-12-21 18:03:05 -0800801 sp<RefBase> obj;
Dongwon Kang41929fb2018-09-09 08:29:56 -0700802 if (msg->findObject("obj", &obj) && obj != NULL) {
803 in = static_cast<PlayerMessageWrapper *>(obj.get());
Wei Jiac5c79da2017-12-21 18:03:05 -0800804 }
Dongwon Kang41929fb2018-09-09 08:29:56 -0700805 sendEvent(srcId, msgId, ext1, ext2, (in == NULL ? NULL : in->getPlayerMessage()));
Wei Jia12b9f4a2017-12-13 15:24:13 -0800806 break;
807 }
808 default:
809 break;
810 }
811}
812
Wei Jia53692fa2017-12-11 10:33:46 -0800813void NuPlayer2Driver::notifyListener(
Dongwon Kang41929fb2018-09-09 08:29:56 -0700814 int64_t srcId, int msg, int ext1, int ext2, const PlayerMessage *in) {
Wei Jia53692fa2017-12-11 10:33:46 -0800815 Mutex::Autolock autoLock(mLock);
Wei Jiad2bb1bd2018-02-08 09:47:37 -0800816 notifyListener_l(srcId, msg, ext1, ext2, in);
Wei Jia53692fa2017-12-11 10:33:46 -0800817}
818
819void NuPlayer2Driver::notifyListener_l(
Dongwon Kang41929fb2018-09-09 08:29:56 -0700820 int64_t srcId, int msg, int ext1, int ext2, const PlayerMessage *in) {
Wei Jiad2bb1bd2018-02-08 09:47:37 -0800821 ALOGD("notifyListener_l(%p), (%lld, %d, %d, %d, %d), loop setting(%d, %d)",
822 this, (long long)srcId, msg, ext1, ext2,
Dongwon Kang41929fb2018-09-09 08:29:56 -0700823 (in == NULL ? -1 : (int)in->ByteSize()), mAutoLoop, mLooping);
Wei Jia57aeffd2018-02-15 16:01:14 -0800824 if (srcId == mSrcId) {
825 switch (msg) {
826 case MEDIA2_PLAYBACK_COMPLETE:
827 {
828 if (mState != STATE_RESET_IN_PROGRESS) {
829 if (mAutoLoop) {
830 audio_stream_type_t streamType = AUDIO_STREAM_MUSIC;
831 if (mAudioSink != NULL) {
832 streamType = mAudioSink->getAudioStreamType();
833 }
834 if (streamType == AUDIO_STREAM_NOTIFICATION) {
835 ALOGW("disabling auto-loop for notification");
836 mAutoLoop = false;
837 }
Wei Jia53692fa2017-12-11 10:33:46 -0800838 }
Wei Jia57aeffd2018-02-15 16:01:14 -0800839 if (mLooping || mAutoLoop) {
Wei Jiad1864f92018-10-19 12:34:56 -0700840 mPlayer->rewind();
Wei Jia57aeffd2018-02-15 16:01:14 -0800841 if (mAudioSink != NULL) {
842 // The renderer has stopped the sink at the end in order to play out
843 // the last little bit of audio. In looping mode, we need to restart it.
844 mAudioSink->start();
845 }
Wei Jia9af566a2018-08-10 16:36:35 -0700846
847 sp<AMessage> notify = new AMessage(kWhatNotifyListener, this);
848 notify->setInt64("srcId", srcId);
849 notify->setInt32("messageId", MEDIA2_INFO);
850 notify->setInt32("ext1", MEDIA2_INFO_DATA_SOURCE_REPEAT);
851 notify->post();
Wei Jia57aeffd2018-02-15 16:01:14 -0800852 return;
Wei Jia53692fa2017-12-11 10:33:46 -0800853 }
Wei Jia57aeffd2018-02-15 16:01:14 -0800854 if (property_get_bool("persist.debug.sf.stats", false)) {
855 Vector<String16> args;
856 dump(-1, args);
Wei Jia53692fa2017-12-11 10:33:46 -0800857 }
Wei Jia57aeffd2018-02-15 16:01:14 -0800858 mPlayer->pause();
859 mState = STATE_PAUSED;
Wei Jia53692fa2017-12-11 10:33:46 -0800860 }
Chih-Hung Hsiehc89566d2018-09-13 14:22:06 -0700861 FALLTHROUGH_INTENDED;
Wei Jia53692fa2017-12-11 10:33:46 -0800862 }
Wei Jia53692fa2017-12-11 10:33:46 -0800863
Wei Jia57aeffd2018-02-15 16:01:14 -0800864 case MEDIA2_ERROR:
865 {
866 // when we have an error, add it to the analytics for this playback.
867 // ext1 is our primary 'error type' value. Only add ext2 when non-zero.
868 // [test against msg is due to fall through from previous switch value]
869 if (msg == MEDIA2_ERROR) {
870 mAnalyticsItem->setInt32(kPlayerError, ext1);
871 if (ext2 != 0) {
872 mAnalyticsItem->setInt32(kPlayerErrorCode, ext2);
873 }
874 mAnalyticsItem->setCString(kPlayerErrorState, stateString(mState).c_str());
Wei Jia53692fa2017-12-11 10:33:46 -0800875 }
Wei Jia57aeffd2018-02-15 16:01:14 -0800876 mAtEOS = true;
877 break;
Wei Jia53692fa2017-12-11 10:33:46 -0800878 }
Wei Jia53692fa2017-12-11 10:33:46 -0800879
Wei Jia57aeffd2018-02-15 16:01:14 -0800880 default:
881 break;
882 }
Wei Jia53692fa2017-12-11 10:33:46 -0800883 }
884
Wei Jiac5c79da2017-12-21 18:03:05 -0800885 sp<AMessage> notify = new AMessage(kWhatNotifyListener, this);
Wei Jiad2bb1bd2018-02-08 09:47:37 -0800886 notify->setInt64("srcId", srcId);
Wei Jiac5c79da2017-12-21 18:03:05 -0800887 notify->setInt32("messageId", msg);
888 notify->setInt32("ext1", ext1);
889 notify->setInt32("ext2", ext2);
Dongwon Kang41929fb2018-09-09 08:29:56 -0700890 notify->setObject("obj", PlayerMessageWrapper::Create((PlayerMessage*)in));
Wei Jiac5c79da2017-12-21 18:03:05 -0800891 notify->post();
Wei Jia12b9f4a2017-12-13 15:24:13 -0800892}
893
Wei Jiad2bb1bd2018-02-08 09:47:37 -0800894void NuPlayer2Driver::notifySetDataSourceCompleted(int64_t /* srcId */, status_t err) {
Wei Jia53692fa2017-12-11 10:33:46 -0800895 Mutex::Autolock autoLock(mLock);
896
897 CHECK_EQ(mState, STATE_SET_DATASOURCE_PENDING);
898
899 mAsyncResult = err;
900 mState = (err == OK) ? STATE_UNPREPARED : STATE_IDLE;
901 mCondition.broadcast();
902}
903
Wei Jiad2bb1bd2018-02-08 09:47:37 -0800904void NuPlayer2Driver::notifyPrepareCompleted(int64_t srcId, status_t err) {
Wei Jia53692fa2017-12-11 10:33:46 -0800905 ALOGV("notifyPrepareCompleted %d", err);
906
907 Mutex::Autolock autoLock(mLock);
908
Wei Jia57aeffd2018-02-15 16:01:14 -0800909 if (srcId != mSrcId) {
910 if (err == OK) {
911 notifyListener_l(srcId, MEDIA2_PREPARED);
912 } else {
913 notifyListener_l(srcId, MEDIA2_ERROR, MEDIA2_ERROR_UNKNOWN, err);
914 }
915 return;
916 }
917
Wei Jia53692fa2017-12-11 10:33:46 -0800918 if (mState != STATE_PREPARING) {
919 // We were preparing asynchronously when the client called
920 // reset(), we sent a premature "prepared" notification and
921 // then initiated the reset. This notification is stale.
922 CHECK(mState == STATE_RESET_IN_PROGRESS || mState == STATE_IDLE);
923 return;
924 }
925
926 CHECK_EQ(mState, STATE_PREPARING);
927
928 mAsyncResult = err;
929
930 if (err == OK) {
931 // update state before notifying client, so that if client calls back into NuPlayer2Driver
932 // in response, NuPlayer2Driver has the right state
933 mState = STATE_PREPARED;
Wei Jia57aeffd2018-02-15 16:01:14 -0800934 notifyListener_l(srcId, MEDIA2_PREPARED);
Wei Jia53692fa2017-12-11 10:33:46 -0800935 } else {
936 mState = STATE_UNPREPARED;
Wei Jia57aeffd2018-02-15 16:01:14 -0800937 notifyListener_l(srcId, MEDIA2_ERROR, MEDIA2_ERROR_UNKNOWN, err);
Wei Jia53692fa2017-12-11 10:33:46 -0800938 }
939
940 sp<MetaData> meta = mPlayer->getFileMeta();
941 int32_t loop;
942 if (meta != NULL
943 && meta->findInt32(kKeyAutoLoop, &loop) && loop != 0) {
944 mAutoLoop = true;
945 }
946
947 mCondition.broadcast();
948}
949
Wei Jiad2bb1bd2018-02-08 09:47:37 -0800950void NuPlayer2Driver::notifyFlagsChanged(int64_t /* srcId */, uint32_t flags) {
Wei Jia53692fa2017-12-11 10:33:46 -0800951 Mutex::Autolock autoLock(mLock);
952
953 mPlayerFlags = flags;
954}
955
956// Modular DRM
Robert Shih3c3728d2018-12-04 17:06:36 -0800957status_t NuPlayer2Driver::prepareDrm(
958 int64_t srcId, const uint8_t uuid[16], const Vector<uint8_t> &drmSessionId)
Wei Jia53692fa2017-12-11 10:33:46 -0800959{
960 ALOGV("prepareDrm(%p) state: %d", this, mState);
961
962 // leaving the state verification for mediaplayer.cpp
Robert Shih3c3728d2018-12-04 17:06:36 -0800963 status_t ret = mPlayer->prepareDrm(srcId, uuid, drmSessionId);
Wei Jia53692fa2017-12-11 10:33:46 -0800964
965 ALOGV("prepareDrm ret: %d", ret);
966
967 return ret;
968}
969
Robert Shih3c3728d2018-12-04 17:06:36 -0800970status_t NuPlayer2Driver::releaseDrm(int64_t srcId)
Wei Jia53692fa2017-12-11 10:33:46 -0800971{
972 ALOGV("releaseDrm(%p) state: %d", this, mState);
973
974 // leaving the state verification for mediaplayer.cpp
Robert Shih3c3728d2018-12-04 17:06:36 -0800975 status_t ret = mPlayer->releaseDrm(srcId);
Wei Jia53692fa2017-12-11 10:33:46 -0800976
977 ALOGV("releaseDrm ret: %d", ret);
978
979 return ret;
980}
981
Ray Essick51f4c872017-12-15 12:27:56 -0800982std::string NuPlayer2Driver::stateString(State state) {
983 const char *rval = NULL;
984 char rawbuffer[16]; // allows "%d"
985
986 switch (state) {
987 case STATE_IDLE: rval = "IDLE"; break;
988 case STATE_SET_DATASOURCE_PENDING: rval = "SET_DATASOURCE_PENDING"; break;
989 case STATE_UNPREPARED: rval = "UNPREPARED"; break;
990 case STATE_PREPARING: rval = "PREPARING"; break;
991 case STATE_PREPARED: rval = "PREPARED"; break;
992 case STATE_RUNNING: rval = "RUNNING"; break;
993 case STATE_PAUSED: rval = "PAUSED"; break;
994 case STATE_RESET_IN_PROGRESS: rval = "RESET_IN_PROGRESS"; break;
Ray Essick51f4c872017-12-15 12:27:56 -0800995 default:
996 // yes, this buffer is shared and vulnerable to races
997 snprintf(rawbuffer, sizeof(rawbuffer), "%d", state);
998 rval = rawbuffer;
999 break;
1000 }
1001
1002 return rval;
1003}
1004
Wei Jia53692fa2017-12-11 10:33:46 -08001005} // namespace android