blob: 1d0246d03102ad041c2b3143fd4c68d067b40a1b [file] [log] [blame]
Ray Essick3938dc62016-11-01 08:56:56 -07001/*
2 * Copyright (C) 2016 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// Proxy for media player implementations
18
19//#define LOG_NDEBUG 0
20#define LOG_TAG "MediaAnalyticsService"
21#include <utils/Log.h>
22
23#include <inttypes.h>
24#include <sys/types.h>
25#include <sys/stat.h>
26#include <sys/time.h>
27#include <dirent.h>
28#include <unistd.h>
29
30#include <string.h>
31
32#include <cutils/atomic.h>
33#include <cutils/properties.h> // for property_get
34
35#include <utils/misc.h>
36
37#include <binder/IPCThreadState.h>
38#include <binder/IServiceManager.h>
39#include <binder/MemoryHeapBase.h>
40#include <binder/MemoryBase.h>
41#include <gui/Surface.h>
42#include <utils/Errors.h> // for status_t
43#include <utils/List.h>
44#include <utils/String8.h>
45#include <utils/SystemClock.h>
46#include <utils/Timers.h>
47#include <utils/Vector.h>
48
49#include <media/AudioPolicyHelper.h>
50#include <media/IMediaHTTPService.h>
51#include <media/IRemoteDisplay.h>
52#include <media/IRemoteDisplayClient.h>
53#include <media/MediaPlayerInterface.h>
54#include <media/mediarecorder.h>
55#include <media/MediaMetadataRetrieverInterface.h>
56#include <media/Metadata.h>
57#include <media/AudioTrack.h>
58#include <media/MemoryLeakTrackUtil.h>
59#include <media/stagefright/MediaCodecList.h>
60#include <media/stagefright/MediaErrors.h>
61#include <media/stagefright/Utils.h>
62#include <media/stagefright/foundation/ADebug.h>
63#include <media/stagefright/foundation/ALooperRoster.h>
64#include <mediautils/BatteryNotifier.h>
65
66//#include <memunreachable/memunreachable.h>
67#include <system/audio.h>
68
69#include <private/android_filesystem_config.h>
70
71#include "MediaAnalyticsService.h"
72
73
74namespace android {
75
76
Ray Essickb5fac8e2016-12-12 11:33:56 -080077#define DEBUG_QUEUE 0
Ray Essick3938dc62016-11-01 08:56:56 -070078
79//using android::status_t;
80//using android::OK;
81//using android::BAD_VALUE;
82//using android::NOT_ENOUGH_DATA;
83//using android::Parcel;
84
85
86void MediaAnalyticsService::instantiate() {
87 defaultServiceManager()->addService(
88 String16("media.analytics"), new MediaAnalyticsService());
89}
90
91// XXX: add dynamic controls for mMaxRecords
92MediaAnalyticsService::MediaAnalyticsService()
93 : mMaxRecords(100) {
94
95 ALOGD("MediaAnalyticsService created");
96 // clear our queues
Ray Essickb5fac8e2016-12-12 11:33:56 -080097 mOpen = new List<MediaAnalyticsItem *>();
98 mFinalized = new List<MediaAnalyticsItem *>();
Ray Essick3938dc62016-11-01 08:56:56 -070099
100 mItemsSubmitted = 0;
101 mItemsFinalized = 0;
102 mItemsDiscarded = 0;
103
104 mLastSessionID = 0;
105 // recover any persistency we set up
106 // etc
107}
108
109MediaAnalyticsService::~MediaAnalyticsService() {
110 ALOGD("MediaAnalyticsService destroyed");
111
112 // XXX: clean out mOpen and mFinalized
113}
114
115
116MediaAnalyticsItem::SessionID_t MediaAnalyticsService::generateUniqueSessionID() {
117 // generate a new sessionid
118
119 Mutex::Autolock _l(mLock_ids);
120 return (++mLastSessionID);
121}
122
Ray Essickb5fac8e2016-12-12 11:33:56 -0800123// caller surrenders ownership of 'item'
124MediaAnalyticsItem::SessionID_t MediaAnalyticsService::submit(MediaAnalyticsItem *item, bool forcenew) {
Ray Essick3938dc62016-11-01 08:56:56 -0700125
126 MediaAnalyticsItem::SessionID_t id = MediaAnalyticsItem::SessionIDInvalid;
127
128 // we control these, not using whatever the user might have sent
129 nsecs_t now = systemTime(SYSTEM_TIME_REALTIME);
130 item->setTimestamp(now);
131 int pid = IPCThreadState::self()->getCallingPid();
132 item->setPid(pid);
133 int uid = IPCThreadState::self()->getCallingUid();
134 item->setUid(uid);
135
136 mItemsSubmitted++;
137
138 // validate the record; we discard if we don't like it
139 if (contentValid(item) == false) {
Ray Essickb5fac8e2016-12-12 11:33:56 -0800140 delete item;
Ray Essick3938dc62016-11-01 08:56:56 -0700141 return MediaAnalyticsItem::SessionIDInvalid;
142 }
143
144
145 // if we have a sesisonid in the new record, look to make
146 // sure it doesn't appear in the finalized list.
147 // XXX: this is for security / DOS prevention.
148 // may also require that we persist the unique sessionIDs
149 // across boots [instead of within a single boot]
150
151
152 // match this new record up against records in the open
153 // list...
154 // if there's a match, merge them together
155 // deal with moving the old / merged record into the finalized que
156
157 bool finalizing = item->getFinalized();
158
159 // if finalizing, we'll remove it
Ray Essickb5fac8e2016-12-12 11:33:56 -0800160 MediaAnalyticsItem *oitem = findItem(mOpen, item, finalizing | forcenew);
Ray Essick3938dc62016-11-01 08:56:56 -0700161 if (oitem != NULL) {
162 if (forcenew) {
163 // old one gets finalized, then we insert the new one
164 // so we'll have 2 records at the end of this.
165 // but don't finalize an empty record
Ray Essickb5fac8e2016-12-12 11:33:56 -0800166 if (oitem->count() == 0) {
167 // we're responsible for disposing of the dead record
168 delete oitem;
169 oitem = NULL;
170 } else {
Ray Essick3938dc62016-11-01 08:56:56 -0700171 oitem->setFinalized(true);
172 saveItem(mFinalized, oitem, 0);
173 }
174 // new record could itself be marked finalized...
175 if (finalizing) {
176 saveItem(mFinalized, item, 0);
177 mItemsFinalized++;
178 } else {
179 saveItem(mOpen, item, 1);
180 }
181 id = item->getSessionID();
182 } else {
183 // combine the records, send it to finalized if appropriate
184 oitem->merge(item);
185 if (finalizing) {
186 saveItem(mFinalized, oitem, 0);
187 mItemsFinalized++;
188 }
189 id = oitem->getSessionID();
Ray Essickb5fac8e2016-12-12 11:33:56 -0800190
191 // we're responsible for disposing of the dead record
192 delete item;
193 item = NULL;
Ray Essick3938dc62016-11-01 08:56:56 -0700194 }
195 } else {
Ray Essickb5fac8e2016-12-12 11:33:56 -0800196 // nothing to merge, save the new record
197 id = item->getSessionID();
198 if (finalizing) {
199 if (item->count() == 0) {
200 // drop empty records
201 delete item;
202 item = NULL;
Ray Essick3938dc62016-11-01 08:56:56 -0700203 } else {
Ray Essickb5fac8e2016-12-12 11:33:56 -0800204 saveItem(mFinalized, item, 0);
205 mItemsFinalized++;
Ray Essick3938dc62016-11-01 08:56:56 -0700206 }
Ray Essickb5fac8e2016-12-12 11:33:56 -0800207 } else {
208 saveItem(mOpen, item, 1);
209 }
Ray Essick3938dc62016-11-01 08:56:56 -0700210 }
Ray Essick3938dc62016-11-01 08:56:56 -0700211 return id;
212}
213
Ray Essickb5fac8e2016-12-12 11:33:56 -0800214List<MediaAnalyticsItem *> *MediaAnalyticsService::getMediaAnalyticsItemList(bool finished, nsecs_t ts) {
Ray Essick3938dc62016-11-01 08:56:56 -0700215 // this might never get called; the binder interface maps to the full parm list
216 // on the client side before making the binder call.
217 // but this lets us be sure...
Ray Essickb5fac8e2016-12-12 11:33:56 -0800218 List<MediaAnalyticsItem*> *list;
Ray Essick3938dc62016-11-01 08:56:56 -0700219 list = getMediaAnalyticsItemList(finished, ts, MediaAnalyticsItem::kKeyAny);
220 return list;
221}
222
Ray Essickb5fac8e2016-12-12 11:33:56 -0800223List<MediaAnalyticsItem *> *MediaAnalyticsService::getMediaAnalyticsItemList(bool , nsecs_t , MediaAnalyticsItem::Key ) {
Ray Essick3938dc62016-11-01 08:56:56 -0700224
225 // XXX: implement the get-item-list semantics
226
Ray Essickb5fac8e2016-12-12 11:33:56 -0800227 List<MediaAnalyticsItem *> *list = NULL;
Ray Essick3938dc62016-11-01 08:56:56 -0700228 // set up our query on the persistent data
229 // slurp in all of the pieces
230 // return that
231 return list;
232}
233
Ray Essickb5fac8e2016-12-12 11:33:56 -0800234status_t MediaAnalyticsService::dump(int fd, const Vector<String16>& args)
Ray Essick3938dc62016-11-01 08:56:56 -0700235{
Ray Essickb5fac8e2016-12-12 11:33:56 -0800236 const size_t SIZE = 512;
Ray Essick3938dc62016-11-01 08:56:56 -0700237 char buffer[SIZE];
238 String8 result;
239
240 if (checkCallingPermission(String16("android.permission.DUMP")) == false) {
241 snprintf(buffer, SIZE, "Permission Denial: "
242 "can't dump MediaAnalyticsService from pid=%d, uid=%d\n",
243 IPCThreadState::self()->getCallingPid(),
244 IPCThreadState::self()->getCallingUid());
245 result.append(buffer);
Ray Essickb5fac8e2016-12-12 11:33:56 -0800246 write(fd, result.string(), result.size());
247 return NO_ERROR;
Ray Essick3938dc62016-11-01 08:56:56 -0700248 }
Ray Essickb5fac8e2016-12-12 11:33:56 -0800249
250 // crack any parameters
251 bool clear = false;
252 nsecs_t ts_since = 0;
253 String16 clearOption("-clear");
254 String16 sinceOption("-since");
255 int n = args.size();
256 for (int i = 0; i < n; i++) {
257 String8 myarg(args[i]);
258 if (args[i] == clearOption) {
259 clear = true;
260 } else if (args[i] == sinceOption) {
261 i++;
262 if (i < n) {
263 String8 value(args[i]);
264 char *endp;
265 const char *p = value.string();
266 ts_since = strtoll(p, &endp, 10);
267 if (endp == p || *endp != '\0') {
268 ts_since = 0;
269 }
270 } else {
271 ts_since = 0;
272 }
273 }
274 }
275
276 Mutex::Autolock _l(mLock);
277
278 snprintf(buffer, SIZE, "Dump of the mediaanalytics process:\n");
279 result.append(buffer);
280
281 int enabled = MediaAnalyticsItem::isEnabled();
282 if (enabled) {
283 snprintf(buffer, SIZE, "Analytics gathering: enabled\n");
284 } else {
285 snprintf(buffer, SIZE, "Analytics gathering: DISABLED via property\n");
286 }
287 result.append(buffer);
288
289 snprintf(buffer, SIZE,
290 "Since Boot: Submissions: %" PRId64
291 " Finalizations: %" PRId64
292 " Discarded: %" PRId64 "\n",
293 mItemsSubmitted, mItemsFinalized, mItemsDiscarded);
294 result.append(buffer);
295 if (ts_since != 0) {
296 snprintf(buffer, SIZE,
297 "Dumping Queue entries more recent than: %" PRId64 "\n",
298 (int64_t) ts_since);
299 result.append(buffer);
300 }
301
302 // show the recently recorded records
303 snprintf(buffer, sizeof(buffer), "\nFinalized Analytics (oldest first):\n");
304 result.append(buffer);
305 result.append(this->dumpQueue(mFinalized, ts_since));
306
307 snprintf(buffer, sizeof(buffer), "\nIn-Progress Analytics (newest first):\n");
308 result.append(buffer);
309 result.append(this->dumpQueue(mOpen, ts_since));
310
311 // show who is connected and injecting records?
312 // talk about # records fed to the 'readers'
313 // talk about # records we discarded, perhaps "discarded w/o reading" too
314
315 if (clear) {
316 // remove everything from the finalized queue
317 while (mFinalized->size() > 0) {
318 MediaAnalyticsItem * oitem = *(mFinalized->begin());
319 if (DEBUG_QUEUE) {
320 ALOGD("zap old record: key %s sessionID %" PRId64 " ts %" PRId64 "",
321 oitem->getKey().c_str(), oitem->getSessionID(),
322 oitem->getTimestamp());
323 }
324 mFinalized->erase(mFinalized->begin());
325 mItemsDiscarded++;
326 }
327 }
328
Ray Essick3938dc62016-11-01 08:56:56 -0700329 write(fd, result.string(), result.size());
330 return NO_ERROR;
331}
332
333// caller has locked mLock...
Ray Essickb5fac8e2016-12-12 11:33:56 -0800334String8 MediaAnalyticsService::dumpQueue(List<MediaAnalyticsItem *> *theList) {
335 return dumpQueue(theList, (nsecs_t) 0);
336}
337
338String8 MediaAnalyticsService::dumpQueue(List<MediaAnalyticsItem *> *theList, nsecs_t ts_since) {
339 const size_t SIZE = 512;
Ray Essick3938dc62016-11-01 08:56:56 -0700340 char buffer[SIZE];
341 String8 result;
342 int slot = 0;
343
344 if (theList->empty()) {
345 result.append("empty\n");
346 } else {
Ray Essickb5fac8e2016-12-12 11:33:56 -0800347 List<MediaAnalyticsItem *>::iterator it = theList->begin();
348 for (; it != theList->end(); it++) {
349 nsecs_t when = (*it)->getTimestamp();
350 if (when < ts_since) {
351 continue;
352 }
Ray Essick3938dc62016-11-01 08:56:56 -0700353 AString entry = (*it)->toString();
Ray Essickb5fac8e2016-12-12 11:33:56 -0800354 snprintf(buffer, sizeof(buffer), "%4d: %s",
Ray Essick3938dc62016-11-01 08:56:56 -0700355 slot, entry.c_str());
356 result.append(buffer);
Ray Essickb5fac8e2016-12-12 11:33:56 -0800357 buffer[0] = '\n';
358 buffer[1] = '\0';
359 result.append(buffer);
360 slot++;
Ray Essick3938dc62016-11-01 08:56:56 -0700361 }
362 }
363
364 return result;
365}
366
367//
368// Our Cheap in-core, non-persistent records management.
369// XXX: rewrite this to manage persistence, etc.
370
371// insert appropriately into queue
Ray Essickb5fac8e2016-12-12 11:33:56 -0800372void MediaAnalyticsService::saveItem(List<MediaAnalyticsItem *> *l, MediaAnalyticsItem * item, int front) {
Ray Essick3938dc62016-11-01 08:56:56 -0700373
374 Mutex::Autolock _l(mLock);
375
Ray Essickb5fac8e2016-12-12 11:33:56 -0800376 if (DEBUG_QUEUE) {
Ray Essick3938dc62016-11-01 08:56:56 -0700377 ALOGD("Inject a record: session %" PRId64 " ts %" PRId64 "",
378 item->getSessionID(), item->getTimestamp());
Ray Essick3938dc62016-11-01 08:56:56 -0700379 String8 before = dumpQueue(l);
380 ALOGD("Q before insert: %s", before.string());
381 }
382
383 // adding at back of queue (fifo order)
384 if (front) {
385 l->push_front(item);
386 } else {
387 l->push_back(item);
388 }
389
Ray Essickb5fac8e2016-12-12 11:33:56 -0800390 if (DEBUG_QUEUE) {
Ray Essick3938dc62016-11-01 08:56:56 -0700391 String8 after = dumpQueue(l);
392 ALOGD("Q after insert: %s", after.string());
393 }
394
395 // keep removing old records the front until we're in-bounds
396 if (mMaxRecords > 0) {
397 while (l->size() > (size_t) mMaxRecords) {
Ray Essickb5fac8e2016-12-12 11:33:56 -0800398 MediaAnalyticsItem * oitem = *(l->begin());
399 if (DEBUG_QUEUE) {
Ray Essick3938dc62016-11-01 08:56:56 -0700400 ALOGD("zap old record: key %s sessionID %" PRId64 " ts %" PRId64 "",
401 oitem->getKey().c_str(), oitem->getSessionID(),
402 oitem->getTimestamp());
403 }
404 l->erase(l->begin());
Ray Essickb5fac8e2016-12-12 11:33:56 -0800405 ALOGD("drop record at %s:%d", __FILE__, __LINE__);
406 delete oitem;
407 ALOGD("[done] drop record at %s:%d", __FILE__, __LINE__);
408 mItemsDiscarded++;
Ray Essick3938dc62016-11-01 08:56:56 -0700409 }
410 }
411
Ray Essickb5fac8e2016-12-12 11:33:56 -0800412 if (DEBUG_QUEUE) {
Ray Essick3938dc62016-11-01 08:56:56 -0700413 String8 after = dumpQueue(l);
414 ALOGD("Q after cleanup: %s", after.string());
415 }
416}
417
418// are they alike enough that nitem can be folded into oitem?
Ray Essickb5fac8e2016-12-12 11:33:56 -0800419static bool compatibleItems(MediaAnalyticsItem * oitem, MediaAnalyticsItem * nitem) {
Ray Essick3938dc62016-11-01 08:56:56 -0700420
421 if (0) {
422 ALOGD("Compare: o %s n %s",
423 oitem->toString().c_str(), nitem->toString().c_str());
424 }
425
426 // general safety
427 if (nitem->getUid() != oitem->getUid()) {
428 return false;
429 }
430 if (nitem->getPid() != oitem->getPid()) {
431 return false;
432 }
433
434 // key -- needs to match
435 if (nitem->getKey() == oitem->getKey()) {
436 // still in the game.
437 } else {
438 return false;
439 }
440
441 // session id -- empty field in new is allowed
442 MediaAnalyticsItem::SessionID_t osession = oitem->getSessionID();
443 MediaAnalyticsItem::SessionID_t nsession = nitem->getSessionID();
444 if (nsession != osession) {
445 // incoming '0' matches value in osession
446 if (nsession != 0) {
447 return false;
448 }
449 }
450
451 return true;
452}
453
454// find the incomplete record that this will overlay
Ray Essickb5fac8e2016-12-12 11:33:56 -0800455MediaAnalyticsItem *MediaAnalyticsService::findItem(List<MediaAnalyticsItem*> *theList, MediaAnalyticsItem *nitem, bool removeit) {
Ray Essick3938dc62016-11-01 08:56:56 -0700456 if (nitem == NULL) {
457 return NULL;
458 }
459
Ray Essickb5fac8e2016-12-12 11:33:56 -0800460 MediaAnalyticsItem *item = NULL;
461
Ray Essick3938dc62016-11-01 08:56:56 -0700462 Mutex::Autolock _l(mLock);
463
Ray Essickb5fac8e2016-12-12 11:33:56 -0800464 for (List<MediaAnalyticsItem *>::iterator it = theList->begin();
Ray Essick3938dc62016-11-01 08:56:56 -0700465 it != theList->end(); it++) {
Ray Essickb5fac8e2016-12-12 11:33:56 -0800466 MediaAnalyticsItem *tmp = (*it);
Ray Essick3938dc62016-11-01 08:56:56 -0700467
468 if (!compatibleItems(tmp, nitem)) {
469 continue;
470 }
471
472 // we match! this is the one I want.
473 if (removeit) {
474 theList->erase(it);
475 }
476 item = tmp;
477 break;
478 }
479 return item;
480}
481
482
483// delete the indicated record
Ray Essickb5fac8e2016-12-12 11:33:56 -0800484void MediaAnalyticsService::deleteItem(List<MediaAnalyticsItem *> *l, MediaAnalyticsItem *item) {
Ray Essick3938dc62016-11-01 08:56:56 -0700485
486 Mutex::Autolock _l(mLock);
487
Ray Essickb5fac8e2016-12-12 11:33:56 -0800488 if(DEBUG_QUEUE) {
Ray Essick3938dc62016-11-01 08:56:56 -0700489 String8 before = dumpQueue(l);
490 ALOGD("Q before delete: %s", before.string());
491 }
492
Ray Essickb5fac8e2016-12-12 11:33:56 -0800493 for (List<MediaAnalyticsItem *>::iterator it = l->begin();
Ray Essick3938dc62016-11-01 08:56:56 -0700494 it != l->end(); it++) {
495 if ((*it)->getSessionID() != item->getSessionID())
496 continue;
497
Ray Essickb5fac8e2016-12-12 11:33:56 -0800498 if (DEBUG_QUEUE) {
499 ALOGD(" --- removing record for SessionID %" PRId64 "", item->getSessionID());
500 ALOGD("drop record at %s:%d", __FILE__, __LINE__);
501 }
502 delete *it;
Ray Essick3938dc62016-11-01 08:56:56 -0700503 l->erase(it);
504 break;
505 }
506
Ray Essickb5fac8e2016-12-12 11:33:56 -0800507 if (DEBUG_QUEUE) {
Ray Essick3938dc62016-11-01 08:56:56 -0700508 String8 after = dumpQueue(l);
509 ALOGD("Q after delete: %s", after.string());
510 }
511}
512
513// are the contents good
Ray Essickb5fac8e2016-12-12 11:33:56 -0800514bool MediaAnalyticsService::contentValid(MediaAnalyticsItem *) {
Ray Essick3938dc62016-11-01 08:56:56 -0700515
516 // certain keys require certain uids
517 // internal consistency
518
519 return true;
520}
521
522// are we rate limited, normally false
Ray Essickb5fac8e2016-12-12 11:33:56 -0800523bool MediaAnalyticsService::rateLimited(MediaAnalyticsItem *) {
Ray Essick3938dc62016-11-01 08:56:56 -0700524
525 return false;
526}
527
528
529} // namespace android