1388fa40468c01b8959ae4bac688241b6d362820fJun Yin/* 2388fa40468c01b8959ae4bac688241b6d362820fJun Yin * Copyright (C) 2018 The Android Open Source Project 3388fa40468c01b8959ae4bac688241b6d362820fJun Yin * 4388fa40468c01b8959ae4bac688241b6d362820fJun Yin * Licensed under the Apache License, Version 2.0 (the "License"); 5388fa40468c01b8959ae4bac688241b6d362820fJun Yin * you may not use this file except in compliance with the License. 6388fa40468c01b8959ae4bac688241b6d362820fJun Yin * You may obtain a copy of the License at 7388fa40468c01b8959ae4bac688241b6d362820fJun Yin * 8388fa40468c01b8959ae4bac688241b6d362820fJun Yin * http://www.apache.org/licenses/LICENSE-2.0 9388fa40468c01b8959ae4bac688241b6d362820fJun Yin * 10388fa40468c01b8959ae4bac688241b6d362820fJun Yin * Unless required by applicable law or agreed to in writing, software 11388fa40468c01b8959ae4bac688241b6d362820fJun Yin * distributed under the License is distributed on an "AS IS" BASIS, 12388fa40468c01b8959ae4bac688241b6d362820fJun Yin * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13388fa40468c01b8959ae4bac688241b6d362820fJun Yin * See the License for the specific language governing permissions and 14388fa40468c01b8959ae4bac688241b6d362820fJun Yin * limitations under the License. 15388fa40468c01b8959ae4bac688241b6d362820fJun Yin */ 16388fa40468c01b8959ae4bac688241b6d362820fJun Yin 17388fa40468c01b8959ae4bac688241b6d362820fJun Yinpackage com.android.internal.telephony.uicc.euicc.apdu; 18388fa40468c01b8959ae4bac688241b6d362820fJun Yin 19388fa40468c01b8959ae4bac688241b6d362820fJun Yinimport static org.junit.Assert.assertEquals; 20388fa40468c01b8959ae4bac688241b6d362820fJun Yinimport static org.junit.Assert.assertNull; 21388fa40468c01b8959ae4bac688241b6d362820fJun Yinimport static org.junit.Assert.assertTrue; 22388fa40468c01b8959ae4bac688241b6d362820fJun Yinimport static org.junit.Assert.fail; 23388fa40468c01b8959ae4bac688241b6d362820fJun Yinimport static org.mockito.ArgumentMatchers.any; 24388fa40468c01b8959ae4bac688241b6d362820fJun Yinimport static org.mockito.ArgumentMatchers.anyInt; 25388fa40468c01b8959ae4bac688241b6d362820fJun Yinimport static org.mockito.ArgumentMatchers.eq; 26388fa40468c01b8959ae4bac688241b6d362820fJun Yinimport static org.mockito.Mockito.never; 27388fa40468c01b8959ae4bac688241b6d362820fJun Yinimport static org.mockito.Mockito.times; 28388fa40468c01b8959ae4bac688241b6d362820fJun Yinimport static org.mockito.Mockito.verify; 29388fa40468c01b8959ae4bac688241b6d362820fJun Yin 30388fa40468c01b8959ae4bac688241b6d362820fJun Yinimport android.os.Handler; 31388fa40468c01b8959ae4bac688241b6d362820fJun Yinimport android.os.HandlerThread; 32388fa40468c01b8959ae4bac688241b6d362820fJun Yin 33388fa40468c01b8959ae4bac688241b6d362820fJun Yinimport com.android.internal.telephony.CommandException; 34388fa40468c01b8959ae4bac688241b6d362820fJun Yinimport com.android.internal.telephony.CommandsInterface; 35388fa40468c01b8959ae4bac688241b6d362820fJun Yinimport com.android.internal.telephony.uicc.IccUtils; 36388fa40468c01b8959ae4bac688241b6d362820fJun Yinimport com.android.internal.telephony.uicc.euicc.async.AsyncResultCallback; 37388fa40468c01b8959ae4bac688241b6d362820fJun Yin 38388fa40468c01b8959ae4bac688241b6d362820fJun Yinimport org.junit.After; 39388fa40468c01b8959ae4bac688241b6d362820fJun Yinimport org.junit.Before; 40388fa40468c01b8959ae4bac688241b6d362820fJun Yinimport org.junit.Test; 41388fa40468c01b8959ae4bac688241b6d362820fJun Yinimport org.mockito.Mock; 42388fa40468c01b8959ae4bac688241b6d362820fJun Yinimport org.mockito.MockitoAnnotations; 43388fa40468c01b8959ae4bac688241b6d362820fJun Yin 44388fa40468c01b8959ae4bac688241b6d362820fJun Yinimport java.util.concurrent.CountDownLatch; 45388fa40468c01b8959ae4bac688241b6d362820fJun Yinimport java.util.concurrent.TimeUnit; 46388fa40468c01b8959ae4bac688241b6d362820fJun Yin 47388fa40468c01b8959ae4bac688241b6d362820fJun Yinpublic class ApduSenderTest { 48388fa40468c01b8959ae4bac688241b6d362820fJun Yin private static final long WAIT_TIMEOUT_MLLIS = 5000; 49388fa40468c01b8959ae4bac688241b6d362820fJun Yin 50388fa40468c01b8959ae4bac688241b6d362820fJun Yin private static class ResponseCaptor extends AsyncResultCallback<byte[]> { 51388fa40468c01b8959ae4bac688241b6d362820fJun Yin public byte[] response; 52388fa40468c01b8959ae4bac688241b6d362820fJun Yin public Throwable exception; 53388fa40468c01b8959ae4bac688241b6d362820fJun Yin 54388fa40468c01b8959ae4bac688241b6d362820fJun Yin private final CountDownLatch mLatch = new CountDownLatch(1); 55388fa40468c01b8959ae4bac688241b6d362820fJun Yin 56388fa40468c01b8959ae4bac688241b6d362820fJun Yin public void await() { 57388fa40468c01b8959ae4bac688241b6d362820fJun Yin try { 58388fa40468c01b8959ae4bac688241b6d362820fJun Yin if (!mLatch.await(WAIT_TIMEOUT_MLLIS, TimeUnit.MILLISECONDS)) { 59388fa40468c01b8959ae4bac688241b6d362820fJun Yin fail("Execution timed out."); 60388fa40468c01b8959ae4bac688241b6d362820fJun Yin } 61388fa40468c01b8959ae4bac688241b6d362820fJun Yin } catch (InterruptedException e) { 62388fa40468c01b8959ae4bac688241b6d362820fJun Yin fail("Execution interrupted: " + e); 63388fa40468c01b8959ae4bac688241b6d362820fJun Yin } 64388fa40468c01b8959ae4bac688241b6d362820fJun Yin } 65388fa40468c01b8959ae4bac688241b6d362820fJun Yin 66388fa40468c01b8959ae4bac688241b6d362820fJun Yin @Override 67388fa40468c01b8959ae4bac688241b6d362820fJun Yin public void onResult(byte[] bytes) { 68388fa40468c01b8959ae4bac688241b6d362820fJun Yin response = bytes; 69388fa40468c01b8959ae4bac688241b6d362820fJun Yin mLatch.countDown(); 70388fa40468c01b8959ae4bac688241b6d362820fJun Yin } 71388fa40468c01b8959ae4bac688241b6d362820fJun Yin 72388fa40468c01b8959ae4bac688241b6d362820fJun Yin @Override 73388fa40468c01b8959ae4bac688241b6d362820fJun Yin public void onException(Throwable e) { 74388fa40468c01b8959ae4bac688241b6d362820fJun Yin exception = e; 75388fa40468c01b8959ae4bac688241b6d362820fJun Yin mLatch.countDown(); 76388fa40468c01b8959ae4bac688241b6d362820fJun Yin } 77388fa40468c01b8959ae4bac688241b6d362820fJun Yin } 78388fa40468c01b8959ae4bac688241b6d362820fJun Yin 79388fa40468c01b8959ae4bac688241b6d362820fJun Yin @Mock 80388fa40468c01b8959ae4bac688241b6d362820fJun Yin private CommandsInterface mMockCi; 81388fa40468c01b8959ae4bac688241b6d362820fJun Yin 82388fa40468c01b8959ae4bac688241b6d362820fJun Yin private HandlerThread mThread; 83388fa40468c01b8959ae4bac688241b6d362820fJun Yin private Handler mHandler; 84388fa40468c01b8959ae4bac688241b6d362820fJun Yin private ResponseCaptor mResponseCaptor; 85388fa40468c01b8959ae4bac688241b6d362820fJun Yin private byte[] mSelectResponse; 86388fa40468c01b8959ae4bac688241b6d362820fJun Yin 87388fa40468c01b8959ae4bac688241b6d362820fJun Yin @Before 88388fa40468c01b8959ae4bac688241b6d362820fJun Yin public void setUp() { 89388fa40468c01b8959ae4bac688241b6d362820fJun Yin MockitoAnnotations.initMocks(this); 90388fa40468c01b8959ae4bac688241b6d362820fJun Yin 91388fa40468c01b8959ae4bac688241b6d362820fJun Yin mThread = new HandlerThread("ApduSenderTest"); 92388fa40468c01b8959ae4bac688241b6d362820fJun Yin mThread.start(); 93388fa40468c01b8959ae4bac688241b6d362820fJun Yin mHandler = mThread.getThreadHandler(); 94388fa40468c01b8959ae4bac688241b6d362820fJun Yin 95388fa40468c01b8959ae4bac688241b6d362820fJun Yin mResponseCaptor = new ResponseCaptor(); 96388fa40468c01b8959ae4bac688241b6d362820fJun Yin mSelectResponse = null; 97388fa40468c01b8959ae4bac688241b6d362820fJun Yin } 98388fa40468c01b8959ae4bac688241b6d362820fJun Yin 99388fa40468c01b8959ae4bac688241b6d362820fJun Yin @After 100388fa40468c01b8959ae4bac688241b6d362820fJun Yin public void tearDown() { 101388fa40468c01b8959ae4bac688241b6d362820fJun Yin mThread.quit(); 102388fa40468c01b8959ae4bac688241b6d362820fJun Yin } 103388fa40468c01b8959ae4bac688241b6d362820fJun Yin 104388fa40468c01b8959ae4bac688241b6d362820fJun Yin @Test 105388fa40468c01b8959ae4bac688241b6d362820fJun Yin public void testSendEmptyCommands() throws InterruptedException { 106388fa40468c01b8959ae4bac688241b6d362820fJun Yin String aid = "B2C3D4"; 107388fa40468c01b8959ae4bac688241b6d362820fJun Yin ApduSender sender = new ApduSender(mMockCi, aid, false /* supportExtendedApdu */); 108388fa40468c01b8959ae4bac688241b6d362820fJun Yin 109388fa40468c01b8959ae4bac688241b6d362820fJun Yin int channel = LogicalChannelMocker.mockOpenLogicalChannelResponse(mMockCi, "A1A1A19000"); 110388fa40468c01b8959ae4bac688241b6d362820fJun Yin LogicalChannelMocker.mockCloseLogicalChannel(mMockCi, channel); 111388fa40468c01b8959ae4bac688241b6d362820fJun Yin 112388fa40468c01b8959ae4bac688241b6d362820fJun Yin sender.send((selectResponse, requestBuilder) -> mSelectResponse = selectResponse, 113388fa40468c01b8959ae4bac688241b6d362820fJun Yin mResponseCaptor, mHandler); 114388fa40468c01b8959ae4bac688241b6d362820fJun Yin mResponseCaptor.await(); 115388fa40468c01b8959ae4bac688241b6d362820fJun Yin 116388fa40468c01b8959ae4bac688241b6d362820fJun Yin assertEquals("A1A1A19000", IccUtils.bytesToHexString(mSelectResponse)); 117388fa40468c01b8959ae4bac688241b6d362820fJun Yin assertNull(mResponseCaptor.response); 118388fa40468c01b8959ae4bac688241b6d362820fJun Yin assertNull(mResponseCaptor.exception); 119388fa40468c01b8959ae4bac688241b6d362820fJun Yin verify(mMockCi).iccOpenLogicalChannel(eq(aid), anyInt(), any()); 120388fa40468c01b8959ae4bac688241b6d362820fJun Yin verify(mMockCi).iccCloseLogicalChannel(eq(channel), any()); 121388fa40468c01b8959ae4bac688241b6d362820fJun Yin } 122388fa40468c01b8959ae4bac688241b6d362820fJun Yin 123388fa40468c01b8959ae4bac688241b6d362820fJun Yin @Test 124388fa40468c01b8959ae4bac688241b6d362820fJun Yin public void testOpenChannelErrorStatus() throws InterruptedException { 125388fa40468c01b8959ae4bac688241b6d362820fJun Yin String aid = "B2C3D4"; 126388fa40468c01b8959ae4bac688241b6d362820fJun Yin ApduSender sender = new ApduSender(mMockCi, aid, false /* supportExtendedApdu */); 127388fa40468c01b8959ae4bac688241b6d362820fJun Yin 128388fa40468c01b8959ae4bac688241b6d362820fJun Yin LogicalChannelMocker.mockOpenLogicalChannelResponse(mMockCi, 129388fa40468c01b8959ae4bac688241b6d362820fJun Yin new CommandException(CommandException.Error.NO_SUCH_ELEMENT)); 130388fa40468c01b8959ae4bac688241b6d362820fJun Yin 131388fa40468c01b8959ae4bac688241b6d362820fJun Yin sender.send((selectResponse, requestBuilder) -> mSelectResponse = new byte[0], 132388fa40468c01b8959ae4bac688241b6d362820fJun Yin mResponseCaptor, mHandler); 133388fa40468c01b8959ae4bac688241b6d362820fJun Yin mResponseCaptor.await(); 134388fa40468c01b8959ae4bac688241b6d362820fJun Yin 135388fa40468c01b8959ae4bac688241b6d362820fJun Yin assertNull("Request provider should not be called when failed to open channel.", 136388fa40468c01b8959ae4bac688241b6d362820fJun Yin mSelectResponse); 137388fa40468c01b8959ae4bac688241b6d362820fJun Yin assertTrue(mResponseCaptor.exception instanceof ApduException); 138388fa40468c01b8959ae4bac688241b6d362820fJun Yin verify(mMockCi).iccOpenLogicalChannel(eq(aid), anyInt(), any()); 139388fa40468c01b8959ae4bac688241b6d362820fJun Yin } 140388fa40468c01b8959ae4bac688241b6d362820fJun Yin 141388fa40468c01b8959ae4bac688241b6d362820fJun Yin @Test 142388fa40468c01b8959ae4bac688241b6d362820fJun Yin public void testSend() throws InterruptedException { 143388fa40468c01b8959ae4bac688241b6d362820fJun Yin String aid = "B2C3D4"; 144388fa40468c01b8959ae4bac688241b6d362820fJun Yin ApduSender sender = new ApduSender(mMockCi, aid, false /* supportExtendedApdu */); 145388fa40468c01b8959ae4bac688241b6d362820fJun Yin 146388fa40468c01b8959ae4bac688241b6d362820fJun Yin int channel = LogicalChannelMocker.mockOpenLogicalChannelResponse(mMockCi, "9000"); 147388fa40468c01b8959ae4bac688241b6d362820fJun Yin LogicalChannelMocker.mockSendToLogicalChannel(mMockCi, channel, "A1A1A19000"); 148388fa40468c01b8959ae4bac688241b6d362820fJun Yin LogicalChannelMocker.mockCloseLogicalChannel(mMockCi, channel); 149388fa40468c01b8959ae4bac688241b6d362820fJun Yin 150388fa40468c01b8959ae4bac688241b6d362820fJun Yin sender.send((selectResponse, requestBuilder) -> requestBuilder.addApdu(10, 1, 2, 3, 0, "a"), 151388fa40468c01b8959ae4bac688241b6d362820fJun Yin mResponseCaptor, mHandler); 152388fa40468c01b8959ae4bac688241b6d362820fJun Yin mResponseCaptor.await(); 153388fa40468c01b8959ae4bac688241b6d362820fJun Yin 154388fa40468c01b8959ae4bac688241b6d362820fJun Yin assertEquals("A1A1A1", IccUtils.bytesToHexString(mResponseCaptor.response)); 155388fa40468c01b8959ae4bac688241b6d362820fJun Yin verify(mMockCi).iccTransmitApduLogicalChannel(eq(channel), eq(channel | 10), eq(1), eq(2), 156388fa40468c01b8959ae4bac688241b6d362820fJun Yin eq(3), eq(0), eq("a"), any()); 157388fa40468c01b8959ae4bac688241b6d362820fJun Yin } 158388fa40468c01b8959ae4bac688241b6d362820fJun Yin 159388fa40468c01b8959ae4bac688241b6d362820fJun Yin @Test 160388fa40468c01b8959ae4bac688241b6d362820fJun Yin public void testSendMultiApdus() throws InterruptedException { 161388fa40468c01b8959ae4bac688241b6d362820fJun Yin String aid = "B2C3D4"; 162388fa40468c01b8959ae4bac688241b6d362820fJun Yin ApduSender sender = new ApduSender(mMockCi, aid, false /* supportExtendedApdu */); 163388fa40468c01b8959ae4bac688241b6d362820fJun Yin 164388fa40468c01b8959ae4bac688241b6d362820fJun Yin int channel = LogicalChannelMocker.mockOpenLogicalChannelResponse(mMockCi, "9000"); 165388fa40468c01b8959ae4bac688241b6d362820fJun Yin LogicalChannelMocker.mockSendToLogicalChannel(mMockCi, channel, "A19000", "A29000", 166388fa40468c01b8959ae4bac688241b6d362820fJun Yin "A39000", "A49000"); 167388fa40468c01b8959ae4bac688241b6d362820fJun Yin LogicalChannelMocker.mockCloseLogicalChannel(mMockCi, channel); 168388fa40468c01b8959ae4bac688241b6d362820fJun Yin 169388fa40468c01b8959ae4bac688241b6d362820fJun Yin sender.send((selectResponse, requestBuilder) -> { 170388fa40468c01b8959ae4bac688241b6d362820fJun Yin requestBuilder.addApdu(10, 1, 2, 3, 0, "a"); 171388fa40468c01b8959ae4bac688241b6d362820fJun Yin requestBuilder.addApdu(10, 1, 2, 3, "ab"); 172388fa40468c01b8959ae4bac688241b6d362820fJun Yin requestBuilder.addApdu(10, 1, 2, 3); 173388fa40468c01b8959ae4bac688241b6d362820fJun Yin requestBuilder.addStoreData("abcd"); 174388fa40468c01b8959ae4bac688241b6d362820fJun Yin }, mResponseCaptor, mHandler); 175388fa40468c01b8959ae4bac688241b6d362820fJun Yin mResponseCaptor.await(); 176388fa40468c01b8959ae4bac688241b6d362820fJun Yin 177388fa40468c01b8959ae4bac688241b6d362820fJun Yin assertEquals("A4", IccUtils.bytesToHexString(mResponseCaptor.response)); 178388fa40468c01b8959ae4bac688241b6d362820fJun Yin verify(mMockCi).iccTransmitApduLogicalChannel(eq(channel), eq(channel | 10), eq(1), eq(2), 179388fa40468c01b8959ae4bac688241b6d362820fJun Yin eq(3), eq(0), eq("a"), any()); 180388fa40468c01b8959ae4bac688241b6d362820fJun Yin verify(mMockCi).iccTransmitApduLogicalChannel(eq(channel), eq(channel | 10), eq(1), eq(2), 181388fa40468c01b8959ae4bac688241b6d362820fJun Yin eq(3), eq(1), eq("ab"), any()); 182388fa40468c01b8959ae4bac688241b6d362820fJun Yin verify(mMockCi).iccTransmitApduLogicalChannel(eq(channel), eq(channel | 10), eq(1), eq(2), 183388fa40468c01b8959ae4bac688241b6d362820fJun Yin eq(3), eq(0), eq(""), any()); 184388fa40468c01b8959ae4bac688241b6d362820fJun Yin verify(mMockCi).iccTransmitApduLogicalChannel(eq(channel), eq(0x81), eq(0xE2), eq(0x91), 185388fa40468c01b8959ae4bac688241b6d362820fJun Yin eq(0), eq(2), eq("abcd"), any()); 186388fa40468c01b8959ae4bac688241b6d362820fJun Yin } 187388fa40468c01b8959ae4bac688241b6d362820fJun Yin 188388fa40468c01b8959ae4bac688241b6d362820fJun Yin @Test 189388fa40468c01b8959ae4bac688241b6d362820fJun Yin public void testSendLongResponse() throws InterruptedException { 190388fa40468c01b8959ae4bac688241b6d362820fJun Yin String aid = "B2C3D4"; 191388fa40468c01b8959ae4bac688241b6d362820fJun Yin ApduSender sender = new ApduSender(mMockCi, aid, false /* supportExtendedApdu */); 192388fa40468c01b8959ae4bac688241b6d362820fJun Yin 193388fa40468c01b8959ae4bac688241b6d362820fJun Yin int channel = LogicalChannelMocker.mockOpenLogicalChannelResponse(mMockCi, "9000"); 194388fa40468c01b8959ae4bac688241b6d362820fJun Yin LogicalChannelMocker.mockSendToLogicalChannel(mMockCi, channel, "A1A1A16104", 195388fa40468c01b8959ae4bac688241b6d362820fJun Yin "B2B2B2B26102", "C3C39000"); 196388fa40468c01b8959ae4bac688241b6d362820fJun Yin LogicalChannelMocker.mockCloseLogicalChannel(mMockCi, channel); 197388fa40468c01b8959ae4bac688241b6d362820fJun Yin 198388fa40468c01b8959ae4bac688241b6d362820fJun Yin sender.send((selectResponse, requestBuilder) -> requestBuilder.addApdu(10, 1, 2, 3, 0, "a"), 199388fa40468c01b8959ae4bac688241b6d362820fJun Yin mResponseCaptor, mHandler); 200388fa40468c01b8959ae4bac688241b6d362820fJun Yin mResponseCaptor.await(); 201388fa40468c01b8959ae4bac688241b6d362820fJun Yin 202388fa40468c01b8959ae4bac688241b6d362820fJun Yin assertEquals("A1A1A1B2B2B2B2C3C3", IccUtils.bytesToHexString(mResponseCaptor.response)); 203388fa40468c01b8959ae4bac688241b6d362820fJun Yin verify(mMockCi).iccTransmitApduLogicalChannel(eq(channel), eq(channel | 10), eq(1), eq(2), 204388fa40468c01b8959ae4bac688241b6d362820fJun Yin eq(3), eq(0), eq("a"), any()); 205388fa40468c01b8959ae4bac688241b6d362820fJun Yin verify(mMockCi).iccTransmitApduLogicalChannel(eq(channel), eq(channel), eq(0xC0), eq(0), 206388fa40468c01b8959ae4bac688241b6d362820fJun Yin eq(0), eq(4), eq(""), any()); 207388fa40468c01b8959ae4bac688241b6d362820fJun Yin verify(mMockCi).iccTransmitApduLogicalChannel(eq(channel), eq(channel), eq(0xC0), eq(0), 208388fa40468c01b8959ae4bac688241b6d362820fJun Yin eq(0), eq(2), eq(""), any()); 209388fa40468c01b8959ae4bac688241b6d362820fJun Yin } 210388fa40468c01b8959ae4bac688241b6d362820fJun Yin 211388fa40468c01b8959ae4bac688241b6d362820fJun Yin @Test 212388fa40468c01b8959ae4bac688241b6d362820fJun Yin public void testSendStoreDataLongDataLongResponse() throws InterruptedException { 213388fa40468c01b8959ae4bac688241b6d362820fJun Yin String aid = "B2C3D4"; 214388fa40468c01b8959ae4bac688241b6d362820fJun Yin ApduSender sender = new ApduSender(mMockCi, aid, false /* supportExtendedApdu */); 215388fa40468c01b8959ae4bac688241b6d362820fJun Yin 216388fa40468c01b8959ae4bac688241b6d362820fJun Yin int channel = LogicalChannelMocker.mockOpenLogicalChannelResponse(mMockCi, "9000"); 217388fa40468c01b8959ae4bac688241b6d362820fJun Yin LogicalChannelMocker.mockSendToLogicalChannel(mMockCi, channel, "A19000", "9000", "9000", 218388fa40468c01b8959ae4bac688241b6d362820fJun Yin "B22B6103", "B2222B9000", "C39000"); 219388fa40468c01b8959ae4bac688241b6d362820fJun Yin LogicalChannelMocker.mockCloseLogicalChannel(mMockCi, channel); 220388fa40468c01b8959ae4bac688241b6d362820fJun Yin 221388fa40468c01b8959ae4bac688241b6d362820fJun Yin // Each segment has 0xFF (the limit of a single command) bytes. 222388fa40468c01b8959ae4bac688241b6d362820fJun Yin String s1 = new String(new char[0xFF]).replace("\0", "AA"); 223388fa40468c01b8959ae4bac688241b6d362820fJun Yin String s2 = new String(new char[0xFF]).replace("\0", "BB"); 224388fa40468c01b8959ae4bac688241b6d362820fJun Yin String s3 = new String(new char[16]).replace("\0", "CC"); 225388fa40468c01b8959ae4bac688241b6d362820fJun Yin String longData = s1 + s2 + s3; 226388fa40468c01b8959ae4bac688241b6d362820fJun Yin sender.send((selectResponse, requestBuilder) -> { 227388fa40468c01b8959ae4bac688241b6d362820fJun Yin requestBuilder.addApdu(10, 1, 2, 3, 0, "a"); 228388fa40468c01b8959ae4bac688241b6d362820fJun Yin requestBuilder.addApdu(10, 1, 2, 3, 0, "b"); 229388fa40468c01b8959ae4bac688241b6d362820fJun Yin requestBuilder.addStoreData(longData); 230388fa40468c01b8959ae4bac688241b6d362820fJun Yin }, mResponseCaptor, mHandler); 231388fa40468c01b8959ae4bac688241b6d362820fJun Yin mResponseCaptor.await(); 232388fa40468c01b8959ae4bac688241b6d362820fJun Yin 233388fa40468c01b8959ae4bac688241b6d362820fJun Yin assertEquals("C3", IccUtils.bytesToHexString(mResponseCaptor.response)); 234388fa40468c01b8959ae4bac688241b6d362820fJun Yin verify(mMockCi).iccTransmitApduLogicalChannel(eq(channel), eq(channel | 10), eq(1), eq(2), 235388fa40468c01b8959ae4bac688241b6d362820fJun Yin eq(3), eq(0), eq("a"), any()); 236388fa40468c01b8959ae4bac688241b6d362820fJun Yin verify(mMockCi).iccTransmitApduLogicalChannel(eq(channel), eq(channel | 10), eq(1), eq(2), 237388fa40468c01b8959ae4bac688241b6d362820fJun Yin eq(3), eq(0), eq("b"), any()); 238388fa40468c01b8959ae4bac688241b6d362820fJun Yin verify(mMockCi).iccTransmitApduLogicalChannel(eq(channel), eq(0x81), eq(0xE2), eq(0x11), 239388fa40468c01b8959ae4bac688241b6d362820fJun Yin eq(0), eq(0xFF), eq(s1), any()); 240388fa40468c01b8959ae4bac688241b6d362820fJun Yin verify(mMockCi).iccTransmitApduLogicalChannel(eq(channel), eq(0x81), eq(0xE2), eq(0x11), 241388fa40468c01b8959ae4bac688241b6d362820fJun Yin eq(1), eq(0xFF), eq(s2), any()); 242388fa40468c01b8959ae4bac688241b6d362820fJun Yin verify(mMockCi).iccTransmitApduLogicalChannel(eq(channel), eq(0x81), eq(0xE2), eq(0x91), 243388fa40468c01b8959ae4bac688241b6d362820fJun Yin eq(2), eq(16), eq(s3), any()); 244388fa40468c01b8959ae4bac688241b6d362820fJun Yin } 245388fa40468c01b8959ae4bac688241b6d362820fJun Yin 246388fa40468c01b8959ae4bac688241b6d362820fJun Yin @Test 247d687148b8a2627ba49296e28d38580697940c5e7Holly Jiuyu Sun public void testSendStoreDataLongDataMod0() throws InterruptedException { 248d687148b8a2627ba49296e28d38580697940c5e7Holly Jiuyu Sun String aid = "B2C3D4"; 249d687148b8a2627ba49296e28d38580697940c5e7Holly Jiuyu Sun ApduSender sender = new ApduSender(mMockCi, aid, false /* supportExtendedApdu */); 250d687148b8a2627ba49296e28d38580697940c5e7Holly Jiuyu Sun 251d687148b8a2627ba49296e28d38580697940c5e7Holly Jiuyu Sun int channel = LogicalChannelMocker.mockOpenLogicalChannelResponse(mMockCi, "9000"); 252d687148b8a2627ba49296e28d38580697940c5e7Holly Jiuyu Sun LogicalChannelMocker.mockSendToLogicalChannel(mMockCi, channel, "9000", "B2222B9000"); 253d687148b8a2627ba49296e28d38580697940c5e7Holly Jiuyu Sun LogicalChannelMocker.mockCloseLogicalChannel(mMockCi, channel); 254d687148b8a2627ba49296e28d38580697940c5e7Holly Jiuyu Sun 255d687148b8a2627ba49296e28d38580697940c5e7Holly Jiuyu Sun // Each segment has 0xFF (the limit of a single command) bytes. 256d687148b8a2627ba49296e28d38580697940c5e7Holly Jiuyu Sun String s1 = new String(new char[0xFF]).replace("\0", "AA"); 257d687148b8a2627ba49296e28d38580697940c5e7Holly Jiuyu Sun String s2 = new String(new char[0xFF]).replace("\0", "BB"); 258d687148b8a2627ba49296e28d38580697940c5e7Holly Jiuyu Sun String longData = s1 + s2; 259d687148b8a2627ba49296e28d38580697940c5e7Holly Jiuyu Sun sender.send((selectResponse, requestBuilder) -> { 260d687148b8a2627ba49296e28d38580697940c5e7Holly Jiuyu Sun requestBuilder.addStoreData(longData); 261d687148b8a2627ba49296e28d38580697940c5e7Holly Jiuyu Sun }, mResponseCaptor, mHandler); 262d687148b8a2627ba49296e28d38580697940c5e7Holly Jiuyu Sun mResponseCaptor.await(); 263d687148b8a2627ba49296e28d38580697940c5e7Holly Jiuyu Sun 264d687148b8a2627ba49296e28d38580697940c5e7Holly Jiuyu Sun assertEquals("B2222B", IccUtils.bytesToHexString(mResponseCaptor.response)); 265d687148b8a2627ba49296e28d38580697940c5e7Holly Jiuyu Sun verify(mMockCi).iccTransmitApduLogicalChannel(eq(channel), eq(0x81), eq(0xE2), eq(0x11), 266d687148b8a2627ba49296e28d38580697940c5e7Holly Jiuyu Sun eq(0), eq(0xFF), eq(s1), any()); 267d687148b8a2627ba49296e28d38580697940c5e7Holly Jiuyu Sun verify(mMockCi).iccTransmitApduLogicalChannel(eq(channel), eq(0x81), eq(0xE2), eq(0x91), 268d687148b8a2627ba49296e28d38580697940c5e7Holly Jiuyu Sun eq(1), eq(0xFF), eq(s2), any()); 269d687148b8a2627ba49296e28d38580697940c5e7Holly Jiuyu Sun } 270d687148b8a2627ba49296e28d38580697940c5e7Holly Jiuyu Sun 271d687148b8a2627ba49296e28d38580697940c5e7Holly Jiuyu Sun @Test 272d687148b8a2627ba49296e28d38580697940c5e7Holly Jiuyu Sun public void testSendStoreDataLen0() throws InterruptedException { 273d687148b8a2627ba49296e28d38580697940c5e7Holly Jiuyu Sun String aid = "B2C3D4"; 274d687148b8a2627ba49296e28d38580697940c5e7Holly Jiuyu Sun ApduSender sender = new ApduSender(mMockCi, aid, false /* supportExtendedApdu */); 275d687148b8a2627ba49296e28d38580697940c5e7Holly Jiuyu Sun 276d687148b8a2627ba49296e28d38580697940c5e7Holly Jiuyu Sun int channel = LogicalChannelMocker.mockOpenLogicalChannelResponse(mMockCi, "9000"); 277d687148b8a2627ba49296e28d38580697940c5e7Holly Jiuyu Sun LogicalChannelMocker.mockSendToLogicalChannel(mMockCi, channel, "B2222B9000"); 278d687148b8a2627ba49296e28d38580697940c5e7Holly Jiuyu Sun LogicalChannelMocker.mockCloseLogicalChannel(mMockCi, channel); 279d687148b8a2627ba49296e28d38580697940c5e7Holly Jiuyu Sun 280d687148b8a2627ba49296e28d38580697940c5e7Holly Jiuyu Sun sender.send((selectResponse, requestBuilder) -> { 281d687148b8a2627ba49296e28d38580697940c5e7Holly Jiuyu Sun requestBuilder.addStoreData(""); 282d687148b8a2627ba49296e28d38580697940c5e7Holly Jiuyu Sun }, mResponseCaptor, mHandler); 283d687148b8a2627ba49296e28d38580697940c5e7Holly Jiuyu Sun mResponseCaptor.await(); 284d687148b8a2627ba49296e28d38580697940c5e7Holly Jiuyu Sun 285d687148b8a2627ba49296e28d38580697940c5e7Holly Jiuyu Sun assertEquals("B2222B", IccUtils.bytesToHexString(mResponseCaptor.response)); 286d687148b8a2627ba49296e28d38580697940c5e7Holly Jiuyu Sun verify(mMockCi).iccTransmitApduLogicalChannel(eq(channel), eq(0x81), eq(0xE2), eq(0x91), 287d687148b8a2627ba49296e28d38580697940c5e7Holly Jiuyu Sun eq(0), eq(0), eq(""), any()); 288d687148b8a2627ba49296e28d38580697940c5e7Holly Jiuyu Sun } 289d687148b8a2627ba49296e28d38580697940c5e7Holly Jiuyu Sun 290d687148b8a2627ba49296e28d38580697940c5e7Holly Jiuyu Sun @Test 291388fa40468c01b8959ae4bac688241b6d362820fJun Yin public void testSendErrorResponseInMiddle() throws InterruptedException { 292388fa40468c01b8959ae4bac688241b6d362820fJun Yin String aid = "B2C3D4"; 293388fa40468c01b8959ae4bac688241b6d362820fJun Yin ApduSender sender = new ApduSender(mMockCi, aid, false /* supportExtendedApdu */); 294388fa40468c01b8959ae4bac688241b6d362820fJun Yin 295388fa40468c01b8959ae4bac688241b6d362820fJun Yin int channel = LogicalChannelMocker.mockOpenLogicalChannelResponse(mMockCi, "9000"); 296388fa40468c01b8959ae4bac688241b6d362820fJun Yin LogicalChannelMocker.mockSendToLogicalChannel(mMockCi, channel, "A19000", "9000", 297388fa40468c01b8959ae4bac688241b6d362820fJun Yin "B22B6103", "6985"); 298388fa40468c01b8959ae4bac688241b6d362820fJun Yin LogicalChannelMocker.mockCloseLogicalChannel(mMockCi, channel); 299388fa40468c01b8959ae4bac688241b6d362820fJun Yin 300388fa40468c01b8959ae4bac688241b6d362820fJun Yin // Each segment has 0xFF (the limit of a single command) bytes. 301388fa40468c01b8959ae4bac688241b6d362820fJun Yin String s1 = new String(new char[0xFF]).replace("\0", "AA"); 302388fa40468c01b8959ae4bac688241b6d362820fJun Yin String s2 = new String(new char[0xFF]).replace("\0", "BB"); 303388fa40468c01b8959ae4bac688241b6d362820fJun Yin String s3 = new String(new char[16]).replace("\0", "CC"); 304388fa40468c01b8959ae4bac688241b6d362820fJun Yin String longData = s1 + s2 + s3; 305388fa40468c01b8959ae4bac688241b6d362820fJun Yin sender.send((selectResponse, requestBuilder) -> { 306388fa40468c01b8959ae4bac688241b6d362820fJun Yin requestBuilder.addApdu(10, 1, 2, 3, 0, "a"); 307388fa40468c01b8959ae4bac688241b6d362820fJun Yin requestBuilder.addStoreData(longData); 308388fa40468c01b8959ae4bac688241b6d362820fJun Yin }, mResponseCaptor, mHandler); 309388fa40468c01b8959ae4bac688241b6d362820fJun Yin mResponseCaptor.await(); 310388fa40468c01b8959ae4bac688241b6d362820fJun Yin 311388fa40468c01b8959ae4bac688241b6d362820fJun Yin assertEquals(0x6985, ((ApduException) mResponseCaptor.exception).getApduStatus()); 312388fa40468c01b8959ae4bac688241b6d362820fJun Yin verify(mMockCi).iccTransmitApduLogicalChannel(eq(channel), eq(channel | 10), eq(1), eq(2), 313388fa40468c01b8959ae4bac688241b6d362820fJun Yin eq(3), eq(0), eq("a"), any()); 314388fa40468c01b8959ae4bac688241b6d362820fJun Yin verify(mMockCi).iccTransmitApduLogicalChannel(eq(channel), eq(0x81), eq(0xE2), eq(0x11), 315388fa40468c01b8959ae4bac688241b6d362820fJun Yin eq(0), eq(0xFF), eq(s1), any()); 316388fa40468c01b8959ae4bac688241b6d362820fJun Yin verify(mMockCi).iccTransmitApduLogicalChannel(eq(channel), eq(0x81), eq(0xE2), eq(0x11), 317388fa40468c01b8959ae4bac688241b6d362820fJun Yin eq(1), eq(0xFF), eq(s2), any()); 318388fa40468c01b8959ae4bac688241b6d362820fJun Yin verify(mMockCi, never()).iccTransmitApduLogicalChannel(eq(channel), eq(0x81), eq(0xE2), 319388fa40468c01b8959ae4bac688241b6d362820fJun Yin eq(0x91), eq(2), eq(16), eq(s3), any()); 320388fa40468c01b8959ae4bac688241b6d362820fJun Yin } 321388fa40468c01b8959ae4bac688241b6d362820fJun Yin 322388fa40468c01b8959ae4bac688241b6d362820fJun Yin @Test 323388fa40468c01b8959ae4bac688241b6d362820fJun Yin public void testChannelAlreadyOpened() throws InterruptedException { 324388fa40468c01b8959ae4bac688241b6d362820fJun Yin String aid = "B2C3D4"; 325388fa40468c01b8959ae4bac688241b6d362820fJun Yin ApduSender sender = new ApduSender(mMockCi, aid, false /* supportExtendedApdu */); 326388fa40468c01b8959ae4bac688241b6d362820fJun Yin 327388fa40468c01b8959ae4bac688241b6d362820fJun Yin int channel = LogicalChannelMocker.mockOpenLogicalChannelResponse(mMockCi, "9000"); 328388fa40468c01b8959ae4bac688241b6d362820fJun Yin LogicalChannelMocker.mockCloseLogicalChannel(mMockCi, channel); 329388fa40468c01b8959ae4bac688241b6d362820fJun Yin 330388fa40468c01b8959ae4bac688241b6d362820fJun Yin ResponseCaptor outerResponseCaptor = new ResponseCaptor(); 331388fa40468c01b8959ae4bac688241b6d362820fJun Yin sender.send( 332388fa40468c01b8959ae4bac688241b6d362820fJun Yin (selectResponse, requestBuilder) -> sender.send( 333388fa40468c01b8959ae4bac688241b6d362820fJun Yin (selectResponseOther, requestBuilderOther) -> 334388fa40468c01b8959ae4bac688241b6d362820fJun Yin mSelectResponse = selectResponseOther, 335388fa40468c01b8959ae4bac688241b6d362820fJun Yin mResponseCaptor, mHandler), 336388fa40468c01b8959ae4bac688241b6d362820fJun Yin outerResponseCaptor, mHandler); 337388fa40468c01b8959ae4bac688241b6d362820fJun Yin mResponseCaptor.await(); 338388fa40468c01b8959ae4bac688241b6d362820fJun Yin outerResponseCaptor.await(); 339388fa40468c01b8959ae4bac688241b6d362820fJun Yin 340388fa40468c01b8959ae4bac688241b6d362820fJun Yin assertNull("Should not open channel when another one is already opened.", mSelectResponse); 341388fa40468c01b8959ae4bac688241b6d362820fJun Yin assertTrue(mResponseCaptor.exception instanceof ApduException); 342388fa40468c01b8959ae4bac688241b6d362820fJun Yin verify(mMockCi, times(1)).iccOpenLogicalChannel(eq(aid), anyInt(), any()); 343388fa40468c01b8959ae4bac688241b6d362820fJun Yin } 344388fa40468c01b8959ae4bac688241b6d362820fJun Yin} 345