• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <android/media/BnMediaMetricsService.h>
28 #include <mediautils/ServiceUtilities.h>
29 #include <stats_pull_atom_callback.h>
30 #include <utils/String8.h>
31 
32 #include "AudioAnalytics.h"
33 
34 namespace android {
35 
36 class MediaMetricsService : public media::BnMediaMetricsService
37 {
38 public:
39     MediaMetricsService();
40     ~MediaMetricsService() override;
41 
42     // AIDL interface
submitBuffer(const std::vector<uint8_t> & buffer)43     binder::Status submitBuffer(const std::vector<uint8_t>& buffer) override {
44         status_t status = submitBuffer((char *)buffer.data(), buffer.size());
45         return binder::Status::fromStatusT(status);
46     }
47 
48     /**
49      * Submits the indicated record to the mediaanalytics service.
50      *
51      * \param item the item to submit.
52      * \return status failure, which is negative on binder transaction failure.
53      *         As the transaction is one-way, remote failures will not be reported.
54      */
submit(mediametrics::Item * item)55     status_t submit(mediametrics::Item *item) {
56         return submitInternal(item, false /* release */);
57     }
58 
submitBuffer(const char * buffer,size_t length)59     status_t submitBuffer(const char *buffer, size_t length) {
60         mediametrics::Item *item = new mediametrics::Item();
61         return item->readFromByteString(buffer, length)
62                 ?: submitInternal(item, true /* release */);
63     }
64 
65     status_t dump(int fd, const Vector<String16>& args) override;
66 
67     static constexpr const char * const kServiceName = "media.metrics";
68 
69     /**
70      * Rounds time to the nearest second.
71      */
72     static nsecs_t roundTime(nsecs_t timeNs);
73 
74     /**
75      * Returns true if we should use uid for package name when uploading to statsd.
76      */
77     static bool useUidForPackage(const std::string& package, const std::string& installer);
78 
79     /**
80      * Returns a std::pair of packageName and versionCode for a given uid.
81      *
82      * The value is sanitized - i.e. if the result is not approved to send,
83      * we use the uid as a string and a version code of 0.
84      */
85     static std::pair<std::string, int64_t> getSanitizedPackageNameAndVersionCode(uid_t uid);
86 
87 protected:
88 
89     // Internal call where release is true if ownership of item is transferred
90     // to the service (that is, the service will eventually delete the item).
91     status_t submitInternal(mediametrics::Item *item, bool release);
92 
93 private:
94     void processExpirations();
95     // input validation after arrival from client
96     static bool isContentValid(const mediametrics::Item *item, bool isTrusted);
97     bool isRateLimited(mediametrics::Item *) const;
98     void saveItem(const std::shared_ptr<const mediametrics::Item>& item);
99 
100     bool expirations(const std::shared_ptr<const mediametrics::Item>& item) REQUIRES(mLock);
101 
102     // support for generating output
103     std::string dumpQueue(int64_t sinceNs, const char* prefix) REQUIRES(mLock);
104     std::string dumpHeaders(int64_t sinceNs, const char* prefix) REQUIRES(mLock);
105 
106     // support statsd pushed atoms
107     static bool isPullable(const std::string &key);
108     static std::string atomTagToKey(int32_t atomTag);
109     static AStatsManager_PullAtomCallbackReturn pullAtomCallback(
110             int32_t atomTag, AStatsEventList* data, void* cookie);
111     AStatsManager_PullAtomCallbackReturn pullItems(int32_t atomTag, AStatsEventList* data);
112     void registerStatsdCallbacksIfNeeded();
113     std::atomic_flag mStatsdRegistered = ATOMIC_FLAG_INIT;
114 
115     // The following variables accessed without mLock
116 
117     // limit how many records we'll retain
118     // by count (in each queue (open, finalized))
119     const size_t mMaxRecords;
120     // by time (none older than this)
121     const nsecs_t mMaxRecordAgeNs;
122     // max to expire per expirations_l() invocation
123     const size_t mMaxRecordsExpiredAtOnce;
124 
125     std::atomic<int64_t> mItemsSubmitted{}; // accessed outside of lock.
126 
127     // mStatsdLog is locked internally (thread-safe) and shows the last atoms logged
128     static constexpr size_t STATSD_LOG_LINES_MAX = 30; // recent log lines to keep
129     static constexpr size_t STATSD_LOG_LINES_DUMP = 4; // normal amount of lines to dump
130     const std::shared_ptr<mediametrics::StatsdLog> mStatsdLog{
131             std::make_shared<mediametrics::StatsdLog>(STATSD_LOG_LINES_MAX)};
132 
133     // mAudioAnalytics is locked internally.
134     mediametrics::AudioAnalytics mAudioAnalytics{mStatsdLog};
135 
136     std::mutex mLock;
137     // statistics about our analytics
138     int64_t mItemsFinalized GUARDED_BY(mLock) = 0;
139     int64_t mItemsDiscarded GUARDED_BY(mLock) = 0;
140     int64_t mItemsDiscardedExpire GUARDED_BY(mLock) = 0;
141     int64_t mItemsDiscardedCount GUARDED_BY(mLock) = 0;
142 
143     // If we have a worker thread to garbage collect
144     std::future<void> mExpireFuture GUARDED_BY(mLock);
145 
146     // Our item queue, generally (oldest at front)
147     // TODO: Make separate class, use segmented queue, write lock only end.
148     // Note: Another analytics module might have ownership of an item longer than the log.
149     std::deque<std::shared_ptr<const mediametrics::Item>> mItems GUARDED_BY(mLock);
150 
151     // Queues per item key, pending to be pulled by statsd.
152     // Use weak_ptr such that a pullable item can still expire.
153     using ItemKey = std::string;
154     using WeakItemQueue = std::deque<std::weak_ptr<const mediametrics::Item>>;
155     std::unordered_map<ItemKey, WeakItemQueue> mPullableItems GUARDED_BY(mLock);
156 };
157 
158 } // namespace android
159