1 /*
2 * Copyright (C) 2020 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 #define LOG_TAG "pixelstats-wlc"
18
19 #include <android-base/file.h>
20 #include <android-base/strings.h>
21 #include <hardware/google/pixel/pixelstats/pixelatoms.pb.h>
22 #include <log/log.h>
23 #include <pixelstats/OrientationCollector.h>
24 #include <pixelstats/WlcReporter.h>
25 #include <sys/timerfd.h>
26 #include <time.h>
27 #include <utils/Timers.h>
28
29 #include <thread>
30
31 /* I set a higher rare limit ti orientation, beacuae user might try to adjust
32 * orientation when start charge
33 **/
34 #define GOOGLE_PTMC_ID (0x72)
35 #define ID_UNKNOWN (0)
36 #define WLC_VENDOR_REPORT_RATE_LIMIT_MIN_SEC (60 * 60)
37 #define WLC_VENDOR_REPORT_RATE_LIMIT_MAX_COUNT_PER_DAY (10)
38 #define ORIENTATION_REPORT_RATE_LIMIT_MIN_SEC (0)
39 #define ORIENTATION_REPORT_RATE_LIMIT_MAX_COUNT_PER_DAY (10)
40 #define DAY_SECOND (86400)
41
42 namespace android {
43 namespace hardware {
44 namespace google {
45 namespace pixel {
46
47 using aidl::android::frameworks::stats::IStats;
48 using aidl::android::frameworks::stats::VendorAtom;
49 using aidl::android::frameworks::stats::VendorAtomValue;
50 using android::base::ReadFileToString;
51
WlcStatus()52 WlcReporter::WlcStatus::WlcStatus()
53 : is_charging(false),
54 check_charger_vendor_id(false),
55 check_charger_vendor_id_scheduled(false),
56 check_vendor_id_attempts(0) {}
57
ReportRecord(char const * name_)58 WlcReporter::ReportRecord::ReportRecord(char const *name_)
59 : name(name_),
60 last_reported_time_in_sec_today(0),
61 last_reported_time_in_sec(0),
62 count_today(0) {}
63
WlcReporter(const char * ptmc_path)64 WlcReporter::WlcReporter(const char *ptmc_path) : kWirelessChargerPtmcPath(ptmc_path) {}
checkAndReport(const std::shared_ptr<IStats> & stats_client,bool online,const char * ptmc_uevent)65 void WlcReporter::checkAndReport(const std::shared_ptr<IStats> &stats_client, bool online,
66 const char *ptmc_uevent) {
67 bool wireless_charging = online;
68 bool started_wireless_charging = wireless_charging && !wlc_status_.is_charging;
69 wlc_status_.is_charging = wireless_charging;
70 ALOGV("reportVendorId is_charging: %s, started_wireless_charging: %s",
71 (wireless_charging) ? "true" : "false", (started_wireless_charging) ? "true" : "false");
72
73 if (started_wireless_charging) {
74 reportOrientation(stats_client);
75 wlc_status_.check_vendor_id_attempts = 0;
76 if (checkRateLimit(WLC_VENDOR_REPORT_RATE_LIMIT_MIN_SEC,
77 WLC_VENDOR_REPORT_RATE_LIMIT_MAX_COUNT_PER_DAY, &rec_wlc_vendor_)) {
78 wlc_status_.check_charger_vendor_id = true;
79 if (kWirelessChargerPtmcPath != nullptr && strlen(kWirelessChargerPtmcPath) != 0) {
80 scheduleReportVendorId(stats_client);
81 } else {
82 ALOGV("ptmc_path not set");
83 }
84 }
85 }
86 if (!wireless_charging) {
87 wlc_status_.check_charger_vendor_id = false;
88 }
89 if (wireless_charging) {
90 checkVendorId(stats_client, ptmc_uevent);
91 }
92 }
93
checkVendorId(const std::shared_ptr<IStats> & stats_client,const char * ptmc_uevent)94 void WlcReporter::checkVendorId(const std::shared_ptr<IStats> &stats_client,
95 const char *ptmc_uevent) {
96 if (!ptmc_uevent || !wlc_status_.check_charger_vendor_id) {
97 return;
98 }
99 if (reportVendorMayRetry(stats_client, ptmc_uevent)) {
100 wlc_status_.check_charger_vendor_id = false;
101 }
102 }
103
reportVendorMayRetry(const std::shared_ptr<IStats> & stats_client,const char * ptmc_uevent)104 bool WlcReporter::reportVendorMayRetry(const std::shared_ptr<IStats> &stats_client,
105 const char *ptmc_uevent) {
106 int ptmcId = readPtmcId(ptmc_uevent);
107 if (ptmcId == ID_UNKNOWN) {
108 if (++(wlc_status_.check_vendor_id_attempts) < kMaxVendorIdAttempts) {
109 return false;
110 } /* else if ptmc not ready after retry assume ptmc not supported by charger */
111 }
112 ALOGV("reportVendorId from Uevent");
113 reportVendor(stats_client, ptmcId);
114 return true;
115 }
reportVendor(const std::shared_ptr<IStats> & stats_client,const int ptmcId)116 void WlcReporter::reportVendor(const std::shared_ptr<IStats> &stats_client, const int ptmcId) {
117 int vendorCharger = (ptmcId == GOOGLE_PTMC_ID)
118 ? PixelAtoms::WirelessChargingStats::VENDOR_GOOGLE
119 : PixelAtoms::WirelessChargingStats::VENDOR_UNKNOWN;
120 ALOGV("ptmcId: 0x%x; vendorCharger: %d", ptmcId, vendorCharger);
121 VendorAtomValue tmp;
122 tmp.set<VendorAtomValue::intValue>(vendorCharger);
123
124 std::vector<VendorAtomValue> values(1);
125 values[PixelAtoms::WirelessChargingStats::kChargerVendorFieldNumber - kVendorAtomOffset] = tmp;
126
127 // Send vendor atom to IStats HAL
128 VendorAtom event = {.reverseDomainName = PixelAtoms::ReverseDomainNames().pixel(),
129 .atomId = PixelAtoms::Atom::kWirelessChargingStats,
130 .values = std::move(values)};
131 const ndk::ScopedAStatus retStat = stats_client->reportVendorAtom(event);
132 if (!retStat.isOk()) {
133 ALOGE("Unable to report WLC_STATS to Stats service");
134 }
135 return;
136 }
137
readPtmcId(const char * ptmc_str)138 int WlcReporter::readPtmcId(const char *ptmc_str) {
139 int id;
140 if (sscanf(ptmc_str, "%x", &id) != 1) {
141 return ID_UNKNOWN;
142 }
143 return id;
144 }
145
scheduleReportVendorId(const std::shared_ptr<IStats> & stats_client)146 void WlcReporter::scheduleReportVendorId(const std::shared_ptr<IStats> &stats_client) {
147 if (wlc_status_.check_charger_vendor_id_scheduled) {
148 return;
149 }
150
151 wlc_status_.check_charger_vendor_id_scheduled = true;
152 std::thread taskThread(&WlcReporter::reportInBackground, this, stats_client,
153 kWirelessChargerPtmcPath);
154 taskThread.detach();
155 }
156
ptmcWaitTimer(int timerfd)157 bool WlcReporter::ptmcWaitTimer(int timerfd) {
158 const int kDelaytimeBeforeReadPtmcId = 60;
159 struct itimerspec period;
160 period.it_interval.tv_sec = 0;
161 period.it_interval.tv_nsec = 0;
162 period.it_value.tv_sec = kDelaytimeBeforeReadPtmcId;
163 period.it_value.tv_nsec = 0;
164
165 if (timerfd_settime(timerfd, 0, &period, nullptr)) {
166 ALOGE("Unable to set timer - %s", strerror(errno));
167 return false;
168 }
169 int readval;
170 do {
171 char buf[8];
172 errno = 0;
173 readval = read(timerfd, buf, sizeof(buf));
174 } while (readval < 0 && errno == EINTR);
175 if (readval < 0) {
176 ALOGE("Timerfd error - %s", strerror(errno));
177 return false;
178 }
179 return true;
180 }
181
182 /*
183 * PTMC path use to sore wireless charger vendor id,
184 * and it take some time to get ready after wireless chagre start,
185 * to prevnt busy wait, I use timer and background thread to check PTMC ID
186 **/
reportInBackground(const std::shared_ptr<IStats> & stats_client,const char * ptmc_path)187 void WlcReporter::reportInBackground(const std::shared_ptr<IStats> &stats_client,
188 const char *ptmc_path) {
189 int timerfd = timerfd_create(CLOCK_BOOTTIME, 0);
190 if (timerfd < 0) {
191 ALOGE("Unable to create timerfd - %s", strerror(errno));
192 return;
193 }
194 if (ptmcWaitTimer(timerfd)) {
195 if (!wlc_status_.is_charging) {
196 ALOGV("Not charging, skip report vender id");
197 } else if (!wlc_status_.check_charger_vendor_id) {
198 ALOGV("id reported by uevnt, skip report vender id");
199 } else {
200 std::string file_contents;
201 if (!ReadFileToString(ptmc_path, &file_contents)) {
202 ALOGE("ReadFileToString %s fail", ptmc_path);
203 } else {
204 int ptmcId = readPtmcId(file_contents.c_str());
205 ALOGV("reportVendorId from file");
206 reportVendor(stats_client, ptmcId);
207 }
208 }
209 }
210 wlc_status_.check_charger_vendor_id_scheduled = false;
211 close(timerfd);
212 }
213
214 /* Reference to frameworks/native/libs/ui/include/ui/DisplayInfo.h
215 * translate orientation value from sensor to enum define in
216 * pixelatoms.proto
217 */
translateDeviceOrientationToAtomValue(int orientation)218 int WlcReporter::translateDeviceOrientationToAtomValue(int orientation) {
219 switch (orientation) {
220 case 0:
221 return PixelAtoms::DeviceOrientation::ORIENTATION_0;
222 case 1:
223 return PixelAtoms::DeviceOrientation::ORIENTATION_90;
224 case 2:
225 return PixelAtoms::DeviceOrientation::ORIENTATION_180;
226 case 3:
227 return PixelAtoms::DeviceOrientation::ORIENTATION_270;
228 default:
229 return PixelAtoms::DeviceOrientation::ORIENTATION_UNKNOWN;
230 }
231 }
232
reportOrientation(const std::shared_ptr<IStats> & stats_client)233 void WlcReporter::reportOrientation(const std::shared_ptr<IStats> &stats_client) {
234 ALOGV("reportOrientation");
235 if (!checkRateLimit(ORIENTATION_REPORT_RATE_LIMIT_MIN_SEC,
236 ORIENTATION_REPORT_RATE_LIMIT_MAX_COUNT_PER_DAY, &rec_orientation_)) {
237 return;
238 }
239 sp<OrientationCollector> orientationCollector =
240 OrientationCollector::createOrientationCollector();
241 if (orientationCollector != nullptr) {
242 int orientationFromSensor = -1;
243 orientationCollector->pollOrientation(&orientationFromSensor);
244 VendorAtomValue tmp;
245 tmp.set<VendorAtomValue::intValue>(
246 translateDeviceOrientationToAtomValue(orientationFromSensor));
247
248 std::vector<VendorAtomValue> values(1);
249 values[PixelAtoms::DeviceOrientation::kOrientationFieldNumber - kVendorAtomOffset] = tmp;
250
251 VendorAtom event = {.reverseDomainName = PixelAtoms::ReverseDomainNames().pixel(),
252 .atomId = PixelAtoms::Atom::kDeviceOrientation,
253 .values = std::move(values)};
254 const ndk::ScopedAStatus retStat = stats_client->reportVendorAtom(event);
255 if (!retStat.isOk()) {
256 ALOGE("Unable to report Orientation to Stats service");
257 }
258 orientationCollector->disableOrientationSensor();
259 }
260 }
261
checkRateLimit(int64_t minSecond,int maxCount,ReportRecord * rec)262 bool WlcReporter::checkRateLimit(int64_t minSecond, int maxCount, ReportRecord *rec) {
263 if (rec == nullptr) {
264 ALOGE("ReportRecord should not be NULL");
265 return false;
266 }
267 int64_t now = nanoseconds_to_seconds(systemTime(SYSTEM_TIME_BOOTTIME));
268 if (rec->last_reported_time_in_sec > 0 && now - rec->last_reported_time_in_sec < minSecond) {
269 ALOGV("%s: Rate limit, min period: %ld", rec->name, minSecond);
270 return false;
271 }
272 if (rec->last_reported_time_in_sec_today == 0) {
273 rec->last_reported_time_in_sec_today = now;
274 ALOGV("%s: reset day time (init)", rec->name);
275 } else if (now - rec->last_reported_time_in_sec_today > DAY_SECOND) {
276 rec->last_reported_time_in_sec_today = now;
277 rec->count_today = 0;
278 ALOGV("%s: reset day time", rec->name);
279 } else if (rec->count_today >= maxCount) {
280 ALOGV("%s: Rate limit, max count: %d", rec->name, maxCount);
281 return false;
282 }
283 (rec->count_today)++;
284 ALOGV("%s: checkRateLimit count: %d", rec->name, rec->count_today);
285 rec->last_reported_time_in_sec = now;
286 return true;
287 }
288 } // namespace pixel
289 } // namespace google
290 } // namespace hardware
291 } // namespace android
292