blob: 874bd0966797fc67f584bcb2e59b37a9c0cf5d1f [file] [log] [blame]
/*
* Copyright 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.bluetooth.sap;
import static com.android.bluetooth.sap.SapMessage.CON_STATUS_OK;
import static com.android.bluetooth.sap.SapMessage.DISC_GRACEFULL;
import static com.android.bluetooth.sap.SapMessage.ID_CONNECT_RESP;
import static com.android.bluetooth.sap.SapMessage.ID_DISCONNECT_RESP;
import static com.android.bluetooth.sap.SapMessage.ID_POWER_SIM_OFF_REQ;
import static com.android.bluetooth.sap.SapMessage.ID_POWER_SIM_OFF_RESP;
import static com.android.bluetooth.sap.SapMessage.ID_POWER_SIM_ON_REQ;
import static com.android.bluetooth.sap.SapMessage.ID_POWER_SIM_ON_RESP;
import static com.android.bluetooth.sap.SapMessage.ID_RESET_SIM_RESP;
import static com.android.bluetooth.sap.SapMessage.ID_RIL_UNKNOWN;
import static com.android.bluetooth.sap.SapMessage.ID_RIL_UNSOL_DISCONNECT_IND;
import static com.android.bluetooth.sap.SapMessage.ID_SET_TRANSPORT_PROTOCOL_RESP;
import static com.android.bluetooth.sap.SapMessage.ID_STATUS_IND;
import static com.android.bluetooth.sap.SapMessage.ID_TRANSFER_APDU_RESP;
import static com.android.bluetooth.sap.SapMessage.ID_TRANSFER_ATR_RESP;
import static com.android.bluetooth.sap.SapMessage.ID_TRANSFER_CARD_READER_STATUS_RESP;
import static com.android.bluetooth.sap.SapMessage.RESULT_OK;
import static com.android.bluetooth.sap.SapMessage.STATUS_CARD_INSERTED;
import static com.android.bluetooth.sap.SapServer.ISAP_GET_SERVICE_DELAY_MILLIS;
import static com.android.bluetooth.sap.SapServer.SAP_MSG_RFC_REPLY;
import static com.android.bluetooth.sap.SapServer.SAP_MSG_RIL_CONNECT;
import static com.android.bluetooth.sap.SapServer.SAP_MSG_RIL_IND;
import static com.android.bluetooth.sap.SapServer.SAP_PROXY_DEAD;
import static com.android.bluetooth.sap.SapServer.SAP_RIL_SOCK_CLOSED;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.argThat;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.verify;
import android.hardware.radio.V1_0.ISap;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentMatcher;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;
import java.util.ArrayList;
import java.util.Arrays;
@LargeTest
@RunWith(AndroidJUnit4.class)
public class SapRilReceiverHidlTest {
private static final long TIMEOUT_MS = 1_000;
private HandlerThread mHandlerThread;
private Handler mServerMsgHandler;
@Spy
private TestHandlerCallback mCallback = new TestHandlerCallback();
@Mock
private Handler mServiceHandler;
@Mock
private ISap mSapProxy;
private SapRilReceiverHidl mReceiver;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mHandlerThread = new HandlerThread("SapRilReceiverTest");
mHandlerThread.start();
mServerMsgHandler = new Handler(mHandlerThread.getLooper(), mCallback);
mReceiver = new SapRilReceiverHidl(mServerMsgHandler, mServiceHandler);
mReceiver.mSapProxy = mSapProxy;
mServerMsgHandler.removeMessages(SAP_PROXY_DEAD);
}
@After
public void tearDown() {
mHandlerThread.quit();
}
@Test
public void getSapProxyLock() {
assertThat(mReceiver.getSapProxyLock()).isNotNull();
}
@Test
public void resetSapProxy() throws Exception {
mReceiver.resetSapProxy();
assertThat(mReceiver.mSapProxy).isNull();
verify(mSapProxy).unlinkToDeath(any());
}
@Test
public void notifyShutdown() throws Exception {
mReceiver.notifyShutdown();
verify(mCallback, timeout(TIMEOUT_MS)).receiveMessage(eq(SAP_RIL_SOCK_CLOSED), any());
}
@Test
public void sendRilConnectMessage() throws Exception {
mReceiver.sendRilConnectMessage();
verify(mCallback, timeout(TIMEOUT_MS)).receiveMessage(eq(SAP_MSG_RIL_CONNECT), any());
}
@Test
public void serviceDied() throws Exception {
long cookie = 1;
mReceiver.mSapProxyDeathRecipient.serviceDied(cookie);
verify(mCallback, timeout(ISAP_GET_SERVICE_DELAY_MILLIS + TIMEOUT_MS))
.receiveMessage(eq(SAP_PROXY_DEAD), argThat(
arg -> (arg instanceof Long) && ((Long) arg == cookie)
));
}
@Test
public void callback_connectResponse() throws Exception {
int token = 1;
int sapConnectRsp = CON_STATUS_OK;
int maxMsgSize = 512;
mReceiver.mSapCallback.connectResponse(token, sapConnectRsp, maxMsgSize);
verify(mCallback, timeout(TIMEOUT_MS)).receiveMessage(eq(SAP_MSG_RFC_REPLY), argThat(
new ArgumentMatcher<Object>() {
@Override
public boolean matches(Object arg) {
if (!(arg instanceof SapMessage)) {
return false;
}
SapMessage sapMsg = (SapMessage) arg;
return sapMsg.getMsgType() == ID_CONNECT_RESP
&& sapMsg.getConnectionStatus() == sapConnectRsp;
}
}
));
}
@Test
public void callback_disconnectResponse() throws Exception {
int token = 1;
mReceiver.mSapCallback.disconnectResponse(token);
verify(mCallback, timeout(TIMEOUT_MS)).receiveMessage(eq(SAP_MSG_RFC_REPLY), argThat(
new ArgumentMatcher<Object>() {
@Override
public boolean matches(Object arg) {
if (!(arg instanceof SapMessage)) {
return false;
}
SapMessage sapMsg = (SapMessage) arg;
return sapMsg.getMsgType() == ID_DISCONNECT_RESP;
}
}
));
}
@Test
public void callback_disconnectIndication() throws Exception {
int token = 1;
int disconnectType = DISC_GRACEFULL;
mReceiver.mSapCallback.disconnectIndication(token, disconnectType);
verify(mCallback, timeout(TIMEOUT_MS)).receiveMessage(eq(SAP_MSG_RIL_IND), argThat(
new ArgumentMatcher<Object>() {
@Override
public boolean matches(Object arg) {
if (!(arg instanceof SapMessage)) {
return false;
}
SapMessage sapMsg = (SapMessage) arg;
return sapMsg.getMsgType() == ID_RIL_UNSOL_DISCONNECT_IND
&& sapMsg.getDisconnectionType() == disconnectType;
}
}
));
}
@Test
public void callback_apduResponse() throws Exception {
int token = 1;
int resultCode = RESULT_OK;
byte[] apduRsp = new byte[]{0x03, 0x04};
ArrayList<Byte> apduRspList = new ArrayList<>();
for (byte b : apduRsp) {
apduRspList.add(b);
}
mReceiver.mSapCallback.apduResponse(token, resultCode, apduRspList);
verify(mCallback, timeout(TIMEOUT_MS)).receiveMessage(eq(SAP_MSG_RFC_REPLY), argThat(
new ArgumentMatcher<Object>() {
@Override
public boolean matches(Object arg) {
if (!(arg instanceof SapMessage)) {
return false;
}
SapMessage sapMsg = (SapMessage) arg;
return sapMsg.getMsgType() == ID_TRANSFER_APDU_RESP
&& sapMsg.getResultCode() == resultCode
&& Arrays.equals(sapMsg.getApduResp(), apduRsp);
}
}
));
}
@Test
public void callback_transferAtrResponse() throws Exception {
int token = 1;
int resultCode = RESULT_OK;
byte[] atr = new byte[]{0x03, 0x04};
ArrayList<Byte> atrList = new ArrayList<>();
for (byte b : atr) {
atrList.add(b);
}
mReceiver.mSapCallback.transferAtrResponse(token, resultCode, atrList);
verify(mCallback, timeout(TIMEOUT_MS)).receiveMessage(eq(SAP_MSG_RFC_REPLY), argThat(
new ArgumentMatcher<Object>() {
@Override
public boolean matches(Object arg) {
if (!(arg instanceof SapMessage)) {
return false;
}
SapMessage sapMsg = (SapMessage) arg;
return sapMsg.getMsgType() == ID_TRANSFER_ATR_RESP
&& sapMsg.getResultCode() == resultCode
&& Arrays.equals(sapMsg.getAtr(), atr);
}
}
));
}
@Test
public void callback_powerResponse_powerOff() throws Exception {
int token = 1;
int reqType = ID_POWER_SIM_OFF_REQ;
int resultCode = RESULT_OK;
SapMessage.sOngoingRequests.clear();
SapMessage.sOngoingRequests.put(token, reqType);
mReceiver.mSapCallback.powerResponse(token, resultCode);
verify(mCallback, timeout(TIMEOUT_MS)).receiveMessage(eq(SAP_MSG_RFC_REPLY), argThat(
new ArgumentMatcher<Object>() {
@Override
public boolean matches(Object arg) {
if (!(arg instanceof SapMessage)) {
return false;
}
SapMessage sapMsg = (SapMessage) arg;
return sapMsg.getMsgType() == ID_POWER_SIM_OFF_RESP
&& sapMsg.getResultCode() == resultCode;
}
}
));
}
@Test
public void callback_powerResponse_powerOn() throws Exception {
int token = 1;
int reqType = ID_POWER_SIM_ON_REQ;
int resultCode = RESULT_OK;
SapMessage.sOngoingRequests.clear();
SapMessage.sOngoingRequests.put(token, reqType);
mReceiver.mSapCallback.powerResponse(token, resultCode);
verify(mCallback, timeout(TIMEOUT_MS)).receiveMessage(eq(SAP_MSG_RFC_REPLY), argThat(
new ArgumentMatcher<Object>() {
@Override
public boolean matches(Object arg) {
if (!(arg instanceof SapMessage)) {
return false;
}
SapMessage sapMsg = (SapMessage) arg;
return sapMsg.getMsgType() == ID_POWER_SIM_ON_RESP
&& sapMsg.getResultCode() == resultCode;
}
}
));
}
@Test
public void callback_resetSimResponse() throws Exception {
int token = 1;
int resultCode = RESULT_OK;
mReceiver.mSapCallback.resetSimResponse(token, resultCode);
verify(mCallback, timeout(TIMEOUT_MS)).receiveMessage(eq(SAP_MSG_RFC_REPLY), argThat(
new ArgumentMatcher<Object>() {
@Override
public boolean matches(Object arg) {
if (!(arg instanceof SapMessage)) {
return false;
}
SapMessage sapMsg = (SapMessage) arg;
return sapMsg.getMsgType() == ID_RESET_SIM_RESP
&& sapMsg.getResultCode() == resultCode;
}
}
));
}
@Test
public void callback_statusIndication() throws Exception {
int token = 1;
int statusChange = 2;
mReceiver.mSapCallback.statusIndication(token, statusChange);
verify(mCallback, timeout(TIMEOUT_MS)).receiveMessage(eq(SAP_MSG_RFC_REPLY), argThat(
new ArgumentMatcher<Object>() {
@Override
public boolean matches(Object arg) {
if (!(arg instanceof SapMessage)) {
return false;
}
SapMessage sapMsg = (SapMessage) arg;
return sapMsg.getMsgType() == ID_STATUS_IND
&& sapMsg.getStatusChange() == statusChange;
}
}
));
}
@Test
public void callback_transferCardReaderStatusResponse() throws Exception {
int token = 1;
int resultCode = RESULT_OK;
int cardReaderStatus = STATUS_CARD_INSERTED;
mReceiver.mSapCallback.transferCardReaderStatusResponse(
token, resultCode, cardReaderStatus);
verify(mCallback, timeout(TIMEOUT_MS)).receiveMessage(eq(SAP_MSG_RFC_REPLY), argThat(
new ArgumentMatcher<Object>() {
@Override
public boolean matches(Object arg) {
if (!(arg instanceof SapMessage)) {
return false;
}
SapMessage sapMsg = (SapMessage) arg;
return sapMsg.getMsgType() == ID_TRANSFER_CARD_READER_STATUS_RESP
&& sapMsg.getResultCode() == resultCode;
}
}
));
}
@Test
public void callback_errorResponse() throws Exception {
int token = 1;
mReceiver.mSapCallback.errorResponse(token);
verify(mCallback, timeout(TIMEOUT_MS)).receiveMessage(eq(SAP_MSG_RIL_IND), argThat(
new ArgumentMatcher<Object>() {
@Override
public boolean matches(Object arg) {
if (!(arg instanceof SapMessage)) {
return false;
}
SapMessage sapMsg = (SapMessage) arg;
return sapMsg.getMsgType() == ID_RIL_UNKNOWN;
}
}
));
}
@Test
public void callback_transferProtocolResponse() throws Exception {
int token = 1;
int resultCode = RESULT_OK;
mReceiver.mSapCallback.transferProtocolResponse(token, resultCode);
verify(mCallback, timeout(TIMEOUT_MS)).receiveMessage(eq(SAP_MSG_RFC_REPLY), argThat(
new ArgumentMatcher<Object>() {
@Override
public boolean matches(Object arg) {
if (!(arg instanceof SapMessage)) {
return false;
}
SapMessage sapMsg = (SapMessage) arg;
return sapMsg.getMsgType() == ID_SET_TRANSPORT_PROTOCOL_RESP
&& sapMsg.getResultCode() == resultCode;
}
}
));
}
public static class TestHandlerCallback implements Handler.Callback {
@Override
public boolean handleMessage(Message msg) {
receiveMessage(msg.what, msg.obj);
return true;
}
public void receiveMessage(int what, Object obj) {
}
}
}