1 /*
2  * Copyright (C) 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 #pragma once
18 
19 #include <atomic>
20 #include <deque>
21 #include <future>
22 #include <mutex>
23 #include <unordered_map>
24 
25 // IMediaMetricsService must include Vector, String16, Errors
26 #include <android-base/thread_annotations.h>
27 #include <media/IMediaMetricsService.h>
28 #include <mediautils/ServiceUtilities.h>
29 #include <utils/String8.h>
30 
31 #include "AudioAnalytics.h"
32 
33 namespace android {
34 
35 class MediaMetricsService : public BnMediaMetricsService
36 {
37 public:
38     MediaMetricsService();
39     ~MediaMetricsService() override;
40 
41     /**
42      * Submits the indicated record to the mediaanalytics service.
43      *
44      * \param item the item to submit.
45      * \return status failure, which is negative on binder transaction failure.
46      *         As the transaction is one-way, remote failures will not be reported.
47      */
submit(mediametrics::Item * item)48     status_t submit(mediametrics::Item *item) override {
49         return submitInternal(item, false /* release */);
50     }
51 
submitBuffer(const char * buffer,size_t length)52     status_t submitBuffer(const char *buffer, size_t length) override {
53         mediametrics::Item *item = new mediametrics::Item();
54         return item->readFromByteString(buffer, length)
55                 ?: submitInternal(item, true /* release */);
56     }
57 
58     status_t dump(int fd, const Vector<String16>& args) override;
59 
60     static constexpr const char * const kServiceName = "media.metrics";
61 
62     /**
63      * Rounds time to the nearest second.
64      */
65     static nsecs_t roundTime(nsecs_t timeNs);
66 
67     /**
68      * Returns true if we should use uid for package name when uploading to statsd.
69      */
70     static bool useUidForPackage(const std::string& package, const std::string& installer);
71 
72     /**
73      * Returns a std::pair of packageName and versionCode for a given uid.
74      *
75      * The value is sanitized - i.e. if the result is not approved to send,
76      * we use the uid as a string and a version code of 0.
77      */
78     static std::pair<std::string, int64_t> getSanitizedPackageNameAndVersionCode(uid_t uid);
79 
80 protected:
81 
82     // Internal call where release is true if ownership of item is transferred
83     // to the service (that is, the service will eventually delete the item).
84     status_t submitInternal(mediametrics::Item *item, bool release) override;
85 
86 private:
87     void processExpirations();
88     // input validation after arrival from client
89     static bool isContentValid(const mediametrics::Item *item, bool isTrusted);
90     bool isRateLimited(mediametrics::Item *) const;
91     void saveItem(const std::shared_ptr<const mediametrics::Item>& item);
92 
93     bool expirations(const std::shared_ptr<const mediametrics::Item>& item) REQUIRES(mLock);
94 
95     // support for generating output
96     void dumpQueue(String8 &result, int64_t sinceNs, const char* prefix) REQUIRES(mLock);
97     void dumpHeaders(String8 &result, int64_t sinceNs, const char* prefix) REQUIRES(mLock);
98 
99     // The following variables accessed without mLock
100 
101     // limit how many records we'll retain
102     // by count (in each queue (open, finalized))
103     const size_t mMaxRecords;
104     // by time (none older than this)
105     const nsecs_t mMaxRecordAgeNs;
106     // max to expire per expirations_l() invocation
107     const size_t mMaxRecordsExpiredAtOnce;
108 
109     std::atomic<int64_t> mItemsSubmitted{}; // accessed outside of lock.
110 
111     mediametrics::AudioAnalytics mAudioAnalytics; // mAudioAnalytics is locked internally.
112 
113     std::mutex mLock;
114     // statistics about our analytics
115     int64_t mItemsFinalized GUARDED_BY(mLock) = 0;
116     int64_t mItemsDiscarded GUARDED_BY(mLock) = 0;
117     int64_t mItemsDiscardedExpire GUARDED_BY(mLock) = 0;
118     int64_t mItemsDiscardedCount GUARDED_BY(mLock) = 0;
119 
120     // If we have a worker thread to garbage collect
121     std::future<void> mExpireFuture GUARDED_BY(mLock);
122 
123     // Our item queue, generally (oldest at front)
124     // TODO: Make separate class, use segmented queue, write lock only end.
125     // Note: Another analytics module might have ownership of an item longer than the log.
126     std::deque<std::shared_ptr<const mediametrics::Item>> mItems GUARDED_BY(mLock);
127 };
128 
129 } // namespace android
130