blob: d3de01ea75c0bf5d9c0d7a155a9ea08b2efe1473 [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#undef LOG_TAG
18#define LOG_TAG "MediaAnalyticsItem"
19
Ray Essick3938dc62016-11-01 08:56:56 -070020#include <inttypes.h>
Ray Essickb5fac8e2016-12-12 11:33:56 -080021#include <stdlib.h>
22#include <string.h>
23#include <sys/types.h>
Ray Essick3938dc62016-11-01 08:56:56 -070024
25#include <binder/Parcel.h>
26#include <utils/Errors.h>
27#include <utils/Log.h>
28#include <utils/Mutex.h>
Ray Essick3938dc62016-11-01 08:56:56 -070029#include <utils/SortedVector.h>
30#include <utils/threads.h>
31
Ray Essick3938dc62016-11-01 08:56:56 -070032#include <binder/IServiceManager.h>
33#include <media/IMediaAnalyticsService.h>
34#include <media/MediaAnalyticsItem.h>
Ray Essick79a89ef2017-04-24 15:52:54 -070035#include <private/android_filesystem_config.h>
Ray Essick3938dc62016-11-01 08:56:56 -070036
37namespace android {
38
39#define DEBUG_SERVICEACCESS 0
Ray Essickb5fac8e2016-12-12 11:33:56 -080040#define DEBUG_API 0
41#define DEBUG_ALLOCATIONS 0
42
43// after this many failed attempts, we stop trying [from this process] and just say that
44// the service is off.
45#define SVC_TRIES 2
Ray Essick3938dc62016-11-01 08:56:56 -070046
47// the few universal keys we have
48const MediaAnalyticsItem::Key MediaAnalyticsItem::kKeyAny = "any";
49const MediaAnalyticsItem::Key MediaAnalyticsItem::kKeyNone = "none";
50
Ray Essickd38e1742017-01-23 15:17:06 -080051const char * const MediaAnalyticsItem::EnabledProperty = "media.metrics.enabled";
52const char * const MediaAnalyticsItem::EnabledPropertyPersist = "persist.media.metrics.enabled";
Ray Essickd6a6c672017-01-25 14:28:51 -080053const int MediaAnalyticsItem::EnabledProperty_default = 1;
Ray Essick3938dc62016-11-01 08:56:56 -070054
55
56// access functions for the class
57MediaAnalyticsItem::MediaAnalyticsItem()
Ray Essickd38e1742017-01-23 15:17:06 -080058 : mPid(-1),
59 mUid(-1),
Ray Essickfa149562017-09-19 09:27:31 -070060 mPkgVersionCode(0),
Ray Essick3938dc62016-11-01 08:56:56 -070061 mSessionID(MediaAnalyticsItem::SessionIDNone),
62 mTimestamp(0),
Ray Essick92d23b42018-01-29 12:10:30 -080063 mFinalized(1),
Ray Essickb5fac8e2016-12-12 11:33:56 -080064 mPropCount(0), mPropSize(0), mProps(NULL)
65{
Ray Essick3938dc62016-11-01 08:56:56 -070066 mKey = MediaAnalyticsItem::kKeyNone;
67}
68
69MediaAnalyticsItem::MediaAnalyticsItem(MediaAnalyticsItem::Key key)
Ray Essickd38e1742017-01-23 15:17:06 -080070 : mPid(-1),
71 mUid(-1),
Ray Essickfa149562017-09-19 09:27:31 -070072 mPkgVersionCode(0),
Ray Essick3938dc62016-11-01 08:56:56 -070073 mSessionID(MediaAnalyticsItem::SessionIDNone),
74 mTimestamp(0),
Ray Essick92d23b42018-01-29 12:10:30 -080075 mFinalized(1),
Ray Essickb5fac8e2016-12-12 11:33:56 -080076 mPropCount(0), mPropSize(0), mProps(NULL)
77{
78 if (DEBUG_ALLOCATIONS) {
79 ALOGD("Allocate MediaAnalyticsItem @ %p", this);
80 }
Ray Essick3938dc62016-11-01 08:56:56 -070081 mKey = key;
82}
83
84MediaAnalyticsItem::~MediaAnalyticsItem() {
Ray Essickb5fac8e2016-12-12 11:33:56 -080085 if (DEBUG_ALLOCATIONS) {
86 ALOGD("Destroy MediaAnalyticsItem @ %p", this);
87 }
Ray Essick3938dc62016-11-01 08:56:56 -070088 clear();
89}
90
Ray Essickb5fac8e2016-12-12 11:33:56 -080091void MediaAnalyticsItem::clear() {
92
93 // clean allocated storage from key
94 mKey.clear();
95
Ray Essickd38e1742017-01-23 15:17:06 -080096 // clean various major parameters
97 mSessionID = MediaAnalyticsItem::SessionIDNone;
98
Ray Essickb5fac8e2016-12-12 11:33:56 -080099 // clean attributes
100 // contents of the attributes
Ray Essick58f58732017-10-02 10:56:18 -0700101 for (size_t i = 0 ; i < mPropCount; i++ ) {
Ray Essickb5fac8e2016-12-12 11:33:56 -0800102 clearProp(&mProps[i]);
103 }
104 // the attribute records themselves
105 if (mProps != NULL) {
106 free(mProps);
107 mProps = NULL;
108 }
109 mPropSize = 0;
110 mPropCount = 0;
111
112 return;
113}
114
115// make a deep copy of myself
116MediaAnalyticsItem *MediaAnalyticsItem::dup() {
117 MediaAnalyticsItem *dst = new MediaAnalyticsItem(this->mKey);
118
119 if (dst != NULL) {
120 // key as part of constructor
121 dst->mPid = this->mPid;
122 dst->mUid = this->mUid;
Ray Essickf65f4212017-08-31 11:41:19 -0700123 dst->mPkgName = this->mPkgName;
124 dst->mPkgVersionCode = this->mPkgVersionCode;
Ray Essickb5fac8e2016-12-12 11:33:56 -0800125 dst->mSessionID = this->mSessionID;
126 dst->mTimestamp = this->mTimestamp;
127 dst->mFinalized = this->mFinalized;
128
129 // properties aka attributes
130 dst->growProps(this->mPropCount);
131 for(size_t i=0;i<mPropCount;i++) {
132 copyProp(&dst->mProps[i], &this->mProps[i]);
133 }
134 dst->mPropCount = this->mPropCount;
135 }
136
137 return dst;
138}
139
Ray Essick3938dc62016-11-01 08:56:56 -0700140MediaAnalyticsItem &MediaAnalyticsItem::setSessionID(MediaAnalyticsItem::SessionID_t id) {
141 mSessionID = id;
142 return *this;
143}
144
145MediaAnalyticsItem::SessionID_t MediaAnalyticsItem::getSessionID() const {
146 return mSessionID;
147}
148
149MediaAnalyticsItem::SessionID_t MediaAnalyticsItem::generateSessionID() {
Ray Essick3938dc62016-11-01 08:56:56 -0700150
151 if (mSessionID == SessionIDNone) {
152 // get one from the server
Ray Essickb5fac8e2016-12-12 11:33:56 -0800153 MediaAnalyticsItem::SessionID_t newid = SessionIDNone;
Ray Essick3938dc62016-11-01 08:56:56 -0700154 sp<IMediaAnalyticsService> svc = getInstance();
155 if (svc != NULL) {
156 newid = svc->generateUniqueSessionID();
157 }
158 mSessionID = newid;
159 }
160
161 return mSessionID;
162}
163
164MediaAnalyticsItem &MediaAnalyticsItem::clearSessionID() {
165 mSessionID = MediaAnalyticsItem::SessionIDNone;
166 return *this;
167}
168
169MediaAnalyticsItem &MediaAnalyticsItem::setTimestamp(nsecs_t ts) {
170 mTimestamp = ts;
171 return *this;
172}
173
174nsecs_t MediaAnalyticsItem::getTimestamp() const {
175 return mTimestamp;
176}
177
178MediaAnalyticsItem &MediaAnalyticsItem::setPid(pid_t pid) {
179 mPid = pid;
180 return *this;
181}
182
183pid_t MediaAnalyticsItem::getPid() const {
184 return mPid;
185}
186
187MediaAnalyticsItem &MediaAnalyticsItem::setUid(uid_t uid) {
188 mUid = uid;
189 return *this;
190}
191
192uid_t MediaAnalyticsItem::getUid() const {
193 return mUid;
194}
195
Ray Essick783bd0d2018-01-11 11:10:35 -0800196MediaAnalyticsItem &MediaAnalyticsItem::setPkgName(const std::string &pkgName) {
Ray Essickf65f4212017-08-31 11:41:19 -0700197 mPkgName = pkgName;
198 return *this;
199}
200
Dianne Hackborn4e2eeff2017-11-27 14:01:29 -0800201MediaAnalyticsItem &MediaAnalyticsItem::setPkgVersionCode(int64_t pkgVersionCode) {
Ray Essickf65f4212017-08-31 11:41:19 -0700202 mPkgVersionCode = pkgVersionCode;
203 return *this;
204}
205
Dianne Hackborn4e2eeff2017-11-27 14:01:29 -0800206int64_t MediaAnalyticsItem::getPkgVersionCode() const {
Ray Essickf65f4212017-08-31 11:41:19 -0700207 return mPkgVersionCode;
208}
209
Ray Essickb5fac8e2016-12-12 11:33:56 -0800210// this key is for the overall record -- "codec", "player", "drm", etc
Ray Essick3938dc62016-11-01 08:56:56 -0700211MediaAnalyticsItem &MediaAnalyticsItem::setKey(MediaAnalyticsItem::Key key) {
Ray Essick3938dc62016-11-01 08:56:56 -0700212 mKey = key;
213 return *this;
214}
215
216MediaAnalyticsItem::Key MediaAnalyticsItem::getKey() {
217 return mKey;
218}
219
Ray Essickb5fac8e2016-12-12 11:33:56 -0800220// number of attributes we have in this record
Ray Essick3938dc62016-11-01 08:56:56 -0700221int32_t MediaAnalyticsItem::count() const {
Ray Essickb5fac8e2016-12-12 11:33:56 -0800222 return mPropCount;
223}
224
225// find the proper entry in the list
226size_t MediaAnalyticsItem::findPropIndex(const char *name, size_t len)
227{
228 size_t i = 0;
229 for (; i < mPropCount; i++) {
230 Prop *prop = &mProps[i];
231 if (prop->mNameLen != len) {
232 continue;
233 }
234 if (memcmp(name, prop->mName, len) == 0) {
235 break;
236 }
237 }
238 return i;
239}
240
241MediaAnalyticsItem::Prop *MediaAnalyticsItem::findProp(const char *name) {
242 size_t len = strlen(name);
243 size_t i = findPropIndex(name, len);
244 if (i < mPropCount) {
245 return &mProps[i];
246 }
247 return NULL;
248}
249
250void MediaAnalyticsItem::Prop::setName(const char *name, size_t len) {
Ray Essick9ce18f72018-05-07 15:54:30 -0700251 free((void *)mName);
Ray Essickb5fac8e2016-12-12 11:33:56 -0800252 mName = (const char *) malloc(len+1);
Ray Essick9ce18f72018-05-07 15:54:30 -0700253 LOG_ALWAYS_FATAL_IF(mName == NULL,
254 "failed malloc() for property '%s' (len %zu)",
255 name, len);
Ray Essickb5fac8e2016-12-12 11:33:56 -0800256 memcpy ((void *)mName, name, len+1);
Ray Essick9ce18f72018-05-07 15:54:30 -0700257 mNameLen = len;
Ray Essickb5fac8e2016-12-12 11:33:56 -0800258}
259
Ray Essick9ce18f72018-05-07 15:54:30 -0700260// consider this "find-or-allocate".
261// caller validates type and uses clearPropValue() accordingly
Ray Essickb5fac8e2016-12-12 11:33:56 -0800262MediaAnalyticsItem::Prop *MediaAnalyticsItem::allocateProp(const char *name) {
263 size_t len = strlen(name);
264 size_t i = findPropIndex(name, len);
265 Prop *prop;
266
267 if (i < mPropCount) {
268 prop = &mProps[i];
269 } else {
270 if (i == mPropSize) {
Ray Essick9bb7e3b2017-10-23 13:01:48 -0700271 if (growProps() == false) {
272 ALOGE("failed allocation for new props");
273 return NULL;
274 }
Ray Essickb5fac8e2016-12-12 11:33:56 -0800275 }
276 i = mPropCount++;
277 prop = &mProps[i];
278 prop->setName(name, len);
279 }
280
281 return prop;
Ray Essick3938dc62016-11-01 08:56:56 -0700282}
283
Ray Essickf65f4212017-08-31 11:41:19 -0700284// used within the summarizers; return whether property existed
285bool MediaAnalyticsItem::removeProp(const char *name) {
286 size_t len = strlen(name);
287 size_t i = findPropIndex(name, len);
288 if (i < mPropCount) {
289 Prop *prop = &mProps[i];
290 clearProp(prop);
291 if (i != mPropCount-1) {
292 // in the middle, bring last one down to fill gap
Ray Essick58f58732017-10-02 10:56:18 -0700293 copyProp(prop, &mProps[mPropCount-1]);
294 clearProp(&mProps[mPropCount-1]);
Ray Essickf65f4212017-08-31 11:41:19 -0700295 }
296 mPropCount--;
297 return true;
298 }
299 return false;
300}
301
Ray Essick3938dc62016-11-01 08:56:56 -0700302// set the values
Ray Essickb5fac8e2016-12-12 11:33:56 -0800303void MediaAnalyticsItem::setInt32(MediaAnalyticsItem::Attr name, int32_t value) {
304 Prop *prop = allocateProp(name);
Ray Essick9bb7e3b2017-10-23 13:01:48 -0700305 if (prop != NULL) {
Ray Essick9ce18f72018-05-07 15:54:30 -0700306 clearPropValue(prop);
Ray Essick9bb7e3b2017-10-23 13:01:48 -0700307 prop->mType = kTypeInt32;
308 prop->u.int32Value = value;
309 }
Ray Essick3938dc62016-11-01 08:56:56 -0700310}
311
Ray Essickb5fac8e2016-12-12 11:33:56 -0800312void MediaAnalyticsItem::setInt64(MediaAnalyticsItem::Attr name, int64_t value) {
313 Prop *prop = allocateProp(name);
Ray Essick9bb7e3b2017-10-23 13:01:48 -0700314 if (prop != NULL) {
Ray Essick9ce18f72018-05-07 15:54:30 -0700315 clearPropValue(prop);
Ray Essick9bb7e3b2017-10-23 13:01:48 -0700316 prop->mType = kTypeInt64;
317 prop->u.int64Value = value;
318 }
Ray Essick3938dc62016-11-01 08:56:56 -0700319}
320
Ray Essickb5fac8e2016-12-12 11:33:56 -0800321void MediaAnalyticsItem::setDouble(MediaAnalyticsItem::Attr name, double value) {
322 Prop *prop = allocateProp(name);
Ray Essick9bb7e3b2017-10-23 13:01:48 -0700323 if (prop != NULL) {
Ray Essick9ce18f72018-05-07 15:54:30 -0700324 clearPropValue(prop);
Ray Essick9bb7e3b2017-10-23 13:01:48 -0700325 prop->mType = kTypeDouble;
326 prop->u.doubleValue = value;
327 }
Ray Essick3938dc62016-11-01 08:56:56 -0700328}
329
Ray Essickb5fac8e2016-12-12 11:33:56 -0800330void MediaAnalyticsItem::setCString(MediaAnalyticsItem::Attr name, const char *value) {
331
332 Prop *prop = allocateProp(name);
333 // any old value will be gone
Ray Essick9bb7e3b2017-10-23 13:01:48 -0700334 if (prop != NULL) {
Ray Essick9ce18f72018-05-07 15:54:30 -0700335 clearPropValue(prop);
Ray Essick9bb7e3b2017-10-23 13:01:48 -0700336 prop->mType = kTypeCString;
337 prop->u.CStringValue = strdup(value);
338 }
Ray Essick3938dc62016-11-01 08:56:56 -0700339}
340
Ray Essickb5fac8e2016-12-12 11:33:56 -0800341void MediaAnalyticsItem::setRate(MediaAnalyticsItem::Attr name, int64_t count, int64_t duration) {
342 Prop *prop = allocateProp(name);
Ray Essick9bb7e3b2017-10-23 13:01:48 -0700343 if (prop != NULL) {
Ray Essick9ce18f72018-05-07 15:54:30 -0700344 clearPropValue(prop);
Ray Essick9bb7e3b2017-10-23 13:01:48 -0700345 prop->mType = kTypeRate;
346 prop->u.rate.count = count;
347 prop->u.rate.duration = duration;
348 }
Ray Essickb5fac8e2016-12-12 11:33:56 -0800349}
350
351
Ray Essick3938dc62016-11-01 08:56:56 -0700352// find/add/set fused into a single operation
Ray Essickb5fac8e2016-12-12 11:33:56 -0800353void MediaAnalyticsItem::addInt32(MediaAnalyticsItem::Attr name, int32_t value) {
354 Prop *prop = allocateProp(name);
Ray Essick9bb7e3b2017-10-23 13:01:48 -0700355 if (prop == NULL) {
356 return;
357 }
Ray Essickb5fac8e2016-12-12 11:33:56 -0800358 switch (prop->mType) {
359 case kTypeInt32:
360 prop->u.int32Value += value;
361 break;
362 default:
363 clearPropValue(prop);
364 prop->mType = kTypeInt32;
365 prop->u.int32Value = value;
366 break;
Ray Essick3938dc62016-11-01 08:56:56 -0700367 }
Ray Essick3938dc62016-11-01 08:56:56 -0700368}
369
Ray Essickb5fac8e2016-12-12 11:33:56 -0800370void MediaAnalyticsItem::addInt64(MediaAnalyticsItem::Attr name, int64_t value) {
371 Prop *prop = allocateProp(name);
Ray Essick9bb7e3b2017-10-23 13:01:48 -0700372 if (prop == NULL) {
373 return;
374 }
Ray Essickb5fac8e2016-12-12 11:33:56 -0800375 switch (prop->mType) {
376 case kTypeInt64:
377 prop->u.int64Value += value;
378 break;
379 default:
380 clearPropValue(prop);
381 prop->mType = kTypeInt64;
382 prop->u.int64Value = value;
383 break;
Ray Essick3938dc62016-11-01 08:56:56 -0700384 }
Ray Essick3938dc62016-11-01 08:56:56 -0700385}
386
Ray Essickb5fac8e2016-12-12 11:33:56 -0800387void MediaAnalyticsItem::addRate(MediaAnalyticsItem::Attr name, int64_t count, int64_t duration) {
388 Prop *prop = allocateProp(name);
Ray Essick9bb7e3b2017-10-23 13:01:48 -0700389 if (prop == NULL) {
390 return;
391 }
Ray Essickb5fac8e2016-12-12 11:33:56 -0800392 switch (prop->mType) {
393 case kTypeRate:
394 prop->u.rate.count += count;
395 prop->u.rate.duration += duration;
396 break;
397 default:
398 clearPropValue(prop);
399 prop->mType = kTypeRate;
400 prop->u.rate.count = count;
401 prop->u.rate.duration = duration;
402 break;
Ray Essick3938dc62016-11-01 08:56:56 -0700403 }
Ray Essickb5fac8e2016-12-12 11:33:56 -0800404}
405
406void MediaAnalyticsItem::addDouble(MediaAnalyticsItem::Attr name, double value) {
407 Prop *prop = allocateProp(name);
Ray Essick9bb7e3b2017-10-23 13:01:48 -0700408 if (prop == NULL) {
409 return;
410 }
Ray Essickb5fac8e2016-12-12 11:33:56 -0800411 switch (prop->mType) {
412 case kTypeDouble:
413 prop->u.doubleValue += value;
414 break;
415 default:
416 clearPropValue(prop);
417 prop->mType = kTypeDouble;
418 prop->u.doubleValue = value;
419 break;
Ray Essick3938dc62016-11-01 08:56:56 -0700420 }
Ray Essick3938dc62016-11-01 08:56:56 -0700421}
422
423// find & extract values
Ray Essickb5fac8e2016-12-12 11:33:56 -0800424bool MediaAnalyticsItem::getInt32(MediaAnalyticsItem::Attr name, int32_t *value) {
425 Prop *prop = findProp(name);
426 if (prop == NULL || prop->mType != kTypeInt32) {
Ray Essick3938dc62016-11-01 08:56:56 -0700427 return false;
428 }
Ray Essickb5fac8e2016-12-12 11:33:56 -0800429 if (value != NULL) {
430 *value = prop->u.int32Value;
431 }
Ray Essick3938dc62016-11-01 08:56:56 -0700432 return true;
433}
Ray Essickb5fac8e2016-12-12 11:33:56 -0800434
435bool MediaAnalyticsItem::getInt64(MediaAnalyticsItem::Attr name, int64_t *value) {
436 Prop *prop = findProp(name);
437 if (prop == NULL || prop->mType != kTypeInt64) {
Ray Essick3938dc62016-11-01 08:56:56 -0700438 return false;
439 }
Ray Essickb5fac8e2016-12-12 11:33:56 -0800440 if (value != NULL) {
441 *value = prop->u.int64Value;
442 }
Ray Essick3938dc62016-11-01 08:56:56 -0700443 return true;
444}
Ray Essickb5fac8e2016-12-12 11:33:56 -0800445
446bool MediaAnalyticsItem::getRate(MediaAnalyticsItem::Attr name, int64_t *count, int64_t *duration, double *rate) {
447 Prop *prop = findProp(name);
448 if (prop == NULL || prop->mType != kTypeRate) {
Ray Essick3938dc62016-11-01 08:56:56 -0700449 return false;
450 }
Ray Essickb5fac8e2016-12-12 11:33:56 -0800451 if (count != NULL) {
452 *count = prop->u.rate.count;
453 }
454 if (duration != NULL) {
455 *duration = prop->u.rate.duration;
456 }
457 if (rate != NULL) {
458 double r = 0.0;
459 if (prop->u.rate.duration != 0) {
460 r = prop->u.rate.count / (double) prop->u.rate.duration;
461 }
462 *rate = r;
463 }
464 return true;
465}
466
467bool MediaAnalyticsItem::getDouble(MediaAnalyticsItem::Attr name, double *value) {
468 Prop *prop = findProp(name);
469 if (prop == NULL || prop->mType != kTypeDouble) {
470 return false;
471 }
472 if (value != NULL) {
473 *value = prop->u.doubleValue;
474 }
Ray Essick3938dc62016-11-01 08:56:56 -0700475 return true;
476}
477
478// caller responsible for the returned string
Ray Essickb5fac8e2016-12-12 11:33:56 -0800479bool MediaAnalyticsItem::getCString(MediaAnalyticsItem::Attr name, char **value) {
480 Prop *prop = findProp(name);
Ray Essick9db7b812018-11-15 12:42:19 -0800481 if (prop == NULL || prop->mType != kTypeCString) {
Ray Essick3938dc62016-11-01 08:56:56 -0700482 return false;
483 }
Ray Essickb5fac8e2016-12-12 11:33:56 -0800484 if (value != NULL) {
485 *value = strdup(prop->u.CStringValue);
486 }
Ray Essick3938dc62016-11-01 08:56:56 -0700487 return true;
488}
489
490// remove indicated keys and their values
491// return value is # keys removed
492int32_t MediaAnalyticsItem::filter(int n, MediaAnalyticsItem::Attr attrs[]) {
493 int zapped = 0;
Ray Essickb5fac8e2016-12-12 11:33:56 -0800494 if (attrs == NULL || n <= 0) {
Ray Essick3938dc62016-11-01 08:56:56 -0700495 return -1;
496 }
497 for (ssize_t i = 0 ; i < n ; i++) {
Ray Essickb5fac8e2016-12-12 11:33:56 -0800498 const char *name = attrs[i];
499 size_t len = strlen(name);
500 size_t j = findPropIndex(name, len);
501 if (j >= mPropCount) {
502 // not there
503 continue;
504 } else if (j+1 == mPropCount) {
505 // last one, shorten
Ray Essick3938dc62016-11-01 08:56:56 -0700506 zapped++;
Ray Essickb5fac8e2016-12-12 11:33:56 -0800507 clearProp(&mProps[j]);
508 mPropCount--;
509 } else {
510 // in the middle, bring last one down and shorten
511 zapped++;
512 clearProp(&mProps[j]);
513 mProps[j] = mProps[mPropCount-1];
514 mPropCount--;
Ray Essick3938dc62016-11-01 08:56:56 -0700515 }
516 }
517 return zapped;
518}
519
520// remove any keys NOT in the provided list
521// return value is # keys removed
522int32_t MediaAnalyticsItem::filterNot(int n, MediaAnalyticsItem::Attr attrs[]) {
523 int zapped = 0;
Ray Essickb5fac8e2016-12-12 11:33:56 -0800524 if (attrs == NULL || n <= 0) {
Ray Essick3938dc62016-11-01 08:56:56 -0700525 return -1;
526 }
Ray Essickb5fac8e2016-12-12 11:33:56 -0800527 for (ssize_t i = mPropCount-1 ; i >=0 ; i--) {
528 Prop *prop = &mProps[i];
529 for (ssize_t j = 0; j < n ; j++) {
530 if (strcmp(prop->mName, attrs[j]) == 0) {
531 clearProp(prop);
Ray Essick3938dc62016-11-01 08:56:56 -0700532 zapped++;
Ray Essickb5fac8e2016-12-12 11:33:56 -0800533 if (i != (ssize_t)(mPropCount-1)) {
534 *prop = mProps[mPropCount-1];
535 }
536 initProp(&mProps[mPropCount-1]);
537 mPropCount--;
Ray Essick3938dc62016-11-01 08:56:56 -0700538 break;
539 }
540 }
541 }
542 return zapped;
543}
544
545// remove a single key
546// return value is 0 (not found) or 1 (found and removed)
Ray Essickb5fac8e2016-12-12 11:33:56 -0800547int32_t MediaAnalyticsItem::filter(MediaAnalyticsItem::Attr name) {
548 return filter(1, &name);
Ray Essick3938dc62016-11-01 08:56:56 -0700549}
550
Ray Essick3938dc62016-11-01 08:56:56 -0700551// handle individual items/properties stored within the class
552//
Ray Essick3938dc62016-11-01 08:56:56 -0700553
Ray Essickb5fac8e2016-12-12 11:33:56 -0800554void MediaAnalyticsItem::initProp(Prop *prop) {
555 if (prop != NULL) {
556 prop->mName = NULL;
557 prop->mNameLen = 0;
Ray Essick3938dc62016-11-01 08:56:56 -0700558
Ray Essickb5fac8e2016-12-12 11:33:56 -0800559 prop->mType = kTypeNone;
Ray Essick3938dc62016-11-01 08:56:56 -0700560 }
Ray Essickb5fac8e2016-12-12 11:33:56 -0800561}
562
563void MediaAnalyticsItem::clearProp(Prop *prop)
564{
565 if (prop != NULL) {
566 if (prop->mName != NULL) {
567 free((void *)prop->mName);
568 prop->mName = NULL;
569 prop->mNameLen = 0;
570 }
571
572 clearPropValue(prop);
573 }
574}
575
576void MediaAnalyticsItem::clearPropValue(Prop *prop)
577{
578 if (prop != NULL) {
579 if (prop->mType == kTypeCString && prop->u.CStringValue != NULL) {
580 free(prop->u.CStringValue);
581 prop->u.CStringValue = NULL;
582 }
583 prop->mType = kTypeNone;
584 }
585}
586
587void MediaAnalyticsItem::copyProp(Prop *dst, const Prop *src)
588{
589 // get rid of any pointers in the dst
590 clearProp(dst);
591
592 *dst = *src;
593
594 // fix any pointers that we blindly copied, so we have our own copies
595 if (dst->mName) {
596 void *p = malloc(dst->mNameLen + 1);
Ray Essick9ce18f72018-05-07 15:54:30 -0700597 LOG_ALWAYS_FATAL_IF(p == NULL,
598 "failed malloc() duping property '%s' (len %zu)",
599 dst->mName, dst->mNameLen);
Ray Essickb5fac8e2016-12-12 11:33:56 -0800600 memcpy (p, src->mName, dst->mNameLen + 1);
601 dst->mName = (const char *) p;
602 }
603 if (dst->mType == kTypeCString) {
604 dst->u.CStringValue = strdup(src->u.CStringValue);
605 }
606}
607
Ray Essick9bb7e3b2017-10-23 13:01:48 -0700608bool MediaAnalyticsItem::growProps(int increment)
Ray Essickb5fac8e2016-12-12 11:33:56 -0800609{
610 if (increment <= 0) {
611 increment = kGrowProps;
612 }
613 int nsize = mPropSize + increment;
614 Prop *ni = (Prop *)realloc(mProps, sizeof(Prop) * nsize);
615
616 if (ni != NULL) {
617 for (int i = mPropSize; i < nsize; i++) {
618 initProp(&ni[i]);
619 }
620 mProps = ni;
621 mPropSize = nsize;
Ray Essick9bb7e3b2017-10-23 13:01:48 -0700622 return true;
623 } else {
624 ALOGW("MediaAnalyticsItem::growProps fails");
625 return false;
Ray Essickb5fac8e2016-12-12 11:33:56 -0800626 }
Ray Essick3938dc62016-11-01 08:56:56 -0700627}
628
629// Parcel / serialize things for binder calls
630//
631
632int32_t MediaAnalyticsItem::readFromParcel(const Parcel& data) {
633 // into 'this' object
634 // .. we make a copy of the string to put away.
635 mKey = data.readCString();
Ray Essickf65f4212017-08-31 11:41:19 -0700636 mPid = data.readInt32();
637 mUid = data.readInt32();
638 mPkgName = data.readCString();
Dianne Hackborn4e2eeff2017-11-27 14:01:29 -0800639 mPkgVersionCode = data.readInt64();
Ray Essick3938dc62016-11-01 08:56:56 -0700640 mSessionID = data.readInt64();
Ray Essick92d23b42018-01-29 12:10:30 -0800641 // We no longer pay attention to user setting of finalized, BUT it's
642 // still part of the wire packet -- so read & discard.
Ray Essick3938dc62016-11-01 08:56:56 -0700643 mFinalized = data.readInt32();
Ray Essick92d23b42018-01-29 12:10:30 -0800644 mFinalized = 1;
Ray Essick3938dc62016-11-01 08:56:56 -0700645 mTimestamp = data.readInt64();
646
647 int count = data.readInt32();
648 for (int i = 0; i < count ; i++) {
649 MediaAnalyticsItem::Attr attr = data.readCString();
650 int32_t ztype = data.readInt32();
651 switch (ztype) {
Ray Essickb5fac8e2016-12-12 11:33:56 -0800652 case MediaAnalyticsItem::kTypeInt32:
Ray Essick3938dc62016-11-01 08:56:56 -0700653 setInt32(attr, data.readInt32());
654 break;
Ray Essickb5fac8e2016-12-12 11:33:56 -0800655 case MediaAnalyticsItem::kTypeInt64:
Ray Essick3938dc62016-11-01 08:56:56 -0700656 setInt64(attr, data.readInt64());
657 break;
Ray Essickb5fac8e2016-12-12 11:33:56 -0800658 case MediaAnalyticsItem::kTypeDouble:
Ray Essick3938dc62016-11-01 08:56:56 -0700659 setDouble(attr, data.readDouble());
660 break;
Ray Essickb5fac8e2016-12-12 11:33:56 -0800661 case MediaAnalyticsItem::kTypeCString:
Ray Essick3938dc62016-11-01 08:56:56 -0700662 setCString(attr, data.readCString());
663 break;
Ray Essickb5fac8e2016-12-12 11:33:56 -0800664 case MediaAnalyticsItem::kTypeRate:
665 {
666 int64_t count = data.readInt64();
667 int64_t duration = data.readInt64();
668 setRate(attr, count, duration);
669 }
670 break;
Ray Essick3938dc62016-11-01 08:56:56 -0700671 default:
672 ALOGE("reading bad item type: %d, idx %d",
673 ztype, i);
674 return -1;
675 }
676 }
677
678 return 0;
679}
680
681int32_t MediaAnalyticsItem::writeToParcel(Parcel *data) {
682 if (data == NULL) return -1;
683
684
685 data->writeCString(mKey.c_str());
Ray Essickf65f4212017-08-31 11:41:19 -0700686 data->writeInt32(mPid);
687 data->writeInt32(mUid);
688 data->writeCString(mPkgName.c_str());
Dianne Hackborn4e2eeff2017-11-27 14:01:29 -0800689 data->writeInt64(mPkgVersionCode);
Ray Essick3938dc62016-11-01 08:56:56 -0700690 data->writeInt64(mSessionID);
691 data->writeInt32(mFinalized);
692 data->writeInt64(mTimestamp);
693
694 // set of items
Ray Essickb5fac8e2016-12-12 11:33:56 -0800695 int count = mPropCount;
Ray Essick3938dc62016-11-01 08:56:56 -0700696 data->writeInt32(count);
697 for (int i = 0 ; i < count; i++ ) {
Ray Essickb5fac8e2016-12-12 11:33:56 -0800698 Prop *prop = &mProps[i];
699 data->writeCString(prop->mName);
700 data->writeInt32(prop->mType);
701 switch (prop->mType) {
702 case MediaAnalyticsItem::kTypeInt32:
703 data->writeInt32(prop->u.int32Value);
704 break;
705 case MediaAnalyticsItem::kTypeInt64:
706 data->writeInt64(prop->u.int64Value);
707 break;
708 case MediaAnalyticsItem::kTypeDouble:
709 data->writeDouble(prop->u.doubleValue);
710 break;
711 case MediaAnalyticsItem::kTypeRate:
712 data->writeInt64(prop->u.rate.count);
713 data->writeInt64(prop->u.rate.duration);
714 break;
715 case MediaAnalyticsItem::kTypeCString:
716 data->writeCString(prop->u.CStringValue);
717 break;
718 default:
719 ALOGE("found bad Prop type: %d, idx %d, name %s",
720 prop->mType, i, prop->mName);
721 break;
Ray Essick3938dc62016-11-01 08:56:56 -0700722 }
723 }
724
725 return 0;
726}
727
728
Ray Essick783bd0d2018-01-11 11:10:35 -0800729std::string MediaAnalyticsItem::toString() {
Ray Essick5b77bd22018-01-23 16:11:06 -0800730 return toString(PROTO_LAST);
Ray Essickf65f4212017-08-31 11:41:19 -0700731}
Ray Essick3938dc62016-11-01 08:56:56 -0700732
Ray Essick783bd0d2018-01-11 11:10:35 -0800733std::string MediaAnalyticsItem::toString(int version) {
Ray Essickf65f4212017-08-31 11:41:19 -0700734
735 // v0 : released with 'o'
736 // v1 : bug fix (missing pid/finalized separator),
737 // adds apk name, apk version code
738
739 if (version <= PROTO_FIRST) {
740 // default to original v0 format, until proper parsers are in place
741 version = PROTO_V0;
742 } else if (version > PROTO_LAST) {
743 version = PROTO_LAST;
744 }
745
Ray Essick783bd0d2018-01-11 11:10:35 -0800746 std::string result;
Ray Essickb5fac8e2016-12-12 11:33:56 -0800747 char buffer[512];
Ray Essick3938dc62016-11-01 08:56:56 -0700748
Ray Essickf65f4212017-08-31 11:41:19 -0700749 if (version == PROTO_V0) {
750 result = "(";
751 } else {
752 snprintf(buffer, sizeof(buffer), "[%d:", version);
753 result.append(buffer);
754 }
755
Ray Essick3938dc62016-11-01 08:56:56 -0700756 // same order as we spill into the parcel, although not required
757 // key+session are our primary matching criteria
758 result.append(mKey.c_str());
759 result.append(":");
760 snprintf(buffer, sizeof(buffer), "%" PRId64 ":", mSessionID);
761 result.append(buffer);
762
Ray Essickf65f4212017-08-31 11:41:19 -0700763 snprintf(buffer, sizeof(buffer), "%d:", mUid);
764 result.append(buffer);
765
766 if (version >= PROTO_V1) {
767 result.append(mPkgName);
Dianne Hackborn4e2eeff2017-11-27 14:01:29 -0800768 snprintf(buffer, sizeof(buffer), ":%" PRId64 ":", mPkgVersionCode);
Ray Essickf65f4212017-08-31 11:41:19 -0700769 result.append(buffer);
770 }
771
772 // in 'o' (v1) , the separator between pid and finalized was omitted
773 if (version <= PROTO_V0) {
774 snprintf(buffer, sizeof(buffer), "%d", mPid);
775 } else {
776 snprintf(buffer, sizeof(buffer), "%d:", mPid);
777 }
Ray Essick3938dc62016-11-01 08:56:56 -0700778 result.append(buffer);
779
780 snprintf(buffer, sizeof(buffer), "%d:", mFinalized);
781 result.append(buffer);
782 snprintf(buffer, sizeof(buffer), "%" PRId64 ":", mTimestamp);
783 result.append(buffer);
784
785 // set of items
Ray Essickb5fac8e2016-12-12 11:33:56 -0800786 int count = mPropCount;
Ray Essick3938dc62016-11-01 08:56:56 -0700787 snprintf(buffer, sizeof(buffer), "%d:", count);
788 result.append(buffer);
789 for (int i = 0 ; i < count; i++ ) {
Ray Essickb5fac8e2016-12-12 11:33:56 -0800790 Prop *prop = &mProps[i];
791 switch (prop->mType) {
792 case MediaAnalyticsItem::kTypeInt32:
Ray Essick3938dc62016-11-01 08:56:56 -0700793 snprintf(buffer,sizeof(buffer),
Ray Essickb5fac8e2016-12-12 11:33:56 -0800794 "%s=%d:", prop->mName, prop->u.int32Value);
Ray Essick3938dc62016-11-01 08:56:56 -0700795 break;
Ray Essickb5fac8e2016-12-12 11:33:56 -0800796 case MediaAnalyticsItem::kTypeInt64:
Ray Essick3938dc62016-11-01 08:56:56 -0700797 snprintf(buffer,sizeof(buffer),
Ray Essickb5fac8e2016-12-12 11:33:56 -0800798 "%s=%" PRId64 ":", prop->mName, prop->u.int64Value);
Ray Essick3938dc62016-11-01 08:56:56 -0700799 break;
Ray Essickb5fac8e2016-12-12 11:33:56 -0800800 case MediaAnalyticsItem::kTypeDouble:
Ray Essick3938dc62016-11-01 08:56:56 -0700801 snprintf(buffer,sizeof(buffer),
Ray Essickb5fac8e2016-12-12 11:33:56 -0800802 "%s=%e:", prop->mName, prop->u.doubleValue);
Ray Essick3938dc62016-11-01 08:56:56 -0700803 break;
Ray Essickb5fac8e2016-12-12 11:33:56 -0800804 case MediaAnalyticsItem::kTypeRate:
805 snprintf(buffer,sizeof(buffer),
806 "%s=%" PRId64 "/%" PRId64 ":", prop->mName,
807 prop->u.rate.count, prop->u.rate.duration);
808 break;
809 case MediaAnalyticsItem::kTypeCString:
810 snprintf(buffer,sizeof(buffer), "%s=", prop->mName);
Ray Essick3938dc62016-11-01 08:56:56 -0700811 result.append(buffer);
Ray Essickb5fac8e2016-12-12 11:33:56 -0800812 // XXX: sanitize string for ':' '='
813 result.append(prop->u.CStringValue);
Ray Essick3938dc62016-11-01 08:56:56 -0700814 buffer[0] = ':';
815 buffer[1] = '\0';
816 break;
817 default:
Ray Essickb5fac8e2016-12-12 11:33:56 -0800818 ALOGE("to_String bad item type: %d for %s",
819 prop->mType, prop->mName);
Ray Essick3938dc62016-11-01 08:56:56 -0700820 break;
821 }
822 result.append(buffer);
823 }
824
Ray Essickf65f4212017-08-31 11:41:19 -0700825 if (version == PROTO_V0) {
826 result.append(")");
827 } else {
828 result.append("]");
829 }
Ray Essick3938dc62016-11-01 08:56:56 -0700830
831 return result;
832}
833
834// for the lazy, we offer methods that finds the service and
835// calls the appropriate daemon
836bool MediaAnalyticsItem::selfrecord() {
837 return selfrecord(false);
838}
839
840bool MediaAnalyticsItem::selfrecord(bool forcenew) {
841
Ray Essickb5fac8e2016-12-12 11:33:56 -0800842 if (DEBUG_API) {
Ray Essick783bd0d2018-01-11 11:10:35 -0800843 std::string p = this->toString();
Ray Essickb5fac8e2016-12-12 11:33:56 -0800844 ALOGD("selfrecord of: %s [forcenew=%d]", p.c_str(), forcenew);
845 }
Ray Essick3938dc62016-11-01 08:56:56 -0700846
847 sp<IMediaAnalyticsService> svc = getInstance();
848
849 if (svc != NULL) {
Ray Essick2ab3c432017-10-02 09:29:49 -0700850 MediaAnalyticsItem::SessionID_t newid = svc->submit(this, forcenew);
851 if (newid == SessionIDInvalid) {
Ray Essick783bd0d2018-01-11 11:10:35 -0800852 std::string p = this->toString();
Ray Essick2ab3c432017-10-02 09:29:49 -0700853 ALOGW("Failed to record: %s [forcenew=%d]", p.c_str(), forcenew);
854 return false;
855 }
Ray Essick3938dc62016-11-01 08:56:56 -0700856 return true;
857 } else {
Ray Essick783bd0d2018-01-11 11:10:35 -0800858 std::string p = this->toString();
Ray Essick2ab3c432017-10-02 09:29:49 -0700859 ALOGW("Unable to record: %s [forcenew=%d]", p.c_str(), forcenew);
Ray Essick3938dc62016-11-01 08:56:56 -0700860 return false;
861 }
862}
863
864// get a connection we can reuse for most of our lifetime
865// static
866sp<IMediaAnalyticsService> MediaAnalyticsItem::sAnalyticsService;
867static Mutex sInitMutex;
Ray Essick2ab3c432017-10-02 09:29:49 -0700868static int remainingBindAttempts = SVC_TRIES;
Ray Essick3938dc62016-11-01 08:56:56 -0700869
870//static
871bool MediaAnalyticsItem::isEnabled() {
872 int enabled = property_get_int32(MediaAnalyticsItem::EnabledProperty, -1);
873
874 if (enabled == -1) {
875 enabled = property_get_int32(MediaAnalyticsItem::EnabledPropertyPersist, -1);
876 }
877 if (enabled == -1) {
878 enabled = MediaAnalyticsItem::EnabledProperty_default;
879 }
880 if (enabled <= 0) {
881 return false;
882 }
883 return true;
884}
885
Ray Essick2ab3c432017-10-02 09:29:49 -0700886
887// monitor health of our connection to the metrics service
888class MediaMetricsDeathNotifier : public IBinder::DeathRecipient {
889 virtual void binderDied(const wp<IBinder> &) {
890 ALOGW("Reacquire service connection on next request");
891 MediaAnalyticsItem::dropInstance();
892 }
893};
894
895static sp<MediaMetricsDeathNotifier> sNotifier = NULL;
896
897// static
898void MediaAnalyticsItem::dropInstance() {
899 Mutex::Autolock _l(sInitMutex);
900 remainingBindAttempts = SVC_TRIES;
901 sAnalyticsService = NULL;
902}
903
Ray Essick3938dc62016-11-01 08:56:56 -0700904//static
905sp<IMediaAnalyticsService> MediaAnalyticsItem::getInstance() {
Ray Essick2ab3c432017-10-02 09:29:49 -0700906
Ray Essickd38e1742017-01-23 15:17:06 -0800907 static const char *servicename = "media.metrics";
Ray Essick3938dc62016-11-01 08:56:56 -0700908 int enabled = isEnabled();
909
910 if (enabled == false) {
911 if (DEBUG_SERVICEACCESS) {
912 ALOGD("disabled");
913 }
914 return NULL;
915 }
916
Ray Essick79a89ef2017-04-24 15:52:54 -0700917 // completely skip logging from certain UIDs. We do this here
918 // to avoid the multi-second timeouts while we learn that
919 // sepolicy will not let us find the service.
920 // We do this only for a select set of UIDs
921 // The sepolicy protection is still in place, we just want a faster
922 // response from this specific, small set of uids.
923 {
924 uid_t uid = getuid();
925 switch (uid) {
926 case AID_RADIO: // telephony subsystem, RIL
927 return NULL;
928 break;
929 default:
930 // let sepolicy deny access if appropriate
931 break;
932 }
933 }
934
Ray Essick3938dc62016-11-01 08:56:56 -0700935 {
936 Mutex::Autolock _l(sInitMutex);
937 const char *badness = "";
938
Ray Essick2ab3c432017-10-02 09:29:49 -0700939 // think of remainingBindAttempts as telling us whether service==NULL because
Ray Essickb5fac8e2016-12-12 11:33:56 -0800940 // (1) we haven't tried to initialize it yet
941 // (2) we've tried to initialize it, but failed.
Ray Essick2ab3c432017-10-02 09:29:49 -0700942 if (sAnalyticsService == NULL && remainingBindAttempts > 0) {
Ray Essick3938dc62016-11-01 08:56:56 -0700943 sp<IServiceManager> sm = defaultServiceManager();
944 if (sm != NULL) {
945 sp<IBinder> binder = sm->getService(String16(servicename));
946 if (binder != NULL) {
947 sAnalyticsService = interface_cast<IMediaAnalyticsService>(binder);
Ray Essick2ab3c432017-10-02 09:29:49 -0700948 if (sNotifier != NULL) {
949 sNotifier = NULL;
950 }
951 sNotifier = new MediaMetricsDeathNotifier();
952 binder->linkToDeath(sNotifier);
Ray Essick3938dc62016-11-01 08:56:56 -0700953 } else {
954 badness = "did not find service";
955 }
956 } else {
957 badness = "No Service Manager access";
958 }
Ray Essickb5fac8e2016-12-12 11:33:56 -0800959
960 if (sAnalyticsService == NULL) {
Ray Essick2ab3c432017-10-02 09:29:49 -0700961 if (remainingBindAttempts > 0) {
962 remainingBindAttempts--;
Ray Essickb5fac8e2016-12-12 11:33:56 -0800963 }
964 if (DEBUG_SERVICEACCESS) {
Ray Essick3938dc62016-11-01 08:56:56 -0700965 ALOGD("Unable to bind to service %s: %s", servicename, badness);
966 }
967 }
968 }
Ray Essickb5fac8e2016-12-12 11:33:56 -0800969
Ray Essick3938dc62016-11-01 08:56:56 -0700970 return sAnalyticsService;
971 }
972}
973
Ray Essick3938dc62016-11-01 08:56:56 -0700974// merge the info from 'incoming' into this record.
975// we finish with a union of this+incoming and special handling for collisions
Ray Essickb5fac8e2016-12-12 11:33:56 -0800976bool MediaAnalyticsItem::merge(MediaAnalyticsItem *incoming) {
Ray Essick3938dc62016-11-01 08:56:56 -0700977
978 // if I don't have key or session id, take them from incoming
979 // 'this' should never be missing both of them...
980 if (mKey.empty()) {
981 mKey = incoming->mKey;
982 } else if (mSessionID == 0) {
983 mSessionID = incoming->mSessionID;
984 }
985
Ray Essick3938dc62016-11-01 08:56:56 -0700986 // for each attribute from 'incoming', resolve appropriately
Ray Essickb5fac8e2016-12-12 11:33:56 -0800987 int nattr = incoming->mPropCount;
Ray Essick3938dc62016-11-01 08:56:56 -0700988 for (int i = 0 ; i < nattr; i++ ) {
Ray Essickb5fac8e2016-12-12 11:33:56 -0800989 Prop *iprop = &incoming->mProps[i];
Ray Essickb5fac8e2016-12-12 11:33:56 -0800990 const char *p = iprop->mName;
991 size_t len = strlen(p);
Ray Essick9bb7e3b2017-10-23 13:01:48 -0700992
993 // should ignore a zero length name...
994 if (len == 0) {
995 continue;
996 }
997
998 Prop *oprop = findProp(iprop->mName);
Ray Essick3938dc62016-11-01 08:56:56 -0700999
Ray Essickb5fac8e2016-12-12 11:33:56 -08001000 if (oprop == NULL) {
1001 // no oprop, so we insert the new one
1002 oprop = allocateProp(p);
Ray Essick9bb7e3b2017-10-23 13:01:48 -07001003 if (oprop != NULL) {
1004 copyProp(oprop, iprop);
1005 } else {
1006 ALOGW("dropped property '%s'", iprop->mName);
Ray Essickb5fac8e2016-12-12 11:33:56 -08001007 }
Ray Essick9bb7e3b2017-10-23 13:01:48 -07001008 } else {
1009 copyProp(oprop, iprop);
Ray Essick3938dc62016-11-01 08:56:56 -07001010 }
1011 }
1012
1013 // not sure when we'd return false...
1014 return true;
1015}
1016
1017} // namespace android
1018