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