1 /*
2  *  Copyright (C) 2023 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  *  Provide extensions for the implementation of the Nfc Charging
17  */
18 
19 package com.android.nfc.wlc;
20 
21 import android.app.Activity;
22 import android.content.Context;
23 import android.nfc.NdefMessage;
24 import android.nfc.NdefRecord;
25 import android.nfc.NfcAdapter;
26 import android.os.Bundle;
27 import android.sysprop.NfcProperties;
28 import android.util.Log;
29 
30 import com.android.nfc.DeviceHost;
31 import com.android.nfc.DeviceHost.TagEndpoint;
32 import com.android.nfc.NfcService;
33 
34 import java.math.*;
35 import java.util.Arrays;
36 import java.util.HashMap;
37 import java.util.Map;
38 
39 public class NfcCharging {
40     static final boolean DBG = NfcProperties.debug_enabled().orElse(true);
41     private static final String TAG = "NfcWlcChargingActivity";
42 
43     static final String VERSION = "1.0.0";
44 
45     private Context mContext;
46     public static final byte[] WLCCAP = {0x57, 0x4c, 0x43, 0x43, 0x41, 0x50};
47     public static final byte[] WLCCTL = {0x57, 0x4c, 0x43, 0x43, 0x54, 0x4C};
48     public static final byte[] WLCSTAI = {0x57, 0x4c, 0x43, 0x53, 0x54, 0x41, 0x49};
49     public static final byte[] USIWLC = {0x75, 0x73, 0x69, 0x3A, 0x77, 0x6C, 0x63};
50     public static final byte[] WLCPI = {0x57, 0x4c, 0x43, 0x49, 0x4e, 0x46};
51 
52     public static final String BatteryLevel = "Battery Level";
53     public static final String ReceivePower = "Receive Power";
54     public static final String ReceiveVoltage = "Receive Voltage";
55     public static final String ReceiveCurrent = "Receive Current";
56     public static final String TemperatureBattery = "Temperature Battery";
57     public static final String TemperatureListener = "Temperature Listener";
58     public static final String VendorId = "Vendor Id";
59     public static final String State = "State";
60     public static final int DISCONNECTED = 0;
61     public static final int CONNECTED_NOT_CHARGING = 1;
62     public static final int CONNECTED_CHARGING = 2;
63 
64     static final byte MODE_REQ_STATIC = 0;
65     static final byte MODE_REQ_NEGOTIATED = 1;
66     static final byte MODE_REQ_BATTERY_FULL = 2;
67 
68     static final int MODE_NON_AUTONOMOUS_WLCP = 0;
69 
70     int mWatchdogTimeout = 1;
71     int mUpdatedBatteryLevel = -1;
72     int mLastState = -1;
73 
74     int WLCState = 0;
75 
76     // WLCCAP
77     int WlcCap_ModeReq = 0;
78     int Nwt_max = 0;
79     int WlcCap_NegoWait = 0;
80     int WlcCap_RdConf = 0;
81     int TNdefRdWt = 0;
82 
83     int WlcCap_NdefRdWt = 0;
84     int WlcCap_CapWt = 0;
85     int TCapWt = 0;
86     int WlcCap_NdefWrTo = 0;
87     int TNdefWrTo = 0;
88     int WlcCap_NdefWrWt = 0;
89     int TNdefWrWt = 0;
90 
91     int mNwcc_retry = 0;
92     int mNretry = 0;
93 
94     // WLCCTL
95     int WlcCtl_ErrorFlag = 0;
96     int WlcCtl_BatteryStatus = 0;
97     int mCnt = -1;
98     int WlcCtl_Cnt_new = 0;
99     int WlcCtl_WptReq = 0;
100     int WlcCtl_WptDuration = 0;
101     int TWptDuration = 0;
102     int WlcCtl_WptInfoReq = 0;
103     int WlcCtl_PowerAdjReq = 0;
104     int WlcCtl_BatteryLevel = 0xFF;
105     int WlcCtl_HoldOffWt = 0;
106     int THoldOffWt = 0;
107 
108     int WlcCtl_ReceivePower = 0;
109     int WlcCtl_ReceiveVoltage = 0;
110     int WlcCtl_TemperatureBattery = 0;
111     int WlcCtl_TemperatureWlcl = 0;
112 
113     // WLCINF
114     int Ptx = 100;
115 
116     // state machine
117     private static final int STATE_2 = 0; // Read WLC_CAP
118     private static final int STATE_6 = 1; // Static WPT
119     private static final int STATE_8 = 2; // Handle NEGO_WAIT
120     private static final int STATE_11 = 3; // Write WLCP_INFO
121     private static final int STATE_12 = 4; // Read WLCL_CTL
122     private static final int STATE_16 = 5; // Read confirmation
123     private static final int STATE_17 = 6; // Check WPT requested
124     private static final int STATE_21 = 7; // Handle WPT
125     private static final int STATE_22 = 8; // Handle INFO_REQ
126     private static final int STATE_24 = 9; // Handle removal detection
127     private static final int STATE_21_1 = 10; // Handle WPT time completed
128     private static final int STATE_21_2 = 11; // Handle FOD detection/removal
129 
130     private DeviceHost mNativeNfcManager;
131     NdefMessage mNdefMessage;
132     byte[] mNdefPayload;
133     byte[] mNdefPayload2;
134     byte[] mNdefType;
135     TagEndpoint TagHandler;
136 
137     public boolean NfcChargingOnGoing = false;
138     public boolean NfcChargingMode = false;
139     public boolean WLCL_Presence = false;
140 
141     public boolean mFirstOccurrence = true;
142 
143     Map<String, Integer> WlcDeviceInfo = new HashMap<>();
144 
startWlcPowerTransfer(int power_adj_req, int wpt_time_int)145     private native boolean startWlcPowerTransfer(int power_adj_req, int wpt_time_int);
146 
enableWlc(int enable)147     private native boolean enableWlc(int enable);
148 
149     private PresenceCheckWatchdog mWatchdogWlc;
150 
NfcCharging(Context context, DeviceHost mDeviceHost)151     public NfcCharging(Context context, DeviceHost mDeviceHost) {
152         if (DBG) Log.d(TAG, "NfcCharging - Constructor");
153         mContext = context;
154         mNativeNfcManager = mDeviceHost;
155 
156         resetInternalValues();
157 
158         mNdefMessage = null;
159         mNdefPayload = null;
160         mNdefPayload2 = null;
161     }
162 
163     private static final char[] hexArray = "0123456789ABCDEF".toCharArray();
164 
bytesToHex(byte[] bytes)165     public static String bytesToHex(byte[] bytes) {
166         char[] hexChars = new char[bytes.length * 2];
167         for (int j = 0; j < bytes.length; j++) {
168             int v = bytes[j] & 0xFF;
169             hexChars[j * 2] = hexArray[v >>> 4];
170             hexChars[j * 2 + 1] = hexArray[v & 0x0F];
171         }
172         return new String(hexChars);
173     }
174 
resetInternalValues()175     public void resetInternalValues() {
176         if (DBG) Log.d(TAG, "resetInternalValues");
177         mCnt = -1;
178         mNretry = 0;
179         WlcCap_ModeReq = 0;
180         WlcCtl_BatteryLevel = -1;
181         WlcCtl_ReceivePower = -1;
182         WlcCtl_ReceiveVoltage = -1;
183         WlcCtl_TemperatureBattery = -1;
184         WlcCtl_TemperatureWlcl = -1;
185         mUpdatedBatteryLevel = -1;
186         mLastState = -1;
187         WlcDeviceInfo.put(BatteryLevel, -1);
188         WlcDeviceInfo.put(ReceivePower, -1);
189         WlcDeviceInfo.put(ReceiveVoltage, -1);
190         WlcDeviceInfo.put(ReceiveCurrent, -1);
191         WlcDeviceInfo.put(TemperatureBattery, -1);
192         WlcDeviceInfo.put(TemperatureListener, -1);
193         WlcDeviceInfo.put(VendorId, -1);
194         WlcDeviceInfo.put(State, -1);
195 
196         WlcCtl_ErrorFlag = 0;
197         mFirstOccurrence = true;
198     }
199 
200     DeviceHost.TagDisconnectedCallback callbackTagDisconnection =
201             new DeviceHost.TagDisconnectedCallback() {
202                 @Override
203                 public void onTagDisconnected() {
204                     Log.d(TAG, "onTagDisconnected");
205                     disconnectNfcCharging();
206                     WLCState = STATE_2;
207                     NfcChargingOnGoing = false;
208                     if (WLCL_Presence == true) {
209                         WLCL_Presence = false;
210                         if (DBG) Log.d(TAG, "Nfc Charging Listener lost");
211                     }
212                     NfcService.getInstance().sendScreenMessageAfterNfcCharging();
213                 }
214             };
215 
startNfcCharging(TagEndpoint t)216     public boolean startNfcCharging(TagEndpoint t) {
217         if (DBG) Log.d(TAG, "startNfcCharging " + VERSION);
218         boolean NfcChargingEnabled = false;
219 
220         TagHandler = t;
221         NfcChargingEnabled = enableWlc(MODE_NON_AUTONOMOUS_WLCP);
222         if (DBG) Log.d(TAG, "NfcChargingEnabled is " + NfcChargingEnabled);
223 
224         if (NfcChargingEnabled) {
225             WLCL_Presence = true;
226             WLCState = STATE_2;
227             startNfcChargingPresenceChecking(50);
228             return true;
229         } else {
230             return false;
231         }
232     }
233 
stopNfcCharging()234     public void stopNfcCharging() {
235         if (DBG) Log.d(TAG, "stopNfcCharging " + VERSION);
236 
237         NfcChargingOnGoing = false;
238         resetInternalValues();
239 
240         mLastState = DISCONNECTED;
241         WlcDeviceInfo.put(State, mLastState);
242         NfcService.getInstance().onWlcData(WlcDeviceInfo);
243         disconnectPresenceCheck();
244 
245         NfcChargingMode = false;
246 
247         // Restart the polling loop
248 
249         TagHandler.disconnect();
250         // Disable discovery and restart polling loop only if not screen state change pending
251         if (!NfcService.getInstance().sendScreenMessageAfterNfcCharging()) {
252             if (DBG) Log.d(TAG, "No pending screen state change, stop Nfc charging presence check");
253             stopNfcChargingPresenceChecking();
254         }
255     }
256 
checkWlcCapMsg(NdefMessage ndefMsg)257     public boolean checkWlcCapMsg(NdefMessage ndefMsg) {
258         if (DBG) Log.d(TAG, "checkWlcCapMsg: enter");
259         boolean status = true;
260         NdefRecord[] ndefRecords = null;
261         long mDeviceId = 0;
262         int mVendorId = 0;
263         Byte ControlByte = 0;
264         if (ndefMsg != null) {
265             mNdefMessage = ndefMsg;
266             try {
267                 ndefRecords = mNdefMessage.getRecords();
268                 if (ndefRecords != null && ndefRecords.length > 0) {
269                     if (DBG)
270                         Log.d(TAG, "checkWlcCapMsg: number of ndefRecords = " + ndefRecords.length);
271                     mNdefType = ndefRecords[0].getType();
272 
273                     if (mNdefType != null) {
274                         mNdefPayload = ndefRecords[0].getPayload();
275                         if (mNdefPayload != null && mNdefType != null) {
276                             if (!Arrays.equals(mNdefType, WLCCAP)) {
277                                 if (DBG) Log.d(TAG, "NdefType not WLC_CAP");
278                                 return (status = false);
279                             }
280                             if (DBG) Log.d(TAG, "mNdefType = " + bytesToHex(mNdefType));
281                         } else {
282                             return (status = false);
283                         }
284                     } else {
285                         Log.e(TAG, "NdefType null");
286                         return (status = false);
287                     }
288                 } else {
289                     Log.e(TAG, "ndefRecords == null or ndefRecords.length = 0)");
290                     return (status = false);
291                 }
292             } catch (Exception e) {
293                 Log.e(TAG, "Error in getRecords " + e);
294                 NfcChargingOnGoing = false;
295                 TagHandler.startPresenceChecking(125, callbackTagDisconnection);
296             }
297 
298             if ((mNdefPayload[1] & 0xC0) == 0xC0) {
299                 if (DBG) Log.d(TAG, "Wrong Mode Req");
300                 return (status = false);
301             }
302 
303             WlcCap_ModeReq = (mNdefPayload[1] >> 6) & 0x3;
304             Nwt_max = (mNdefPayload[1] >> 2) & 0xF;
305             WlcCap_NegoWait = (mNdefPayload[1] >> 1) & 0x1;
306             if (DBG) Log.d(TAG, "WlcCap_NegoWait = " + WlcCap_NegoWait);
307             if (DBG) Log.d(TAG, "Nwt_max = " + Nwt_max);
308             WlcCap_RdConf = mNdefPayload[1] & 0x1;
309 
310             WlcCap_CapWt = (mNdefPayload[2] & 0x1F);
311             if (WlcCap_CapWt > 0x13) WlcCap_CapWt = 0x13;
312             TCapWt = (int) Math.pow(2, (WlcCap_CapWt + 3));
313             if (TCapWt < 250) TCapWt = 250;
314             if (DBG) Log.d(TAG, "TCapWt = " + TCapWt);
315             TNdefRdWt = (int) (mNdefPayload[3] & 0xFF) * 10;
316             if (mNdefPayload[3] == 0 || mNdefPayload[3] == (byte)0xFF) TNdefRdWt = 2540;
317             if (DBG) Log.d(TAG, "TNdefRdWt = " + TNdefRdWt);
318             WlcCap_NdefWrTo = mNdefPayload[4];
319             if (WlcCap_NdefWrTo == 0 || WlcCap_NdefWrTo > 4) WlcCap_NdefWrTo = 4;
320             TNdefWrTo = (int) Math.pow(2, (WlcCap_NdefWrTo + 5));
321             if (DBG) Log.d(TAG, "TNdefWrTo = " + TNdefWrTo);
322             TNdefWrWt = mNdefPayload[5];
323             if (TNdefWrWt > 0x0A) TNdefWrWt = 0x0A;
324             if (DBG) Log.d(TAG, "TNdefWrWt = " + TNdefWrWt);
325 
326             Log.d(TAG, " " + ndefRecords.length + " NdefRecords");
327             if (ndefRecords != null && ndefRecords.length > 1) {
328                 for (int i = 1; i < ndefRecords.length; i++) {
329                     mNdefType = ndefRecords[i].getType();
330                     if (DBG) Log.d(TAG, "mNdefType = " + bytesToHex(mNdefType));
331                     mNdefPayload2 = ndefRecords[i].getPayload();
332                     if (mNdefPayload2 != null && mNdefType != null) {
333                         if (Arrays.equals(mNdefType, WLCSTAI)) {
334                             checkWlcStaiMsg(mNdefPayload2);
335                         } else if (Arrays.equals(mNdefType, USIWLC)) {
336                             if (DBG)
337                                 Log.d(
338                                         TAG,
339                                         "mNdefPayload USIWLC = "
340                                                 + bytesToHex(mNdefPayload2)
341                                                 + " length = "
342                                                 + mNdefPayload2.length);
343 
344                             if (mNdefPayload2.length > 8) {
345                                 mVendorId = (mNdefPayload2[8] << 8 | mNdefPayload2[7]) >> 4;
346                                 Log.d(TAG, "VendorId = " + Integer.toHexString(mVendorId));
347                                 WlcDeviceInfo.put(VendorId, mVendorId);
348                                 mDeviceId = (long) ((mNdefPayload2[7] & 0x0F)) << 48;
349                                 for (int j = 6; j > 0; j--) {
350                                     mDeviceId |= (long) (mNdefPayload2[j] & 0xFF) << ((j - 1) * 8);
351                                 }
352                                 if (DBG) Log.d(TAG, "DeviceId = " + Long.toHexString(mDeviceId));
353                             }
354                         }
355                     }
356                 }
357             }
358             NfcChargingOnGoing = true;
359         } else {
360             status = false;
361         }
362         if (WlcDeviceInfo.get(BatteryLevel) > (mUpdatedBatteryLevel + 5)) {
363             NfcService.getInstance().onWlcData(WlcDeviceInfo);
364             mUpdatedBatteryLevel = WlcDeviceInfo.get(BatteryLevel);
365         }
366         if (DBG) Log.d(TAG, "checkWlcCapMsg: exit, status = " + status);
367         return status;
368     }
369 
checkWlcCtlMsg(NdefMessage mNdefMessage)370     public boolean checkWlcCtlMsg(NdefMessage mNdefMessage) {
371         if (DBG) Log.d(TAG, "checkWlcCtlMsg: enter");
372 
373         boolean status = true;
374         NdefRecord[] ndefRecords = null;
375 
376         if (mNdefMessage != null) {
377             if (DBG) Log.d(TAG, "ndefMessage non null");
378             try {
379                 ndefRecords = mNdefMessage.getRecords();
380                 if (ndefRecords != null && ndefRecords.length > 0) {
381                     mNdefType = ndefRecords[0].getType();
382                     mNdefPayload = ndefRecords[0].getPayload();
383                     if (mNdefPayload != null && mNdefType != null) {
384                         if (!Arrays.equals(mNdefType, NfcCharging.WLCCTL)) {
385                             return (status = false);
386                         }
387                         if (DBG) Log.d(TAG, "mNdefType = " + bytesToHex(mNdefType));
388                     } else {
389                         return (status = false);
390                     }
391                 } else {
392                     return (status = false);
393                 }
394             } catch (Exception e) {
395                 Log.e(TAG, "Error in getRecords " + e);
396                 NfcChargingOnGoing = false;
397                 TagHandler.startPresenceChecking(125, callbackTagDisconnection);
398             }
399             WlcCtl_ErrorFlag = (mNdefPayload[0] >> 7);
400             WlcCtl_BatteryStatus = (mNdefPayload[0] & 0x18) >> 3;
401             WlcCtl_Cnt_new = (mNdefPayload[0] & 0x7);
402             WlcCtl_WptReq = (mNdefPayload[1] & 0xC0) >> 6;
403             if (WlcCtl_WptReq > 1) WlcCtl_WptReq = 0;
404 
405             WlcCtl_WptDuration = (mNdefPayload[1] & 0x3e) >> 1;
406             if (WlcCtl_WptDuration > 0x13) WlcCtl_WptReq = 0x13;
407             if (DBG) Log.d(TAG, "WlcCtl_WptDuration = " + WlcCtl_WptDuration);
408             TWptDuration = (int) Math.pow(2, (WlcCtl_WptDuration + 3));
409             WlcCtl_WptInfoReq = (mNdefPayload[1] & 0x1);
410             if (WlcCtl_WptReq == 0) WlcCtl_WptInfoReq = 0;
411 
412             if ((mNdefPayload[2] <= 0x14) || (mNdefPayload[2] >= (byte)0xF6)) {
413                 WlcCtl_PowerAdjReq = mNdefPayload[2];
414             } else {
415                 WlcCtl_PowerAdjReq = 0;
416             }
417 
418             if (DBG) Log.d(TAG, "checkWlcCtlMsg WlcCtl_PowerAdjReq = " + WlcCtl_PowerAdjReq);
419 
420             if ((mNdefPayload[3] < 0x64) && (WlcCtl_BatteryStatus == 0x1)) {
421                 WlcCtl_BatteryLevel = mNdefPayload[3];
422                 WlcDeviceInfo.put(BatteryLevel, WlcCtl_BatteryLevel);
423                 if (DBG) Log.d(TAG, "checkWlcCtlMsg WlcCtl_BatteryLevel = " + WlcCtl_BatteryLevel);
424             }
425 
426             if (mNdefPayload[5] > 0xF) {
427                 WlcCtl_HoldOffWt = 0xF;
428             } else {
429                 WlcCtl_HoldOffWt = mNdefPayload[5];
430             }
431             THoldOffWt = (int) WlcCtl_HoldOffWt * 2;
432 
433             if (DBG) Log.d(TAG, " " + ndefRecords.length + " NdefRecords");
434             if (ndefRecords != null && ndefRecords.length > 1) {
435                 for (int i = 1; i < ndefRecords.length; i++) {
436                     mNdefType = ndefRecords[i].getType();
437                     if (DBG) Log.d(TAG, "mNdefType = " + bytesToHex(mNdefType));
438                     mNdefPayload2 = ndefRecords[i].getPayload();
439                     if (mNdefPayload2 != null && mNdefType != null) {
440                         if (Arrays.equals(mNdefType, WLCSTAI)) {
441                             checkWlcStaiMsg(mNdefPayload2);
442                         }
443                     }
444                 }
445             }
446 
447         } else {
448             status = false;
449         }
450 
451         if (WlcDeviceInfo.get(BatteryLevel) > (mUpdatedBatteryLevel + 5)) {
452             NfcService.getInstance().onWlcData(WlcDeviceInfo);
453             mUpdatedBatteryLevel = WlcDeviceInfo.get(BatteryLevel);
454         }
455         if (DBG) Log.d(TAG, "checkWlcCtlMsg status = " + status);
456         return status;
457     }
458 
checkWlcStaiMsg(byte[] mPayload)459     public void checkWlcStaiMsg(byte[] mPayload) {
460         Byte ControlByte = 0;
461         if (DBG) Log.d(TAG, "mNdefPayload WLCSTAI = " + bytesToHex(mPayload));
462         ControlByte = mPayload[0];
463         int pos = 0;
464         if (((ControlByte & 0x01) == 0x01) && pos < mPayload.length) {
465             pos++;
466             WlcCtl_BatteryLevel = mPayload[pos];
467             WlcDeviceInfo.put(BatteryLevel, (int) mPayload[pos]);
468             if (DBG) Log.d(TAG, "WlcCtl_BatteryLevel = " + WlcDeviceInfo.get(BatteryLevel));
469         }
470         if (((ControlByte & 0x02) == 0x02) && pos < mPayload.length) {
471             pos++;
472             WlcCtl_ReceivePower = mPayload[pos];
473             WlcDeviceInfo.put(ReceivePower, (int) mPayload[pos]);
474         }
475         if (((ControlByte & 0x04) == 0x04) && pos < mPayload.length) {
476             pos++;
477             WlcCtl_ReceiveVoltage = mPayload[pos];
478             WlcDeviceInfo.put(ReceiveVoltage, (int) mPayload[pos]);
479         }
480         if (((ControlByte & 0x08) == 0x08) && pos < mPayload.length) {
481             pos++;
482             WlcCtl_TemperatureBattery = mPayload[pos];
483             WlcDeviceInfo.put(TemperatureBattery, (int) mPayload[pos]);
484         }
485         if (((ControlByte & 0x10) == 0x10) && pos < mPayload.length) {
486             pos++;
487             WlcCtl_TemperatureWlcl = mPayload[pos];
488             WlcDeviceInfo.put(TemperatureListener, (int) mPayload[pos]);
489         }
490     }
491 
sendWLCPI(TagEndpoint tag, NdefMessage ndefMsg)492     public void sendWLCPI(TagEndpoint tag, NdefMessage ndefMsg) {
493         NdefMessage WLCP_INFO =
494                 constructWLCPI(
495                         (byte) Ptx,
496                         (byte) 0x00,
497                         (byte) 0x00,
498                         (byte) 0x00,
499                         (byte) 0x00,
500                         (byte) 0x00);
501         if (tag.writeNdef(WLCP_INFO.toByteArray())) {
502             Log.d(TAG, "Write NDEF success");
503         } else {
504             Log.d(TAG, "Write NDEF Error");
505         }
506     }
507 
constructWLCPI( byte ptx, byte power_class, byte tps, byte cps, byte nmsi, byte nmsd)508     public NdefMessage constructWLCPI(
509             byte ptx, byte power_class, byte tps, byte cps, byte nmsi, byte nmsd) {
510         byte[] WLCPI_payload = {ptx, power_class, tps, cps, nmsi, nmsd};
511 
512         NdefRecord WLCP_INFO_RECORD =
513                 new NdefRecord(NdefRecord.TNF_WELL_KNOWN, WLCPI, new byte[] {}, WLCPI_payload);
514 
515         NdefMessage WLCP_INFO = new NdefMessage(WLCP_INFO_RECORD);
516 
517         return WLCP_INFO;
518     }
519 
sendEmptyNdef()520     public void sendEmptyNdef() {
521         NdefRecord WLCP_RD_CONF_RECORD = new NdefRecord(NdefRecord.TNF_EMPTY, null, null, null);
522 
523         NdefMessage WLCP_RD_CONF = new NdefMessage(WLCP_RD_CONF_RECORD);
524         if (TagHandler.writeNdef(WLCP_RD_CONF.toByteArray())) {
525             Log.d(TAG, "Write NDEF success");
526         } else {
527             Log.d(TAG, "Write NDEF Error");
528         }
529     }
530 
stopNfcChargingPresenceChecking()531     public synchronized void stopNfcChargingPresenceChecking() {
532         if (mWatchdogWlc != null) {
533             mWatchdogWlc.end(true);
534         }
535     }
536 
startNfcChargingPresenceChecking(int presenceCheckDelay)537     public synchronized void startNfcChargingPresenceChecking(int presenceCheckDelay) {
538         // Once we start presence checking, we allow the upper layers
539         // to know the tag is in the field.
540         if (mWatchdogWlc != null) {
541             if (DBG) Log.d(TAG, "mWatchDog non null");
542         }
543         if (mWatchdogWlc == null) {
544             if (DBG) Log.d(TAG, "mWatchdogWlc about to start...");
545             mWatchdogWlc = new PresenceCheckWatchdog(presenceCheckDelay);
546             mWatchdogWlc.start();
547         }
548     }
549 
550     class PresenceCheckWatchdog extends Thread {
551         private int watchdogTimeout;
552 
553         private boolean isPresent = true;
554         private boolean isStopped = false;
555         private boolean isPaused = false;
556         private boolean doCheck = true;
557         private boolean isFull = false;
558 
PresenceCheckWatchdog(int presenceCheckDelay)559         public PresenceCheckWatchdog(int presenceCheckDelay) {
560             watchdogTimeout = presenceCheckDelay;
561         }
562 
pause()563         public synchronized void pause() {
564             isPaused = true;
565             doCheck = false;
566             this.notifyAll();
567             if (DBG) Log.d(TAG, "pause - isPaused = " + isPaused);
568         }
569 
setTimeout(int timeout)570         public synchronized void setTimeout(int timeout) {
571             if (DBG) Log.d(TAG, "PresenceCheckWatchdog watchdogTimeout " + timeout);
572             watchdogTimeout = timeout;
573         }
574 
full()575         public synchronized void full() {
576             isFull = true;
577             this.notifyAll();
578         }
579 
lost()580         public synchronized void lost() {
581             isPresent = false;
582             if (DBG) Log.d(TAG, "PresenceCheckWatchdog isPresent " + isPresent);
583             doCheck = false;
584             this.notifyAll();
585         }
586 
doResume()587         public synchronized void doResume() {
588             isPaused = false;
589             // We don't want to resume presence checking immediately,
590             // but go through at least one more wait period.
591             doCheck = false;
592             this.notifyAll();
593             if (DBG) Log.d(TAG, "doResume - isPaused = " + isPaused);
594         }
595 
end(boolean disableCallback)596         public synchronized void end(boolean disableCallback) {
597             isStopped = true;
598             if (DBG) Log.d(TAG, "PresenceCheckWatchdog end isStopped = " + isStopped);
599             doCheck = false;
600             if (disableCallback) {
601                 //  tagDisconnectedCallback = null;
602             }
603             this.notifyAll();
604         }
605 
606         @Override
run()607         public void run() {
608             synchronized (this) {
609                 if (DBG) Log.d(TAG, "Starting WLC flow");
610                 while (isPresent && !isStopped && !isFull) {
611                     if (DBG)
612                         Log.d(
613                                 TAG,
614                                 "isPresent= "
615                                         + isPresent
616                                         + " isStopped= "
617                                         + isStopped
618                                         + " isFull= "
619                                         + isFull);
620                     try {
621                         if (watchdogTimeout > 0) {
622                             this.wait(watchdogTimeout);
623                         }
624 
625                         watchdogTimeout = HandleWLCState();
626                         if (DBG) Log.d(TAG, "Next watchdog timeout : " + watchdogTimeout);
627                     } catch (InterruptedException e) {
628                         // Activity detected, loop
629                         if (DBG) Log.d(TAG, "Interrupted thread: " + WLCState);
630                     }
631                 }
632             }
633             synchronized (NfcCharging.this) {
634                 isPresent = false;
635                 NfcChargingOnGoing = false;
636                 if (DBG)
637                     Log.d(
638                             TAG,
639                             "WLC state machine interrupted, NfcChargingOnGoing is "
640                                     + NfcChargingOnGoing);
641                 resetInternalValues();
642             }
643             mLastState = DISCONNECTED;
644             WlcDeviceInfo.put(State, mLastState);
645             NfcService.getInstance().onWlcData(WlcDeviceInfo);
646             disconnectPresenceCheck();
647             if (DBG) Log.d(TAG, "disconnectPresenceCheck done");
648 
649             // Restart the polling loop
650             NfcChargingMode = false;
651             TagHandler.disconnect();
652             // Disable discovery and restart polling loop only if not screen state change pending
653             if (!NfcService.getInstance().sendScreenMessageAfterNfcCharging()) {
654                 if (DBG)
655                     Log.d(TAG, "No pending screen state change, stop Nfc charging presence check");
656                 stopNfcChargingPresenceChecking();
657             }
658 
659             if (DBG) Log.d(TAG, "Stopping background presence check");
660         }
661     }
662 
disconnectPresenceCheck()663     public boolean disconnectPresenceCheck() {
664         boolean result = false;
665         PresenceCheckWatchdog watchdog;
666         if (DBG) Log.d(TAG, "disconnectPresenceCheck");
667         synchronized (this) {
668             watchdog = mWatchdogWlc;
669         }
670         if (watchdog != null) {
671             // Watchdog has already disconnected or will do it
672             watchdog.end(false);
673             synchronized (this) {
674                 mWatchdogWlc = null;
675             }
676         }
677         result = true;
678         return result;
679     }
680 
HandleWLCState()681     public int HandleWLCState() {
682         int wt = 1;
683         switch (WLCState) {
684             case STATE_2:
685                 { // SM2
686                     if (DBG)
687                         Log.d(
688                                 TAG,
689                                 "HandleWLCState: STATE_2 (" + convert_state_2_str(STATE_2) + ")");
690                     if (mLastState != CONNECTED_CHARGING) {
691                         mLastState = CONNECTED_CHARGING;
692                         WlcDeviceInfo.put(State, mLastState);
693                         NfcService.getInstance().onWlcData(WlcDeviceInfo);
694                     }
695                     if (TagHandler != null) {
696                         if (!mFirstOccurrence) {
697                             mNdefMessage = TagHandler.getNdef();
698                         }
699 
700                         if (mNdefMessage != null) {
701                             if (!mFirstOccurrence) {
702                                 if (checkWlcCapMsg(mNdefMessage) == false) {
703                                     if (mWatchdogWlc != null) {
704                                         mWatchdogWlc.lost();
705                                     }
706                                     WLCL_Presence = false;
707                                     Log.d(TAG, " WLC_CAP : Presence Check FAILED ");
708                                     break;
709                                 }
710                             } else {
711                                 mFirstOccurrence = false;
712                             }
713 
714                             if (WlcCap_ModeReq == MODE_REQ_BATTERY_FULL) {
715                                 mWatchdogWlc.full();
716                                 NfcChargingOnGoing = false;
717                                 if (DBG)
718                                     Log.d(
719                                             TAG,
720                                             "MODE_REQ is BATTERY_FULL, NfcChargingOnGoing is "
721                                                     + NfcChargingOnGoing);
722                                 wt = TCapWt;
723 
724                                 WLCState = STATE_24;
725                                 WlcDeviceInfo.put(BatteryLevel, 0x64);
726                                 mUpdatedBatteryLevel = WlcDeviceInfo.get(BatteryLevel);
727                                 WlcDeviceInfo.put(State, mLastState);
728                                 mLastState = CONNECTED_NOT_CHARGING;
729                                 NfcService.getInstance().onWlcData(WlcDeviceInfo);
730                                 if (DBG) Log.d(TAG, " Battery full");
731                                 break;
732 
733                             } else if (WlcCap_ModeReq == MODE_REQ_STATIC
734                                     || mNativeNfcManager.isMultiTag() == true) {
735                                 if (DBG) Log.d(TAG, " Static mode");
736                                 wt = 0; // TCapWt;
737 
738                                 WLCState = STATE_6;
739                                 break;
740 
741                             } else {
742                                 if (DBG) Log.d(TAG, " Negotiated mode");
743                                 wt = 5;
744 
745                                 WLCState = STATE_8;
746                                 break;
747                             }
748                         } else {
749                             if (mWatchdogWlc != null) {
750                                 mWatchdogWlc.lost();
751                             }
752                             WLCL_Presence = false;
753                             if (DBG) Log.d(TAG, " WLC_CAP: Presence Check FAILED");
754                         }
755                     }
756                     break;
757                 }
758 
759             case STATE_6:
760                 { // SM6
761                     if (DBG)
762                         Log.d(
763                                 TAG,
764                                 "HandleWLCState: STATE_6 (" + convert_state_2_str(STATE_6) + ")");
765 
766                     WLCState = STATE_2;
767                     wt = TCapWt + 5000;
768                     startWlcPowerTransfer(WlcCtl_PowerAdjReq, WlcCap_CapWt);
769                     break;
770                 }
771 
772             case STATE_8:
773                 { // SM8
774                     if (DBG)
775                         Log.d(
776                                 TAG,
777                                 "HandleWLCState: STATE_8 (" + convert_state_2_str(STATE_8) + ")");
778 
779                     if (WlcCap_NegoWait == 1) {
780                         if (mNretry > Nwt_max) {
781                             if (mWatchdogWlc != null) {
782                                 mWatchdogWlc.lost();
783                             }
784                             WLCL_Presence = false;
785                             if (DBG) Log.d(TAG, " WLCCAP :too much retry, conclude procedure ");
786                             WLCState = STATE_2;
787                             wt = 1;
788                             break;
789                         } else {
790                             mNretry += 1;
791                             if (DBG) Log.d(TAG, "mNretry = " + mNretry);
792                             wt = TCapWt;
793                             WLCState = STATE_2;
794                             break;
795                         }
796                     }
797                     WLCState = STATE_11;
798                     wt = 5;
799 
800                     break;
801                 }
802 
803             case STATE_11:
804                 { // SM11
805                     if (DBG)
806                         Log.d(
807                                 TAG,
808                                 "HandleWLCState: STATE_11 (" + convert_state_2_str(STATE_11) + ")");
809 
810                     sendWLCPI(TagHandler, null);
811                     if (DBG) Log.d(TAG, "end writing WLCP_INFO");
812                     wt = TNdefRdWt + 20;
813                     WLCState = STATE_12;
814                     break;
815                 }
816 
817             case STATE_12:
818                 { // SM12-SM15
819                     if (DBG)
820                         Log.d(
821                                 TAG,
822                                 "HandleWLCState: STATE_12 (" + convert_state_2_str(STATE_12) + ")");
823 
824                     if (TagHandler != null) {
825                         mNdefMessage = TagHandler.getNdef();
826                         if (mNdefMessage != null) {
827                             if (checkWlcCtlMsg(mNdefMessage)) {
828                                 if (DBG)
829                                     Log.d(
830                                             TAG,
831                                             " WlcCtl_Cnt_new: "
832                                                     + WlcCtl_Cnt_new
833                                                     + "(mCnt +1)%8) = "
834                                                     + ((mCnt + 1) % 7));
835 
836                                 if (mCnt == -1) {
837                                     mCnt = WlcCtl_Cnt_new;
838                                 } else if (WlcCtl_Cnt_new == mCnt) {
839                                     if (mNwcc_retry < 3) {
840                                         wt = 30; // Twcc,retry
841                                         mNwcc_retry++;
842                                         break;
843                                     } else if (mNwcc_retry == 3) {
844                                         // go to error
845                                         if (DBG) Log.d(TAG, " WLCL_CTL : Max mNwcc_retry reached");
846                                         mNwcc_retry = 0;
847                                         if (mWatchdogWlc != null) {
848                                             mWatchdogWlc.lost();
849                                         }
850                                         break;
851                                     }
852                                 }
853                                 mNwcc_retry = 0;
854                                 mCnt = WlcCtl_Cnt_new;
855                                 if (WlcCap_RdConf == 1) {
856                                     WLCState = STATE_16;
857                                     wt = TNdefWrWt;
858                                     break;
859                                 }
860                                 wt = 1;
861                                 WLCState = STATE_17;
862                             } else {
863                                 if (mNwcc_retry < 3) {
864                                     wt = 30; // Twcc,retry
865                                     mNwcc_retry++;
866                                     break;
867                                 } else if (mNwcc_retry == 3) {
868                                     // go to error
869                                     if (DBG)
870                                         Log.d(TAG, " WLCL_CTL not valid: Max mNwcc_retry reached");
871                                     mNwcc_retry = 0;
872                                     if (mWatchdogWlc != null) {
873                                         mWatchdogWlc.lost();
874                                     }
875                                     break;
876                                 }
877 
878                                 WLCL_Presence = false;
879                                 if (DBG) Log.d(TAG, " WLCL_CTL : Presence Check Failed ");
880                             }
881                         } else {
882                             // no more tag
883                             if (mWatchdogWlc != null) {
884                                 mWatchdogWlc.lost();
885                             }
886                             WLCL_Presence = false;
887                             if (DBG) Log.d(TAG, " WLCL_CTL : Presence Check Failed ");
888                         }
889                     } else {
890                         // conclude - go to error
891                     }
892                     break;
893                 }
894 
895             case STATE_16:
896                 { // SM16
897                     if (DBG)
898                         Log.d(
899                                 TAG,
900                                 "HandleWLCState: STATE_16 (" + convert_state_2_str(STATE_16) + ")");
901 
902                     sendEmptyNdef();
903                     WLCState = STATE_17;
904                     wt = 1;
905                     break;
906                 }
907 
908             case STATE_17:
909                 { // SM17
910                     if (DBG)
911                         Log.d(
912                                 TAG,
913                                 "HandleWLCState: STATE_17 (" + convert_state_2_str(STATE_17) + ")");
914 
915                     if (WlcCtl_WptReq == 0x0) {
916                         // No Power transfer Required
917                         if (DBG) Log.d(TAG, "No power transfer required");
918                         // go to presence check SM24
919                         WLCState = STATE_24;
920                         wt = TWptDuration;
921                         if (TWptDuration > 4000) {
922                             TagHandler.startPresenceChecking(200, callbackTagDisconnection);
923                         }
924                         break;
925                     }
926 
927                     // Adjust WPT
928                     WLCState = STATE_21;
929                     wt = 1 + THoldOffWt;
930                     break;
931                 }
932 
933             case STATE_21:
934                 { // SM21
935                     if (DBG)
936                         Log.d(
937                                 TAG,
938                                 "HandleWLCState: STATE_21 (" + convert_state_2_str(STATE_21) + ")");
939 
940                     startWlcPowerTransfer(WlcCtl_PowerAdjReq, WlcCtl_WptDuration);
941                     WLCState = STATE_22;
942                     wt = TWptDuration + 5000;
943                     break;
944                 }
945 
946             case STATE_22:
947                 { // SM22
948                     if (DBG)
949                         Log.d(
950                                 TAG,
951                                 "HandleWLCState: STATE_22 (" + convert_state_2_str(STATE_22) + ")");
952 
953                     if (WlcCtl_WptInfoReq == 1) {
954                         WLCState = STATE_11;
955                         break;
956                     }
957                     WLCState = STATE_12;
958                     wt = 0;
959                     break;
960                 }
961 
962             case STATE_24:
963                 { // SM24
964                     if (DBG)
965                         Log.d(
966                                 TAG,
967                                 "HandleWLCState: STATE_24 (" + convert_state_2_str(STATE_24) + ")");
968 
969                     TagHandler.stopPresenceChecking();
970                     WLCState = STATE_2;
971                     NfcChargingOnGoing = false;
972                     if (mWatchdogWlc != null) {
973                         mWatchdogWlc.lost();
974                     }
975                     wt = 1;
976                     break;
977                 }
978             case STATE_21_1:
979                 { // Stop WPT
980                     if (DBG) Log.d(TAG, "HandleWLCState Time completed");
981                     WLCState = STATE_22;
982                     wt = 0;
983                     break;
984                 }
985             case STATE_21_2:
986                 { // Stop WPT
987                     if (DBG) Log.d(TAG, "HandleWLCState: STATE_21_2 (exit)");
988                     WLCState = STATE_2;
989                     NfcChargingOnGoing = false;
990 
991                     if (mWatchdogWlc != null) {
992                         mWatchdogWlc.lost();
993                     }
994                     wt = 0;
995                     break;
996                 }
997         }
998 
999         return wt;
1000     }
1001 
disconnectNfcCharging()1002     public void disconnectNfcCharging() {
1003         Log.d(TAG, "disconnectNfcCharging");
1004         NfcChargingOnGoing = false;
1005         NfcChargingMode = false;
1006         resetInternalValues();
1007         disconnectPresenceCheck();
1008         if (TagHandler != null) {
1009             TagHandler.disconnect();
1010         }
1011     }
1012 
onWlcStopped(int wpt_end_condition)1013     public void onWlcStopped(int wpt_end_condition) {
1014         Log.d(TAG, "onWlcStopped");
1015 
1016         switch (wpt_end_condition) {
1017             case 0x0:
1018                 // Time completed
1019                 mWatchdogWlc.setTimeout(0);
1020                 if (WlcCap_ModeReq == MODE_REQ_NEGOTIATED) {
1021                     WLCState = STATE_21_1;
1022                 } else {
1023                     WLCState = STATE_2;
1024                 }
1025                 mWatchdogWlc.interrupt();
1026                 if (DBG) Log.d(TAG, "Time completed");
1027                 break;
1028 
1029             case 0x1:
1030                 // FOD detection or Removal
1031                 mWatchdogWlc.setTimeout(0);
1032                 if (WlcCap_ModeReq == MODE_REQ_NEGOTIATED) {
1033                     WLCState = STATE_21_2;
1034                 } else {
1035                     WLCState = STATE_2;
1036                 }
1037                 mWatchdogWlc.interrupt();
1038                 if (DBG) Log.d(TAG, "FOD detection or removal");
1039                 break;
1040 
1041             case 0x3:
1042             default:
1043                 // Error
1044                 mWatchdogWlc.setTimeout(0);
1045                 WLCState = STATE_21_2;
1046                 mWatchdogWlc.interrupt();
1047                 if (DBG) Log.d(TAG, "FOD error detection");
1048                 break;
1049         }
1050     }
1051 
convert_state_2_str(int state)1052     public String convert_state_2_str(int state) {
1053         switch (state) {
1054             case STATE_2:
1055                 return "Read WLC_CAP";
1056             case STATE_6:
1057                 return "Static WPT";
1058             case STATE_8:
1059                 return "Handle NEGO_WAIT?";
1060             case STATE_11:
1061                 return "Write WLCP_INFO";
1062             case STATE_12:
1063                 return "Read WLCL_CTL";
1064             case STATE_16:
1065                 return "Read confirmation?";
1066             case STATE_17:
1067                 return "Check WPT requested?";
1068             case STATE_21:
1069                 return "Handle WPT";
1070             case STATE_22:
1071                 return "Handle INFO_REQ?";
1072             case STATE_24:
1073                 return "Handle removal detection";
1074             case STATE_21_1:
1075                 return "Handle WPT time completed";
1076             case STATE_21_2:
1077                 return "Handle FOD detection/removal";
1078 
1079             default:
1080                 return "Unknown";
1081         }
1082     }
1083 }
1084