Loading src/java/com/android/internal/telephony/uicc/IccCardProxy.java +44 −19 Original line number Diff line number Diff line Loading @@ -32,6 +32,7 @@ import android.telephony.TelephonyManager; import android.text.TextUtils; import com.android.internal.telephony.CommandsInterface; import com.android.internal.telephony.CommandsInterface.RadioState; import com.android.internal.telephony.IccCard; import com.android.internal.telephony.IccCardConstants; import com.android.internal.telephony.IccCardConstants.State; Loading Loading @@ -105,7 +106,7 @@ public class IccCardProxy extends Handler implements IccCard { private UiccCardApplication mUiccApplication = null; private IccRecords mIccRecords = null; private CdmaSubscriptionSourceManager mCdmaSSM = null; private boolean mRadioOn = false; private RadioState mRadioState = RadioState.RADIO_UNAVAILABLE; private boolean mQuietMode = false; // when set to true IccCardProxy will not broadcast // ACTION_SIM_STATE_CHANGED intents private boolean mInitialized = false; Loading @@ -128,7 +129,6 @@ public class IccCardProxy extends Handler implements IccCard { ci.registerForOffOrNotAvailable(this, EVENT_RADIO_OFF_OR_UNAVAILABLE, null); resetProperties(); setExternalState(State.NOT_READY, false); } public void dispose() { Loading Loading @@ -209,15 +209,18 @@ public class IccCardProxy extends Handler implements IccCard { public void handleMessage(Message msg) { switch (msg.what) { case EVENT_RADIO_OFF_OR_UNAVAILABLE: mRadioOn = false; if (CommandsInterface.RadioState.RADIO_UNAVAILABLE == mCi.getRadioState()) { setExternalState(State.NOT_READY); } mRadioState = mCi.getRadioState(); updateExternalState(); break; case EVENT_RADIO_ON: mRadioOn = true; mRadioState = RadioState.RADIO_ON; if (!mInitialized) { updateQuietMode(); } else { // updateQuietMode() triggers ICC_CHANGED, which eventually // calls updateExternalState; thus, we don't need this in the // above case updateExternalState(); } break; case EVENT_ICC_CHANGED: Loading Loading @@ -324,11 +327,9 @@ public class IccCardProxy extends Handler implements IccCard { private void updateIccAvailability() { synchronized (mLock) { UiccCard newCard = mUiccController.getUiccCard(mPhoneId); CardState state = CardState.CARDSTATE_ABSENT; UiccCardApplication newApp = null; IccRecords newRecords = null; if (newCard != null) { state = newCard.getCardState(); newApp = newCard.getApplication(mCurrentAppType); if (newApp != null) { newRecords = newApp.getIccRecords(); Loading @@ -336,7 +337,7 @@ public class IccCardProxy extends Handler implements IccCard { } if (mIccRecords != newRecords || mUiccApplication != newApp || mUiccCard != newCard) { if (DBG) log("Icc changed. Reregestering."); if (DBG) log("Icc changed. Reregistering."); unregisterUiccCardEvents(); mUiccCard = newCard; mUiccApplication = newApp; Loading Loading @@ -366,15 +367,27 @@ public class IccCardProxy extends Handler implements IccCard { // mUiccCard could be null at bootup, before valid card states have // been received from UiccController. if (mUiccCard == null) { setExternalState(State.NOT_READY); setExternalState(State.UNKNOWN); return; } if (mUiccCard.getCardState() == CardState.CARDSTATE_ABSENT) { if (mRadioOn) { setExternalState(State.ABSENT); /* * Both IccCardProxy and UiccController are registered for * RadioState changes. When the UiccController receives a radio * state changed to Unknown it will dispose of all of the IccCard * objects, which will then notify the IccCardProxy and the null * object will force the state to unknown. However, because the * IccCardProxy is also registered for RadioState changes, it will * recieve that signal first. By triggering on radio state changes * directly, we reduce the time window during which the modem is * UNAVAILABLE but the IccStatus is reported as something valid. * This is not ideal. */ if (mRadioState == RadioState.RADIO_UNAVAILABLE) { setExternalState(State.UNKNOWN); } else { setExternalState(State.NOT_READY); setExternalState(State.ABSENT); } return; } Loading @@ -394,9 +407,20 @@ public class IccCardProxy extends Handler implements IccCard { return; } // By process of elimination, the UICC Card State = PRESENT switch (mUiccApplication.getState()) { case APPSTATE_UNKNOWN: setExternalState(State.UNKNOWN); /* * APPSTATE_UNKNOWN is a catch-all state reported whenever the app * is not explicitly in one of the other states. To differentiate the * case where we know that there is a card present, but the APP is not * ready, we choose NOT_READY here instead of unknown. This is possible * in at least two cases: * 1) A transient during the process of the SIM bringup * 2) There is no valid App on the SIM to load, which can be the case with an * eSIM/soft SIM. */ setExternalState(State.NOT_READY); break; case APPSTATE_DETECTED: HandleDetectedState(); Loading Loading @@ -513,15 +537,16 @@ public class IccCardProxy extends Handler implements IccCard { } if (!override && newState == mExternalState) { loge("setExternalState: !override and newstate unchanged from " + newState); log("setExternalState: !override and newstate unchanged from " + newState); return; } mExternalState = newState; loge("setExternalState: set mPhoneId=" + mPhoneId + " mExternalState=" + mExternalState); log("setExternalState: set mPhoneId=" + mPhoneId + " mExternalState=" + mExternalState); mTelephonyManager.setSimStateForPhone(mPhoneId, getState().toString()); // For locked states, we should be sending internal broadcast. if (IccCardConstants.INTENT_VALUE_ICC_LOCKED.equals(getIccStateIntentString(mExternalState))) { if (IccCardConstants.INTENT_VALUE_ICC_LOCKED.equals( getIccStateIntentString(mExternalState))) { broadcastInternalIccStateChangedIntent(getIccStateIntentString(mExternalState), getIccStateReason(mExternalState)); } else { Loading Loading @@ -934,7 +959,7 @@ public class IccCardProxy extends Handler implements IccCard { pw.println(" mUiccApplication=" + mUiccApplication); pw.println(" mIccRecords=" + mIccRecords); pw.println(" mCdmaSSM=" + mCdmaSSM); pw.println(" mRadioOn=" + mRadioOn); pw.println(" mRadioState=" + mRadioState); pw.println(" mQuietMode=" + mQuietMode); pw.println(" mInitialized=" + mInitialized); pw.println(" mExternalState=" + mExternalState); Loading tests/telephonytests/src/com/android/internal/telephony/uicc/IccCardProxyTest.java 0 → 100644 +141 −0 Original line number Diff line number Diff line /* * Copyright (C) 2017 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.internal.telephony.uicc; import static com.android.internal.telephony.TelephonyTestUtils.waitForMs; import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.when; import android.os.Handler; import android.os.HandlerThread; import android.test.suitebuilder.annotation.SmallTest; import com.android.internal.telephony.CommandsInterface; import com.android.internal.telephony.IccCardConstants.State; import com.android.internal.telephony.TelephonyTest; import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppState; import com.android.internal.telephony.uicc.IccCardStatus.CardState; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.mockito.Mock; public class IccCardProxyTest extends TelephonyTest { private IccCardProxy mIccCardProxyUT; // private UiccCard mUiccCard; private IccCardProxyHandlerThread mIccCardProxyHandlerThread; private static final int PHONE_ID = 0; private static final int PHONE_COUNT = 1; private static final int SCARY_SLEEP_MS = 200; // Must match IccCardProxy.EVENT_ICC_CHANGED private static final int EVENT_ICC_CHANGED = 3; @Mock private Handler mMockedHandler; @Mock private IccCardStatus mIccCardStatus; @Mock private UiccCard mUiccCard; @Mock private UiccCardApplication mUiccCardApplication; private class IccCardProxyHandlerThread extends HandlerThread { private IccCardProxyHandlerThread(String name) { super(name); } @Override public void onLooperPrepared() { /* create a new UICC Controller associated with the simulated Commands */ mIccCardProxyUT = new IccCardProxy(mContext, mSimulatedCommands, PHONE_ID); setReady(true); } } @Before public void setUp() throws Exception { super.setUp(this.getClass().getSimpleName()); doReturn(PHONE_COUNT).when(mTelephonyManager).getPhoneCount(); doReturn(PHONE_COUNT).when(mTelephonyManager).getSimCount(); mSimulatedCommands.setIccCardStatus(mIccCardStatus); mIccCardProxyHandlerThread = new IccCardProxyHandlerThread(TAG); mIccCardProxyHandlerThread.start(); waitUntilReady(); } @After public void tearDown() throws Exception { mIccCardProxyHandlerThread.quitSafely(); super.tearDown(); } @Test @SmallTest public void testInitialCardState() { assertEquals(mIccCardProxyUT.getState(), State.UNKNOWN); } @Test @SmallTest public void testPowerOn() { mSimulatedCommands.setRadioPower(true, null); mSimulatedCommands.notifyRadioOn(); when(mUiccController.getUiccCard(anyInt())).thenReturn(mUiccCard); mIccCardProxyUT.sendMessage(mIccCardProxyUT.obtainMessage(EVENT_ICC_CHANGED)); waitForMs(SCARY_SLEEP_MS); assertEquals(CommandsInterface.RadioState.RADIO_ON, mSimulatedCommands.getRadioState()); assertEquals(mIccCardProxyUT.getState(), State.NOT_READY); logd("IccCardProxy state = " + mIccCardProxyUT.getState()); } @Test @SmallTest public void testCardLoaded() { testPowerOn(); when(mUiccCard.getCardState()).thenReturn(CardState.CARDSTATE_PRESENT); mIccCardProxyUT.sendMessage(mIccCardProxyUT.obtainMessage(EVENT_ICC_CHANGED)); waitForMs(SCARY_SLEEP_MS); assertEquals(mIccCardProxyUT.getState(), State.NOT_READY); } @Test @SmallTest public void testAppNotLoaded() { testPowerOn(); when(mUiccCard.getCardState()).thenReturn(CardState.CARDSTATE_PRESENT); mIccCardProxyUT.sendMessage(mIccCardProxyUT.obtainMessage(EVENT_ICC_CHANGED)); when(mUiccCardApplication.getState()).thenReturn(AppState.APPSTATE_UNKNOWN); when(mUiccCard.getApplication(anyInt())).thenReturn(mUiccCardApplication); waitForMs(SCARY_SLEEP_MS); assertEquals(mIccCardProxyUT.getState(), State.NOT_READY); } @Test @SmallTest public void testAppReady() { testPowerOn(); when(mUiccCard.getCardState()).thenReturn(CardState.CARDSTATE_PRESENT); mIccCardProxyUT.sendMessage(mIccCardProxyUT.obtainMessage(EVENT_ICC_CHANGED)); when(mUiccCardApplication.getState()).thenReturn(AppState.APPSTATE_READY); when(mUiccCard.getApplication(anyInt())).thenReturn(mUiccCardApplication); waitForMs(SCARY_SLEEP_MS); assertEquals(mIccCardProxyUT.getState(), State.READY); } } Loading
src/java/com/android/internal/telephony/uicc/IccCardProxy.java +44 −19 Original line number Diff line number Diff line Loading @@ -32,6 +32,7 @@ import android.telephony.TelephonyManager; import android.text.TextUtils; import com.android.internal.telephony.CommandsInterface; import com.android.internal.telephony.CommandsInterface.RadioState; import com.android.internal.telephony.IccCard; import com.android.internal.telephony.IccCardConstants; import com.android.internal.telephony.IccCardConstants.State; Loading Loading @@ -105,7 +106,7 @@ public class IccCardProxy extends Handler implements IccCard { private UiccCardApplication mUiccApplication = null; private IccRecords mIccRecords = null; private CdmaSubscriptionSourceManager mCdmaSSM = null; private boolean mRadioOn = false; private RadioState mRadioState = RadioState.RADIO_UNAVAILABLE; private boolean mQuietMode = false; // when set to true IccCardProxy will not broadcast // ACTION_SIM_STATE_CHANGED intents private boolean mInitialized = false; Loading @@ -128,7 +129,6 @@ public class IccCardProxy extends Handler implements IccCard { ci.registerForOffOrNotAvailable(this, EVENT_RADIO_OFF_OR_UNAVAILABLE, null); resetProperties(); setExternalState(State.NOT_READY, false); } public void dispose() { Loading Loading @@ -209,15 +209,18 @@ public class IccCardProxy extends Handler implements IccCard { public void handleMessage(Message msg) { switch (msg.what) { case EVENT_RADIO_OFF_OR_UNAVAILABLE: mRadioOn = false; if (CommandsInterface.RadioState.RADIO_UNAVAILABLE == mCi.getRadioState()) { setExternalState(State.NOT_READY); } mRadioState = mCi.getRadioState(); updateExternalState(); break; case EVENT_RADIO_ON: mRadioOn = true; mRadioState = RadioState.RADIO_ON; if (!mInitialized) { updateQuietMode(); } else { // updateQuietMode() triggers ICC_CHANGED, which eventually // calls updateExternalState; thus, we don't need this in the // above case updateExternalState(); } break; case EVENT_ICC_CHANGED: Loading Loading @@ -324,11 +327,9 @@ public class IccCardProxy extends Handler implements IccCard { private void updateIccAvailability() { synchronized (mLock) { UiccCard newCard = mUiccController.getUiccCard(mPhoneId); CardState state = CardState.CARDSTATE_ABSENT; UiccCardApplication newApp = null; IccRecords newRecords = null; if (newCard != null) { state = newCard.getCardState(); newApp = newCard.getApplication(mCurrentAppType); if (newApp != null) { newRecords = newApp.getIccRecords(); Loading @@ -336,7 +337,7 @@ public class IccCardProxy extends Handler implements IccCard { } if (mIccRecords != newRecords || mUiccApplication != newApp || mUiccCard != newCard) { if (DBG) log("Icc changed. Reregestering."); if (DBG) log("Icc changed. Reregistering."); unregisterUiccCardEvents(); mUiccCard = newCard; mUiccApplication = newApp; Loading Loading @@ -366,15 +367,27 @@ public class IccCardProxy extends Handler implements IccCard { // mUiccCard could be null at bootup, before valid card states have // been received from UiccController. if (mUiccCard == null) { setExternalState(State.NOT_READY); setExternalState(State.UNKNOWN); return; } if (mUiccCard.getCardState() == CardState.CARDSTATE_ABSENT) { if (mRadioOn) { setExternalState(State.ABSENT); /* * Both IccCardProxy and UiccController are registered for * RadioState changes. When the UiccController receives a radio * state changed to Unknown it will dispose of all of the IccCard * objects, which will then notify the IccCardProxy and the null * object will force the state to unknown. However, because the * IccCardProxy is also registered for RadioState changes, it will * recieve that signal first. By triggering on radio state changes * directly, we reduce the time window during which the modem is * UNAVAILABLE but the IccStatus is reported as something valid. * This is not ideal. */ if (mRadioState == RadioState.RADIO_UNAVAILABLE) { setExternalState(State.UNKNOWN); } else { setExternalState(State.NOT_READY); setExternalState(State.ABSENT); } return; } Loading @@ -394,9 +407,20 @@ public class IccCardProxy extends Handler implements IccCard { return; } // By process of elimination, the UICC Card State = PRESENT switch (mUiccApplication.getState()) { case APPSTATE_UNKNOWN: setExternalState(State.UNKNOWN); /* * APPSTATE_UNKNOWN is a catch-all state reported whenever the app * is not explicitly in one of the other states. To differentiate the * case where we know that there is a card present, but the APP is not * ready, we choose NOT_READY here instead of unknown. This is possible * in at least two cases: * 1) A transient during the process of the SIM bringup * 2) There is no valid App on the SIM to load, which can be the case with an * eSIM/soft SIM. */ setExternalState(State.NOT_READY); break; case APPSTATE_DETECTED: HandleDetectedState(); Loading Loading @@ -513,15 +537,16 @@ public class IccCardProxy extends Handler implements IccCard { } if (!override && newState == mExternalState) { loge("setExternalState: !override and newstate unchanged from " + newState); log("setExternalState: !override and newstate unchanged from " + newState); return; } mExternalState = newState; loge("setExternalState: set mPhoneId=" + mPhoneId + " mExternalState=" + mExternalState); log("setExternalState: set mPhoneId=" + mPhoneId + " mExternalState=" + mExternalState); mTelephonyManager.setSimStateForPhone(mPhoneId, getState().toString()); // For locked states, we should be sending internal broadcast. if (IccCardConstants.INTENT_VALUE_ICC_LOCKED.equals(getIccStateIntentString(mExternalState))) { if (IccCardConstants.INTENT_VALUE_ICC_LOCKED.equals( getIccStateIntentString(mExternalState))) { broadcastInternalIccStateChangedIntent(getIccStateIntentString(mExternalState), getIccStateReason(mExternalState)); } else { Loading Loading @@ -934,7 +959,7 @@ public class IccCardProxy extends Handler implements IccCard { pw.println(" mUiccApplication=" + mUiccApplication); pw.println(" mIccRecords=" + mIccRecords); pw.println(" mCdmaSSM=" + mCdmaSSM); pw.println(" mRadioOn=" + mRadioOn); pw.println(" mRadioState=" + mRadioState); pw.println(" mQuietMode=" + mQuietMode); pw.println(" mInitialized=" + mInitialized); pw.println(" mExternalState=" + mExternalState); Loading
tests/telephonytests/src/com/android/internal/telephony/uicc/IccCardProxyTest.java 0 → 100644 +141 −0 Original line number Diff line number Diff line /* * Copyright (C) 2017 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.internal.telephony.uicc; import static com.android.internal.telephony.TelephonyTestUtils.waitForMs; import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.when; import android.os.Handler; import android.os.HandlerThread; import android.test.suitebuilder.annotation.SmallTest; import com.android.internal.telephony.CommandsInterface; import com.android.internal.telephony.IccCardConstants.State; import com.android.internal.telephony.TelephonyTest; import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppState; import com.android.internal.telephony.uicc.IccCardStatus.CardState; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.mockito.Mock; public class IccCardProxyTest extends TelephonyTest { private IccCardProxy mIccCardProxyUT; // private UiccCard mUiccCard; private IccCardProxyHandlerThread mIccCardProxyHandlerThread; private static final int PHONE_ID = 0; private static final int PHONE_COUNT = 1; private static final int SCARY_SLEEP_MS = 200; // Must match IccCardProxy.EVENT_ICC_CHANGED private static final int EVENT_ICC_CHANGED = 3; @Mock private Handler mMockedHandler; @Mock private IccCardStatus mIccCardStatus; @Mock private UiccCard mUiccCard; @Mock private UiccCardApplication mUiccCardApplication; private class IccCardProxyHandlerThread extends HandlerThread { private IccCardProxyHandlerThread(String name) { super(name); } @Override public void onLooperPrepared() { /* create a new UICC Controller associated with the simulated Commands */ mIccCardProxyUT = new IccCardProxy(mContext, mSimulatedCommands, PHONE_ID); setReady(true); } } @Before public void setUp() throws Exception { super.setUp(this.getClass().getSimpleName()); doReturn(PHONE_COUNT).when(mTelephonyManager).getPhoneCount(); doReturn(PHONE_COUNT).when(mTelephonyManager).getSimCount(); mSimulatedCommands.setIccCardStatus(mIccCardStatus); mIccCardProxyHandlerThread = new IccCardProxyHandlerThread(TAG); mIccCardProxyHandlerThread.start(); waitUntilReady(); } @After public void tearDown() throws Exception { mIccCardProxyHandlerThread.quitSafely(); super.tearDown(); } @Test @SmallTest public void testInitialCardState() { assertEquals(mIccCardProxyUT.getState(), State.UNKNOWN); } @Test @SmallTest public void testPowerOn() { mSimulatedCommands.setRadioPower(true, null); mSimulatedCommands.notifyRadioOn(); when(mUiccController.getUiccCard(anyInt())).thenReturn(mUiccCard); mIccCardProxyUT.sendMessage(mIccCardProxyUT.obtainMessage(EVENT_ICC_CHANGED)); waitForMs(SCARY_SLEEP_MS); assertEquals(CommandsInterface.RadioState.RADIO_ON, mSimulatedCommands.getRadioState()); assertEquals(mIccCardProxyUT.getState(), State.NOT_READY); logd("IccCardProxy state = " + mIccCardProxyUT.getState()); } @Test @SmallTest public void testCardLoaded() { testPowerOn(); when(mUiccCard.getCardState()).thenReturn(CardState.CARDSTATE_PRESENT); mIccCardProxyUT.sendMessage(mIccCardProxyUT.obtainMessage(EVENT_ICC_CHANGED)); waitForMs(SCARY_SLEEP_MS); assertEquals(mIccCardProxyUT.getState(), State.NOT_READY); } @Test @SmallTest public void testAppNotLoaded() { testPowerOn(); when(mUiccCard.getCardState()).thenReturn(CardState.CARDSTATE_PRESENT); mIccCardProxyUT.sendMessage(mIccCardProxyUT.obtainMessage(EVENT_ICC_CHANGED)); when(mUiccCardApplication.getState()).thenReturn(AppState.APPSTATE_UNKNOWN); when(mUiccCard.getApplication(anyInt())).thenReturn(mUiccCardApplication); waitForMs(SCARY_SLEEP_MS); assertEquals(mIccCardProxyUT.getState(), State.NOT_READY); } @Test @SmallTest public void testAppReady() { testPowerOn(); when(mUiccCard.getCardState()).thenReturn(CardState.CARDSTATE_PRESENT); mIccCardProxyUT.sendMessage(mIccCardProxyUT.obtainMessage(EVENT_ICC_CHANGED)); when(mUiccCardApplication.getState()).thenReturn(AppState.APPSTATE_READY); when(mUiccCard.getApplication(anyInt())).thenReturn(mUiccCardApplication); waitForMs(SCARY_SLEEP_MS); assertEquals(mIccCardProxyUT.getState(), State.READY); } }