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 #include "include/StatsEventCompat.h"
18 
19 #include <chrono>
20 
21 #include <android-base/chrono_utils.h>
22 #include <android-base/properties.h>
23 #include <android/api-level.h>
24 #include <android/log.h>
25 #include <dlfcn.h>
26 
27 using android::base::boot_clock;
28 using android::base::GetProperty;
29 
30 const static int kStatsEventTag = 1937006964;
31 const bool StatsEventCompat::mPlatformAtLeastR =
32         android_get_device_api_level() >= __ANDROID_API_R__;
33 
34 // initializations of static class variables
35 bool StatsEventCompat::mAttemptedLoad = false;
36 std::mutex StatsEventCompat::mLoadLock;
37 AStatsEventApi StatsEventCompat::mAStatsEventApi;
38 
elapsedRealtimeNano()39 static int64_t elapsedRealtimeNano() {
40     return std::chrono::time_point_cast<std::chrono::nanoseconds>(boot_clock::now())
41             .time_since_epoch()
42             .count();
43 }
44 
StatsEventCompat()45 StatsEventCompat::StatsEventCompat() : mEventQ(kStatsEventTag) {
46     // guard loading because StatsEventCompat might be called from multithreaded
47     // environment
48     {
49         std::lock_guard<std::mutex> lg(mLoadLock);
50         if (!mAttemptedLoad && mPlatformAtLeastR) {
51             void* handle = dlopen("libstatssocket.so", RTLD_NOW);
52             if (handle) {
53                 initializeApiTableLocked(handle);
54             } else {
55                 ALOGE("dlopen failed: %s\n", dlerror());
56             }
57         }
58         mAttemptedLoad = true;
59     }
60 
61     if (useRSchema()) {
62         mEventR = mAStatsEventApi.obtain();
63     } else if (useQSchema()) {
64         mEventQ << elapsedRealtimeNano();
65     }
66 }
67 
~StatsEventCompat()68 StatsEventCompat::~StatsEventCompat() {
69     if (useRSchema()) mAStatsEventApi.release(mEventR);
70 }
71 
72 // Populates the AStatsEventApi struct by calling dlsym to find the address of
73 // each API function.
initializeApiTableLocked(void * handle)74 void StatsEventCompat::initializeApiTableLocked(void* handle) {
75     mAStatsEventApi.obtain = (AStatsEvent* (*)())dlsym(handle, "AStatsEvent_obtain");
76     mAStatsEventApi.build = (void (*)(AStatsEvent*))dlsym(handle, "AStatsEvent_build");
77     mAStatsEventApi.write = (int (*)(AStatsEvent*))dlsym(handle, "AStatsEvent_write");
78     mAStatsEventApi.release = (void (*)(AStatsEvent*))dlsym(handle, "AStatsEvent_release");
79     mAStatsEventApi.setAtomId =
80             (void (*)(AStatsEvent*, uint32_t))dlsym(handle, "AStatsEvent_setAtomId");
81     mAStatsEventApi.writeInt32 =
82             (void (*)(AStatsEvent*, int32_t))dlsym(handle, "AStatsEvent_writeInt32");
83     mAStatsEventApi.writeInt64 =
84             (void (*)(AStatsEvent*, int64_t))dlsym(handle, "AStatsEvent_writeInt64");
85     mAStatsEventApi.writeFloat =
86             (void (*)(AStatsEvent*, float))dlsym(handle, "AStatsEvent_writeFloat");
87     mAStatsEventApi.writeBool =
88             (void (*)(AStatsEvent*, bool))dlsym(handle, "AStatsEvent_writeBool");
89     mAStatsEventApi.writeByteArray = (void (*)(AStatsEvent*, const uint8_t*, size_t))dlsym(
90             handle, "AStatsEvent_writeByteArray");
91     mAStatsEventApi.writeString =
92             (void (*)(AStatsEvent*, const char*))dlsym(handle, "AStatsEvent_writeString");
93     mAStatsEventApi.writeAttributionChain =
94             (void (*)(AStatsEvent*, const uint32_t*, const char* const*, uint8_t))dlsym(
95                     handle, "AStatsEvent_writeAttributionChain");
96     mAStatsEventApi.addBoolAnnotation =
97             (void (*)(AStatsEvent*, uint8_t, bool))dlsym(handle, "AStatsEvent_addBoolAnnotation");
98     mAStatsEventApi.addInt32Annotation = (void (*)(AStatsEvent*, uint8_t, int32_t))dlsym(
99             handle, "AStatsEvent_addInt32Annotation");
100 
101     mAStatsEventApi.initialized = true;
102 }
103 
setAtomId(int32_t atomId)104 void StatsEventCompat::setAtomId(int32_t atomId) {
105     if (useRSchema()) {
106         mAStatsEventApi.setAtomId(mEventR, (uint32_t)atomId);
107     } else if (useQSchema()) {
108         mEventQ << atomId;
109     }
110 }
111 
writeInt32(int32_t value)112 void StatsEventCompat::writeInt32(int32_t value) {
113     if (useRSchema()) {
114         mAStatsEventApi.writeInt32(mEventR, value);
115     } else if (useQSchema()) {
116         mEventQ << value;
117     }
118 }
119 
writeInt64(int64_t value)120 void StatsEventCompat::writeInt64(int64_t value) {
121     if (useRSchema()) {
122         mAStatsEventApi.writeInt64(mEventR, value);
123     } else if (useQSchema()) {
124         mEventQ << value;
125     }
126 }
127 
writeFloat(float value)128 void StatsEventCompat::writeFloat(float value) {
129     if (useRSchema()) {
130         mAStatsEventApi.writeFloat(mEventR, value);
131     } else if (useQSchema()) {
132         mEventQ << value;
133     }
134 }
135 
writeBool(bool value)136 void StatsEventCompat::writeBool(bool value) {
137     if (useRSchema()) {
138         mAStatsEventApi.writeBool(mEventR, value);
139     } else if (useQSchema()) {
140         mEventQ << value;
141     }
142 }
143 
writeByteArray(const char * buffer,size_t length)144 void StatsEventCompat::writeByteArray(const char* buffer, size_t length) {
145     if (useRSchema()) {
146         mAStatsEventApi.writeByteArray(mEventR, reinterpret_cast<const uint8_t*>(buffer), length);
147     } else if (useQSchema()) {
148         mEventQ.AppendCharArray(buffer, length);
149     }
150 }
151 
writeString(const char * value)152 void StatsEventCompat::writeString(const char* value) {
153     if (value == nullptr) value = "";
154 
155     if (useRSchema()) {
156         mAStatsEventApi.writeString(mEventR, value);
157     } else if (useQSchema()) {
158         mEventQ << value;
159     }
160 }
161 
writeAttributionChain(const int32_t * uids,size_t numUids,const vector<const char * > & tags)162 void StatsEventCompat::writeAttributionChain(const int32_t* uids, size_t numUids,
163                                              const vector<const char*>& tags) {
164     if (useRSchema()) {
165         mAStatsEventApi.writeAttributionChain(mEventR, (const uint32_t*)uids, tags.data(),
166                                               (uint8_t)numUids);
167     } else if (useQSchema()) {
168         mEventQ.begin();
169         for (size_t i = 0; i < numUids; i++) {
170             mEventQ.begin();
171             mEventQ << uids[i];
172             const char* tag = tags[i] ? tags[i] : "";
173             mEventQ << tag;
174             mEventQ.end();
175         }
176         mEventQ.end();
177     }
178 }
179 
writeKeyValuePairs(const map<int,int32_t> & int32Map,const map<int,int64_t> & int64Map,const map<int,const char * > & stringMap,const map<int,float> & floatMap)180 void StatsEventCompat::writeKeyValuePairs(const map<int, int32_t>& int32Map,
181                                           const map<int, int64_t>& int64Map,
182                                           const map<int, const char*>& stringMap,
183                                           const map<int, float>& floatMap) {
184     // AStatsEvent does not support key value pairs.
185     if (useQSchema()) {
186         mEventQ.begin();
187         writeKeyValuePairMap(int32Map);
188         writeKeyValuePairMap(int64Map);
189         writeKeyValuePairMap(stringMap);
190         writeKeyValuePairMap(floatMap);
191         mEventQ.end();
192     }
193 }
194 
195 template <class T>
writeKeyValuePairMap(const map<int,T> & keyValuePairMap)196 void StatsEventCompat::writeKeyValuePairMap(const map<int, T>& keyValuePairMap) {
197     for (const auto& it : keyValuePairMap) {
198         mEventQ.begin();
199         mEventQ << it.first;
200         mEventQ << it.second;
201         mEventQ.end();
202     }
203 }
204 
205 // explicitly specify which types we're going to use
206 template void StatsEventCompat::writeKeyValuePairMap<int32_t>(const map<int, int32_t>&);
207 template void StatsEventCompat::writeKeyValuePairMap<int64_t>(const map<int, int64_t>&);
208 template void StatsEventCompat::writeKeyValuePairMap<float>(const map<int, float>&);
209 template void StatsEventCompat::writeKeyValuePairMap<const char*>(const map<int, const char*>&);
210 
addBoolAnnotation(uint8_t annotationId,bool value)211 void StatsEventCompat::addBoolAnnotation(uint8_t annotationId, bool value) {
212     if (useRSchema()) {
213         mAStatsEventApi.addBoolAnnotation(mEventR, annotationId, value);
214     }
215     // Don't do anything if on Q.
216 }
217 
addInt32Annotation(uint8_t annotationId,int32_t value)218 void StatsEventCompat::addInt32Annotation(uint8_t annotationId, int32_t value) {
219     if (useRSchema()) {
220         mAStatsEventApi.addInt32Annotation(mEventR, annotationId, value);
221     }
222     // Don't do anything if on Q.
223 }
224 
writeToSocket()225 int StatsEventCompat::writeToSocket() {
226     if (useRSchema()) {
227         return mAStatsEventApi.write(mEventR);
228     }
229 
230     if (useQSchema()) return mEventQ.write(LOG_ID_STATS);
231 
232     // We reach here only if we're on R, but libstatssocket was unable to
233     // be loaded using dlopen.
234     return -ENOLINK;
235 }
236 
useRSchema()237 bool StatsEventCompat::useRSchema() {
238     return mPlatformAtLeastR && mAStatsEventApi.initialized;
239 }
240 
useQSchema()241 bool StatsEventCompat::useQSchema() {
242     return !mPlatformAtLeastR;
243 }
244