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 "phNxpNciHal_ext.h"
21 #include <assert.h>
22 #include <fstream>
23 #include <iostream>
24 #include <log/log.h>
25 #include <sstream>
26 #include <stdio.h>
27 #include <sys/file.h>
28 #include <sys/time.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,Recieved len :%d", len);
128   bool screenStateCommand;
129   if (cmd[0] == 0x20 && cmd[1] == 0x09) {
130     screenStateCommand = true;
131   } else {
132     screenStateCommand = false;
133   }
134 
135   if (screenStateCommand && (cmd[3] == 0x00 || cmd[3] == 0x02)) {
136     /* Command for Screen State On-Locked or Unlocked */
137     clock_gettime(CLOCK_BOOTTIME, &mLastScreenOnTimeStamp);
138     mIsLastUpdateScreenOn = true;
139   } else if (screenStateCommand && (cmd[3] == 0x01 || cmd[3] == 0x03)) {
140     /* Command for Screen State OFF-locked or Unlocked */
141     clock_gettime(CLOCK_BOOTTIME, &mLastScreenOffTimeStamp);
142     mIsLastUpdateScreenOn = false;
143   } else if (cmd[0] == 0x20 && cmd[1] == 0x02 && cmd[2] == 0x05 &&
144              cmd[3] == 0x01 && cmd[4] == 0x00 && cmd[5] == 0x02) {
145     /* Command to update duration of discovery loop */
146     mStandbyTimePerDiscLoopInMillisec = (cmd[7] << 8 | cmd[6]);
147     ALOGD_IF(nfc_debug_enabled, "mStandbyTimePerDiscLoopInMillisec value : %d",
148              mStandbyTimePerDiscLoopInMillisec);
149   }
150 }
151 
152 /*******************************************************************************
153 **
154 ** Function         NfccPowerTracker::ProcessNtf
155 **
156 ** Description      Parse the Notifications coming from NFCC,
157 **                  get the time at which power relevant notifications are
158 **                  received
159 **                  (ex:RF ON-OFF/ACTIVATE-DEACTIVATE NTF/PROP_PWR_TRACKINFO)
160 **                  calculate error in standby time by comparing the
161 **                  expectated value from NFC HAL and received value from NFCC.
162 **                  Cache relevant info (timestamps) to file
163 **
164 ** Returns          void
165 **
166 *******************************************************************************/
ProcessNtf(uint8_t * rsp,uint16_t rsp_len)167 void NfccPowerTracker::ProcessNtf(uint8_t *rsp, uint16_t rsp_len) {
168   ALOGD_IF(nfc_debug_enabled, "NfccPowerTracker::ProcessNtf: Enter");
169 
170   /* Screen State Notification recieved */
171   if ((rsp[0] == 0x6F && rsp[1] == 0x05)) {
172     ProcessPowerTrackNtf(rsp, rsp_len);
173   } else if (rsp[0] == 0x61 && rsp[1] == 0x05) {
174     /*Activation notification received. Calculate the time NFCC is
175     active in Reader/P2P/CE duration */
176     clock_gettime(CLOCK_BOOTTIME, &mActiveTimeStart);
177     if (!mIsLastUpdateScreenOn) {
178       mActiveInfo.totalTransitions++;
179     }
180   } else if (rsp[0] == 0x61 && rsp[1] == 0x06) {
181     /* Deactivation notification received Calculate the time NFCC is
182     active in Reader/P2P/CE duration.Time between Activation and
183     Deacivation gives the active time*/
184     clock_gettime(CLOCK_BOOTTIME, &mActiveTimeEnd);
185     mActiveDurationFromLastScreenUpdate +=
186         TimeDiff(mActiveTimeStart, mActiveTimeEnd);
187     if (!mIsLastUpdateScreenOn) {
188       mStandbyInfo.totalTransitions++;
189     }
190     ALOGD_IF(nfc_debug_enabled, "mActiveDurationFromLastScreenUpdate: %llu",
191              (unsigned long long)mActiveDurationFromLastScreenUpdate);
192   }
193 }
194 
195 /*******************************************************************************
196 **
197 ** Function         ProcessPowerTrackNtf
198 **
199 ** Description      Process Power Tracker notification and update timingInfo to
200 **                  Log File.
201 **
202 ** Returns          void
203 **
204 *******************************************************************************/
ProcessPowerTrackNtf(uint8_t * rsp,uint16_t rsp_len)205 void NfccPowerTracker::ProcessPowerTrackNtf(uint8_t *rsp, uint16_t rsp_len) {
206   /* Enable Power Tracking computations after 1st Power tracker notification
207    * is received. */
208   if (!mIsFirstPwrTrkNtfRecvd) {
209     mIsFirstPwrTrkNtfRecvd = true;
210     ifstream ifile(POWER_TRACKER_LOG_FILE.c_str());
211     if ((bool)ifile == true) {
212       mLastPowerTrackAborted = true;
213     }
214     return;
215   }
216 
217   /*Duration between screen state change is taken as reference for calculating
218   active and standby time*/
219   uint64_t totalDuration = 0;
220   totalDuration =
221       mIsLastUpdateScreenOn
222           ? TimeDiff(mLastScreenOffTimeStamp, mLastScreenOnTimeStamp)
223           : TimeDiff(mLastScreenOnTimeStamp, mLastScreenOffTimeStamp);
224   if (totalDuration == 0)
225     return;
226 
227   /*Calculate Active and Standby time based on the pollCount provided in the
228   Power tracker Notification from NFCC*/
229   uint16_t sPollCount = (TIMER_COUNT_MASK & ((rsp[5] << 8) | rsp[4]));
230   ALOGD_IF(nfc_debug_enabled,
231            "Poll/Timer count recived from FW is %d and rsp_len :%d", sPollCount,
232            rsp_len);
233   uint64_t standbyTime = 0, activeTime = 0;
234   if (mIsLastUpdateScreenOn) {
235     activeTime = sPollCount * ACTIVE_TIME_PER_TIMER_COUNT_IN_MILLISEC;
236     /*Check for errors in count provided by NFCC*/
237     uint64_t error = (activeTime > mActiveDurationFromLastScreenUpdate)
238                          ? (activeTime - mActiveDurationFromLastScreenUpdate)
239                          : (mActiveDurationFromLastScreenUpdate - activeTime);
240     if (error > PWR_TRK_ERROR_MARGIN_IN_MILLISEC) {
241       ALOGD_IF(nfc_debug_enabled,
242                "Active Time Error observed with value is %llu",
243                (unsigned long long)error);
244       mErrorInStandbyInfo.residencyInMsecSinceBoot += error;
245     }
246     standbyTime = (totalDuration > activeTime) ? (totalDuration - activeTime)
247                                                : (activeTime - totalDuration);
248     if (rsp[3]) {
249       /*If notification trigger is counter overflow, update the screen on
250       timestamp as there is no screen state change*/
251       clock_gettime(CLOCK_BOOTTIME, &mLastScreenOnTimeStamp);
252     }
253     mActiveInfo.totalTransitions++;
254   } else {
255     standbyTime = (sPollCount * ((uint64_t)mStandbyTimePerDiscLoopInMillisec));
256     activeTime = totalDuration > standbyTime ? (totalDuration - standbyTime)
257                                              : (standbyTime - totalDuration);
258     if (rsp[3]) {
259       /*If notification trigger is counter overflow, update the screen off
260       timestamp as there is no screen state change*/
261       clock_gettime(CLOCK_BOOTTIME, &mLastScreenOffTimeStamp);
262     }
263     /*Total transitions in screen on -> Screen Off window is same as poll count
264     provided by NFCC, as, there is transition in each discovery loop*/
265     mActiveInfo.totalTransitions += sPollCount;
266     /*1 additional transition for screen state update*/
267     mStandbyInfo.totalTransitions += (sPollCount + 1);
268   }
269 
270   ALOGD_IF(nfc_debug_enabled,
271            "activeTime: %llu, standbyTime: %llu, totalDuration :%llu",
272            (unsigned long long)activeTime, (unsigned long long)standbyTime,
273            (unsigned long long)totalDuration);
274   if (mLastPowerTrackAborted) {
275     ALOGD_IF(nfc_debug_enabled,
276              "Last Hal service aborted,so retrive the power info data and "
277              "continue\n");
278     /*Read the file content and store in mActiveInfo.residencyInMsecSinceBoot
279     and mStandbyInfo.residencyInMsecSinceBoot*/
280     if (ReadPowerStateLog()) {
281       mLastPowerTrackAborted = false;
282     }
283   }
284   mStandbyInfo.residencyInMsecSinceBoot += standbyTime;
285   mActiveInfo.residencyInMsecSinceBoot += activeTime;
286   UpdatePowerStateLog(mStandbyInfo, mActiveInfo);
287   mActiveDurationFromLastScreenUpdate = 0;
288 }
289 /*******************************************************************************
290 **
291 ** Function         NfccPowerTracker::UpdatePowerStateLog
292 **
293 ** Description      update the powerstate related information in log file
294 **
295 ** Returns          void
296 **
297 *******************************************************************************/
UpdatePowerStateLog(NfccPowerStateInfo_t mStandbyInfo,NfccPowerStateInfo_t mActiveInfo)298 void NfccPowerTracker::UpdatePowerStateLog(NfccPowerStateInfo_t mStandbyInfo,
299                                            NfccPowerStateInfo_t mActiveInfo) {
300   FILE *fp;
301   const string PWR_TRK_LOG_FILE_VERSION = "1.0";
302   /*Write the Active and standby timestamp into the file*/
303   fp = fopen(POWER_TRACKER_LOG_FILE.c_str(), "w");
304   if (fp == NULL) {
305     ALOGD_IF(nfc_debug_enabled, "Failed to Open Pwr Tracker Info File\n");
306     return;
307   }
308   ostringstream PwrTrackerInfo;
309   PwrTrackerInfo << "Version: " << PWR_TRK_LOG_FILE_VERSION.c_str() << endl;
310   PwrTrackerInfo << "NFC {" << endl;
311   PwrTrackerInfo << " { " << STR_ACTIVE
312                  << std::to_string(mActiveInfo.residencyInMsecSinceBoot) << " }"
313                  << endl;
314   PwrTrackerInfo << " { " << STR_STANDBY
315                  << std::to_string(mStandbyInfo.residencyInMsecSinceBoot)
316                  << " }" << endl;
317   PwrTrackerInfo << "}";
318   ALOGD_IF(nfc_debug_enabled,
319            "mActiveInfo.residencyInMsecSinceBoot: %llu, "
320            "mActiveInfo.totalTransitions: %llu,"
321            "mStandbyInfo.residencyInMsecSinceBoot "
322            ":%llu,mStandbyInfo.totalTransitions: %llu"
323            "mErrorInStandbyInfo.residencyInMsecSinceBoot: %llu",
324            (unsigned long long)mActiveInfo.residencyInMsecSinceBoot,
325            (unsigned long long)mActiveInfo.totalTransitions,
326            (unsigned long long)mStandbyInfo.residencyInMsecSinceBoot,
327            (unsigned long long)mStandbyInfo.totalTransitions,
328            (unsigned long long)mErrorInStandbyInfo.residencyInMsecSinceBoot);
329   string PwrInfo = PwrTrackerInfo.str();
330   if (!TryLockFile(fp)) {
331     ALOGD_IF(nfc_debug_enabled,
332              "Failed to Lock PwrTracker File.Skipping update\n");
333     fclose(fp);
334     return;
335   }
336   fwrite(PwrInfo.c_str(), sizeof(char), PwrInfo.length(), fp);
337   fflush(fp);
338   UnlockFile(fp);
339   fclose(fp);
340 }
341 /*******************************************************************************
342  **
343  ** Function         ReadPowerStateLog
344  **
345  ** Description      Retrieve powerstate related information from log file.
346  **
347  ** Returns          true if read successful, false otherwise.
348  **
349  *******************************************************************************/
ReadPowerStateLog()350 bool NfccPowerTracker::ReadPowerStateLog() {
351   ifstream pwrStateFileStream;
352   string itemName;
353   ALOGD_IF(nfc_debug_enabled, "NfccPowerTracker::ReadPowerStateLog: Enter \n");
354   pwrStateFileStream.open(POWER_TRACKER_LOG_FILE.c_str());
355   if (pwrStateFileStream.fail()) {
356     ALOGE("Error: %s", strerror(errno));
357     return false;
358   }
359 
360   /*Check for required string(time in millisec) in the log file and convert it
361     to integer*/
362   while (pwrStateFileStream >> itemName) {
363     if (STR_ACTIVE.compare(itemName) == 0) {
364       pwrStateFileStream >> itemName;
365       mActiveInfo.residencyInMsecSinceBoot = stoull(itemName.c_str(), nullptr);
366     } else if (STR_STANDBY.compare(itemName) == 0) {
367       pwrStateFileStream >> itemName;
368       mStandbyInfo.residencyInMsecSinceBoot = stoull(itemName.c_str(), nullptr);
369     }
370   }
371 
372   ALOGD_IF(nfc_debug_enabled,
373            "Value retrieved from Powertracker file is"
374            "activeTime: %llu and standbyTime: %llu\n",
375            (unsigned long long)mActiveInfo.residencyInMsecSinceBoot,
376            (unsigned long long)mStandbyInfo.residencyInMsecSinceBoot);
377   pwrStateFileStream.close();
378   return true;
379 }
380 /*******************************************************************************
381 **
382 ** Function         Pause
383 **
384 ** Description      Pause Power state Information Tracking,Tracking will resume
385 **                  once next power tracker notification is recieved as part of
386 **                  ProcessNtf.
387 **
388 ** Returns          void
389 **
390 *******************************************************************************/
Pause()391 void NfccPowerTracker::Pause() { mIsFirstPwrTrkNtfRecvd = false; }
392 
393 /*******************************************************************************
394 **
395 ** Function         Reset
396 **
397 ** Description      Stop power track information processing and delete
398 **                  power tracker log file.
399 **
400 ** Returns          void
401 **
402 *******************************************************************************/
Reset()403 void NfccPowerTracker::Reset() {
404   ALOGD_IF(nfc_debug_enabled, "NfccPowerTracker::Reset enter");
405   if (remove(POWER_TRACKER_LOG_FILE.c_str()) != 0) {
406     ALOGD_IF(nfc_debug_enabled, "Error deleting Power tracker file");
407   }
408 }
409 /*******************************************************************************
410 **
411 ** Function         TryLockFile
412 **
413 ** Description      Lock PowerTracker log file. Any application trying to read
414 **                  from PowerTracker log file shall acquire lock before reading
415 **                  to avoid inconsistent data.
416 **
417 ** Returns          true if locking was successful
418 **                  false if there was a failure to lock PowerTracker log file.
419 *******************************************************************************/
TryLockFile(FILE * fp)420 bool NfccPowerTracker::TryLockFile(FILE *fp) {
421   uint8_t retryCount = 5;
422   do {
423     if (!flock(fileno(fp), LOCK_EX | LOCK_NB))
424       return true;
425     usleep(10000); /*10 millisec*/
426   } while (retryCount--);
427 
428   return false;
429 }
430 /*******************************************************************************
431 **
432 ** Function         UnlockFile
433 **
434 ** Description      Unlock previously locked PowerTracker log file.
435 **
436 ** Returns          void
437 **
438 *******************************************************************************/
UnlockFile(FILE * fp)439 void NfccPowerTracker::UnlockFile(FILE *fp) { flock(fileno(fp), LOCK_UN); }
440