Loading src/java/com/android/internal/telephony/euicc/EuiccConnector.java +2 −0 Original line number Original line Diff line number Diff line Loading @@ -749,10 +749,12 @@ public class EuiccConnector extends StateMachine implements ServiceConnection { @Override @Override public boolean processMessage(Message message) { public boolean processMessage(Message message) { if (message.what == CMD_SERVICE_DISCONNECTED) { if (message.what == CMD_SERVICE_DISCONNECTED) { EuiccSession.get().endAllSessions(); mEuiccService = null; mEuiccService = null; transitionTo(mDisconnectedState); transitionTo(mDisconnectedState); return HANDLED; return HANDLED; } else if (message.what == CMD_LINGER_TIMEOUT) { } else if (message.what == CMD_LINGER_TIMEOUT) { EuiccSession.get().endAllSessions(); unbind(); unbind(); transitionTo(mAvailableState); transitionTo(mAvailableState); return HANDLED; return HANDLED; Loading src/java/com/android/internal/telephony/euicc/EuiccSession.java +52 −10 Original line number Original line Diff line number Diff line Loading @@ -15,6 +15,7 @@ */ */ package com.android.internal.telephony.euicc; package com.android.internal.telephony.euicc; import android.annotation.Nullable; import android.util.ArraySet; import android.util.ArraySet; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.GuardedBy; Loading Loading @@ -85,14 +86,18 @@ public class EuiccSession { /** Returns {@code true} if there is at least one session ongoing. */ /** Returns {@code true} if there is at least one session ongoing. */ public boolean hasSession() { public boolean hasSession() { boolean hasSession; boolean hasSession = hasSessionInternal(); synchronized(this) { hasSession = !mSessions.isEmpty(); } Rlog.i(TAG, "hasSession: " + hasSession); Rlog.i(TAG, "hasSession: " + hasSession); return hasSession; return hasSession; } } // The bare metal implementation of hasSession() without logging. private boolean hasSessionInternal() { synchronized(this) { return !mSessions.isEmpty(); } } /** /** * Notes that a logical channel may be opened by the {@code apduSender}, which will * Notes that a logical channel may be opened by the {@code apduSender}, which will * be used to close the channel when session ends (see {@link #endSession()}). * be used to close the channel when session ends (see {@link #endSession()}). Loading @@ -104,7 +109,7 @@ public class EuiccSession { public void noteChannelOpen(ApduSender apduSender) { public void noteChannelOpen(ApduSender apduSender) { Rlog.i(TAG, "noteChannelOpen: " + apduSender); Rlog.i(TAG, "noteChannelOpen: " + apduSender); synchronized(this) { synchronized(this) { if (hasSession()) { if (hasSessionInternal()) { mApduSenders.add(apduSender); mApduSenders.add(apduSender); } } } } Loading @@ -112,19 +117,41 @@ public class EuiccSession { /** /** * Marks the end of a eUICC transaction session. If this ends the last ongoing session, * Marks the end of a eUICC transaction session. If this ends the last ongoing session, * try to close the logical channel using the noted {@code apduSender} * try to close the logical channel using the noted {@code apduSender}s * (see {@link #noteChannelOpen()}). * (see {@link #noteChannelOpen()}). * * * @param sessionId The session ID. * @param sessionId The session ID. */ */ public void endSession(String sessionId) { public void endSession(String sessionId) { Rlog.i(TAG, "endSession: " + sessionId); Rlog.i(TAG, "endSession: " + sessionId); endSessionInternal(sessionId); } /** * Marks the end of all eUICC transaction sessions and close the logical * channels using the noted {@code apduSender}s * (see {@link #noteChannelOpen()}). * * <p>This is useful in global cleanup e.g. when EuiccService * implementation app crashes and indivial {@link #endSession()} calls * won't happen in {@link EuiccConnector}. */ public void endAllSessions() { Rlog.i(TAG, "endAllSessions"); endSessionInternal(null); } // The implementation of endSession(sessionId) or endAllSessions() when the sessionId is null, // without logging. private void endSessionInternal(@Nullable String sessionId) { ApduSender[] apduSenders = new ApduSender[0]; ApduSender[] apduSenders = new ApduSender[0]; synchronized(this) { synchronized(this) { boolean sessionRemoved = mSessions.remove(sessionId); boolean sessionRemoved = removeOrClear(mSessions, sessionId); // sessionRemoved is false if the `sessionId` was never started or there was // 1. sessionRemoved is false if the `sessionId` was never started or there was // no session at all i.e. `sessions` is empty. Don't bother invoke `apduSender`. // no session. Don't bother invoke `apduSender`. if (sessionRemoved && mSessions.isEmpty()) { // 2. If some session is removed, and as a result there is no more session, we // can clsoe channels. if (sessionRemoved && !hasSessionInternal()) { // copy mApduSenders to a local variable so we don't call closeAnyOpenChannel() // copy mApduSenders to a local variable so we don't call closeAnyOpenChannel() // which can take time in synchronized block. // which can take time in synchronized block. apduSenders = mApduSenders.toArray(apduSenders); apduSenders = mApduSenders.toArray(apduSenders); Loading @@ -136,6 +163,21 @@ public class EuiccSession { } } } } /** * Removes the given element from the set. If the element is null, clears the set. * * @return true if the set changed as a result of the call */ private static boolean removeOrClear(Set<String> collection, @Nullable String element) { if (element == null) { boolean collectionChanged = !collection.isEmpty(); collection.clear(); return collectionChanged; } else { return collection.remove(element); } } @VisibleForTesting @VisibleForTesting public EuiccSession() {} public EuiccSession() {} } } tests/telephonytests/src/com/android/internal/telephony/euicc/EuiccSessionTest.java +37 −1 Original line number Original line Diff line number Diff line Loading @@ -103,6 +103,7 @@ public class EuiccSessionTest extends TelephonyTest { private EuiccSession mEuiccSession; private EuiccSession mEuiccSession; @Mock private ApduSender mApduSender; @Mock private ApduSender mApduSender; @Mock private ApduSender mApduSender2; @Before @Before public void setUp() throws Exception { public void setUp() throws Exception { Loading @@ -114,6 +115,7 @@ public class EuiccSessionTest extends TelephonyTest { public void startOneSession_featureDisabled_noop() throws Exception { public void startOneSession_featureDisabled_noop() throws Exception { mEuiccSession.startSession(SESSION_ID_1); mEuiccSession.startSession(SESSION_ID_1); mEuiccSession.noteChannelOpen(mApduSender); mEuiccSession.noteChannelOpen(mApduSender); mEuiccSession.noteChannelOpen(mApduSender2); assertThat(mEuiccSession.hasSession()).isFalse(); assertThat(mEuiccSession.hasSession()).isFalse(); Loading @@ -121,6 +123,7 @@ public class EuiccSessionTest extends TelephonyTest { assertThat(mEuiccSession.hasSession()).isFalse(); assertThat(mEuiccSession.hasSession()).isFalse(); verify(mApduSender, never()).closeAnyOpenChannel(); verify(mApduSender, never()).closeAnyOpenChannel(); verify(mApduSender2, never()).closeAnyOpenChannel(); } } @Test @Test Loading @@ -128,6 +131,7 @@ public class EuiccSessionTest extends TelephonyTest { public void startOneSession_endSession_hasSession() throws Exception { public void startOneSession_endSession_hasSession() throws Exception { mEuiccSession.startSession(SESSION_ID_1); mEuiccSession.startSession(SESSION_ID_1); mEuiccSession.noteChannelOpen(mApduSender); mEuiccSession.noteChannelOpen(mApduSender); mEuiccSession.noteChannelOpen(mApduSender2); assertThat(mEuiccSession.hasSession()).isTrue(); assertThat(mEuiccSession.hasSession()).isTrue(); Loading @@ -140,6 +144,7 @@ public class EuiccSessionTest extends TelephonyTest { assertThat(mEuiccSession.hasSession()).isFalse(); assertThat(mEuiccSession.hasSession()).isFalse(); verify(mApduSender).closeAnyOpenChannel(); verify(mApduSender).closeAnyOpenChannel(); verify(mApduSender2).closeAnyOpenChannel(); } } @Test @Test Loading @@ -164,7 +169,24 @@ public class EuiccSessionTest extends TelephonyTest { @Test @Test @EnableFlags(Flags.FLAG_OPTIMIZATION_APDU_SENDER) @EnableFlags(Flags.FLAG_OPTIMIZATION_APDU_SENDER) public void noteChannelOpen_noSession_noop() throws Exception { public void startTwoSessions_endAllSessions_hasSession() throws Exception { mEuiccSession.startSession(SESSION_ID_1); mEuiccSession.noteChannelOpen(mApduSender); mEuiccSession.startSession(SESSION_ID_2); mEuiccSession.noteChannelOpen(mApduSender2); assertThat(mEuiccSession.hasSession()).isTrue(); mEuiccSession.endAllSessions(); assertThat(mEuiccSession.hasSession()).isFalse(); verify(mApduSender).closeAnyOpenChannel(); verify(mApduSender2).closeAnyOpenChannel(); } @Test @EnableFlags(Flags.FLAG_OPTIMIZATION_APDU_SENDER) public void noteChannelOpen_noSession_endSession_noop() throws Exception { // noteChannelOpen called without a session started // noteChannelOpen called without a session started mEuiccSession.noteChannelOpen(mApduSender); mEuiccSession.noteChannelOpen(mApduSender); Loading @@ -175,4 +197,18 @@ public class EuiccSessionTest extends TelephonyTest { assertThat(mEuiccSession.hasSession()).isFalse(); assertThat(mEuiccSession.hasSession()).isFalse(); verify(mApduSender, never()).closeAnyOpenChannel(); verify(mApduSender, never()).closeAnyOpenChannel(); } } @Test @EnableFlags(Flags.FLAG_OPTIMIZATION_APDU_SENDER) public void endAllSessions_noSession_endAllSessions_noOp() throws Exception { // noteChannelOpen called without a session started mEuiccSession.noteChannelOpen(mApduSender); assertThat(mEuiccSession.hasSession()).isFalse(); mEuiccSession.endAllSessions(); assertThat(mEuiccSession.hasSession()).isFalse(); verify(mApduSender, never()).closeAnyOpenChannel(); } } } Loading
src/java/com/android/internal/telephony/euicc/EuiccConnector.java +2 −0 Original line number Original line Diff line number Diff line Loading @@ -749,10 +749,12 @@ public class EuiccConnector extends StateMachine implements ServiceConnection { @Override @Override public boolean processMessage(Message message) { public boolean processMessage(Message message) { if (message.what == CMD_SERVICE_DISCONNECTED) { if (message.what == CMD_SERVICE_DISCONNECTED) { EuiccSession.get().endAllSessions(); mEuiccService = null; mEuiccService = null; transitionTo(mDisconnectedState); transitionTo(mDisconnectedState); return HANDLED; return HANDLED; } else if (message.what == CMD_LINGER_TIMEOUT) { } else if (message.what == CMD_LINGER_TIMEOUT) { EuiccSession.get().endAllSessions(); unbind(); unbind(); transitionTo(mAvailableState); transitionTo(mAvailableState); return HANDLED; return HANDLED; Loading
src/java/com/android/internal/telephony/euicc/EuiccSession.java +52 −10 Original line number Original line Diff line number Diff line Loading @@ -15,6 +15,7 @@ */ */ package com.android.internal.telephony.euicc; package com.android.internal.telephony.euicc; import android.annotation.Nullable; import android.util.ArraySet; import android.util.ArraySet; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.GuardedBy; Loading Loading @@ -85,14 +86,18 @@ public class EuiccSession { /** Returns {@code true} if there is at least one session ongoing. */ /** Returns {@code true} if there is at least one session ongoing. */ public boolean hasSession() { public boolean hasSession() { boolean hasSession; boolean hasSession = hasSessionInternal(); synchronized(this) { hasSession = !mSessions.isEmpty(); } Rlog.i(TAG, "hasSession: " + hasSession); Rlog.i(TAG, "hasSession: " + hasSession); return hasSession; return hasSession; } } // The bare metal implementation of hasSession() without logging. private boolean hasSessionInternal() { synchronized(this) { return !mSessions.isEmpty(); } } /** /** * Notes that a logical channel may be opened by the {@code apduSender}, which will * Notes that a logical channel may be opened by the {@code apduSender}, which will * be used to close the channel when session ends (see {@link #endSession()}). * be used to close the channel when session ends (see {@link #endSession()}). Loading @@ -104,7 +109,7 @@ public class EuiccSession { public void noteChannelOpen(ApduSender apduSender) { public void noteChannelOpen(ApduSender apduSender) { Rlog.i(TAG, "noteChannelOpen: " + apduSender); Rlog.i(TAG, "noteChannelOpen: " + apduSender); synchronized(this) { synchronized(this) { if (hasSession()) { if (hasSessionInternal()) { mApduSenders.add(apduSender); mApduSenders.add(apduSender); } } } } Loading @@ -112,19 +117,41 @@ public class EuiccSession { /** /** * Marks the end of a eUICC transaction session. If this ends the last ongoing session, * Marks the end of a eUICC transaction session. If this ends the last ongoing session, * try to close the logical channel using the noted {@code apduSender} * try to close the logical channel using the noted {@code apduSender}s * (see {@link #noteChannelOpen()}). * (see {@link #noteChannelOpen()}). * * * @param sessionId The session ID. * @param sessionId The session ID. */ */ public void endSession(String sessionId) { public void endSession(String sessionId) { Rlog.i(TAG, "endSession: " + sessionId); Rlog.i(TAG, "endSession: " + sessionId); endSessionInternal(sessionId); } /** * Marks the end of all eUICC transaction sessions and close the logical * channels using the noted {@code apduSender}s * (see {@link #noteChannelOpen()}). * * <p>This is useful in global cleanup e.g. when EuiccService * implementation app crashes and indivial {@link #endSession()} calls * won't happen in {@link EuiccConnector}. */ public void endAllSessions() { Rlog.i(TAG, "endAllSessions"); endSessionInternal(null); } // The implementation of endSession(sessionId) or endAllSessions() when the sessionId is null, // without logging. private void endSessionInternal(@Nullable String sessionId) { ApduSender[] apduSenders = new ApduSender[0]; ApduSender[] apduSenders = new ApduSender[0]; synchronized(this) { synchronized(this) { boolean sessionRemoved = mSessions.remove(sessionId); boolean sessionRemoved = removeOrClear(mSessions, sessionId); // sessionRemoved is false if the `sessionId` was never started or there was // 1. sessionRemoved is false if the `sessionId` was never started or there was // no session at all i.e. `sessions` is empty. Don't bother invoke `apduSender`. // no session. Don't bother invoke `apduSender`. if (sessionRemoved && mSessions.isEmpty()) { // 2. If some session is removed, and as a result there is no more session, we // can clsoe channels. if (sessionRemoved && !hasSessionInternal()) { // copy mApduSenders to a local variable so we don't call closeAnyOpenChannel() // copy mApduSenders to a local variable so we don't call closeAnyOpenChannel() // which can take time in synchronized block. // which can take time in synchronized block. apduSenders = mApduSenders.toArray(apduSenders); apduSenders = mApduSenders.toArray(apduSenders); Loading @@ -136,6 +163,21 @@ public class EuiccSession { } } } } /** * Removes the given element from the set. If the element is null, clears the set. * * @return true if the set changed as a result of the call */ private static boolean removeOrClear(Set<String> collection, @Nullable String element) { if (element == null) { boolean collectionChanged = !collection.isEmpty(); collection.clear(); return collectionChanged; } else { return collection.remove(element); } } @VisibleForTesting @VisibleForTesting public EuiccSession() {} public EuiccSession() {} } }
tests/telephonytests/src/com/android/internal/telephony/euicc/EuiccSessionTest.java +37 −1 Original line number Original line Diff line number Diff line Loading @@ -103,6 +103,7 @@ public class EuiccSessionTest extends TelephonyTest { private EuiccSession mEuiccSession; private EuiccSession mEuiccSession; @Mock private ApduSender mApduSender; @Mock private ApduSender mApduSender; @Mock private ApduSender mApduSender2; @Before @Before public void setUp() throws Exception { public void setUp() throws Exception { Loading @@ -114,6 +115,7 @@ public class EuiccSessionTest extends TelephonyTest { public void startOneSession_featureDisabled_noop() throws Exception { public void startOneSession_featureDisabled_noop() throws Exception { mEuiccSession.startSession(SESSION_ID_1); mEuiccSession.startSession(SESSION_ID_1); mEuiccSession.noteChannelOpen(mApduSender); mEuiccSession.noteChannelOpen(mApduSender); mEuiccSession.noteChannelOpen(mApduSender2); assertThat(mEuiccSession.hasSession()).isFalse(); assertThat(mEuiccSession.hasSession()).isFalse(); Loading @@ -121,6 +123,7 @@ public class EuiccSessionTest extends TelephonyTest { assertThat(mEuiccSession.hasSession()).isFalse(); assertThat(mEuiccSession.hasSession()).isFalse(); verify(mApduSender, never()).closeAnyOpenChannel(); verify(mApduSender, never()).closeAnyOpenChannel(); verify(mApduSender2, never()).closeAnyOpenChannel(); } } @Test @Test Loading @@ -128,6 +131,7 @@ public class EuiccSessionTest extends TelephonyTest { public void startOneSession_endSession_hasSession() throws Exception { public void startOneSession_endSession_hasSession() throws Exception { mEuiccSession.startSession(SESSION_ID_1); mEuiccSession.startSession(SESSION_ID_1); mEuiccSession.noteChannelOpen(mApduSender); mEuiccSession.noteChannelOpen(mApduSender); mEuiccSession.noteChannelOpen(mApduSender2); assertThat(mEuiccSession.hasSession()).isTrue(); assertThat(mEuiccSession.hasSession()).isTrue(); Loading @@ -140,6 +144,7 @@ public class EuiccSessionTest extends TelephonyTest { assertThat(mEuiccSession.hasSession()).isFalse(); assertThat(mEuiccSession.hasSession()).isFalse(); verify(mApduSender).closeAnyOpenChannel(); verify(mApduSender).closeAnyOpenChannel(); verify(mApduSender2).closeAnyOpenChannel(); } } @Test @Test Loading @@ -164,7 +169,24 @@ public class EuiccSessionTest extends TelephonyTest { @Test @Test @EnableFlags(Flags.FLAG_OPTIMIZATION_APDU_SENDER) @EnableFlags(Flags.FLAG_OPTIMIZATION_APDU_SENDER) public void noteChannelOpen_noSession_noop() throws Exception { public void startTwoSessions_endAllSessions_hasSession() throws Exception { mEuiccSession.startSession(SESSION_ID_1); mEuiccSession.noteChannelOpen(mApduSender); mEuiccSession.startSession(SESSION_ID_2); mEuiccSession.noteChannelOpen(mApduSender2); assertThat(mEuiccSession.hasSession()).isTrue(); mEuiccSession.endAllSessions(); assertThat(mEuiccSession.hasSession()).isFalse(); verify(mApduSender).closeAnyOpenChannel(); verify(mApduSender2).closeAnyOpenChannel(); } @Test @EnableFlags(Flags.FLAG_OPTIMIZATION_APDU_SENDER) public void noteChannelOpen_noSession_endSession_noop() throws Exception { // noteChannelOpen called without a session started // noteChannelOpen called without a session started mEuiccSession.noteChannelOpen(mApduSender); mEuiccSession.noteChannelOpen(mApduSender); Loading @@ -175,4 +197,18 @@ public class EuiccSessionTest extends TelephonyTest { assertThat(mEuiccSession.hasSession()).isFalse(); assertThat(mEuiccSession.hasSession()).isFalse(); verify(mApduSender, never()).closeAnyOpenChannel(); verify(mApduSender, never()).closeAnyOpenChannel(); } } @Test @EnableFlags(Flags.FLAG_OPTIMIZATION_APDU_SENDER) public void endAllSessions_noSession_endAllSessions_noOp() throws Exception { // noteChannelOpen called without a session started mEuiccSession.noteChannelOpen(mApduSender); assertThat(mEuiccSession.hasSession()).isFalse(); mEuiccSession.endAllSessions(); assertThat(mEuiccSession.hasSession()).isFalse(); verify(mApduSender, never()).closeAnyOpenChannel(); } } }