1 /******************************************************************************
2 *
3 * Copyright 2018 NXP
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *
17 ******************************************************************************/
18 #define LOG_TAG "NfccPowerTracker"
19 #include "NfccPowerTracker.h"
20 #include <assert.h>
21 #include <log/log.h>
22 #include <stdio.h>
23 #include <sys/file.h>
24 #include <sys/time.h>
25 #include <fstream>
26 #include <iostream>
27 #include <sstream>
28 #include "phNxpNciHal_ext.h"
29 using namespace std;
30
31 extern bool nfc_debug_enabled;
32 extern phNxpNciHal_Control_t nxpncihal_ctrl;
33 static const uint64_t PWR_TRK_ERROR_MARGIN_IN_MILLISEC = 60000;
34 static const std::string POWER_TRACKER_LOG_FILE =
35 "/data/vendor/nfc/nfc_power_state.txt";
36 static const uint16_t TIMER_COUNT_MASK = 0x7FFF;
37
NfccPowerTracker()38 NfccPowerTracker::NfccPowerTracker() {
39 mIsLastUpdateScreenOn = false;
40 mIsFirstPwrTrkNtfRecvd = false;
41 mLastPowerTrackAborted = false;
42 /*Default standby time*/
43 mStandbyTimePerDiscLoopInMillisec = 1000;
44 }
~NfccPowerTracker()45 NfccPowerTracker::~NfccPowerTracker() {}
46
47 /*******************************************************************************
48 **
49 ** Function NfccPowerTracker::getInstance
50 **
51 ** Description access class singleton
52 **
53 ** Returns pointer to the singleton object
54 **
55 *******************************************************************************/
getInstance()56 NfccPowerTracker& NfccPowerTracker::getInstance() {
57 static NfccPowerTracker sPwrInstance;
58 return sPwrInstance;
59 }
60 /*******************************************************************************
61 **
62 ** Function Initialize
63 **
64 ** Description get all prerequisite information from NFCC needed for
65 ** Power tracker calculations.
66 **
67 ** Returns void
68 **
69 *******************************************************************************/
Initialize()70 void NfccPowerTracker::Initialize() {
71 /*get total duration of discovery loop from NFCC using GET CONFIG command*/
72 uint8_t cmdGetConfigDiscLoopDuration[] = {0x20, 0x03, 0x02, 0x01, 0x00};
73 int status = phNxpNciHal_send_ext_cmd(sizeof(cmdGetConfigDiscLoopDuration),
74 cmdGetConfigDiscLoopDuration);
75 if (status != 0) {
76 ALOGD_IF(nfc_debug_enabled, "NfccPowerTracker::Initialize: failed");
77 return;
78 }
79 /*Check for valid get config response and update stanby time*/
80 if (nxpncihal_ctrl.p_rx_data[0] == 0x40 &&
81 nxpncihal_ctrl.p_rx_data[1] == 0x03 &&
82 nxpncihal_ctrl.p_rx_data[2] == 0x06 &&
83 nxpncihal_ctrl.p_rx_data[3] == 0x00 &&
84 nxpncihal_ctrl.p_rx_data[4] == 0x01 &&
85 nxpncihal_ctrl.p_rx_data[5] == 0x00 &&
86 nxpncihal_ctrl.p_rx_data[6] == 0x02) {
87 mStandbyTimePerDiscLoopInMillisec = (uint32_t)(
88 (nxpncihal_ctrl.p_rx_data[8] << 8) | nxpncihal_ctrl.p_rx_data[7]);
89 ALOGD_IF(nfc_debug_enabled, "mStandbyTimePerDiscLoopInMillisec value : %d",
90 mStandbyTimePerDiscLoopInMillisec);
91 }
92 }
93
94 /*******************************************************************************
95 **
96 ** Function TimeDiff
97 **
98 ** Description Computes time difference in milliseconds.
99 **
100 ** Returns Time difference in milliseconds
101 **
102 *******************************************************************************/
TimeDiff(struct timespec start,struct timespec end)103 uint64_t NfccPowerTracker::TimeDiff(struct timespec start,
104 struct timespec end) {
105 uint64_t startTimeInMillisec =
106 start.tv_sec * 1000 + (start.tv_nsec / 1000000);
107 uint64_t endTimeInMillisec = end.tv_sec * 1000 + (end.tv_nsec / 1000000);
108
109 assert(startTimeInMillisec > endTimeInMillisec);
110 return (endTimeInMillisec - startTimeInMillisec);
111 }
112
113 /*******************************************************************************
114 **
115 ** Function NfccPowerTracker::ProcessCmd
116 **
117 ** Description Parse the commands going to NFCC,
118 ** get the time at which power relevant commands are sent
119 ** (ex:Screen state/OMAPI session)is sent and
120 ** log/cache the timestamp to file
121 **
122 ** Returns void
123 **
124 *******************************************************************************/
ProcessCmd(uint8_t * cmd,uint16_t len)125 void NfccPowerTracker::ProcessCmd(uint8_t* cmd, uint16_t len) {
126 ALOGD_IF(nfc_debug_enabled,
127 "NfccPowerTracker::ProcessCmd: Enter, Received len :%d", len);
128 bool screenStateCommand;
129
130 if (len < 4) {
131 android_errorWriteLog(0x534e4554, "153879824");
132 return;
133 }
134 if (cmd[0] == 0x20 && cmd[1] == 0x09) {
135 screenStateCommand = true;
136 } else {
137 screenStateCommand = false;
138 if (len < 8) {
139 android_errorWriteLog(0x534e4554, "153879824");
140 return;
141 }
142 }
143
144 if (screenStateCommand && (cmd[3] == 0x00 || cmd[3] == 0x02)) {
145 /* Command for Screen State On-Locked or Unlocked */
146 clock_gettime(CLOCK_BOOTTIME, &mLastScreenOnTimeStamp);
147 mIsLastUpdateScreenOn = true;
148 } else if (screenStateCommand && (cmd[3] == 0x01 || cmd[3] == 0x03)) {
149 /* Command for Screen State OFF-locked or Unlocked */
150 clock_gettime(CLOCK_BOOTTIME, &mLastScreenOffTimeStamp);
151 mIsLastUpdateScreenOn = false;
152 } else if (cmd[0] == 0x20 && cmd[1] == 0x02 && cmd[2] == 0x05 &&
153 cmd[3] == 0x01 && cmd[4] == 0x00 && cmd[5] == 0x02) {
154 /* Command to update duration of discovery loop */
155 mStandbyTimePerDiscLoopInMillisec = (cmd[7] << 8 | cmd[6]);
156 ALOGD_IF(nfc_debug_enabled, "mStandbyTimePerDiscLoopInMillisec value : %d",
157 mStandbyTimePerDiscLoopInMillisec);
158 }
159 }
160
161 /*******************************************************************************
162 **
163 ** Function NfccPowerTracker::ProcessNtf
164 **
165 ** Description Parse the Notifications coming from NFCC,
166 ** get the time at which power relevant notifications are
167 ** received
168 ** (ex:RF ON-OFF/ACTIVATE-DEACTIVATE NTF/PROP_PWR_TRACKINFO)
169 ** calculate error in standby time by comparing the
170 ** expectated value from NFC HAL and received value from NFCC.
171 ** Cache relevant info (timestamps) to file
172 **
173 ** Returns void
174 **
175 *******************************************************************************/
ProcessNtf(uint8_t * rsp,uint16_t rsp_len)176 void NfccPowerTracker::ProcessNtf(uint8_t* rsp, uint16_t rsp_len) {
177 ALOGD_IF(nfc_debug_enabled, "NfccPowerTracker::ProcessNtf: Enter");
178
179 /* Screen State Notification recieved */
180 if ((rsp[0] == 0x6F && rsp[1] == 0x05)) {
181 ProcessPowerTrackNtf(rsp, rsp_len);
182 } else if (rsp[0] == 0x61 && rsp[1] == 0x05) {
183 /*Activation notification received. Calculate the time NFCC is
184 active in Reader/P2P/CE duration */
185 clock_gettime(CLOCK_BOOTTIME, &mActiveTimeStart);
186 if (!mIsLastUpdateScreenOn) {
187 mActiveInfo.totalTransitions++;
188 }
189 } else if (rsp[0] == 0x61 && rsp[1] == 0x06) {
190 /* Deactivation notification received Calculate the time NFCC is
191 active in Reader/P2P/CE duration.Time between Activation and
192 Deacivation gives the active time*/
193 clock_gettime(CLOCK_BOOTTIME, &mActiveTimeEnd);
194 mActiveDurationFromLastScreenUpdate +=
195 TimeDiff(mActiveTimeStart, mActiveTimeEnd);
196 if (!mIsLastUpdateScreenOn) {
197 mStandbyInfo.totalTransitions++;
198 }
199 ALOGD_IF(nfc_debug_enabled, "mActiveDurationFromLastScreenUpdate: %llu",
200 (unsigned long long)mActiveDurationFromLastScreenUpdate);
201 }
202 }
203
204 /*******************************************************************************
205 **
206 ** Function ProcessPowerTrackNtf
207 **
208 ** Description Process Power Tracker notification and update timingInfo to
209 ** Log File.
210 **
211 ** Returns void
212 **
213 *******************************************************************************/
ProcessPowerTrackNtf(uint8_t * rsp,uint16_t rsp_len)214 void NfccPowerTracker::ProcessPowerTrackNtf(uint8_t* rsp, uint16_t rsp_len) {
215 /* Enable Power Tracking computations after 1st Power tracker notification
216 * is received. */
217 if (!mIsFirstPwrTrkNtfRecvd) {
218 mIsFirstPwrTrkNtfRecvd = true;
219 ifstream ifile(POWER_TRACKER_LOG_FILE.c_str());
220 if ((bool)ifile == true) {
221 mLastPowerTrackAborted = true;
222 }
223 return;
224 }
225
226 /*Duration between screen state change is taken as reference for calculating
227 active and standby time*/
228 uint64_t totalDuration = 0;
229 totalDuration =
230 mIsLastUpdateScreenOn
231 ? TimeDiff(mLastScreenOffTimeStamp, mLastScreenOnTimeStamp)
232 : TimeDiff(mLastScreenOnTimeStamp, mLastScreenOffTimeStamp);
233 if (totalDuration == 0) return;
234
235 /*Calculate Active and Standby time based on the pollCount provided in the
236 Power tracker Notification from NFCC*/
237 uint16_t sPollCount = (TIMER_COUNT_MASK & ((rsp[5] << 8) | rsp[4]));
238 ALOGD_IF(nfc_debug_enabled,
239 "Poll/Timer count recived from FW is %d and rsp_len :%d", sPollCount,
240 rsp_len);
241 uint64_t standbyTime = 0, activeTime = 0;
242 if (mIsLastUpdateScreenOn) {
243 activeTime = sPollCount * ACTIVE_TIME_PER_TIMER_COUNT_IN_MILLISEC;
244 /*Check for errors in count provided by NFCC*/
245 uint64_t error = (activeTime > mActiveDurationFromLastScreenUpdate)
246 ? (activeTime - mActiveDurationFromLastScreenUpdate)
247 : (mActiveDurationFromLastScreenUpdate - activeTime);
248 if (error > PWR_TRK_ERROR_MARGIN_IN_MILLISEC) {
249 ALOGD_IF(nfc_debug_enabled,
250 "Active Time Error observed with value is %llu",
251 (unsigned long long)error);
252 mErrorInStandbyInfo.residencyInMsecSinceBoot += error;
253 }
254 standbyTime = (totalDuration > activeTime) ? (totalDuration - activeTime)
255 : (activeTime - totalDuration);
256 if (rsp[3]) {
257 /*If notification trigger is counter overflow, update the screen on
258 timestamp as there is no screen state change*/
259 clock_gettime(CLOCK_BOOTTIME, &mLastScreenOnTimeStamp);
260 }
261 mActiveInfo.totalTransitions++;
262 } else {
263 standbyTime = (sPollCount * ((uint64_t)mStandbyTimePerDiscLoopInMillisec));
264 activeTime = totalDuration > standbyTime ? (totalDuration - standbyTime)
265 : (standbyTime - totalDuration);
266 if (rsp[3]) {
267 /*If notification trigger is counter overflow, update the screen off
268 timestamp as there is no screen state change*/
269 clock_gettime(CLOCK_BOOTTIME, &mLastScreenOffTimeStamp);
270 }
271 /*Total transitions in screen on -> Screen Off window is same as poll count
272 provided by NFCC, as, there is transition in each discovery loop*/
273 mActiveInfo.totalTransitions += sPollCount;
274 /*1 additional transition for screen state update*/
275 mStandbyInfo.totalTransitions += (sPollCount + 1);
276 }
277
278 ALOGD_IF(nfc_debug_enabled,
279 "activeTime: %llu, standbyTime: %llu, totalDuration :%llu",
280 (unsigned long long)activeTime, (unsigned long long)standbyTime,
281 (unsigned long long)totalDuration);
282 if (mLastPowerTrackAborted) {
283 ALOGD_IF(nfc_debug_enabled,
284 "Last Hal service aborted,so retrive the power info data and "
285 "continue\n");
286 /*Read the file content and store in mActiveInfo.residencyInMsecSinceBoot
287 and mStandbyInfo.residencyInMsecSinceBoot*/
288 if (ReadPowerStateLog()) {
289 mLastPowerTrackAborted = false;
290 }
291 }
292 mStandbyInfo.residencyInMsecSinceBoot += standbyTime;
293 mActiveInfo.residencyInMsecSinceBoot += activeTime;
294 UpdatePowerStateLog(mStandbyInfo, mActiveInfo);
295 mActiveDurationFromLastScreenUpdate = 0;
296 }
297 /*******************************************************************************
298 **
299 ** Function NfccPowerTracker::UpdatePowerStateLog
300 **
301 ** Description update the powerstate related information in log file
302 **
303 ** Returns void
304 **
305 *******************************************************************************/
UpdatePowerStateLog(NfccPowerStateInfo_t mStandbyInfo,NfccPowerStateInfo_t mActiveInfo)306 void NfccPowerTracker::UpdatePowerStateLog(NfccPowerStateInfo_t mStandbyInfo,
307 NfccPowerStateInfo_t mActiveInfo) {
308 FILE* fp;
309 const string PWR_TRK_LOG_FILE_VERSION = "1.0";
310 /*Write the Active and standby timestamp into the file*/
311 fp = fopen(POWER_TRACKER_LOG_FILE.c_str(), "w");
312 if (fp == NULL) {
313 ALOGD_IF(nfc_debug_enabled, "Failed to Open Pwr Tracker Info File\n");
314 return;
315 }
316 ostringstream PwrTrackerInfo;
317 PwrTrackerInfo << "Version: " << PWR_TRK_LOG_FILE_VERSION.c_str() << endl;
318 PwrTrackerInfo << "NFC {" << endl;
319 PwrTrackerInfo << " { " << STR_ACTIVE
320 << std::to_string(mActiveInfo.residencyInMsecSinceBoot) << " }"
321 << endl;
322 PwrTrackerInfo << " { " << STR_STANDBY
323 << std::to_string(mStandbyInfo.residencyInMsecSinceBoot)
324 << " }" << endl;
325 PwrTrackerInfo << "}";
326 ALOGD_IF(nfc_debug_enabled,
327 "mActiveInfo.residencyInMsecSinceBoot: %llu, "
328 "mActiveInfo.totalTransitions: %llu,"
329 "mStandbyInfo.residencyInMsecSinceBoot "
330 ":%llu,mStandbyInfo.totalTransitions: %llu"
331 "mErrorInStandbyInfo.residencyInMsecSinceBoot: %llu",
332 (unsigned long long)mActiveInfo.residencyInMsecSinceBoot,
333 (unsigned long long)mActiveInfo.totalTransitions,
334 (unsigned long long)mStandbyInfo.residencyInMsecSinceBoot,
335 (unsigned long long)mStandbyInfo.totalTransitions,
336 (unsigned long long)mErrorInStandbyInfo.residencyInMsecSinceBoot);
337 string PwrInfo = PwrTrackerInfo.str();
338 if (!TryLockFile(fp)) {
339 ALOGD_IF(nfc_debug_enabled,
340 "Failed to Lock PwrTracker File.Skipping update\n");
341 fclose(fp);
342 return;
343 }
344 fwrite(PwrInfo.c_str(), sizeof(char), PwrInfo.length(), fp);
345 fflush(fp);
346 UnlockFile(fp);
347 fclose(fp);
348 }
349 /*******************************************************************************
350 **
351 ** Function ReadPowerStateLog
352 **
353 ** Description Retrieve powerstate related information from log file.
354 **
355 ** Returns true if read successful, false otherwise.
356 **
357 *******************************************************************************/
ReadPowerStateLog()358 bool NfccPowerTracker::ReadPowerStateLog() {
359 ifstream pwrStateFileStream;
360 string itemName;
361 ALOGD_IF(nfc_debug_enabled, "NfccPowerTracker::ReadPowerStateLog: Enter \n");
362 pwrStateFileStream.open(POWER_TRACKER_LOG_FILE.c_str());
363 if (pwrStateFileStream.fail()) {
364 ALOGE("Error: %s", strerror(errno));
365 return false;
366 }
367
368 /*Check for required string(time in millisec) in the log file and convert it
369 to integer*/
370 while (pwrStateFileStream >> itemName) {
371 if (STR_ACTIVE.compare(itemName) == 0) {
372 pwrStateFileStream >> itemName;
373 mActiveInfo.residencyInMsecSinceBoot = stoull(itemName.c_str(), nullptr);
374 } else if (STR_STANDBY.compare(itemName) == 0) {
375 pwrStateFileStream >> itemName;
376 mStandbyInfo.residencyInMsecSinceBoot = stoull(itemName.c_str(), nullptr);
377 }
378 }
379
380 ALOGD_IF(nfc_debug_enabled,
381 "Value retrieved from Powertracker file is"
382 "activeTime: %llu and standbyTime: %llu\n",
383 (unsigned long long)mActiveInfo.residencyInMsecSinceBoot,
384 (unsigned long long)mStandbyInfo.residencyInMsecSinceBoot);
385 pwrStateFileStream.close();
386 return true;
387 }
388 /*******************************************************************************
389 **
390 ** Function Pause
391 **
392 ** Description Pause Power state Information Tracking,Tracking will resume
393 ** once next power tracker notification is recieved as part of
394 ** ProcessNtf.
395 **
396 ** Returns void
397 **
398 *******************************************************************************/
Pause()399 void NfccPowerTracker::Pause() { mIsFirstPwrTrkNtfRecvd = false; }
400
401 /*******************************************************************************
402 **
403 ** Function Reset
404 **
405 ** Description Stop power track information processing and delete
406 ** power tracker log file.
407 **
408 ** Returns void
409 **
410 *******************************************************************************/
Reset()411 void NfccPowerTracker::Reset() {
412 ALOGD_IF(nfc_debug_enabled, "NfccPowerTracker::Reset enter");
413 if (remove(POWER_TRACKER_LOG_FILE.c_str()) != 0) {
414 ALOGD_IF(nfc_debug_enabled, "Error deleting Power tracker file");
415 }
416 }
417 /*******************************************************************************
418 **
419 ** Function TryLockFile
420 **
421 ** Description Lock PowerTracker log file. Any application trying to read
422 ** from PowerTracker log file shall acquire lock before reading
423 ** to avoid inconsistent data.
424 **
425 ** Returns true if locking was successful
426 ** false if there was a failure to lock PowerTracker log file.
427 *******************************************************************************/
TryLockFile(FILE * fp)428 bool NfccPowerTracker::TryLockFile(FILE* fp) {
429 uint8_t retryCount = 5;
430 do {
431 if (!flock(fileno(fp), LOCK_EX | LOCK_NB)) return true;
432 usleep(10000); /*10 millisec*/
433 } while (retryCount--);
434
435 return false;
436 }
437 /*******************************************************************************
438 **
439 ** Function UnlockFile
440 **
441 ** Description Unlock previously locked PowerTracker log file.
442 **
443 ** Returns void
444 **
445 *******************************************************************************/
UnlockFile(FILE * fp)446 void NfccPowerTracker::UnlockFile(FILE* fp) { flock(fileno(fp), LOCK_UN); }
447