private void executeAPDUCommands(final byte[][] commands) { reader.sleep(); cordova .getThreadPool() .execute( new Runnable() { @Override public void run() { reader.setMute(false); reader.reset( new AudioJackReader.OnResetCompleteListener() { @Override public void onResetComplete(AudioJackReader audioJackReader) { if (!reader.piccPowerOn(5, 0x8F)) Log.w(TAG, "Error"); for (byte[] command : commands) { reader.piccTransmit(5, command); } reader.piccPowerOff(); } }); } }); }
@Override public void run() { Log.w("Timing out", "Timing out"); this.controller.timedOut = true; PluginResult dataResult = new PluginResult(PluginResult.Status.OK, "TIMEDOUT"); callbackContext.sendPluginResult(dataResult); reader.reset( new AudioJackReader.OnResetCompleteListener() { @Override public void onResetComplete(AudioJackReader audioJackReader) { // reader.sleep(); } }); }
@Override public void initialize(final CordovaInterface cordova, final CordovaWebView webView) { super.initialize(cordova, webView); am = (AudioManager) cordova.getActivity().getSystemService(Context.AUDIO_SERVICE); reader = new AudioJackReader(am, true); IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_HEADSET_PLUG); cordova .getActivity() .registerReceiver( new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (intent.getAction().equals(Intent.ACTION_HEADSET_PLUG)) { boolean plugged = (intent.getIntExtra("state", 0) == 1); /* Mute the audio output if the reader is unplugged. */ reader.setMute(!plugged); mute = !plugged; } } }, filter); final StringBuffer buffer = new StringBuffer(); final StringBuffer atrBuffer = new StringBuffer(); reader.setSleepTimeout(30); reader.setOnPiccAtrAvailableListener( new AudioJackReader.OnPiccAtrAvailableListener() { @Override public void onPiccAtrAvailable(AudioJackReader audioJackReader, byte[] bytes) { Log.w(TAG, bytesToHex(bytes)); atrBuffer.append(bytesToHex(bytes)); } }); reader.setOnPiccResponseApduAvailableListener( new AudioJackReader.OnPiccResponseApduAvailableListener() { @Override public void onPiccResponseApduAvailable(AudioJackReader audioJackReader, byte[] bytes) { byte[] resultBytes = new byte[bytes.length - 2]; byte[] statusBytes = new byte[2]; System.arraycopy(bytes, 0, resultBytes, 0, bytes.length - 2); System.arraycopy(bytes, bytes.length - 2, statusBytes, 0, 2); Log.w(TAG, bytesToHex(statusBytes)); buffer.append(bytesToHex(resultBytes)); buffer.append(""); } }); reader.setOnStatusAvailableListener( new AudioJackReader.OnStatusAvailableListener() { @Override public void onStatusAvailable(AudioJackReader audioJackReader, final Status status) { timer.cancel(); cordova .getActivity() .runOnUiThread( new Runnable() { @Override public void run() { JSONArray resultArray = new JSONArray(); resultArray.put(Integer.toString(status.getBatteryLevel())); resultArray.put(Integer.toString(status.getSleepTimeout())); PluginResult dataResult = new PluginResult(PluginResult.Status.OK, resultArray); callbackContext.sendPluginResult(dataResult); } }); } }); reader.setOnDeviceIdAvailableListener( new AudioJackReader.OnDeviceIdAvailableListener() { @Override public void onDeviceIdAvailable(AudioJackReader audioJackReader, final byte[] bytes) { // reader.sleep(); timer.cancel(); cordova .getActivity() .runOnUiThread( new Runnable() { @Override public void run() { PluginResult dataResult = new PluginResult(PluginResult.Status.OK, bytesToHex(bytes)); callbackContext.sendPluginResult(dataResult); } }); } }); reader.setOnResultAvailableListener( new AudioJackReader.OnResultAvailableListener() { @Override public void onResultAvailable(AudioJackReader audioJackReader, Result result) { // reader.sleep(); timer.cancel(); final String stringResult = buffer.toString(); final String atrResult = atrBuffer.toString(); Log.i(TAG, "Result Available"); Log.i(TAG, stringResult); cordova .getActivity() .runOnUiThread( new Runnable() { @Override public void run() { if (timedOut) { PluginResult dataResult = new PluginResult(PluginResult.Status.OK, "TIMEDOUT"); callbackContext.sendPluginResult(dataResult); } else { JSONArray resultArray = new JSONArray(); resultArray.put(stringResult.replaceAll("\\s", "")); resultArray.put(atrResult.replaceAll("\\s", "")); PluginResult dataResult = new PluginResult(PluginResult.Status.OK, resultArray); callbackContext.sendPluginResult(dataResult); } } }); buffer.delete(0, buffer.length()); atrBuffer.delete(0, atrBuffer.length()); } }); }
@Override public boolean execute(String action, JSONArray data, CallbackContext callbackContext) throws JSONException { reader.start(); am.setStreamVolume( AudioManager.STREAM_MUSIC, am.getStreamMaxVolume(AudioManager.STREAM_MUSIC), 0); this.callbackContext = callbackContext; timedOut = false; Log.w(TAG, action); if (mute) { PluginResult dataResult = new PluginResult(PluginResult.Status.OK, "NOTFOUND"); callbackContext.sendPluginResult(dataResult); return true; } final ACR35Controller self = this; final String loadKeyCommand = "FF 82 00 00 06 %s"; final String authCommand = "FF 86 00 00 05 01 00 %s 60 00"; final String defaultKey = "FF FF FF FF FF FF"; String authKeyCommand = "FF 86 00 00 05 01 00 00 60 00"; if (action.equals("getDeviceId")) { cordova .getThreadPool() .execute( new Runnable() { @Override public void run() { reader.setMute(false); reader.reset( new AudioJackReader.OnResetCompleteListener() { @Override public void onResetComplete(AudioJackReader audioJackReader) { reader.getDeviceId(); } }); } }); } if (action.equals("getDeviceStatus")) { cordova .getThreadPool() .execute( new Runnable() { @Override public void run() { reader.setMute(false); reader.reset( new AudioJackReader.OnResetCompleteListener() { @Override public void onResetComplete(AudioJackReader audioJackReader) { reader.getStatus(); } }); } }); } if (action.equals("readIdFromTag")) { executeAPDUCommands(new byte[][] {hexToBytes("FFCA000000")}); } if (action.equals("readDataFromTag")) { executeAPDUCommands( new byte[][] { hexToBytes(String.format(loadKeyCommand, defaultKey)), hexToBytes(String.format(authCommand, "04")), hexToBytes("FF B0 00 04 10"), hexToBytes("FF B0 00 05 10"), hexToBytes("FF B0 00 06 10"), hexToBytes(String.format(authCommand, "08")), hexToBytes("FF B0 00 08 10"), hexToBytes("FF B0 00 09 10"), hexToBytes("FF B0 00 0A 10"), hexToBytes(String.format(authCommand, "10")), hexToBytes("FF B0 00 10 10"), hexToBytes("FF B0 00 11 10") }); } if (action.equals("writeDataIntoTag")) { try { String dataString = data.get(0).toString(); byte[] dataToWrite = new byte[128]; Arrays.fill(dataToWrite, (byte) 0); byte[] dataBytes = hexToBytes(dataString); System.arraycopy(dataBytes, 0, dataToWrite, 0, dataBytes.length); String dataStringToWrite = bytesToHex(dataToWrite).replaceAll("\\s", ""); String commandString1 = "FF D6 00 04 30" + dataStringToWrite.substring(0, 95); String commandString2 = "FF D6 00 08 30" + dataStringToWrite.substring(96, (96 * 2) - 1); String commandString3 = "FF D6 00 10 20" + dataStringToWrite.substring(96 * 2, (96 * 2 + 64) - 1); Log.w(TAG, dataStringToWrite); executeAPDUCommands( new byte[][] { hexToBytes(String.format(loadKeyCommand, defaultKey)), hexToBytes(String.format(authCommand, "04")), hexToBytes(commandString1), hexToBytes(String.format(authCommand, "08")), hexToBytes(commandString2), hexToBytes(String.format(authCommand, "10")), hexToBytes(commandString3), }); } catch (java.lang.Exception e) { Log.w(TAG, e); } } Calendar calendar = Calendar.getInstance(); // gets a calendar using the default time zone and locale. calendar.add(Calendar.SECOND, timeOut); timer = new Timer(); timer.schedule(new TimeoutClass(reader, self), calendar.getTime()); PluginResult dataResult = new PluginResult(PluginResult.Status.OK, "IGNORE"); dataResult.setKeepCallback(true); callbackContext.sendPluginResult(dataResult); return true; }