1 /*
2  * Copyright (C) 2018 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 
17 package com.android.internal.telephony.uicc.euicc.apdu;
18 
19 import static org.mockito.ArgumentMatchers.anyInt;
20 import static org.mockito.ArgumentMatchers.anyString;
21 import static org.mockito.ArgumentMatchers.eq;
22 import static org.mockito.Mockito.doAnswer;
23 
24 import android.annotation.Nullable;
25 import android.os.AsyncResult;
26 import android.os.Message;
27 
28 import com.android.internal.telephony.CommandsInterface;
29 import com.android.internal.telephony.uicc.IccIoResult;
30 import com.android.internal.telephony.uicc.IccUtils;
31 
32 import org.mockito.ArgumentCaptor;
33 import org.mockito.invocation.InvocationOnMock;
34 import org.mockito.stubbing.Answer;
35 
36 /** Utility to set up mocks for communication with UICC through logical channel. */
37 public final class LogicalChannelMocker {
38     private static final int LOGICAL_CHANNEL = 1;
39 
40     /**
41      * @param responseObject Can be either a string or an exception.
42      * @return The mock channel number.
43      */
mockOpenLogicalChannelResponse(CommandsInterface mockCi, @Nullable Object responseObject)44     public static int mockOpenLogicalChannelResponse(CommandsInterface mockCi,
45             @Nullable Object responseObject) {
46         boolean isException = responseObject instanceof Throwable;
47         int[] responseInts = isException ? null : getSelectResponse(responseObject.toString());
48         Throwable exception = isException ? (Throwable) responseObject : null;
49 
50         ArgumentCaptor<Message> response = ArgumentCaptor.forClass(Message.class);
51         doAnswer((Answer<Void>) invocation -> {
52             Message msg = response.getValue();
53             AsyncResult.forMessage(msg, responseInts, exception);
54             msg.sendToTarget();
55             return null;
56         }).when(mockCi).iccOpenLogicalChannel(anyString(), anyInt(), response.capture());
57         return LOGICAL_CHANNEL;
58     }
59 
60     /**
61      * @param responseObjects Can be either a string or an exception. For string, the last 4
62      *         digits are the status (sw1, sw1).
63      */
mockSendToLogicalChannel(CommandsInterface mockCi, int channel, Object... responseObjects)64     public static void mockSendToLogicalChannel(CommandsInterface mockCi, int channel,
65             Object... responseObjects) {
66         ArgumentCaptor<Message> response = ArgumentCaptor.forClass(Message.class);
67         doAnswer(new Answer() {
68             private int mIndex = 0;
69 
70             @Override
71             public Object answer(InvocationOnMock invocation) throws Throwable {
72                 Object responseObject = responseObjects[mIndex++];
73                 boolean isException = responseObject instanceof Throwable;
74                 int sw1 = 0;
75                 int sw2 = 0;
76                 String hex = responseObject.toString();
77                 if (!isException) {
78                     int l = hex.length();
79                     sw1 = Integer.parseInt(hex.substring(l - 4, l - 2), 16);
80                     sw2 = Integer.parseInt(hex.substring(l - 2), 16);
81                     hex = hex.substring(0, l - 4);
82                 }
83                 IccIoResult result = isException ? null : new IccIoResult(sw1, sw2, hex);
84                 Throwable exception = isException ? (Throwable) responseObject : null;
85 
86                 Message msg = response.getValue();
87                 AsyncResult.forMessage(msg, result, exception);
88                 msg.sendToTarget();
89                 return null;
90             }
91         }).when(mockCi).iccTransmitApduLogicalChannel(eq(channel), anyInt(), anyInt(), anyInt(),
92                 anyInt(), anyInt(), anyString(), response.capture());
93     }
94 
mockCloseLogicalChannel(CommandsInterface mockCi, int channel)95     public static void mockCloseLogicalChannel(CommandsInterface mockCi, int channel) {
96         ArgumentCaptor<Message> response = ArgumentCaptor.forClass(Message.class);
97         doAnswer((Answer<Void>) invocation -> {
98             Message msg = response.getValue();
99             AsyncResult.forMessage(msg);
100             msg.sendToTarget();
101             return null;
102         }).when(mockCi).iccCloseLogicalChannel(eq(channel), response.capture());
103     }
104 
getSelectResponse(String responseHex)105     private static int[] getSelectResponse(String responseHex) {
106         byte[] responseBytes = IccUtils.hexStringToBytes("00" + responseHex);
107         int[] responseInts = new int[responseBytes.length];
108         responseInts[0] = LOGICAL_CHANNEL;
109         for (int i = 1; i < responseInts.length; ++i) {
110             responseInts[i] = responseBytes[i];
111         }
112         return responseInts;
113     }
114 
LogicalChannelMocker()115     private LogicalChannelMocker() {
116     }
117 }
118