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