Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e0f7674c authored by Meng Wang's avatar Meng Wang
Browse files

Always close channel after apdu sending fails

Once in a EuiccSession, previous design depends on specific error codes
to detect channel closing and only re-open channel if specific error
codes returned by apdu sending.

Problem is there is always some new error codes because of timing &
vendor behavior. In such cases, apdu sending will be attempted without
opening a new channel and hence will fail.

New design will close and re-open channel when apdu sending fails,
without checking the error code. This design is safer as it is same as
the behavior as if not in EuiccSession.

Bug: 335257880
Test: unit test, atest <path>/ApduSenderTest.java
Test: manual, see b/335257880#comment53
Flag: com.android.internal.telephony.flags.optimization_apdu_sender
Change-Id: I87b73bf848cd8a70cfe3ed32fd3b5df1ad3a5080
parent ceb9a695
Loading
Loading
Loading
Loading
+10 −11
Original line number Diff line number Diff line
@@ -61,7 +61,6 @@ public class ApduSender {
    // Status code of APDU response
    private static final int STATUS_NO_ERROR = 0x9000;
    private static final int SW1_NO_ERROR = 0x91;
    private static final int STATUS_CHANNEL_CLOSED = 0x6881; // b/359336875

    private static final int WAIT_TIME_MS = 2000;
    private static final String CHANNEL_ID_PRE = "esim-channel";
@@ -282,15 +281,12 @@ public class ApduSender {
                                int status = (fullResponse.sw1 << 8) | fullResponse.sw2;
                                if (status != STATUS_NO_ERROR
                                        && fullResponse.sw1 != SW1_NO_ERROR) {
                                    if (status == STATUS_CHANNEL_CLOSED) {
                                        // Channel is closed by EUICC e.g. REFRESH.
                                        tearDownPreferences();
                                        mChannelOpened = false;
                                        // TODO: add retry
                                    }
                                    // During a EuiccSession, on errors like b/359336875, close the
                                    // channel immediately so next sendCommand will open a new
                                    // channel. This is same as the behavior without EuiccSession.
                                    returnRespnseOrException(
                                            command.channel,
                                            closeChannelImmediately,
                                            true /* closeChannelImmediately */,
                                            null /* response */,
                                            new ApduException(status),
                                            resultCallback,
@@ -399,12 +395,15 @@ public class ApduSender {
    }

    /**
     * Closes the opened logical channel.
     * Closes the given logical channel and return a response or exception via callback.
     *
     * <p>The successfulness of closing the channel isn't checked and doesn't affect the behavior
     * of this method.
     *
     * @param response If {@code exception} is null, this will be returned to {@code resultCallback}
     *     after the channel has been closed.
     * @param exception If not null, this will be returned to {@code resultCallback} after the
     *     channel has been closed.
     * @param exception If not {@code null}, this will be returned to {@code resultCallback} after
     *     the channel has been closed.
     */
    private void closeAndReturn(
            int channel,
+21 −0
Original line number Diff line number Diff line
@@ -446,6 +446,27 @@ public class ApduSenderTest {
        inOrder.verifyNoMoreInteractions();
    }

    @Test
    public void testSend_euiccSession_sendFailure_shouldCloseChannel() throws Exception {
        int channel = LogicalChannelMocker.mockOpenLogicalChannelResponse(mMockCi, "9000");
        // Send failure: `sw1:0x6f sw2:0x0 Error: technical problem with no diagnostic given`
        LogicalChannelMocker.mockSendToLogicalChannel(mMockCi, channel, "6F00");
        LogicalChannelMocker.mockCloseLogicalChannel(mMockCi, channel, /* error= */ null);
        EuiccSession.get(mContext).startSession(SESSION_ID);

        mSender.send((selectResponse, requestBuilder) -> requestBuilder.addApdu(
                10, 1, 2, 3, 0, "a"), mResponseCaptor, mHandler);
        mLooper.processAllMessages();

        assertEquals(0x6F00, ((ApduException) mResponseCaptor.exception).getApduStatus());
        InOrder inOrder = inOrder(mMockCi);
        inOrder.verify(mMockCi).iccOpenLogicalChannel(eq(ApduSender.ISD_R_AID), anyInt(), any());
        inOrder.verify(mMockCi).iccTransmitApduLogicalChannel(eq(channel), eq(channel | 10),
                eq(1), eq(2), eq(3), eq(0), eq("a"), anyBoolean(), any());
        inOrder.verify(mMockCi).iccCloseLogicalChannel(eq(channel), eq(true /*isEs10*/), any());
        inOrder.verifyNoMoreInteractions();
    }

    @Test
    public void testSendTwice_euiccSession_shouldOpenChannelOnceNotCloseChannel()
            throws InterruptedException {