Loading src/java/com/android/internal/telephony/DeviceStateMonitor.java +51 −8 Original line number Diff line number Diff line Loading @@ -20,10 +20,12 @@ import static android.hardware.radio.V1_0.DeviceStateType.CHARGING_STATE; import static android.hardware.radio.V1_0.DeviceStateType.LOW_DATA_EXPECTED; import static android.hardware.radio.V1_0.DeviceStateType.POWER_SAVE_MODE; import android.app.UiModeManager; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.res.Configuration; import android.hardware.display.DisplayManager; import android.hardware.radio.V1_5.IndicationFilter; import android.net.ConnectivityManager; Loading Loading @@ -69,6 +71,7 @@ public class DeviceStateMonitor extends Handler { protected static final String TAG = DeviceStateMonitor.class.getSimpleName(); static final int EVENT_RIL_CONNECTED = 0; static final int EVENT_CAR_MODE_CHANGED = 1; @VisibleForTesting static final int EVENT_SCREEN_STATE_CHANGED = 2; static final int EVENT_POWER_SAVE_MODE_CHANGED = 3; Loading Loading @@ -169,6 +172,13 @@ public class DeviceStateMonitor extends Handler { */ private boolean mIsWifiConnected; /** * Car mode is on. True means the device is currently connected to Android Auto. This should be * handled by mIsScreenOn, but the Android Auto display is private and not accessible by * DeviceStateMonitor from DisplayMonitor. */ private boolean mIsCarModeOn; /** * True indicates we should always enable the signal strength reporting from radio. */ Loading Loading @@ -238,6 +248,14 @@ public class DeviceStateMonitor extends Handler { msg = obtainMessage(EVENT_TETHERING_STATE_CHANGED); msg.arg1 = isTetheringOn ? 1 : 0; break; case UiModeManager.ACTION_ENTER_CAR_MODE_PRIORITIZED: msg = obtainMessage(EVENT_CAR_MODE_CHANGED); msg.arg1 = 1; // car mode on break; case UiModeManager.ACTION_EXIT_CAR_MODE_PRIORITIZED: msg = obtainMessage(EVENT_CAR_MODE_CHANGED); msg.arg1 = 0; // car mode off break; default: log("Unexpected broadcast intent: " + intent, false); return; Loading @@ -261,6 +279,7 @@ public class DeviceStateMonitor extends Handler { mIsPowerSaveOn = isPowerSaveModeOn(); mIsCharging = isDeviceCharging(); mIsScreenOn = isScreenOn(); mIsCarModeOn = isCarModeOn(); // Assuming tethering is always off after boot up. mIsTetheringOn = false; mIsLowDataExpected = false; Loading @@ -270,6 +289,7 @@ public class DeviceStateMonitor extends Handler { + ", mIsCharging=" + mIsCharging + ", mIsPowerSaveOn=" + mIsPowerSaveOn + ", mIsLowDataExpected=" + mIsLowDataExpected + ", mIsCarModeOn=" + mIsCarModeOn + ", mIsWifiConnected=" + mIsWifiConnected + ", mIsAlwaysSignalStrengthReportingEnabled=" + mIsAlwaysSignalStrengthReportingEnabled, false); Loading @@ -279,6 +299,8 @@ public class DeviceStateMonitor extends Handler { filter.addAction(BatteryManager.ACTION_CHARGING); filter.addAction(BatteryManager.ACTION_DISCHARGING); filter.addAction(ConnectivityManager.ACTION_TETHER_STATE_CHANGED); filter.addAction(UiModeManager.ACTION_ENTER_CAR_MODE_PRIORITIZED); filter.addAction(UiModeManager.ACTION_EXIT_CAR_MODE_PRIORITIZED); mPhone.getContext().registerReceiver(mBroadcastReceiver, filter, null, mPhone); mPhone.mCi.registerForRilConnected(this, EVENT_RIL_CONNECTED, null); Loading Loading @@ -381,7 +403,8 @@ public class DeviceStateMonitor extends Handler { // 1. The device is charging. // 2. When the screen is on. // 3. When the tethering is on. return mIsCharging || mIsScreenOn || mIsTetheringOn; // 4. When car mode (Android Auto) is on. return mIsCharging || mIsScreenOn || mIsTetheringOn || mIsCarModeOn; } /** Loading Loading @@ -434,14 +457,13 @@ public class DeviceStateMonitor extends Handler { case EVENT_POWER_SAVE_MODE_CHANGED: case EVENT_CHARGING_STATE_CHANGED: case EVENT_TETHERING_STATE_CHANGED: case EVENT_UPDATE_ALWAYS_REPORT_SIGNAL_STRENGTH: case EVENT_CAR_MODE_CHANGED: onUpdateDeviceState(msg.what, msg.arg1 != 0); break; case EVENT_WIFI_CONNECTION_CHANGED: onUpdateDeviceState(msg.what, msg.arg1 != WIFI_UNAVAILABLE); break; case EVENT_UPDATE_ALWAYS_REPORT_SIGNAL_STRENGTH: onUpdateDeviceState(msg.what, msg.arg1 != 0); break; default: throw new IllegalStateException("Unexpected message arrives. msg = " + msg.what); } Loading Loading @@ -482,6 +504,10 @@ public class DeviceStateMonitor extends Handler { if (mIsAlwaysSignalStrengthReportingEnabled == state) return; mIsAlwaysSignalStrengthReportingEnabled = state; break; case EVENT_CAR_MODE_CHANGED: if (mIsCarModeOn == state) return; mIsCarModeOn = state; break; default: return; } Loading Loading @@ -652,7 +678,9 @@ public class DeviceStateMonitor extends Handler { private boolean isPowerSaveModeOn() { final PowerManager pm = (PowerManager) mPhone.getContext().getSystemService( Context.POWER_SERVICE); return pm.isPowerSaveMode(); boolean retval = pm.isPowerSaveMode(); log("isPowerSaveModeOn=" + retval, true); return retval; } /** Loading @@ -664,12 +692,14 @@ public class DeviceStateMonitor extends Handler { private boolean isDeviceCharging() { final BatteryManager bm = (BatteryManager) mPhone.getContext().getSystemService( Context.BATTERY_SERVICE); return bm.isCharging(); boolean retval = bm.isCharging(); log("isDeviceCharging=" + retval, true); return retval; } /** * @return True if one the device's screen (e.g. main screen, wifi display, HDMI display, or * Android auto, etc...) is on. * @return True if one the device's screen (e.g. main screen, wifi display, HDMI display etc...) * is on. */ private boolean isScreenOn() { // Note that we don't listen to Intent.SCREEN_ON and Intent.SCREEN_OFF because they are no Loading @@ -696,6 +726,18 @@ public class DeviceStateMonitor extends Handler { return false; } /** * @return True if car mode (Android Auto) is on. */ private boolean isCarModeOn() { final UiModeManager umm = (UiModeManager) mPhone.getContext().getSystemService( Context.UI_MODE_SERVICE); if (umm == null) return false; boolean retval = umm.getCurrentModeType() == Configuration.UI_MODE_TYPE_CAR; log("isCarModeOn=" + retval, true); return retval; } /** * Register for PhysicalChannelConfig notifications changed. On change, msg.obj will be an * AsyncResult with a boolean result. AsyncResult.result is true if notifications are enabled Loading Loading @@ -744,6 +786,7 @@ public class DeviceStateMonitor extends Handler { ipw.println("mIsCharging=" + mIsCharging); ipw.println("mIsPowerSaveOn=" + mIsPowerSaveOn); ipw.println("mIsLowDataExpected=" + mIsLowDataExpected); ipw.println("mIsCarModeOn=" + mIsCarModeOn); ipw.println("mUnsolicitedResponseFilter=" + mUnsolicitedResponseFilter); ipw.println("mIsWifiConnected=" + mIsWifiConnected); ipw.println("mIsAlwaysSignalStrengthReportingEnabled=" Loading src/java/com/android/internal/telephony/GsmCdmaPhone.java +23 −14 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ package com.android.internal.telephony; import static com.android.internal.telephony.CommandException.Error.GENERIC_FAILURE; import static com.android.internal.telephony.CommandException.Error.SIM_BUSY; import static com.android.internal.telephony.CommandsInterface.CF_ACTION_DISABLE; import static com.android.internal.telephony.CommandsInterface.CF_ACTION_ENABLE; import static com.android.internal.telephony.CommandsInterface.CF_ACTION_ERASURE; Loading Loading @@ -163,6 +165,9 @@ public class GsmCdmaPhone extends Phone { // keeps track of when we have triggered an emergency call due to the ril.test.emergencynumber // param being set and we should generate a simulated exit from the modem upon exit of ECbM. private boolean mIsTestingEmergencyCallbackMode = false; @VisibleForTesting public static int ENABLE_UICC_APPS_MAX_RETRIES = 3; private static final int REAPPLY_UICC_APPS_SETTING_RETRY_TIME_GAP_IN_MS = 5000; // A runnable which is used to automatically exit from Ecm after a period of time. private Runnable mExitEcmRunnable = new Runnable() { Loading Loading @@ -3066,24 +3071,27 @@ public class GsmCdmaPhone extends Phone { mUiccApplicationsEnabled = (Boolean) ar.result; // Intentional falling through. case EVENT_UICC_APPS_ENABLEMENT_SETTING_CHANGED: reapplyUiccAppsEnablementIfNeeded(); reapplyUiccAppsEnablementIfNeeded(ENABLE_UICC_APPS_MAX_RETRIES); break; case EVENT_REAPPLY_UICC_APPS_ENABLEMENT_DONE: { ar = (AsyncResult) msg.obj; if (ar == null || ar.exception == null) return; // TODO: b/146181737 don't throw exception and uncomment the retry below. boolean expectedValue = (boolean) ar.userObj; Pair<Boolean, Integer> userObject = (Pair) ar.userObj; if (userObject == null) return; boolean expectedValue = userObject.first; int retries = userObject.second; CommandException.Error error = ((CommandException) ar.exception).getCommandError(); throw new RuntimeException("Error received when re-applying uicc application" loge("Error received when re-applying uicc application" + " setting to " + expectedValue + " on phone " + mPhoneId + " Error code: " + error); // if (error == INTERNAL_ERR || error == SIM_BUSY) { // // Retry for certain errors, but not for others like RADIO_NOT_AVAILABLE or // // SIM_ABSENT, as they will trigger it whey they become available. // postDelayed(()->reapplyUiccAppsEnablementIfNeeded(), 1000); // } // break; + " Error code: " + error + " retry count left: " + retries); if (retries > 0 && (error == GENERIC_FAILURE || error == SIM_BUSY)) { // Retry for certain errors, but not for others like RADIO_NOT_AVAILABLE or // SIM_ABSENT, as they will trigger it whey they become available. postDelayed(()->reapplyUiccAppsEnablementIfNeeded(retries - 1), REAPPLY_UICC_APPS_SETTING_RETRY_TIME_GAP_IN_MS); } break; } default: super.handleMessage(msg); Loading Loading @@ -3183,7 +3191,7 @@ public class GsmCdmaPhone extends Phone { } } reapplyUiccAppsEnablementIfNeeded(); reapplyUiccAppsEnablementIfNeeded(ENABLE_UICC_APPS_MAX_RETRIES); } private void processIccRecordEvents(int eventCode) { Loading Loading @@ -4346,7 +4354,7 @@ public class GsmCdmaPhone extends Phone { updateUiTtyMode(ttyMode); } private void reapplyUiccAppsEnablementIfNeeded() { private void reapplyUiccAppsEnablementIfNeeded(int retries) { UiccSlot slot = mUiccController.getUiccSlotForPhone(mPhoneId); // If no card is present or we don't have mUiccApplicationsEnabled yet, do nothing. Loading @@ -4368,7 +4376,8 @@ public class GsmCdmaPhone extends Phone { // configured state. if (expectedValue != mUiccApplicationsEnabled) { mCi.enableUiccApplications(expectedValue, Message.obtain( this, EVENT_REAPPLY_UICC_APPS_ENABLEMENT_DONE, expectedValue)); this, EVENT_REAPPLY_UICC_APPS_ENABLEMENT_DONE, new Pair<Boolean, Integer>(expectedValue, retries))); } } Loading tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java +42 −39 Original line number Diff line number Diff line Loading @@ -33,6 +33,7 @@ import static org.mockito.Matchers.nullable; import static org.mockito.Mockito.anyBoolean; import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.eq; Loading Loading @@ -1265,45 +1266,47 @@ public class GsmCdmaPhoneTest extends TelephonyTest { verify(mSST).setRadioPower(true, true, false, true); } // TODO: b/146181737 uncomment below test. // @Test // @SmallTest // public void testReapplyUiccApplicationEnablementRetry() throws Exception { // mPhoneUT.mCi = mMockCi; // // Set SIM to be present, with a fake iccId, and notify enablement being false. // doReturn(mUiccSlot).when(mUiccController).getUiccSlotForPhone(anyInt()); // doReturn(IccCardStatus.CardState.CARDSTATE_PRESENT).when(mUiccSlot).getCardState(); // String iccId = "Fake iccId"; // doReturn(iccId).when(mUiccSlot).getIccId(); // Message.obtain(mPhoneUT, EVENT_UICC_APPS_ENABLEMENT_CHANGED, // new AsyncResult(null, false, null)).sendToTarget(); // processAllMessages(); // // // Should try to enable uicc applications as by default hey are expected to be true. // ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class); // verify(mMockCi).enableUiccApplications(eq(true), messageCaptor.capture()); // clearInvocations(mMockCi); // // Send message back with SIM_BUSY exception. Should retry. // AsyncResult.forMessage(messageCaptor.getValue(), null, new CommandException( // CommandException.Error.SIM_BUSY)); // messageCaptor.getValue().sendToTarget(); // processAllMessages(); // // There should be a retry message. // moveTimeForward(5000); // processAllMessages(); // verify(mMockCi).enableUiccApplications(eq(true), messageCaptor.capture()); // clearInvocations(mMockCi); // // // Send message back with NOT_SUPPORTED exception. Should retry. // AsyncResult.forMessage(messageCaptor.getValue(), null, new CommandException( // CommandException.Error.REQUEST_NOT_SUPPORTED)); // messageCaptor.getValue().sendToTarget(); // processAllMessages(); // // There should not be a retry message. // moveTimeForward(5000); // processAllMessages(); // verify(mMockCi, never()).enableUiccApplications(eq(true), messageCaptor.capture()); // } @Test @SmallTest public void testReapplyUiccApplicationEnablementRetry() throws Exception { mPhoneUT.mCi = mMockCi; // Set SIM to be present, with a fake iccId, and notify enablement being false. doReturn(mUiccSlot).when(mUiccController).getUiccSlotForPhone(anyInt()); doReturn(IccCardStatus.CardState.CARDSTATE_PRESENT).when(mUiccSlot).getCardState(); String iccId = "Fake iccId"; doReturn(iccId).when(mUiccSlot).getIccId(); Message.obtain(mPhoneUT, EVENT_UICC_APPS_ENABLEMENT_STATUS_CHANGED, new AsyncResult(null, false, null)).sendToTarget(); processAllMessages(); // Should try to enable uicc applications as by default hey are expected to be true. ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class); verify(mMockCi).enableUiccApplications(eq(true), messageCaptor.capture()); clearInvocations(mMockCi); for (int i = 0; i < GsmCdmaPhone.ENABLE_UICC_APPS_MAX_RETRIES; i++) { // Send message back with SIM_BUSY exception. Should retry. AsyncResult.forMessage(messageCaptor.getValue(), null, new CommandException( CommandException.Error.SIM_BUSY)); messageCaptor.getValue().sendToTarget(); processAllMessages(); // There should be a retry message. moveTimeForward(5000); processAllMessages(); verify(mMockCi).enableUiccApplications(eq(true), messageCaptor.capture()); clearInvocations(mMockCi); } // Reaches max retries. Should NOT retry. AsyncResult.forMessage(messageCaptor.getValue(), null, new CommandException( CommandException.Error.SIM_BUSY)); messageCaptor.getValue().sendToTarget(); processAllMessages(); // There should NOT be a retry message. moveTimeForward(5000); processAllMessages(); verify(mMockCi, never()).enableUiccApplications(eq(true), messageCaptor.capture()); clearInvocations(mMockCi); } @Test @SmallTest Loading Loading
src/java/com/android/internal/telephony/DeviceStateMonitor.java +51 −8 Original line number Diff line number Diff line Loading @@ -20,10 +20,12 @@ import static android.hardware.radio.V1_0.DeviceStateType.CHARGING_STATE; import static android.hardware.radio.V1_0.DeviceStateType.LOW_DATA_EXPECTED; import static android.hardware.radio.V1_0.DeviceStateType.POWER_SAVE_MODE; import android.app.UiModeManager; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.res.Configuration; import android.hardware.display.DisplayManager; import android.hardware.radio.V1_5.IndicationFilter; import android.net.ConnectivityManager; Loading Loading @@ -69,6 +71,7 @@ public class DeviceStateMonitor extends Handler { protected static final String TAG = DeviceStateMonitor.class.getSimpleName(); static final int EVENT_RIL_CONNECTED = 0; static final int EVENT_CAR_MODE_CHANGED = 1; @VisibleForTesting static final int EVENT_SCREEN_STATE_CHANGED = 2; static final int EVENT_POWER_SAVE_MODE_CHANGED = 3; Loading Loading @@ -169,6 +172,13 @@ public class DeviceStateMonitor extends Handler { */ private boolean mIsWifiConnected; /** * Car mode is on. True means the device is currently connected to Android Auto. This should be * handled by mIsScreenOn, but the Android Auto display is private and not accessible by * DeviceStateMonitor from DisplayMonitor. */ private boolean mIsCarModeOn; /** * True indicates we should always enable the signal strength reporting from radio. */ Loading Loading @@ -238,6 +248,14 @@ public class DeviceStateMonitor extends Handler { msg = obtainMessage(EVENT_TETHERING_STATE_CHANGED); msg.arg1 = isTetheringOn ? 1 : 0; break; case UiModeManager.ACTION_ENTER_CAR_MODE_PRIORITIZED: msg = obtainMessage(EVENT_CAR_MODE_CHANGED); msg.arg1 = 1; // car mode on break; case UiModeManager.ACTION_EXIT_CAR_MODE_PRIORITIZED: msg = obtainMessage(EVENT_CAR_MODE_CHANGED); msg.arg1 = 0; // car mode off break; default: log("Unexpected broadcast intent: " + intent, false); return; Loading @@ -261,6 +279,7 @@ public class DeviceStateMonitor extends Handler { mIsPowerSaveOn = isPowerSaveModeOn(); mIsCharging = isDeviceCharging(); mIsScreenOn = isScreenOn(); mIsCarModeOn = isCarModeOn(); // Assuming tethering is always off after boot up. mIsTetheringOn = false; mIsLowDataExpected = false; Loading @@ -270,6 +289,7 @@ public class DeviceStateMonitor extends Handler { + ", mIsCharging=" + mIsCharging + ", mIsPowerSaveOn=" + mIsPowerSaveOn + ", mIsLowDataExpected=" + mIsLowDataExpected + ", mIsCarModeOn=" + mIsCarModeOn + ", mIsWifiConnected=" + mIsWifiConnected + ", mIsAlwaysSignalStrengthReportingEnabled=" + mIsAlwaysSignalStrengthReportingEnabled, false); Loading @@ -279,6 +299,8 @@ public class DeviceStateMonitor extends Handler { filter.addAction(BatteryManager.ACTION_CHARGING); filter.addAction(BatteryManager.ACTION_DISCHARGING); filter.addAction(ConnectivityManager.ACTION_TETHER_STATE_CHANGED); filter.addAction(UiModeManager.ACTION_ENTER_CAR_MODE_PRIORITIZED); filter.addAction(UiModeManager.ACTION_EXIT_CAR_MODE_PRIORITIZED); mPhone.getContext().registerReceiver(mBroadcastReceiver, filter, null, mPhone); mPhone.mCi.registerForRilConnected(this, EVENT_RIL_CONNECTED, null); Loading Loading @@ -381,7 +403,8 @@ public class DeviceStateMonitor extends Handler { // 1. The device is charging. // 2. When the screen is on. // 3. When the tethering is on. return mIsCharging || mIsScreenOn || mIsTetheringOn; // 4. When car mode (Android Auto) is on. return mIsCharging || mIsScreenOn || mIsTetheringOn || mIsCarModeOn; } /** Loading Loading @@ -434,14 +457,13 @@ public class DeviceStateMonitor extends Handler { case EVENT_POWER_SAVE_MODE_CHANGED: case EVENT_CHARGING_STATE_CHANGED: case EVENT_TETHERING_STATE_CHANGED: case EVENT_UPDATE_ALWAYS_REPORT_SIGNAL_STRENGTH: case EVENT_CAR_MODE_CHANGED: onUpdateDeviceState(msg.what, msg.arg1 != 0); break; case EVENT_WIFI_CONNECTION_CHANGED: onUpdateDeviceState(msg.what, msg.arg1 != WIFI_UNAVAILABLE); break; case EVENT_UPDATE_ALWAYS_REPORT_SIGNAL_STRENGTH: onUpdateDeviceState(msg.what, msg.arg1 != 0); break; default: throw new IllegalStateException("Unexpected message arrives. msg = " + msg.what); } Loading Loading @@ -482,6 +504,10 @@ public class DeviceStateMonitor extends Handler { if (mIsAlwaysSignalStrengthReportingEnabled == state) return; mIsAlwaysSignalStrengthReportingEnabled = state; break; case EVENT_CAR_MODE_CHANGED: if (mIsCarModeOn == state) return; mIsCarModeOn = state; break; default: return; } Loading Loading @@ -652,7 +678,9 @@ public class DeviceStateMonitor extends Handler { private boolean isPowerSaveModeOn() { final PowerManager pm = (PowerManager) mPhone.getContext().getSystemService( Context.POWER_SERVICE); return pm.isPowerSaveMode(); boolean retval = pm.isPowerSaveMode(); log("isPowerSaveModeOn=" + retval, true); return retval; } /** Loading @@ -664,12 +692,14 @@ public class DeviceStateMonitor extends Handler { private boolean isDeviceCharging() { final BatteryManager bm = (BatteryManager) mPhone.getContext().getSystemService( Context.BATTERY_SERVICE); return bm.isCharging(); boolean retval = bm.isCharging(); log("isDeviceCharging=" + retval, true); return retval; } /** * @return True if one the device's screen (e.g. main screen, wifi display, HDMI display, or * Android auto, etc...) is on. * @return True if one the device's screen (e.g. main screen, wifi display, HDMI display etc...) * is on. */ private boolean isScreenOn() { // Note that we don't listen to Intent.SCREEN_ON and Intent.SCREEN_OFF because they are no Loading @@ -696,6 +726,18 @@ public class DeviceStateMonitor extends Handler { return false; } /** * @return True if car mode (Android Auto) is on. */ private boolean isCarModeOn() { final UiModeManager umm = (UiModeManager) mPhone.getContext().getSystemService( Context.UI_MODE_SERVICE); if (umm == null) return false; boolean retval = umm.getCurrentModeType() == Configuration.UI_MODE_TYPE_CAR; log("isCarModeOn=" + retval, true); return retval; } /** * Register for PhysicalChannelConfig notifications changed. On change, msg.obj will be an * AsyncResult with a boolean result. AsyncResult.result is true if notifications are enabled Loading Loading @@ -744,6 +786,7 @@ public class DeviceStateMonitor extends Handler { ipw.println("mIsCharging=" + mIsCharging); ipw.println("mIsPowerSaveOn=" + mIsPowerSaveOn); ipw.println("mIsLowDataExpected=" + mIsLowDataExpected); ipw.println("mIsCarModeOn=" + mIsCarModeOn); ipw.println("mUnsolicitedResponseFilter=" + mUnsolicitedResponseFilter); ipw.println("mIsWifiConnected=" + mIsWifiConnected); ipw.println("mIsAlwaysSignalStrengthReportingEnabled=" Loading
src/java/com/android/internal/telephony/GsmCdmaPhone.java +23 −14 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ package com.android.internal.telephony; import static com.android.internal.telephony.CommandException.Error.GENERIC_FAILURE; import static com.android.internal.telephony.CommandException.Error.SIM_BUSY; import static com.android.internal.telephony.CommandsInterface.CF_ACTION_DISABLE; import static com.android.internal.telephony.CommandsInterface.CF_ACTION_ENABLE; import static com.android.internal.telephony.CommandsInterface.CF_ACTION_ERASURE; Loading Loading @@ -163,6 +165,9 @@ public class GsmCdmaPhone extends Phone { // keeps track of when we have triggered an emergency call due to the ril.test.emergencynumber // param being set and we should generate a simulated exit from the modem upon exit of ECbM. private boolean mIsTestingEmergencyCallbackMode = false; @VisibleForTesting public static int ENABLE_UICC_APPS_MAX_RETRIES = 3; private static final int REAPPLY_UICC_APPS_SETTING_RETRY_TIME_GAP_IN_MS = 5000; // A runnable which is used to automatically exit from Ecm after a period of time. private Runnable mExitEcmRunnable = new Runnable() { Loading Loading @@ -3066,24 +3071,27 @@ public class GsmCdmaPhone extends Phone { mUiccApplicationsEnabled = (Boolean) ar.result; // Intentional falling through. case EVENT_UICC_APPS_ENABLEMENT_SETTING_CHANGED: reapplyUiccAppsEnablementIfNeeded(); reapplyUiccAppsEnablementIfNeeded(ENABLE_UICC_APPS_MAX_RETRIES); break; case EVENT_REAPPLY_UICC_APPS_ENABLEMENT_DONE: { ar = (AsyncResult) msg.obj; if (ar == null || ar.exception == null) return; // TODO: b/146181737 don't throw exception and uncomment the retry below. boolean expectedValue = (boolean) ar.userObj; Pair<Boolean, Integer> userObject = (Pair) ar.userObj; if (userObject == null) return; boolean expectedValue = userObject.first; int retries = userObject.second; CommandException.Error error = ((CommandException) ar.exception).getCommandError(); throw new RuntimeException("Error received when re-applying uicc application" loge("Error received when re-applying uicc application" + " setting to " + expectedValue + " on phone " + mPhoneId + " Error code: " + error); // if (error == INTERNAL_ERR || error == SIM_BUSY) { // // Retry for certain errors, but not for others like RADIO_NOT_AVAILABLE or // // SIM_ABSENT, as they will trigger it whey they become available. // postDelayed(()->reapplyUiccAppsEnablementIfNeeded(), 1000); // } // break; + " Error code: " + error + " retry count left: " + retries); if (retries > 0 && (error == GENERIC_FAILURE || error == SIM_BUSY)) { // Retry for certain errors, but not for others like RADIO_NOT_AVAILABLE or // SIM_ABSENT, as they will trigger it whey they become available. postDelayed(()->reapplyUiccAppsEnablementIfNeeded(retries - 1), REAPPLY_UICC_APPS_SETTING_RETRY_TIME_GAP_IN_MS); } break; } default: super.handleMessage(msg); Loading Loading @@ -3183,7 +3191,7 @@ public class GsmCdmaPhone extends Phone { } } reapplyUiccAppsEnablementIfNeeded(); reapplyUiccAppsEnablementIfNeeded(ENABLE_UICC_APPS_MAX_RETRIES); } private void processIccRecordEvents(int eventCode) { Loading Loading @@ -4346,7 +4354,7 @@ public class GsmCdmaPhone extends Phone { updateUiTtyMode(ttyMode); } private void reapplyUiccAppsEnablementIfNeeded() { private void reapplyUiccAppsEnablementIfNeeded(int retries) { UiccSlot slot = mUiccController.getUiccSlotForPhone(mPhoneId); // If no card is present or we don't have mUiccApplicationsEnabled yet, do nothing. Loading @@ -4368,7 +4376,8 @@ public class GsmCdmaPhone extends Phone { // configured state. if (expectedValue != mUiccApplicationsEnabled) { mCi.enableUiccApplications(expectedValue, Message.obtain( this, EVENT_REAPPLY_UICC_APPS_ENABLEMENT_DONE, expectedValue)); this, EVENT_REAPPLY_UICC_APPS_ENABLEMENT_DONE, new Pair<Boolean, Integer>(expectedValue, retries))); } } Loading
tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java +42 −39 Original line number Diff line number Diff line Loading @@ -33,6 +33,7 @@ import static org.mockito.Matchers.nullable; import static org.mockito.Mockito.anyBoolean; import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.eq; Loading Loading @@ -1265,45 +1266,47 @@ public class GsmCdmaPhoneTest extends TelephonyTest { verify(mSST).setRadioPower(true, true, false, true); } // TODO: b/146181737 uncomment below test. // @Test // @SmallTest // public void testReapplyUiccApplicationEnablementRetry() throws Exception { // mPhoneUT.mCi = mMockCi; // // Set SIM to be present, with a fake iccId, and notify enablement being false. // doReturn(mUiccSlot).when(mUiccController).getUiccSlotForPhone(anyInt()); // doReturn(IccCardStatus.CardState.CARDSTATE_PRESENT).when(mUiccSlot).getCardState(); // String iccId = "Fake iccId"; // doReturn(iccId).when(mUiccSlot).getIccId(); // Message.obtain(mPhoneUT, EVENT_UICC_APPS_ENABLEMENT_CHANGED, // new AsyncResult(null, false, null)).sendToTarget(); // processAllMessages(); // // // Should try to enable uicc applications as by default hey are expected to be true. // ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class); // verify(mMockCi).enableUiccApplications(eq(true), messageCaptor.capture()); // clearInvocations(mMockCi); // // Send message back with SIM_BUSY exception. Should retry. // AsyncResult.forMessage(messageCaptor.getValue(), null, new CommandException( // CommandException.Error.SIM_BUSY)); // messageCaptor.getValue().sendToTarget(); // processAllMessages(); // // There should be a retry message. // moveTimeForward(5000); // processAllMessages(); // verify(mMockCi).enableUiccApplications(eq(true), messageCaptor.capture()); // clearInvocations(mMockCi); // // // Send message back with NOT_SUPPORTED exception. Should retry. // AsyncResult.forMessage(messageCaptor.getValue(), null, new CommandException( // CommandException.Error.REQUEST_NOT_SUPPORTED)); // messageCaptor.getValue().sendToTarget(); // processAllMessages(); // // There should not be a retry message. // moveTimeForward(5000); // processAllMessages(); // verify(mMockCi, never()).enableUiccApplications(eq(true), messageCaptor.capture()); // } @Test @SmallTest public void testReapplyUiccApplicationEnablementRetry() throws Exception { mPhoneUT.mCi = mMockCi; // Set SIM to be present, with a fake iccId, and notify enablement being false. doReturn(mUiccSlot).when(mUiccController).getUiccSlotForPhone(anyInt()); doReturn(IccCardStatus.CardState.CARDSTATE_PRESENT).when(mUiccSlot).getCardState(); String iccId = "Fake iccId"; doReturn(iccId).when(mUiccSlot).getIccId(); Message.obtain(mPhoneUT, EVENT_UICC_APPS_ENABLEMENT_STATUS_CHANGED, new AsyncResult(null, false, null)).sendToTarget(); processAllMessages(); // Should try to enable uicc applications as by default hey are expected to be true. ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class); verify(mMockCi).enableUiccApplications(eq(true), messageCaptor.capture()); clearInvocations(mMockCi); for (int i = 0; i < GsmCdmaPhone.ENABLE_UICC_APPS_MAX_RETRIES; i++) { // Send message back with SIM_BUSY exception. Should retry. AsyncResult.forMessage(messageCaptor.getValue(), null, new CommandException( CommandException.Error.SIM_BUSY)); messageCaptor.getValue().sendToTarget(); processAllMessages(); // There should be a retry message. moveTimeForward(5000); processAllMessages(); verify(mMockCi).enableUiccApplications(eq(true), messageCaptor.capture()); clearInvocations(mMockCi); } // Reaches max retries. Should NOT retry. AsyncResult.forMessage(messageCaptor.getValue(), null, new CommandException( CommandException.Error.SIM_BUSY)); messageCaptor.getValue().sendToTarget(); processAllMessages(); // There should NOT be a retry message. moveTimeForward(5000); processAllMessages(); verify(mMockCi, never()).enableUiccApplications(eq(true), messageCaptor.capture()); clearInvocations(mMockCi); } @Test @SmallTest Loading