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