• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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