1 /*
2  * Copyright (C) 2019 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 <android-base/thread_annotations.h>
20 #include "AnalyticsActions.h"
21 #include "AnalyticsState.h"
22 #include "AudioPowerUsage.h"
23 #include "HeatMap.h"
24 #include "StatsdLog.h"
25 #include "TimedAction.h"
26 #include "Wrap.h"
27 
28 namespace android::mediametrics {
29 
30 class AudioAnalytics
31 {
32     // AudioAnalytics action / state helper classes
33     friend AudioPowerUsage;
34 
35 public:
36     explicit AudioAnalytics(const std::shared_ptr<StatsdLog>& statsdLog);
37     ~AudioAnalytics();
38 
39     /**
40      * Returns success if AudioAnalytics recognizes item.
41      *
42      * AudioAnalytics requires the item key to start with "audio.".
43      *
44      * A trusted source can create a new key, an untrusted source
45      * can only modify the key if the uid will match that authorized
46      * on the existing key.
47      *
48      * \param item the item to be submitted.
49      * \param isTrusted whether the transaction comes from a trusted source.
50      *        In this case, a trusted source is verified by binder
51      *        UID to be a system service by MediaMetrics service.
52      *        Do not use true if you haven't really checked!
53      *
54      * \return NO_ERROR on success,
55      *         PERMISSION_DENIED if the item cannot be put into the AnalyticsState,
56      *         BAD_VALUE if the item key does not start with "audio.".
57      */
58     status_t submit(const std::shared_ptr<const mediametrics::Item>& item, bool isTrusted);
59 
60     /**
61      * Returns a pair consisting of the dump string, and the number of lines in the string.
62      *
63      * The number of lines in the returned pair is used as an optimization
64      * for subsequent line limiting.
65      *
66      * The TimeMachine and the TransactionLog are dumped separately under
67      * different locks, so may not be 100% consistent with the last data
68      * delivered.
69      *
70      * \param lines the maximum number of lines in the string returned.
71      * \param sinceNs the nanoseconds since Unix epoch to start dump (0 shows all)
72      * \param prefix the desired key prefix to match (nullptr shows all)
73      */
74     std::pair<std::string, int32_t> dump(
75             int32_t lines = INT32_MAX, int64_t sinceNs = 0, const char *prefix = nullptr) const;
76 
77     /**
78      * Returns a pair consisting of the dump string and the number of lines in the string.
79      *
80      * HeatMap dump.
81      */
82     std::pair<std::string, int32_t> dumpHeatMap(int32_t lines = INT32_MAX) const {
83         return mHeatMap.dump(lines);
84     }
85 
clear()86     void clear() {
87         // underlying state is locked.
88         mPreviousAnalyticsState->clear();
89         mAnalyticsState->clear();
90 
91         // Clears the status map
92         mHeatMap.clear();
93 
94         // Clear power usage state.
95         mAudioPowerUsage.clear();
96     }
97 
98 private:
99 
100     /*
101      * AudioAnalytics class does not contain a monitor mutex.
102      * Instead, all of its variables are individually locked for access.
103      * Since data and items are generally added only (gc removes it), this is a reasonable
104      * compromise for availability/concurrency versus consistency.
105      *
106      * It is possible for concurrent threads to be reading and writing inside of AudioAnalytics.
107      * Reads based on a prior time (e.g. one second) in the past from the TimeMachine can be
108      * used to achieve better consistency if needed.
109      */
110 
111     /**
112      * Processes any pending actions for a particular item.
113      *
114      * \param item to check against the current AnalyticsActions.
115      */
116     void processActions(const std::shared_ptr<const mediametrics::Item>& item);
117 
118     /**
119      * Processes status information contained in the item.
120      *
121      * \param item to check against for status handling
122      */
123     void processStatus(const std::shared_ptr<const mediametrics::Item>& item);
124 
125     // HELPER METHODS
126     /**
127      * Return the audio thread associated with an audio track name.
128      * e.g. "audio.track.32" -> "audio.thread.10" if the associated
129      * threadId for the audio track is 10.
130      */
131     std::string getThreadFromTrack(const std::string& track) const;
132 
133     const bool mDeliverStatistics;
134 
135     // Actions is individually locked
136     AnalyticsActions mActions;
137 
138     // AnalyticsState is individually locked, and we use SharedPtrWrap
139     // to allow safe access even if the shared pointer changes underneath.
140     // These wrap pointers always point to a valid state object.
141     SharedPtrWrap<AnalyticsState> mAnalyticsState;
142     SharedPtrWrap<AnalyticsState> mPreviousAnalyticsState;
143 
144     TimedAction mTimedAction; // locked internally
145     const std::shared_ptr<StatsdLog> mStatsdLog; // locked internally, ok for multiple threads.
146 
147     static constexpr size_t kHeatEntries = 100;
148     HeatMap mHeatMap{kHeatEntries}; // locked internally, ok for multiple threads.
149 
150     // DeviceUse is a nested class which handles audio device usage accounting.
151     // We define this class at the end to ensure prior variables all properly constructed.
152     // TODO: Track / Thread interaction
153     // TODO: Consider statistics aggregation.
154     class DeviceUse {
155     public:
156         enum ItemType {
157             RECORD = 0,
158             THREAD = 1,
159             TRACK = 2,
160         };
161 
DeviceUse(AudioAnalytics & audioAnalytics)162         explicit DeviceUse(AudioAnalytics &audioAnalytics) : mAudioAnalytics{audioAnalytics} {}
163 
164         // Called every time an endAudioIntervalGroup message is received.
165         void endAudioIntervalGroup(
166                 const std::shared_ptr<const android::mediametrics::Item> &item,
167                 ItemType itemType) const;
168 
169     private:
170         AudioAnalytics &mAudioAnalytics;
171     } mDeviceUse{*this};
172 
173     // DeviceConnected is a nested class which handles audio device connection
174     // We define this class at the end to ensure prior variables all properly constructed.
175     // TODO: Track / Thread interaction
176     // TODO: Consider statistics aggregation.
177     class DeviceConnection {
178     public:
DeviceConnection(AudioAnalytics & audioAnalytics)179         explicit DeviceConnection(AudioAnalytics &audioAnalytics)
180             : mAudioAnalytics{audioAnalytics} {}
181 
182         // Called every time an endAudioIntervalGroup message is received.
183         void a2dpConnected(
184                 const std::shared_ptr<const android::mediametrics::Item> &item);
185 
186         // Called when we have an AudioFlinger createPatch
187         void createPatch(
188                 const std::shared_ptr<const android::mediametrics::Item> &item);
189 
190         // Called through AudioManager when the BT service wants to notify connection
191         void postBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(
192                 const std::shared_ptr<const android::mediametrics::Item> &item);
193 
194         // When the timer expires.
195         void expire();
196 
197     private:
198         AudioAnalytics &mAudioAnalytics;
199 
200         mutable std::mutex mLock;
201         std::string mA2dpDeviceName;
202         int64_t mA2dpConnectionRequestNs GUARDED_BY(mLock) = 0;  // Time for BT service request.
203         int64_t mA2dpConnectionServiceNs GUARDED_BY(mLock) = 0;  // Time audio service agrees.
204 
205         int32_t mA2dpConnectionRequests GUARDED_BY(mLock) = 0;
206         int32_t mA2dpConnectionServices GUARDED_BY(mLock) = 0;
207 
208         // See the statsd atoms.proto
209         int32_t mA2dpConnectionSuccesses GUARDED_BY(mLock) = 0;
210         int32_t mA2dpConnectionJavaServiceCancels GUARDED_BY(mLock) = 0;
211         int32_t mA2dpConnectionUnknowns GUARDED_BY(mLock) = 0;
212     } mDeviceConnection{*this};
213 
214     // AAudioStreamInfo is a nested class which collect aaudio stream info from both client and
215     // server side.
216     class AAudioStreamInfo {
217     public:
218         // All the enum here must be kept the same as the ones defined in atoms.proto
219         enum CallerPath {
220             CALLER_PATH_UNKNOWN = 0,
221             CALLER_PATH_LEGACY = 1,
222             CALLER_PATH_MMAP = 2,
223         };
224 
AAudioStreamInfo(AudioAnalytics & audioAnalytics)225         explicit AAudioStreamInfo(AudioAnalytics &audioAnalytics)
226             : mAudioAnalytics(audioAnalytics) {}
227 
228         void endAAudioStream(
229                 const std::shared_ptr<const android::mediametrics::Item> &item,
230                 CallerPath path) const;
231 
232     private:
233 
234         AudioAnalytics &mAudioAnalytics;
235     } mAAudioStreamInfo{*this};
236 
237     AudioPowerUsage mAudioPowerUsage;
238 };
239 
240 } // namespace android::mediametrics
241