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