Loading src/java/com/android/internal/telephony/MultiSimSettingController.java +84 −1 Original line number Diff line number Diff line Loading @@ -148,6 +148,8 @@ public class MultiSimSettingController extends Handler { // device. private final boolean mIsAskEverytimeSupportedForSms; private static final String SETTING_USER_PREF_DATA_SUB = "user_preferred_data_sub"; private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { Loading Loading @@ -309,6 +311,8 @@ public class MultiSimSettingController extends Handler { // If user is enabling a non-default non-opportunistic subscription, make it default. if (mSubController.getDefaultDataSubId() != subId && !mSubController.isOpportunistic(subId) && enable && mSubController.isActiveSubId(subId)) { android.provider.Settings.Global.putInt(mContext.getContentResolver(), SETTING_USER_PREF_DATA_SUB, subId); mSubController.setDefaultDataSubId(subId); } } Loading Loading @@ -572,7 +576,17 @@ public class MultiSimSettingController extends Handler { (newValue -> mSubController.setDefaultSmsSubId(newValue)), mIsAskEverytimeSupportedForSms); boolean autoFallbackEnabled = mContext.getResources().getBoolean( com.android.internal.R.bool.config_voice_data_sms_auto_fallback); // Based on config config_voice_data_sms_auto_fallback value choose voice/data/sms // preference auto selection logic or display notification for end used to // select voice/data/SMS preferences. if (!autoFallbackEnabled) { sendSubChangeNotificationIfNeeded(change, dataSelected, voiceSelected, smsSelected); } else { updateUserPreferences(mPrimarySubList, dataSelected, voiceSelected, smsSelected); } } @PrimarySubChangeType Loading Loading @@ -888,6 +902,75 @@ public class MultiSimSettingController extends Handler { } } // Voice/Data/SMS preferences would be auto selected without any user // confirmation in following scenarios, // 1. When device powered-up with only one SIM Inserted or while two SIM cards // present if one SIM is removed(or turned OFF) the reaiming SIM would be // selected as preferred voice/data/sms SIM. // 2. When device powered-up with two SIM cards or if two SIM cards // present on device with new SIM insert(or SIM turn ON) the first inserted SIM // would be selected as preferred voice/data/sms SIM. private void updateUserPreferences(List<Integer> primarySubList, boolean dataSelected, boolean voiceSelected, boolean smsSelected) { // In Single SIM case or if there are no activated subs available, no need to update. EXIT. if ((primarySubList.isEmpty()) || (mSubController.getActiveSubInfoCountMax() == 1)) return; if (!isRadioAvailableOnAllSubs()) { log("Radio is in Invalid state, Ignore Updating User Preference!!!"); return; } final int defaultDataSubId = mSubController.getDefaultDataSubId(); if (DBG) log("updateUserPreferences: dds = " + defaultDataSubId + " voice = " + mSubController.getDefaultVoiceSubId() + " sms = " + mSubController.getDefaultSmsSubId()); int autoDefaultSubId = primarySubList.get(0); if ((primarySubList.size() == 1) && !smsSelected) { mSubController.setDefaultSmsSubId(autoDefaultSubId); } if ((primarySubList.size() == 1) && !voiceSelected) { mSubController.setDefaultVoiceSubId(autoDefaultSubId); } int userPrefDataSubId = getUserPrefDataSubIdFromDB(); if (DBG) log("User pref subId = " + userPrefDataSubId + " current dds " + defaultDataSubId + " next active subId " + autoDefaultSubId); // If earlier user selected DDS is now available, set that as DDS subId. if (primarySubList.contains(userPrefDataSubId) && SubscriptionManager.isValidSubscriptionId(userPrefDataSubId) && (defaultDataSubId != userPrefDataSubId)) { mSubController.setDefaultDataSubId(userPrefDataSubId); } else if (!dataSelected) { mSubController.setDefaultDataSubId(autoDefaultSubId); } if (DBG) log("updateUserPreferences: after dds = " + mSubController.getDefaultDataSubId() + " voice = " + mSubController.getDefaultVoiceSubId() + " sms = " + mSubController.getDefaultSmsSubId()); } private int getUserPrefDataSubIdFromDB() { return android.provider.Settings.Global.getInt(mContext.getContentResolver(), SETTING_USER_PREF_DATA_SUB, SubscriptionManager.INVALID_SUBSCRIPTION_ID); } private boolean isRadioAvailableOnAllSubs() { for (Phone phone : PhoneFactory.getPhones()) { if ((phone.mCi != null && phone.mCi.getRadioState() == TelephonyManager.RADIO_POWER_UNAVAILABLE) || phone.isShuttingDown()) { return false; } } return true; } private void log(String msg) { Log.d(LOG_TAG, msg); } Loading src/java/com/android/internal/telephony/vendor/VendorSubscriptionController.java +0 −343 Original line number Diff line number Diff line Loading @@ -56,8 +56,6 @@ public class VendorSubscriptionController extends SubscriptionController { private static int sNumPhones; private static final int EVENT_UICC_APPS_ENABLEMENT_DONE = 101; private static final int PROVISIONED = 1; private static final int NOT_PROVISIONED = 0; Loading @@ -67,12 +65,6 @@ public class VendorSubscriptionController extends SubscriptionController { private RegistrantList mAddSubscriptionRecordRegistrants = new RegistrantList(); private static final String SETTING_USER_PREF_DATA_SUB = "user_preferred_data_sub"; /** * This intent would be broadcasted when a subId/slotId pair added to the * sSlotIdxToSubId hashmap. */ private static final String ACTION_SUBSCRIPTION_RECORD_ADDED = "android.intent.action.SUBSCRIPTION_INFO_RECORD_ADDED"; public static VendorSubscriptionController init(Context c) { synchronized (VendorSubscriptionController.class) { Loading @@ -95,345 +87,10 @@ public class VendorSubscriptionController extends SubscriptionController { protected VendorSubscriptionController(Context c) { super(c); if (DBG) logd(" init by Context"); mTelecomManager = (TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE); mTelephonyManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE); sNumPhones = TelephonyManager.getDefault().getPhoneCount(); } public void registerForAddSubscriptionRecord(Handler handler, int what, Object obj) { Registrant r = new Registrant(handler, what, obj); synchronized (mAddSubscriptionRecordRegistrants) { mAddSubscriptionRecordRegistrants.add(r); List<SubscriptionInfo> subInfoList = getActiveSubscriptionInfoList(mContext.getOpPackageName()); if (subInfoList != null) { r.notifyRegistrant(); } } } public void unregisterForAddSubscriptionRecord(Handler handler) { synchronized (mAddSubscriptionRecordRegistrants) { mAddSubscriptionRecordRegistrants.remove(handler); } } @Override public int addSubInfoRecord(String iccId, int slotIndex) { logd("addSubInfoRecord: broadcast intent subId[" + slotIndex + "]"); return addSubInfo(iccId, null, slotIndex, SubscriptionManager.SUBSCRIPTION_TYPE_LOCAL_SIM); } @Override public int addSubInfo(String uniqueId, String displayName, int slotIndex, int subscriptionType) { int retVal = super.addSubInfo(uniqueId, displayName, slotIndex, subscriptionType); int[] subId = getSubId(slotIndex); if (subId != null && (subId.length > 0)) { // When a new entry added in sSlotIdxToSubId for slotId, broadcast intent logd("addSubInfoRecord: broadcast intent subId[" + slotIndex + "] = " + subId[0]); mAddSubscriptionRecordRegistrants.notifyRegistrants( new AsyncResult(null, slotIndex, null)); Intent intent = new Intent(ACTION_SUBSCRIPTION_RECORD_ADDED); SubscriptionManager.putPhoneIdAndSubIdExtra(intent, slotIndex, subId[0]); mContext.sendBroadcast(intent, Manifest.permission.READ_PRIVILEGED_PHONE_STATE); } return retVal; } @Override public int setUiccApplicationsEnabled(boolean enabled, int subId) { if (DBG) logd("[setUiccApplicationsEnabled]+ enabled:" + enabled + " subId:" + subId); ContentValues value = new ContentValues(1); value.put(SubscriptionManager.UICC_APPLICATIONS_ENABLED, enabled); int result = mContext.getContentResolver().update( SubscriptionManager.getUriForSubscriptionId(subId), value, null, null); // Refresh the Cache of Active Subscription Info List refreshCachedActiveSubscriptionInfoList(); notifySubscriptionInfoChanged(); if (isActiveSubId(subId)) { Phone phone = PhoneFactory.getPhone(getPhoneId(subId)); phone.enableUiccApplications(enabled, Message.obtain( mSubscriptionHandler, EVENT_UICC_APPS_ENABLEMENT_DONE, enabled)); } return result; } /* * Handler Class */ private Handler mSubscriptionHandler = new Handler() { @Override public void handleMessage(Message msg) { switch (msg.what) { case EVENT_UICC_APPS_ENABLEMENT_DONE: { logd("EVENT_UICC_APPS_ENABLEMENT_DONE"); AsyncResult ar = (AsyncResult) msg.obj; if (ar.exception != null) { logd("Received exception: " + ar.exception); return; } updateUserPreferences(); break; } } } }; protected boolean isRadioAvailableOnAllSubs() { for (int i = 0; i < sNumPhones; i++) { if (PhoneFactory.getPhone(i).mCi != null && PhoneFactory.getPhone(i).mCi.getRadioState() == TelephonyManager.RADIO_POWER_UNAVAILABLE) { return false; } } return true; } protected boolean isShuttingDown() { for (int i = 0; i < sNumPhones; i++) { if (PhoneFactory.getPhone(i) != null && PhoneFactory.getPhone(i).isShuttingDown()) return true; } return false; } public boolean isRadioInValidState() { // Radio Unavailable, do not updateUserPrefs. As this may happened due to SSR or RIL Crash. if (!isRadioAvailableOnAllSubs()) { logd(" isRadioInValidState, radio not available"); return false; } //Do not updateUserPrefs when Shutdown is in progress if (isShuttingDown()) { logd(" isRadioInValidState: device shutdown in progress "); return false; } return true; } // If any of the voice/data/sms preference related SIM // deactivated/re-activated this will update the preference // with next available/activated SIM. public void updateUserPreferences() { SubscriptionInfo mNextActivatedSub = null; int activeCount = 0; if (!isRadioInValidState()) { logd("Radio is in Invalid state, Ignore Updating User Preference!!!"); return; } List<SubscriptionInfo> sil = getActiveSubscriptionInfoList(mContext.getOpPackageName()); // If list of active subscriptions empty OR non of the SIM provisioned // clear defaults preference of voice/sms/data. if (sil == null || sil.size() < 1) { logi("updateUserPreferences: Subscription list is empty"); return; } // Do not fallback to next available sub if AOSP feature // "User choice of selecting data/sms fallback preference" enabled. if (SystemProperties.getBoolean("persist.vendor.radio.aosp_usr_pref_sel", false)) { logi("updateUserPreferences: AOSP user preference option enabled "); return; } final int defaultVoiceSubId = getDefaultVoiceSubId(); final int defaultDataSubId = getDefaultDataSubId(); final int defaultSmsSubId = getDefaultSmsSubId(); //Get num of activated Subs and next available activated sub info. for (SubscriptionInfo subInfo : sil) { if (isUiccProvisioned(subInfo.getSimSlotIndex())) { activeCount++; if (mNextActivatedSub == null) mNextActivatedSub = subInfo; } } logd("updateUserPreferences:: active sub count = " + activeCount + " dds = " + defaultDataSubId + " voice = " + defaultVoiceSubId + " sms = " + defaultSmsSubId); // If active SUB count is 1, Always Ask Prompt to be disabled and // preference fallback to the next available SUB. if (activeCount == 1) { setSmsPromptEnabled(false); } // TODO Set all prompt options to false ? // in Single SIM case or if there are no activated subs available, no need to update. EXIT. if ((mNextActivatedSub == null) || (getActiveSubInfoCountMax() == 1)) return; handleDataPreference(mNextActivatedSub.getSubscriptionId()); if ((defaultSmsSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID || activeCount == 1) && !isSubProvisioned(defaultSmsSubId)) { setDefaultSmsSubId(mNextActivatedSub.getSubscriptionId()); } if ((defaultVoiceSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID || activeCount == 1) && !isSubProvisioned(defaultVoiceSubId)) { setDefaultVoiceSubId(mNextActivatedSub.getSubscriptionId()); } // voice preference is handled in such a way that // 1. Whenever current Sub is deactivated or removed It fall backs to // next available Sub. // 2. When device is flashed for the first time, initial voice preference // would be set to always ask. if (!isNonSimAccountFound() && activeCount == 1) { final int subId = mNextActivatedSub.getSubscriptionId(); PhoneAccountHandle phoneAccountHandle = subscriptionIdToPhoneAccountHandle(subId); logi("set default phoneaccount to " + subId); mTelecomManager.setUserSelectedOutgoingPhoneAccount(phoneAccountHandle); } if (!isSubProvisioned(sDefaultFallbackSubId.get())) { setDefaultFallbackSubId(mNextActivatedSub.getSubscriptionId(), SubscriptionManager.SUBSCRIPTION_TYPE_LOCAL_SIM); } notifySubscriptionInfoChanged(); logd("updateUserPreferences: after currentDds = " + getDefaultDataSubId() + " voice = " + getDefaultVoiceSubId() + " sms = " + getDefaultSmsSubId()); } protected void handleDataPreference(int nextActiveSubId) { int userPrefDataSubId = getUserPrefDataSubIdFromDB(); int currentDataSubId = getDefaultDataSubId(); List<SubscriptionInfo> subInfoList = getActiveSubscriptionInfoList(mContext.getOpPackageName()); if (subInfoList == null) { return; } boolean userPrefSubValid = false; for (SubscriptionInfo subInfo : subInfoList) { if (subInfo.getSubscriptionId() == userPrefDataSubId) { userPrefSubValid = true; } } logd("havePrefSub = " + userPrefSubValid + " user pref subId = " + userPrefDataSubId + " current dds " + currentDataSubId + " next active subId " + nextActiveSubId); // If earlier user selected DDS is now available, set that as DDS subId. if (userPrefSubValid && isSubProvisioned(userPrefDataSubId) && (currentDataSubId != userPrefDataSubId)) { setDefaultDataSubId(userPrefDataSubId); } else if (!isSubProvisioned(currentDataSubId)) { setDefaultDataSubId(nextActiveSubId); } } protected boolean isUiccProvisioned(int slotId) { // return isSubscriptionEnabled(); return true; } // This method returns true if subId and corresponding slotId is in valid // range and the Uicc card corresponds to this slot is provisioned. protected boolean isSubProvisioned(int subId) { boolean isSubIdUsable = SubscriptionManager.isUsableSubIdValue(subId); if (isSubIdUsable) { int slotId = getSlotIndex(subId); if (!SubscriptionManager.isValidSlotIndex(slotId)) { loge(" Invalid slotId " + slotId + " or subId = " + subId); isSubIdUsable = false; } else { if (!isUiccProvisioned(slotId)) { isSubIdUsable = false; } loge("isSubProvisioned, state = " + isSubIdUsable + " subId = " + subId); } } return isSubIdUsable; } /* Returns User SMS Prompt property, enabled or not */ public boolean isSmsPromptEnabled() { boolean prompt = false; int value = 0; try { value = Settings.Global.getInt(mContext.getContentResolver(), Settings.Global.MULTI_SIM_SMS_PROMPT); } catch (SettingNotFoundException snfe) { loge("Settings Exception Reading Dual Sim SMS Prompt Values"); } prompt = (value == 0) ? false : true ; if (VDBG) logd("SMS Prompt option:" + prompt); return prompt; } /*Sets User SMS Prompt property, enable or not */ public void setSmsPromptEnabled(boolean enabled) { enforceModifyPhoneState("setSMSPromptEnabled"); int value = (enabled == false) ? 0 : 1; Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.MULTI_SIM_SMS_PROMPT, value); logi("setSMSPromptOption to " + enabled); } protected boolean isNonSimAccountFound() { final Iterator<PhoneAccountHandle> phoneAccounts = mTelecomManager.getCallCapablePhoneAccounts().listIterator(); while (phoneAccounts.hasNext()) { final PhoneAccountHandle phoneAccountHandle = phoneAccounts.next(); final PhoneAccount phoneAccount = mTelecomManager.getPhoneAccount(phoneAccountHandle); if (mTelephonyManager.getSubIdForPhoneAccount(phoneAccount) == SubscriptionManager.INVALID_SUBSCRIPTION_ID) { logi("Other than SIM account found. "); return true; } } logi("Other than SIM account not found "); return false; } protected PhoneAccountHandle subscriptionIdToPhoneAccountHandle(final int subId) { final Iterator<PhoneAccountHandle> phoneAccounts = mTelecomManager.getCallCapablePhoneAccounts().listIterator(); while (phoneAccounts.hasNext()) { final PhoneAccountHandle phoneAccountHandle = phoneAccounts.next(); final PhoneAccount phoneAccount = mTelecomManager.getPhoneAccount(phoneAccountHandle); if (subId == mTelephonyManager.getSubIdForPhoneAccount(phoneAccount)) { return phoneAccountHandle; } } return null; } protected int getUserPrefDataSubIdFromDB() { return android.provider.Settings.Global.getInt(mContext.getContentResolver(), SETTING_USER_PREF_DATA_SUB, SubscriptionManager.INVALID_SUBSCRIPTION_ID); } private void logd(String string) { if (DBG) Rlog.d(LOG_TAG, string); } private void logi(String string) { Rlog.i(LOG_TAG, string); } private void loge(String string) { Rlog.e(LOG_TAG, string); } } tests/telephonytests/src/com/android/internal/telephony/MultiSimSettingControllerTest.java +33 −0 Original line number Diff line number Diff line Loading @@ -38,6 +38,7 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import android.content.Intent; import android.content.res.Resources; import android.os.HandlerThread; import android.os.ParcelUuid; import android.os.PersistableBundle; Loading @@ -49,6 +50,7 @@ import android.telephony.TelephonyManager; import android.test.suitebuilder.annotation.SmallTest; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import androidx.test.InstrumentationRegistry; import com.android.internal.telephony.dataconnection.DataEnabledSettings; Loading Loading @@ -84,6 +86,8 @@ public class MultiSimSettingControllerTest extends TelephonyTest { @Mock private DataEnabledSettings mDataEnabledSettingsMock2; private Phone[] mPhones; @Mock private CommandsInterface mMockCi; ParcelUuid mGroupUuid1 = new ParcelUuid(UUID.randomUUID()); Loading Loading @@ -750,4 +754,33 @@ public class MultiSimSettingControllerTest extends TelephonyTest { Assert.fail("InterruptedException during latch.await"); } } @Test @SmallTest public void testVoiceDataSmsAutoFallback() throws Exception { doReturn(1).when(mSubControllerMock).getDefaultDataSubId(); mMultiSimSettingControllerUT.notifyAllSubscriptionLoaded(); mMultiSimSettingControllerUT.notifyCarrierConfigChanged(0,1); mMultiSimSettingControllerUT.notifyCarrierConfigChanged(2, 3); processAllMessages(); verify(mSubControllerMock, never()).setDefaultDataSubId(anyInt()); verify(mSubControllerMock, never()).getActiveSubInfoCountMax(); doReturn(2).when(mSubControllerMock).getActiveSubInfoCountMax(); mPhoneMock1.mCi = mMockCi; mPhoneMock2.mCi = mMockCi; doReturn(TelephonyManager.RADIO_POWER_ON).when(mMockCi).getRadioState(); doReturn(false).when(mPhoneMock1).isShuttingDown(); doReturn(false).when(mPhoneMock2).isShuttingDown(); android.provider.Settings.Global.putInt(InstrumentationRegistry.getTargetContext(). getContentResolver(), "user_preferred_data_sub", 2); Resources resources = mContext.getResources(); doReturn(true).when(resources).getBoolean( com.android.internal.R.bool.config_voice_data_sms_auto_fallback); mMultiSimSettingControllerUT.notifyAllSubscriptionLoaded(); mMultiSimSettingControllerUT.notifyCarrierConfigChanged(0,1); mMultiSimSettingControllerUT.notifyCarrierConfigChanged(1, 2); processAllMessages(); verify(mSubControllerMock).getActiveSubInfoCountMax(); verify(mSubControllerMock).setDefaultDataSubId(anyInt()); } } Loading
src/java/com/android/internal/telephony/MultiSimSettingController.java +84 −1 Original line number Diff line number Diff line Loading @@ -148,6 +148,8 @@ public class MultiSimSettingController extends Handler { // device. private final boolean mIsAskEverytimeSupportedForSms; private static final String SETTING_USER_PREF_DATA_SUB = "user_preferred_data_sub"; private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { Loading Loading @@ -309,6 +311,8 @@ public class MultiSimSettingController extends Handler { // If user is enabling a non-default non-opportunistic subscription, make it default. if (mSubController.getDefaultDataSubId() != subId && !mSubController.isOpportunistic(subId) && enable && mSubController.isActiveSubId(subId)) { android.provider.Settings.Global.putInt(mContext.getContentResolver(), SETTING_USER_PREF_DATA_SUB, subId); mSubController.setDefaultDataSubId(subId); } } Loading Loading @@ -572,7 +576,17 @@ public class MultiSimSettingController extends Handler { (newValue -> mSubController.setDefaultSmsSubId(newValue)), mIsAskEverytimeSupportedForSms); boolean autoFallbackEnabled = mContext.getResources().getBoolean( com.android.internal.R.bool.config_voice_data_sms_auto_fallback); // Based on config config_voice_data_sms_auto_fallback value choose voice/data/sms // preference auto selection logic or display notification for end used to // select voice/data/SMS preferences. if (!autoFallbackEnabled) { sendSubChangeNotificationIfNeeded(change, dataSelected, voiceSelected, smsSelected); } else { updateUserPreferences(mPrimarySubList, dataSelected, voiceSelected, smsSelected); } } @PrimarySubChangeType Loading Loading @@ -888,6 +902,75 @@ public class MultiSimSettingController extends Handler { } } // Voice/Data/SMS preferences would be auto selected without any user // confirmation in following scenarios, // 1. When device powered-up with only one SIM Inserted or while two SIM cards // present if one SIM is removed(or turned OFF) the reaiming SIM would be // selected as preferred voice/data/sms SIM. // 2. When device powered-up with two SIM cards or if two SIM cards // present on device with new SIM insert(or SIM turn ON) the first inserted SIM // would be selected as preferred voice/data/sms SIM. private void updateUserPreferences(List<Integer> primarySubList, boolean dataSelected, boolean voiceSelected, boolean smsSelected) { // In Single SIM case or if there are no activated subs available, no need to update. EXIT. if ((primarySubList.isEmpty()) || (mSubController.getActiveSubInfoCountMax() == 1)) return; if (!isRadioAvailableOnAllSubs()) { log("Radio is in Invalid state, Ignore Updating User Preference!!!"); return; } final int defaultDataSubId = mSubController.getDefaultDataSubId(); if (DBG) log("updateUserPreferences: dds = " + defaultDataSubId + " voice = " + mSubController.getDefaultVoiceSubId() + " sms = " + mSubController.getDefaultSmsSubId()); int autoDefaultSubId = primarySubList.get(0); if ((primarySubList.size() == 1) && !smsSelected) { mSubController.setDefaultSmsSubId(autoDefaultSubId); } if ((primarySubList.size() == 1) && !voiceSelected) { mSubController.setDefaultVoiceSubId(autoDefaultSubId); } int userPrefDataSubId = getUserPrefDataSubIdFromDB(); if (DBG) log("User pref subId = " + userPrefDataSubId + " current dds " + defaultDataSubId + " next active subId " + autoDefaultSubId); // If earlier user selected DDS is now available, set that as DDS subId. if (primarySubList.contains(userPrefDataSubId) && SubscriptionManager.isValidSubscriptionId(userPrefDataSubId) && (defaultDataSubId != userPrefDataSubId)) { mSubController.setDefaultDataSubId(userPrefDataSubId); } else if (!dataSelected) { mSubController.setDefaultDataSubId(autoDefaultSubId); } if (DBG) log("updateUserPreferences: after dds = " + mSubController.getDefaultDataSubId() + " voice = " + mSubController.getDefaultVoiceSubId() + " sms = " + mSubController.getDefaultSmsSubId()); } private int getUserPrefDataSubIdFromDB() { return android.provider.Settings.Global.getInt(mContext.getContentResolver(), SETTING_USER_PREF_DATA_SUB, SubscriptionManager.INVALID_SUBSCRIPTION_ID); } private boolean isRadioAvailableOnAllSubs() { for (Phone phone : PhoneFactory.getPhones()) { if ((phone.mCi != null && phone.mCi.getRadioState() == TelephonyManager.RADIO_POWER_UNAVAILABLE) || phone.isShuttingDown()) { return false; } } return true; } private void log(String msg) { Log.d(LOG_TAG, msg); } Loading
src/java/com/android/internal/telephony/vendor/VendorSubscriptionController.java +0 −343 Original line number Diff line number Diff line Loading @@ -56,8 +56,6 @@ public class VendorSubscriptionController extends SubscriptionController { private static int sNumPhones; private static final int EVENT_UICC_APPS_ENABLEMENT_DONE = 101; private static final int PROVISIONED = 1; private static final int NOT_PROVISIONED = 0; Loading @@ -67,12 +65,6 @@ public class VendorSubscriptionController extends SubscriptionController { private RegistrantList mAddSubscriptionRecordRegistrants = new RegistrantList(); private static final String SETTING_USER_PREF_DATA_SUB = "user_preferred_data_sub"; /** * This intent would be broadcasted when a subId/slotId pair added to the * sSlotIdxToSubId hashmap. */ private static final String ACTION_SUBSCRIPTION_RECORD_ADDED = "android.intent.action.SUBSCRIPTION_INFO_RECORD_ADDED"; public static VendorSubscriptionController init(Context c) { synchronized (VendorSubscriptionController.class) { Loading @@ -95,345 +87,10 @@ public class VendorSubscriptionController extends SubscriptionController { protected VendorSubscriptionController(Context c) { super(c); if (DBG) logd(" init by Context"); mTelecomManager = (TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE); mTelephonyManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE); sNumPhones = TelephonyManager.getDefault().getPhoneCount(); } public void registerForAddSubscriptionRecord(Handler handler, int what, Object obj) { Registrant r = new Registrant(handler, what, obj); synchronized (mAddSubscriptionRecordRegistrants) { mAddSubscriptionRecordRegistrants.add(r); List<SubscriptionInfo> subInfoList = getActiveSubscriptionInfoList(mContext.getOpPackageName()); if (subInfoList != null) { r.notifyRegistrant(); } } } public void unregisterForAddSubscriptionRecord(Handler handler) { synchronized (mAddSubscriptionRecordRegistrants) { mAddSubscriptionRecordRegistrants.remove(handler); } } @Override public int addSubInfoRecord(String iccId, int slotIndex) { logd("addSubInfoRecord: broadcast intent subId[" + slotIndex + "]"); return addSubInfo(iccId, null, slotIndex, SubscriptionManager.SUBSCRIPTION_TYPE_LOCAL_SIM); } @Override public int addSubInfo(String uniqueId, String displayName, int slotIndex, int subscriptionType) { int retVal = super.addSubInfo(uniqueId, displayName, slotIndex, subscriptionType); int[] subId = getSubId(slotIndex); if (subId != null && (subId.length > 0)) { // When a new entry added in sSlotIdxToSubId for slotId, broadcast intent logd("addSubInfoRecord: broadcast intent subId[" + slotIndex + "] = " + subId[0]); mAddSubscriptionRecordRegistrants.notifyRegistrants( new AsyncResult(null, slotIndex, null)); Intent intent = new Intent(ACTION_SUBSCRIPTION_RECORD_ADDED); SubscriptionManager.putPhoneIdAndSubIdExtra(intent, slotIndex, subId[0]); mContext.sendBroadcast(intent, Manifest.permission.READ_PRIVILEGED_PHONE_STATE); } return retVal; } @Override public int setUiccApplicationsEnabled(boolean enabled, int subId) { if (DBG) logd("[setUiccApplicationsEnabled]+ enabled:" + enabled + " subId:" + subId); ContentValues value = new ContentValues(1); value.put(SubscriptionManager.UICC_APPLICATIONS_ENABLED, enabled); int result = mContext.getContentResolver().update( SubscriptionManager.getUriForSubscriptionId(subId), value, null, null); // Refresh the Cache of Active Subscription Info List refreshCachedActiveSubscriptionInfoList(); notifySubscriptionInfoChanged(); if (isActiveSubId(subId)) { Phone phone = PhoneFactory.getPhone(getPhoneId(subId)); phone.enableUiccApplications(enabled, Message.obtain( mSubscriptionHandler, EVENT_UICC_APPS_ENABLEMENT_DONE, enabled)); } return result; } /* * Handler Class */ private Handler mSubscriptionHandler = new Handler() { @Override public void handleMessage(Message msg) { switch (msg.what) { case EVENT_UICC_APPS_ENABLEMENT_DONE: { logd("EVENT_UICC_APPS_ENABLEMENT_DONE"); AsyncResult ar = (AsyncResult) msg.obj; if (ar.exception != null) { logd("Received exception: " + ar.exception); return; } updateUserPreferences(); break; } } } }; protected boolean isRadioAvailableOnAllSubs() { for (int i = 0; i < sNumPhones; i++) { if (PhoneFactory.getPhone(i).mCi != null && PhoneFactory.getPhone(i).mCi.getRadioState() == TelephonyManager.RADIO_POWER_UNAVAILABLE) { return false; } } return true; } protected boolean isShuttingDown() { for (int i = 0; i < sNumPhones; i++) { if (PhoneFactory.getPhone(i) != null && PhoneFactory.getPhone(i).isShuttingDown()) return true; } return false; } public boolean isRadioInValidState() { // Radio Unavailable, do not updateUserPrefs. As this may happened due to SSR or RIL Crash. if (!isRadioAvailableOnAllSubs()) { logd(" isRadioInValidState, radio not available"); return false; } //Do not updateUserPrefs when Shutdown is in progress if (isShuttingDown()) { logd(" isRadioInValidState: device shutdown in progress "); return false; } return true; } // If any of the voice/data/sms preference related SIM // deactivated/re-activated this will update the preference // with next available/activated SIM. public void updateUserPreferences() { SubscriptionInfo mNextActivatedSub = null; int activeCount = 0; if (!isRadioInValidState()) { logd("Radio is in Invalid state, Ignore Updating User Preference!!!"); return; } List<SubscriptionInfo> sil = getActiveSubscriptionInfoList(mContext.getOpPackageName()); // If list of active subscriptions empty OR non of the SIM provisioned // clear defaults preference of voice/sms/data. if (sil == null || sil.size() < 1) { logi("updateUserPreferences: Subscription list is empty"); return; } // Do not fallback to next available sub if AOSP feature // "User choice of selecting data/sms fallback preference" enabled. if (SystemProperties.getBoolean("persist.vendor.radio.aosp_usr_pref_sel", false)) { logi("updateUserPreferences: AOSP user preference option enabled "); return; } final int defaultVoiceSubId = getDefaultVoiceSubId(); final int defaultDataSubId = getDefaultDataSubId(); final int defaultSmsSubId = getDefaultSmsSubId(); //Get num of activated Subs and next available activated sub info. for (SubscriptionInfo subInfo : sil) { if (isUiccProvisioned(subInfo.getSimSlotIndex())) { activeCount++; if (mNextActivatedSub == null) mNextActivatedSub = subInfo; } } logd("updateUserPreferences:: active sub count = " + activeCount + " dds = " + defaultDataSubId + " voice = " + defaultVoiceSubId + " sms = " + defaultSmsSubId); // If active SUB count is 1, Always Ask Prompt to be disabled and // preference fallback to the next available SUB. if (activeCount == 1) { setSmsPromptEnabled(false); } // TODO Set all prompt options to false ? // in Single SIM case or if there are no activated subs available, no need to update. EXIT. if ((mNextActivatedSub == null) || (getActiveSubInfoCountMax() == 1)) return; handleDataPreference(mNextActivatedSub.getSubscriptionId()); if ((defaultSmsSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID || activeCount == 1) && !isSubProvisioned(defaultSmsSubId)) { setDefaultSmsSubId(mNextActivatedSub.getSubscriptionId()); } if ((defaultVoiceSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID || activeCount == 1) && !isSubProvisioned(defaultVoiceSubId)) { setDefaultVoiceSubId(mNextActivatedSub.getSubscriptionId()); } // voice preference is handled in such a way that // 1. Whenever current Sub is deactivated or removed It fall backs to // next available Sub. // 2. When device is flashed for the first time, initial voice preference // would be set to always ask. if (!isNonSimAccountFound() && activeCount == 1) { final int subId = mNextActivatedSub.getSubscriptionId(); PhoneAccountHandle phoneAccountHandle = subscriptionIdToPhoneAccountHandle(subId); logi("set default phoneaccount to " + subId); mTelecomManager.setUserSelectedOutgoingPhoneAccount(phoneAccountHandle); } if (!isSubProvisioned(sDefaultFallbackSubId.get())) { setDefaultFallbackSubId(mNextActivatedSub.getSubscriptionId(), SubscriptionManager.SUBSCRIPTION_TYPE_LOCAL_SIM); } notifySubscriptionInfoChanged(); logd("updateUserPreferences: after currentDds = " + getDefaultDataSubId() + " voice = " + getDefaultVoiceSubId() + " sms = " + getDefaultSmsSubId()); } protected void handleDataPreference(int nextActiveSubId) { int userPrefDataSubId = getUserPrefDataSubIdFromDB(); int currentDataSubId = getDefaultDataSubId(); List<SubscriptionInfo> subInfoList = getActiveSubscriptionInfoList(mContext.getOpPackageName()); if (subInfoList == null) { return; } boolean userPrefSubValid = false; for (SubscriptionInfo subInfo : subInfoList) { if (subInfo.getSubscriptionId() == userPrefDataSubId) { userPrefSubValid = true; } } logd("havePrefSub = " + userPrefSubValid + " user pref subId = " + userPrefDataSubId + " current dds " + currentDataSubId + " next active subId " + nextActiveSubId); // If earlier user selected DDS is now available, set that as DDS subId. if (userPrefSubValid && isSubProvisioned(userPrefDataSubId) && (currentDataSubId != userPrefDataSubId)) { setDefaultDataSubId(userPrefDataSubId); } else if (!isSubProvisioned(currentDataSubId)) { setDefaultDataSubId(nextActiveSubId); } } protected boolean isUiccProvisioned(int slotId) { // return isSubscriptionEnabled(); return true; } // This method returns true if subId and corresponding slotId is in valid // range and the Uicc card corresponds to this slot is provisioned. protected boolean isSubProvisioned(int subId) { boolean isSubIdUsable = SubscriptionManager.isUsableSubIdValue(subId); if (isSubIdUsable) { int slotId = getSlotIndex(subId); if (!SubscriptionManager.isValidSlotIndex(slotId)) { loge(" Invalid slotId " + slotId + " or subId = " + subId); isSubIdUsable = false; } else { if (!isUiccProvisioned(slotId)) { isSubIdUsable = false; } loge("isSubProvisioned, state = " + isSubIdUsable + " subId = " + subId); } } return isSubIdUsable; } /* Returns User SMS Prompt property, enabled or not */ public boolean isSmsPromptEnabled() { boolean prompt = false; int value = 0; try { value = Settings.Global.getInt(mContext.getContentResolver(), Settings.Global.MULTI_SIM_SMS_PROMPT); } catch (SettingNotFoundException snfe) { loge("Settings Exception Reading Dual Sim SMS Prompt Values"); } prompt = (value == 0) ? false : true ; if (VDBG) logd("SMS Prompt option:" + prompt); return prompt; } /*Sets User SMS Prompt property, enable or not */ public void setSmsPromptEnabled(boolean enabled) { enforceModifyPhoneState("setSMSPromptEnabled"); int value = (enabled == false) ? 0 : 1; Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.MULTI_SIM_SMS_PROMPT, value); logi("setSMSPromptOption to " + enabled); } protected boolean isNonSimAccountFound() { final Iterator<PhoneAccountHandle> phoneAccounts = mTelecomManager.getCallCapablePhoneAccounts().listIterator(); while (phoneAccounts.hasNext()) { final PhoneAccountHandle phoneAccountHandle = phoneAccounts.next(); final PhoneAccount phoneAccount = mTelecomManager.getPhoneAccount(phoneAccountHandle); if (mTelephonyManager.getSubIdForPhoneAccount(phoneAccount) == SubscriptionManager.INVALID_SUBSCRIPTION_ID) { logi("Other than SIM account found. "); return true; } } logi("Other than SIM account not found "); return false; } protected PhoneAccountHandle subscriptionIdToPhoneAccountHandle(final int subId) { final Iterator<PhoneAccountHandle> phoneAccounts = mTelecomManager.getCallCapablePhoneAccounts().listIterator(); while (phoneAccounts.hasNext()) { final PhoneAccountHandle phoneAccountHandle = phoneAccounts.next(); final PhoneAccount phoneAccount = mTelecomManager.getPhoneAccount(phoneAccountHandle); if (subId == mTelephonyManager.getSubIdForPhoneAccount(phoneAccount)) { return phoneAccountHandle; } } return null; } protected int getUserPrefDataSubIdFromDB() { return android.provider.Settings.Global.getInt(mContext.getContentResolver(), SETTING_USER_PREF_DATA_SUB, SubscriptionManager.INVALID_SUBSCRIPTION_ID); } private void logd(String string) { if (DBG) Rlog.d(LOG_TAG, string); } private void logi(String string) { Rlog.i(LOG_TAG, string); } private void loge(String string) { Rlog.e(LOG_TAG, string); } }
tests/telephonytests/src/com/android/internal/telephony/MultiSimSettingControllerTest.java +33 −0 Original line number Diff line number Diff line Loading @@ -38,6 +38,7 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import android.content.Intent; import android.content.res.Resources; import android.os.HandlerThread; import android.os.ParcelUuid; import android.os.PersistableBundle; Loading @@ -49,6 +50,7 @@ import android.telephony.TelephonyManager; import android.test.suitebuilder.annotation.SmallTest; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import androidx.test.InstrumentationRegistry; import com.android.internal.telephony.dataconnection.DataEnabledSettings; Loading Loading @@ -84,6 +86,8 @@ public class MultiSimSettingControllerTest extends TelephonyTest { @Mock private DataEnabledSettings mDataEnabledSettingsMock2; private Phone[] mPhones; @Mock private CommandsInterface mMockCi; ParcelUuid mGroupUuid1 = new ParcelUuid(UUID.randomUUID()); Loading Loading @@ -750,4 +754,33 @@ public class MultiSimSettingControllerTest extends TelephonyTest { Assert.fail("InterruptedException during latch.await"); } } @Test @SmallTest public void testVoiceDataSmsAutoFallback() throws Exception { doReturn(1).when(mSubControllerMock).getDefaultDataSubId(); mMultiSimSettingControllerUT.notifyAllSubscriptionLoaded(); mMultiSimSettingControllerUT.notifyCarrierConfigChanged(0,1); mMultiSimSettingControllerUT.notifyCarrierConfigChanged(2, 3); processAllMessages(); verify(mSubControllerMock, never()).setDefaultDataSubId(anyInt()); verify(mSubControllerMock, never()).getActiveSubInfoCountMax(); doReturn(2).when(mSubControllerMock).getActiveSubInfoCountMax(); mPhoneMock1.mCi = mMockCi; mPhoneMock2.mCi = mMockCi; doReturn(TelephonyManager.RADIO_POWER_ON).when(mMockCi).getRadioState(); doReturn(false).when(mPhoneMock1).isShuttingDown(); doReturn(false).when(mPhoneMock2).isShuttingDown(); android.provider.Settings.Global.putInt(InstrumentationRegistry.getTargetContext(). getContentResolver(), "user_preferred_data_sub", 2); Resources resources = mContext.getResources(); doReturn(true).when(resources).getBoolean( com.android.internal.R.bool.config_voice_data_sms_auto_fallback); mMultiSimSettingControllerUT.notifyAllSubscriptionLoaded(); mMultiSimSettingControllerUT.notifyCarrierConfigChanged(0,1); mMultiSimSettingControllerUT.notifyCarrierConfigChanged(1, 2); processAllMessages(); verify(mSubControllerMock).getActiveSubInfoCountMax(); verify(mSubControllerMock).setDefaultDataSubId(anyInt()); } }