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 <audio_utils/SimpleLog.h>
21 #include "AnalyticsActions.h"
22 #include "AnalyticsState.h"
23 #include "AudioPowerUsage.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     AudioAnalytics();
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 
126     SimpleLog mStatsdLog{16 /* log lines */}; // locked internally
127 
128     // DeviceUse is a nested class which handles audio device usage accounting.
129     // We define this class at the end to ensure prior variables all properly constructed.
130     // TODO: Track / Thread interaction
131     // TODO: Consider statistics aggregation.
132     class DeviceUse {
133     public:
134         enum ItemType {
135             RECORD = 0,
136             THREAD = 1,
137             TRACK = 2,
138         };
139 
DeviceUse(AudioAnalytics & audioAnalytics)140         explicit DeviceUse(AudioAnalytics &audioAnalytics) : mAudioAnalytics{audioAnalytics} {}
141 
142         // Called every time an endAudioIntervalGroup message is received.
143         void endAudioIntervalGroup(
144                 const std::shared_ptr<const android::mediametrics::Item> &item,
145                 ItemType itemType) const;
146 
147     private:
148         AudioAnalytics &mAudioAnalytics;
149     } mDeviceUse{*this};
150 
151     // DeviceConnected is a nested class which handles audio device connection
152     // We define this class at the end to ensure prior variables all properly constructed.
153     // TODO: Track / Thread interaction
154     // TODO: Consider statistics aggregation.
155     class DeviceConnection {
156     public:
DeviceConnection(AudioAnalytics & audioAnalytics)157         explicit DeviceConnection(AudioAnalytics &audioAnalytics)
158             : mAudioAnalytics{audioAnalytics} {}
159 
160         // Called every time an endAudioIntervalGroup message is received.
161         void a2dpConnected(
162                 const std::shared_ptr<const android::mediametrics::Item> &item);
163 
164         // Called when we have an AudioFlinger createPatch
165         void createPatch(
166                 const std::shared_ptr<const android::mediametrics::Item> &item);
167 
168         // Called through AudioManager when the BT service wants to notify connection
169         void postBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(
170                 const std::shared_ptr<const android::mediametrics::Item> &item);
171 
172         // When the timer expires.
173         void expire();
174 
175     private:
176         AudioAnalytics &mAudioAnalytics;
177 
178         mutable std::mutex mLock;
179         std::string mA2dpDeviceName;
180         int64_t mA2dpConnectionRequestNs GUARDED_BY(mLock) = 0;  // Time for BT service request.
181         int64_t mA2dpConnectionServiceNs GUARDED_BY(mLock) = 0;  // Time audio service agrees.
182 
183         int32_t mA2dpConnectionRequests GUARDED_BY(mLock) = 0;
184         int32_t mA2dpConnectionServices GUARDED_BY(mLock) = 0;
185 
186         // See the statsd atoms.proto
187         int32_t mA2dpConnectionSuccesses GUARDED_BY(mLock) = 0;
188         int32_t mA2dpConnectionJavaServiceCancels GUARDED_BY(mLock) = 0;
189         int32_t mA2dpConnectionUnknowns GUARDED_BY(mLock) = 0;
190     } mDeviceConnection{*this};
191 
192     AudioPowerUsage mAudioPowerUsage{this};
193 };
194 
195 } // namespace android::mediametrics
196