1 /*
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 #define LOG_TAG "mediametrics::Item"
18
19 #include <inttypes.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <sys/types.h>
23
24 #include <mutex>
25 #include <set>
26 #include <unordered_map>
27
28 #include <binder/Parcel.h>
29 #include <cutils/multiuser.h>
30 #include <cutils/properties.h>
31 #include <utils/Errors.h>
32 #include <utils/Log.h>
33 #include <utils/SortedVector.h>
34 #include <utils/threads.h>
35
36 #include <android/media/BnMediaMetricsService.h> // for direct Binder access
37 #include <android/media/IMediaMetricsService.h>
38 #include <binder/IServiceManager.h>
39 #include <media/MediaMetricsItem.h>
40 #include <private/android_filesystem_config.h>
41
42 // Max per-property string size before truncation in toString().
43 // Do not make too large, as this is used for dumpsys purposes.
44 static constexpr size_t kMaxPropertyStringSize = 4096;
45
46 namespace android::mediametrics {
47
48 #define DEBUG_SERVICEACCESS 0
49 #define DEBUG_API 0
50 #define DEBUG_ALLOCATIONS 0
51
52 // after this many failed attempts, we stop trying [from this process] and just say that
53 // the service is off.
54 #define SVC_TRIES 2
55
getErrorStringMap()56 static const std::unordered_map<std::string, int32_t>& getErrorStringMap() {
57 // DO NOT MODIFY VALUES (OK to add new ones).
58 // This may be found in frameworks/av/media/libmediametrics/include/MediaMetricsConstants.h
59 static std::unordered_map<std::string, int32_t> map{
60 {"", NO_ERROR},
61 {AMEDIAMETRICS_PROP_STATUS_VALUE_OK, NO_ERROR},
62 {AMEDIAMETRICS_PROP_STATUS_VALUE_ARGUMENT, BAD_VALUE},
63 {AMEDIAMETRICS_PROP_STATUS_VALUE_IO, DEAD_OBJECT},
64 {AMEDIAMETRICS_PROP_STATUS_VALUE_MEMORY, NO_MEMORY},
65 {AMEDIAMETRICS_PROP_STATUS_VALUE_SECURITY, PERMISSION_DENIED},
66 {AMEDIAMETRICS_PROP_STATUS_VALUE_STATE, INVALID_OPERATION},
67 {AMEDIAMETRICS_PROP_STATUS_VALUE_TIMEOUT, WOULD_BLOCK},
68 {AMEDIAMETRICS_PROP_STATUS_VALUE_UNKNOWN, UNKNOWN_ERROR},
69 };
70 return map;
71 }
72
statusStringToStatus(const char * error)73 status_t statusStringToStatus(const char *error) {
74 const auto& map = getErrorStringMap();
75 if (error == nullptr || error[0] == '\0') return NO_ERROR;
76 auto it = map.find(error);
77 if (it != map.end()) {
78 return it->second;
79 }
80 return UNKNOWN_ERROR;
81 }
82
convert(mediametrics_handle_t handle)83 mediametrics::Item* mediametrics::Item::convert(mediametrics_handle_t handle) {
84 mediametrics::Item *item = (android::mediametrics::Item *) handle;
85 return item;
86 }
87
convert(mediametrics::Item * item)88 mediametrics_handle_t mediametrics::Item::convert(mediametrics::Item *item ) {
89 mediametrics_handle_t handle = (mediametrics_handle_t) item;
90 return handle;
91 }
92
~Item()93 mediametrics::Item::~Item() {
94 if (DEBUG_ALLOCATIONS) {
95 ALOGD("Destroy mediametrics::Item @ %p", this);
96 }
97 }
98
setTimestamp(nsecs_t ts)99 mediametrics::Item &mediametrics::Item::setTimestamp(nsecs_t ts) {
100 mTimestamp = ts;
101 return *this;
102 }
103
getTimestamp() const104 nsecs_t mediametrics::Item::getTimestamp() const {
105 return mTimestamp;
106 }
107
setPid(pid_t pid)108 mediametrics::Item &mediametrics::Item::setPid(pid_t pid) {
109 mPid = pid;
110 return *this;
111 }
112
getPid() const113 pid_t mediametrics::Item::getPid() const {
114 return mPid;
115 }
116
setUid(uid_t uid)117 mediametrics::Item &mediametrics::Item::setUid(uid_t uid) {
118 mUid = uid;
119 return *this;
120 }
121
getUid() const122 uid_t mediametrics::Item::getUid() const {
123 return mUid;
124 }
125
setPkgName(const std::string & pkgName)126 mediametrics::Item &mediametrics::Item::setPkgName(const std::string &pkgName) {
127 mPkgName = pkgName;
128 return *this;
129 }
130
setPkgVersionCode(int64_t pkgVersionCode)131 mediametrics::Item &mediametrics::Item::setPkgVersionCode(int64_t pkgVersionCode) {
132 mPkgVersionCode = pkgVersionCode;
133 return *this;
134 }
135
getPkgVersionCode() const136 int64_t mediametrics::Item::getPkgVersionCode() const {
137 return mPkgVersionCode;
138 }
139
140 // remove indicated keys and their values
141 // return value is # keys removed
filter(size_t n,const char * attrs[])142 size_t mediametrics::Item::filter(size_t n, const char *attrs[]) {
143 size_t zapped = 0;
144 for (size_t i = 0; i < n; ++i) {
145 zapped += mProps.erase(attrs[i]);
146 }
147 return zapped;
148 }
149
150 // remove any keys NOT in the provided list
151 // return value is # keys removed
filterNot(size_t n,const char * attrs[])152 size_t mediametrics::Item::filterNot(size_t n, const char *attrs[]) {
153 std::set<std::string> check(attrs, attrs + n);
154 size_t zapped = 0;
155 for (auto it = mProps.begin(); it != mProps.end();) {
156 if (check.find(it->first) != check.end()) {
157 ++it;
158 } else {
159 it = mProps.erase(it);
160 ++zapped;
161 }
162 }
163 return zapped;
164 }
165
toCString()166 const char *mediametrics::Item::toCString() {
167 std::string val = toString();
168 return strdup(val.c_str());
169 }
170
171 /*
172 * Similar to audio_utils/clock.h but customized for displaying mediametrics time.
173 */
174
nsToString(int64_t ns,char * buffer,size_t bufferSize,PrintFormat format)175 void nsToString(int64_t ns, char *buffer, size_t bufferSize, PrintFormat format)
176 {
177 if (bufferSize == 0) return;
178
179 const int one_second = 1000000000;
180 const time_t sec = ns / one_second;
181 struct tm tm;
182
183 // Supported on bionic, glibc, and macOS, but not mingw.
184 if (localtime_r(&sec, &tm) == NULL) {
185 buffer[0] = '\0';
186 return;
187 }
188
189 switch (format) {
190 default:
191 case kPrintFormatLong:
192 if (snprintf(buffer, bufferSize, "%02d-%02d %02d:%02d:%02d.%03d",
193 tm.tm_mon + 1, // localtime_r uses months in 0 - 11 range
194 tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec,
195 (int)(ns % one_second / 1000000)) < 0) {
196 buffer[0] = '\0'; // null terminate on format error, which should not happen
197 }
198 break;
199 case kPrintFormatShort:
200 if (snprintf(buffer, bufferSize, "%02d:%02d:%02d.%03d",
201 tm.tm_hour, tm.tm_min, tm.tm_sec,
202 (int)(ns % one_second / 1000000)) < 0) {
203 buffer[0] = '\0'; // null terminate on format error, which should not happen
204 }
205 break;
206 }
207 }
208
toString() const209 std::string mediametrics::Item::toString() const {
210 std::string result;
211 char buffer[kMaxPropertyStringSize];
212
213 snprintf(buffer, sizeof(buffer), "{%s, (%s), (%s, %d, %d)",
214 mKey.c_str(),
215 timeStringFromNs(mTimestamp, kPrintFormatLong).time,
216 mPkgName.c_str(), mPid, mUid
217 );
218 result.append(buffer);
219 bool first = true;
220 for (auto &prop : *this) {
221 prop.toStringBuffer(buffer, sizeof(buffer));
222 result += first ? ", (" : ", ";
223 result += buffer;
224 first = false;
225 }
226 result.append(")}");
227 return result;
228 }
229
230 // for the lazy, we offer methods that finds the service and
231 // calls the appropriate daemon
selfrecord()232 bool mediametrics::Item::selfrecord() {
233 ALOGD_IF(DEBUG_API, "%s: delivering %s", __func__, this->toString().c_str());
234
235 char *str;
236 size_t size;
237 status_t status = writeToByteString(&str, &size);
238 if (status == NO_ERROR) {
239 status = submitBuffer(str, size);
240 free(str);
241 }
242 if (status != NO_ERROR) {
243 ALOGW("%s: failed to record: %s", __func__, this->toString().c_str());
244 return false;
245 }
246 return true;
247 }
248
249 //static
isEnabled()250 bool BaseItem::isEnabled() {
251 // completely skip logging from certain UIDs. We do this here
252 // to avoid the multi-second timeouts while we learn that
253 // sepolicy will not let us find the service.
254 // We do this only for a select set of UIDs
255 // The sepolicy protection is still in place, we just want a faster
256 // response from this specific, small set of uids.
257
258 // This is checked only once in the lifetime of the process.
259 const uid_t uid = getuid();
260 const uid_t appid = multiuser_get_app_id(uid);
261
262 if (appid == AID_RADIO) {
263 // telephony subsystem, RIL
264 return false;
265 }
266
267 if (appid >= AID_ISOLATED_START && appid <= AID_ISOLATED_END) {
268 // Some isolated processes can access the audio system; see
269 // AudioSystem::setAudioFlingerBinder (currently only the HotwordDetectionService). Instead
270 // of also allowing access to the MediaMetrics service, it's simpler to just disable it for
271 // now.
272 // TODO(b/190151205): Either allow the HotwordDetectionService to access MediaMetrics or
273 // make this disabling specific to that process.
274 return false;
275 }
276
277 int enabled = property_get_int32(Item::EnabledProperty, -1);
278 if (enabled == -1) {
279 enabled = property_get_int32(Item::EnabledPropertyPersist, -1);
280 }
281 if (enabled == -1) {
282 enabled = Item::EnabledProperty_default;
283 }
284 return enabled > 0;
285 }
286
287 // monitor health of our connection to the metrics service
288 class MediaMetricsDeathNotifier : public IBinder::DeathRecipient {
binderDied(const wp<IBinder> &)289 virtual void binderDied(const wp<IBinder> &) {
290 ALOGW("Reacquire service connection on next request");
291 BaseItem::dropInstance();
292 }
293 };
294
295 static sp<MediaMetricsDeathNotifier> sNotifier;
296 static sp<media::IMediaMetricsService> sMediaMetricsService;
297 static std::mutex sServiceMutex;
298 static int sRemainingBindAttempts = SVC_TRIES;
299
300 // moving this out of the class removes all service references from <MediaMetricsItem.h>
301 // and simplifies moving things to a module
302 static
getService()303 sp<media::IMediaMetricsService> getService() {
304 static const char *servicename = "media.metrics";
305 static const bool enabled = BaseItem::isEnabled(); // singleton initialized
306
307 if (enabled == false) {
308 ALOGD_IF(DEBUG_SERVICEACCESS, "disabled");
309 return nullptr;
310 }
311 std::lock_guard _l(sServiceMutex);
312 // think of remainingBindAttempts as telling us whether service == nullptr because
313 // (1) we haven't tried to initialize it yet
314 // (2) we've tried to initialize it, but failed.
315 if (sMediaMetricsService == nullptr && sRemainingBindAttempts > 0) {
316 const char *badness = "";
317 sp<IServiceManager> sm = defaultServiceManager();
318 if (sm != nullptr) {
319 sp<IBinder> binder = sm->getService(String16(servicename));
320 if (binder != nullptr) {
321 sMediaMetricsService = interface_cast<media::IMediaMetricsService>(binder);
322 sNotifier = new MediaMetricsDeathNotifier();
323 binder->linkToDeath(sNotifier);
324 } else {
325 badness = "did not find service";
326 }
327 } else {
328 badness = "No Service Manager access";
329 }
330 if (sMediaMetricsService == nullptr) {
331 if (sRemainingBindAttempts > 0) {
332 sRemainingBindAttempts--;
333 }
334 ALOGD_IF(DEBUG_SERVICEACCESS, "%s: unable to bind to service %s: %s",
335 __func__, servicename, badness);
336 }
337 }
338 return sMediaMetricsService;
339 }
340
341 // static
dropInstance()342 void BaseItem::dropInstance() {
343 std::lock_guard _l(sServiceMutex);
344 sRemainingBindAttempts = SVC_TRIES;
345 sMediaMetricsService = nullptr;
346 }
347
348 // static
submitBuffer(const char * buffer,size_t size)349 status_t BaseItem::submitBuffer(const char *buffer, size_t size) {
350 ALOGD_IF(DEBUG_API, "%s: delivering %zu bytes", __func__, size);
351
352 // Validate size
353 if (size > std::numeric_limits<int32_t>::max()) return BAD_VALUE;
354
355 // Do we have the service available?
356 sp<media::IMediaMetricsService> svc = getService();
357 if (svc == nullptr) return NO_INIT;
358
359 ::android::status_t status = NO_ERROR;
360 if constexpr (/* DISABLES CODE */ (false)) {
361 // THIS PATH IS FOR REFERENCE ONLY.
362 // It is compiled so that any changes to IMediaMetricsService::submitBuffer()
363 // will lead here. If this code is changed, the else branch must
364 // be changed as well.
365 //
366 // Use the AIDL calling interface - this is a bit slower as a byte vector must be
367 // constructed. As the call is one-way, the only a transaction error occurs.
368 status = svc->submitBuffer({buffer, buffer + size}).transactionError();
369 } else {
370 // Use the Binder calling interface - this direct implementation avoids
371 // malloc/copy/free for the vector and reduces the overhead for logging.
372 // We based this off of the AIDL generated file:
373 // out/soong/.intermediates/frameworks/av/media/libmediametrics/mediametricsservice-aidl-unstable-cpp-source/gen/android/media/IMediaMetricsService.cpp
374 // TODO: Create an AIDL C++ back end optimized form of vector writing.
375 ::android::Parcel _aidl_data;
376 ::android::Parcel _aidl_reply; // we don't care about this as it is one-way.
377
378 status = _aidl_data.writeInterfaceToken(svc->getInterfaceDescriptor());
379 if (status != ::android::OK) goto _aidl_error;
380
381 status = _aidl_data.writeInt32(static_cast<int32_t>(size));
382 if (status != ::android::OK) goto _aidl_error;
383
384 status = _aidl_data.write(buffer, static_cast<int32_t>(size));
385 if (status != ::android::OK) goto _aidl_error;
386
387 status = ::android::IInterface::asBinder(svc)->transact(
388 ::android::media::BnMediaMetricsService::TRANSACTION_submitBuffer,
389 _aidl_data, &_aidl_reply, ::android::IBinder::FLAG_ONEWAY);
390
391 // AIDL permits setting a default implementation for additional functionality.
392 // See go/aog/713984. This is not used here.
393 // if (status == ::android::UNKNOWN_TRANSACTION
394 // && ::android::media::IMediaMetricsService::getDefaultImpl()) {
395 // status = ::android::media::IMediaMetricsService::getDefaultImpl()
396 // ->submitBuffer(immutableByteVectorFromBuffer(buffer, size))
397 // .transactionError();
398 // }
399 }
400
401 if (status == NO_ERROR) return NO_ERROR;
402
403 _aidl_error:
404 ALOGW("%s: failed(%d) to record: %zu bytes", __func__, status, size);
405 return status;
406 }
407
408 } // namespace android::mediametrics
409