If the phone crash try to clean up the channel which was kept opened. If that fails then try to reuse the existing channel.
Bug: 332924032
Bug: 331603987
Test: manual
Change-Id: I186bc76a3421f27855fa9cd48680749201a94b97
(cherry picked from commit e32a4eec38409fb1aef83f52a507d3d8980db49d)
diff --git a/src/java/com/android/internal/telephony/uicc/euicc/EuiccPort.java b/src/java/com/android/internal/telephony/uicc/euicc/EuiccPort.java
index 3bd66f8..7bdec47 100644
--- a/src/java/com/android/internal/telephony/uicc/euicc/EuiccPort.java
+++ b/src/java/com/android/internal/telephony/uicc/euicc/EuiccPort.java
@@ -133,7 +133,8 @@
UiccCard card, MultipleEnabledProfilesMode supportedMepMode) {
super(c, ci, ics, phoneId, lock, card);
// TODO: Set supportExtendedApdu based on ATR.
- mApduSender = new ApduSender(ci, ISD_R_AID, false /* supportExtendedApdu */);
+ mApduSender = new ApduSender(c, phoneId, ci, ISD_R_AID,
+ false /* supportExtendedApdu */);
if (TextUtils.isEmpty(ics.eid)) {
loge("no eid given in constructor for phone " + phoneId);
} else {
diff --git a/src/java/com/android/internal/telephony/uicc/euicc/apdu/ApduSender.java b/src/java/com/android/internal/telephony/uicc/euicc/apdu/ApduSender.java
index 8e7237e..f42d5a2 100644
--- a/src/java/com/android/internal/telephony/uicc/euicc/apdu/ApduSender.java
+++ b/src/java/com/android/internal/telephony/uicc/euicc/apdu/ApduSender.java
@@ -17,9 +17,13 @@
package com.android.internal.telephony.uicc.euicc.apdu;
import android.annotation.Nullable;
+import android.content.Context;
+import android.content.SharedPreferences;
import android.os.Handler;
import android.os.Looper;
+import android.preference.PreferenceManager;
import android.telephony.IccOpenLogicalChannelResponse;
+import android.util.Base64;
import com.android.internal.telephony.CommandsInterface;
import com.android.internal.telephony.uicc.IccIoResult;
@@ -30,6 +34,7 @@
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.List;
+import java.util.NoSuchElementException;
/**
* This class sends a list of APDU commands to an AID on a UICC. A logical channel will be opened
@@ -52,6 +57,9 @@
private static final int SW1_NO_ERROR = 0x91;
private static final int WAIT_TIME_MS = 2000;
+ private static final String CHANNEL_ID_PRE = "esim-channel";
+ private static final String ISD_R_AID = "A0000005591010FFFFFFFF8900000100";
+ private static final String CHANNEL_RESPONSE_ID_PRE = "esim-res-id";
private static void logv(String msg) {
Rlog.v(LOG_TAG, msg);
@@ -66,6 +74,9 @@
private final OpenLogicalChannelInvocation mOpenChannel;
private final CloseLogicalChannelInvocation mCloseChannel;
private final TransmitApduLogicalChannelInvocation mTransmitApdu;
+ private final Context mContext;
+ private final String mChannelKey;
+ private final String mChannelResponseKey;
// Lock for accessing mChannelOpened. We only allow to open a single logical channel at any
// time for an AID.
@@ -75,12 +86,17 @@
/**
* @param aid The AID that will be used to open a logical channel to.
*/
- public ApduSender(CommandsInterface ci, String aid, boolean supportExtendedApdu) {
+ public ApduSender(Context context, int phoneId, CommandsInterface ci, String aid,
+ boolean supportExtendedApdu) {
mAid = aid;
+ mContext = context;
mSupportExtendedApdu = supportExtendedApdu;
mOpenChannel = new OpenLogicalChannelInvocation(ci);
mCloseChannel = new CloseLogicalChannelInvocation(ci);
mTransmitApdu = new TransmitApduLogicalChannelInvocation(ci);
+ mChannelKey = CHANNEL_ID_PRE + "_" + phoneId;
+ mChannelResponseKey = CHANNEL_RESPONSE_ID_PRE + "_" + phoneId;
+ closeExistingChannelIfExists();
}
/**
@@ -129,6 +145,20 @@
public void onResult(IccOpenLogicalChannelResponse openChannelResponse) {
int channel = openChannelResponse.getChannel();
int status = openChannelResponse.getStatus();
+ byte[] selectResponse = openChannelResponse.getSelectResponse();
+ if (mAid.equals(ISD_R_AID)
+ && status == IccOpenLogicalChannelResponse.STATUS_NO_SUCH_ELEMENT) {
+ channel = PreferenceManager.getDefaultSharedPreferences(mContext)
+ .getInt(mChannelKey, IccOpenLogicalChannelResponse.INVALID_CHANNEL);
+ if (channel != IccOpenLogicalChannelResponse.INVALID_CHANNEL) {
+ logv("Try to use already opened channel: " + channel);
+ status = IccOpenLogicalChannelResponse.STATUS_NO_ERROR;
+ String storedResponse = PreferenceManager
+ .getDefaultSharedPreferences(mContext)
+ .getString(mChannelResponseKey, "");
+ selectResponse = Base64.decode(storedResponse, Base64.DEFAULT);
+ }
+ }
if (channel == IccOpenLogicalChannelResponse.INVALID_CHANNEL
|| status != IccOpenLogicalChannelResponse.STATUS_NO_ERROR) {
synchronized (mChannelLock) {
@@ -143,8 +173,15 @@
RequestBuilder builder = new RequestBuilder(channel, mSupportExtendedApdu);
Throwable requestException = null;
+ if (mAid.equals(ISD_R_AID)) {
+ PreferenceManager.getDefaultSharedPreferences(mContext)
+ .edit().putInt(mChannelKey, channel).apply();
+ PreferenceManager.getDefaultSharedPreferences(mContext)
+ .edit().putString(mChannelResponseKey,
+ Base64.encodeToString(selectResponse, Base64.DEFAULT)).apply();
+ }
try {
- requestProvider.buildRequest(openChannelResponse.getSelectResponse(), builder);
+ requestProvider.buildRequest(selectResponse, builder);
} catch (Throwable e) {
requestException = e;
}
@@ -223,7 +260,7 @@
AsyncResultCallback<IccIoResult> resultCallback,
Handler handler) {
ByteArrayOutputStream resultBuilder =
- responseBuilder == null ? new ByteArrayOutputStream() : responseBuilder;
+ responseBuilder == null ? new ByteArrayOutputStream() : responseBuilder;
if (lastResponse.payload != null) {
try {
resultBuilder.write(lastResponse.payload);
@@ -267,6 +304,12 @@
@Override
public void onResult(Boolean aBoolean) {
synchronized (mChannelLock) {
+ if (mAid.equals(ISD_R_AID)) {
+ PreferenceManager.getDefaultSharedPreferences(mContext)
+ .edit().remove(mChannelKey).apply();
+ PreferenceManager.getDefaultSharedPreferences(mContext)
+ .edit().remove(mChannelResponseKey).apply();
+ }
mChannelOpened = false;
mChannelLock.notify();
}
@@ -279,4 +322,39 @@
}
}, handler);
}
+
+ /**
+ * Cleanup the existing opened channel which was remainined opened earlier due
+ * to failure or crash.
+ */
+ private void closeExistingChannelIfExists() {
+ if (mCloseChannel != null) {
+ int channelId = PreferenceManager.getDefaultSharedPreferences(mContext)
+ .getInt(mChannelKey, IccOpenLogicalChannelResponse.INVALID_CHANNEL);
+ if (channelId != IccOpenLogicalChannelResponse.INVALID_CHANNEL) {
+ logv("Trying to clean up the opened channel : " + channelId);
+ synchronized (mChannelLock) {
+ mChannelOpened = true;
+ mChannelLock.notify();
+ }
+ mCloseChannel.invoke(channelId, new AsyncResultCallback<Boolean>() {
+ @Override
+ public void onResult(Boolean isSuccess) {
+ if (isSuccess) {
+ logv("Channel closed successfully: " + channelId);
+ PreferenceManager.getDefaultSharedPreferences(mContext)
+ .edit().remove(mChannelResponseKey).apply();
+ PreferenceManager.getDefaultSharedPreferences(mContext)
+ .edit().remove(mChannelKey).apply();
+ }
+
+ synchronized (mChannelLock) {
+ mChannelOpened = false;
+ mChannelLock.notify();
+ }
+ }
+ }, new Handler());
+ }
+ }
+ }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/apdu/ApduSenderTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/apdu/ApduSenderTest.java
index b073c6a..cf3f900 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/apdu/ApduSenderTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/apdu/ApduSenderTest.java
@@ -37,6 +37,7 @@
import com.android.internal.telephony.CommandsInterface;
import com.android.internal.telephony.uicc.IccIoResult;
import com.android.internal.telephony.uicc.IccUtils;
+import androidx.test.InstrumentationRegistry;
import org.junit.After;
import org.junit.Before;
@@ -93,7 +94,8 @@
mResponseCaptor = new ResponseCaptor();
mSelectResponse = null;
- mSender = new ApduSender(mMockCi, AID, false /* supportExtendedApdu */);
+ mSender = new ApduSender(InstrumentationRegistry.getContext(), 0 /* phoneId= */,
+ mMockCi, AID, false /* supportExtendedApdu */);
mLooper = TestableLooper.get(this);
}