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