Loading src/java/com/android/internal/telephony/SubscriptionController.java +55 −5 Original line number Diff line number Diff line Loading @@ -237,6 +237,12 @@ public class SubscriptionController extends ISub.Stub { mLastISubServiceRegTime = System.currentTimeMillis(); } /** * Switching between DSDS and single sim mode needs to clear the slot index of * subscription info. */ clearSlotIndexForSubInfoRecords(); if (DBG) logdl("[SubscriptionController] init by Context"); } Loading @@ -263,6 +269,25 @@ public class SubscriptionController extends ISub.Stub { return sSlotIndexToSubIds.size() > 0; } /** * Switching between DSDS and single sim mode needs to update the slot index of subscription * info into invalid if the slot index is not active. */ private void clearSlotIndexForSubInfoRecords() { if (mTelephonyManager == null || mContext == null) { logel("[clearSlotIndexForSubInfoRecords] TelephonyManager or mContext is null"); return; } int phoneCount = mTelephonyManager.getPhoneCount(); // Update simInfo db with invalid slot index ContentResolver resolver = mContext.getContentResolver(); ContentValues value = new ContentValues(1); value.put(SubscriptionManager.SIM_SLOT_INDEX, SubscriptionManager.INVALID_SIM_SLOT_INDEX); mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI, value, SubscriptionManager.SIM_SLOT_INDEX + ">=" + phoneCount, null); } private SubscriptionController(Phone phone) { mContext = phone.getContext(); mAppOps = mContext.getSystemService(AppOpsManager.class); Loading @@ -273,6 +298,12 @@ public class SubscriptionController extends ISub.Stub { migrateImsSettings(); /** * Switching between DSDS and single sim mode needs to clear the slot index of * subscription info. */ clearSlotIndexForSubInfoRecords(); if (DBG) logdl("[SubscriptionController] init by Phone"); } Loading Loading @@ -514,6 +545,20 @@ public class SubscriptionController extends ISub.Stub { return null; } /** * Get a single subscription info record for a given subscription. * * @param subId the subId to query. * * @hide */ public SubscriptionInfo getSubscriptionInfo(int subId) { List<SubscriptionInfo> subInfoList = getSubInfo( SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=" + subId, null); if (subInfoList == null || subInfoList.isEmpty()) return null; return subInfoList.get(0); } /** * Get the active SubscriptionInfo associated with the iccId * @param iccId the IccId of SIM card Loading Loading @@ -1060,9 +1105,6 @@ public class SubscriptionController extends ISub.Stub { if (value.size() > 0) { resolver.update(SubscriptionManager.getUriForSubscriptionId(subId), value, null, null); // Refresh the Cache of Active Subscription Info List refreshCachedActiveSubscriptionInfoList(); } if (DBG) logdl("[addSubInfoRecord] Record already exists"); Loading Loading @@ -1145,8 +1187,11 @@ public class SubscriptionController extends ISub.Stub { } } if (isSubscriptionForRemoteSim(subscriptionType)) { // Refresh the Cache of Active Subscription Info List. This should be done after // updating sSlotIndexToSubIds which is done through addToSubIdList() above. refreshCachedActiveSubscriptionInfoList(); if (isSubscriptionForRemoteSim(subscriptionType)) { notifySubscriptionInfoChanged(); } else { // Handle Local SIM devices // Set Display name after sub id is set above so as to get valid simCarrierName Loading Loading @@ -2344,7 +2389,12 @@ public class SubscriptionController extends ISub.Stub { simState = IccCardConstants.State.UNKNOWN; err = "invalid slotIndex"; } else { Phone phone = PhoneFactory.getPhone(slotIndex); Phone phone = null; try { phone = PhoneFactory.getPhone(slotIndex); } catch (IllegalStateException e) { // ignore } if (phone == null) { simState = IccCardConstants.State.UNKNOWN; err = "phone == null"; Loading src/java/com/android/internal/telephony/SubscriptionInfoUpdater.java +112 −2 Original line number Diff line number Diff line Loading @@ -32,12 +32,15 @@ import android.os.Handler; import android.os.IRemoteCallback; import android.os.Looper; import android.os.Message; import android.os.ParcelUuid; import android.os.PersistableBundle; import android.os.RemoteException; import android.os.ServiceManager; import android.preference.PreferenceManager; import android.provider.Settings; import android.provider.Settings.Global; import android.provider.Settings.SettingNotFoundException; import android.service.carrier.CarrierService; import android.service.euicc.EuiccProfileInfo; import android.service.euicc.EuiccService; import android.service.euicc.GetEuiccProfileInfoListResult; Loading Loading @@ -90,6 +93,9 @@ public class SubscriptionInfoUpdater extends Handler { private static final String ICCID_STRING_FOR_NO_SIM = ""; private static final ParcelUuid REMOVE_GROUP_UUID = ParcelUuid.fromString(CarrierConfigManager.REMOVE_GROUP_UUID_STRING); // Key used to read/write the current IMSI. Updated on SIM_STATE_CHANGED - LOADED. public static final String CURR_SUBID = "curr_subid"; Loading @@ -111,6 +117,8 @@ public class SubscriptionInfoUpdater extends Handler { private int mCurrentlyActiveUserId; private CarrierServiceBindHelper mCarrierServiceBindHelper; // TODO: The SubscriptionController instance should be passed in here from PhoneFactory // rather than invoking the static getter all over the place. public SubscriptionInfoUpdater( Looper looper, Context context, Phone[] phone, CommandsInterface[] ci) { this(looper, context, phone, ci, Loading Loading @@ -512,8 +520,8 @@ public class SubscriptionInfoUpdater extends Handler { } private void updateCarrierServices(int slotId, String simState) { CarrierConfigManager configManager = (CarrierConfigManager) mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE); CarrierConfigManager configManager = (CarrierConfigManager) mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE); configManager.updateConfigForPhoneId(slotId, simState); mCarrierServiceBindHelper.updateForPhoneId(slotId, simState); } Loading Loading @@ -753,6 +761,108 @@ public class SubscriptionInfoUpdater extends Handler { return hasChanges; } /** * Called by CarrierConfigLoader to update the subscription before sending a broadcast. */ public void updateSubscriptionByCarrierConfigAndNotifyComplete(int phoneId, String configPackageName, PersistableBundle config, Message onComplete) { post(() -> { updateSubscriptionByCarrierConfig(phoneId, configPackageName, config); onComplete.sendToTarget(); }); } private String getDefaultCarrierServicePackageName() { CarrierConfigManager configManager = (CarrierConfigManager) mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE); return configManager.getDefaultCarrierServicePackageName(); } private boolean isCarrierServicePackage(int phoneId, String pkgName) { if (pkgName.equals(getDefaultCarrierServicePackageName())) return false; List<String> carrierPackageNames = TelephonyManager.from(mContext) .getCarrierPackageNamesForIntentAndPhone( new Intent(CarrierService.CARRIER_SERVICE_INTERFACE), phoneId); if (DBG) logd("Carrier Packages For Subscription = " + carrierPackageNames); return carrierPackageNames != null && carrierPackageNames.contains(pkgName); } /** * Update the currently active Subscription based on information from CarrierConfig */ @VisibleForTesting public void updateSubscriptionByCarrierConfig( int phoneId, String configPackageName, PersistableBundle config) { if (!SubscriptionManager.isValidPhoneId(phoneId) || TextUtils.isEmpty(configPackageName) || config == null) { if (DBG) { logd("In updateSubscriptionByCarrierConfig(): phoneId=" + phoneId + " configPackageName=" + configPackageName + " config=" + ((config == null) ? "null" : config.hashCode())); } return; } SubscriptionController sc = SubscriptionController.getInstance(); if (sc == null) { loge("SubscriptionController was null"); return; } int currentSubId = sc.getSubIdUsingPhoneId(phoneId); if (!SubscriptionManager.isValidSubscriptionId(currentSubId) || currentSubId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) { if (DBG) logd("No subscription is active for phone being updated"); return; } SubscriptionInfo currentSubInfo = sc.getSubscriptionInfo(currentSubId); if (currentSubInfo == null) { loge("Couldn't retrieve subscription info for current subscription"); return; } if (!isCarrierServicePackage(phoneId, configPackageName)) { loge("Cannot manage subId=" + currentSubId + ", carrierPackage=" + configPackageName); return; } ContentValues cv = new ContentValues(); boolean isOpportunistic = config.getBoolean( CarrierConfigManager.KEY_IS_OPPORTUNISTIC_SUBSCRIPTION_BOOL, false); if (currentSubInfo.isOpportunistic() != isOpportunistic) { if (DBG) logd("Set SubId=" + currentSubId + " isOpportunistic=" + isOpportunistic); cv.put(SubscriptionManager.IS_OPPORTUNISTIC, isOpportunistic ? "1" : "0"); } String groupUuidString = config.getString(CarrierConfigManager.KEY_SUBSCRIPTION_GROUP_UUID_STRING, ""); ParcelUuid groupId = null; if (!TextUtils.isEmpty(groupUuidString)) { try { // Update via a UUID Structure to ensure consistent formatting ParcelUuid groupUuid = ParcelUuid.fromString(groupUuidString); if (groupUuid.equals(REMOVE_GROUP_UUID) && currentSubInfo.getGroupUuid() != null) { cv.put(SubscriptionManager.GROUP_UUID, (String) null); if (DBG) logd("Group Removed for" + currentSubId); } else { // TODO: validate and update group owner information once feasible. cv.put(SubscriptionManager.GROUP_UUID, groupUuid.toString()); if (DBG) logd("Group Added for" + currentSubId); } } catch (IllegalArgumentException e) { loge("Invalid Group UUID=" + groupUuidString); } } if (cv.size() > 0 && mContext.getContentResolver().update(SubscriptionManager .getUriForSubscriptionId(currentSubId), cv, null, null) > 0) { sc.refreshCachedActiveSubscriptionInfoList(); sc.notifySubscriptionInfoChanged(); } } private static int findSubscriptionInfoForIccid(List<SubscriptionInfo> list, String iccid) { for (int i = 0; i < list.size(); i++) { if (TextUtils.equals(iccid, list.get(i).getIccId())) { Loading src/java/com/android/internal/telephony/dataconnection/DataConnection.java +11 −0 Original line number Diff line number Diff line Loading @@ -233,6 +233,11 @@ public class DataConnection extends StateMachine { int mTag; public int mCid; /** * Indicate this data connection has been transferred to the other transport type during * IWLAN and WWAN handover. */ private boolean mHasTransferred; private final Map<ApnContext, ConnectionParams> mApnContexts = new ConcurrentHashMap<>(); PendingIntent mReconnectIntent = null; Loading Loading @@ -366,6 +371,10 @@ public class DataConnection extends StateMachine { return getCurrentState() == mActivatingState; } boolean hasBeenTransferred() { return mHasTransferred; } int getCid() { return mCid; } Loading Loading @@ -1706,6 +1715,7 @@ public class DataConnection extends StateMachine { mApnSetting != null ? (long) mApnSetting.getApnTypeBitmask() : 0L, mApnSetting != null ? mApnSetting.canHandleType(ApnSetting.TYPE_DEFAULT) : false); mHasTransferred = false; } @Override public boolean processMessage(Message msg) { Loading Loading @@ -2882,6 +2892,7 @@ public class DataConnection extends StateMachine { public DcNetworkAgent transferNetworkAgent(DataConnection dataConnection, @TransportType int transportType) { mNetworkAgent.acquireOwnership(dataConnection, transportType); this.mHasTransferred = true; return mNetworkAgent; } Loading src/java/com/android/internal/telephony/dataconnection/DcTracker.java +11 −3 Original line number Diff line number Diff line Loading @@ -2919,9 +2919,17 @@ public class DcTracker extends Handler { private void onDisconnectDone(ApnContext apnContext) { if(DBG) log("onDisconnectDone: EVENT_DISCONNECT_DONE apnContext=" + apnContext); apnContext.setState(DctConstants.State.IDLE); mPhone.notifyDataConnection(apnContext.getApnType()); final DataConnection dc = apnContext.getDataConnection(); // when data connection is gone and not for handover, notify all apn types which // this data connection can handle. Note, this might not work if one apn type served for // multiple data connection. if (dc != null && dc.isInactive() && !dc.hasBeenTransferred()) { String[] types = ApnSetting.getApnTypesStringFromBitmask( apnContext.getApnSetting().getApnTypeBitmask()).split(","); for (String type : types) { mPhone.notifyDataConnection(type); } } // if all data connection are gone, check whether Airplane mode request was // pending. if (isDisconnected()) { Loading tests/telephonytests/src/com/android/internal/telephony/SubscriptionInfoUpdaterTest.java +126 −0 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ import static com.android.internal.telephony.TelephonyTestUtils.waitForMs; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.any; Loading @@ -40,6 +41,8 @@ import android.content.pm.IPackageManager; import android.content.pm.UserInfo; import android.net.Uri; import android.os.HandlerThread; import android.os.ParcelUuid; import android.os.PersistableBundle; import android.service.euicc.EuiccProfileInfo; import android.service.euicc.EuiccService; import android.service.euicc.GetEuiccProfileInfoListResult; Loading @@ -64,6 +67,7 @@ import org.mockito.stubbing.Answer; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; Loading Loading @@ -571,4 +575,126 @@ public class SubscriptionInfoUpdaterTest extends TelephonyTest { eq(FAKE_SUB_ID_1)); verify(mSubscriptionController, times(0)).clearSubInfo(); } PersistableBundle getCarrierConfigForSubInfoUpdate( boolean isOpportunistic, String groupUuid) { PersistableBundle p = new PersistableBundle(); p.putBoolean(CarrierConfigManager.KEY_IS_OPPORTUNISTIC_SUBSCRIPTION_BOOL, isOpportunistic); p.putString(CarrierConfigManager.KEY_SUBSCRIPTION_GROUP_UUID_STRING, groupUuid); return p; } @Test @SmallTest public void testUpdateFromCarrierConfigOpportunisticUnchanged() throws Exception { final int phoneId = mPhone.getPhoneId(); String carrierPackageName = "FakeCarrierPackageName"; doReturn(FAKE_SUB_ID_1).when(mSubscriptionController).getSubIdUsingPhoneId(phoneId); doReturn(mSubInfo).when(mSubscriptionController).getSubscriptionInfo(eq(FAKE_SUB_ID_1)); doReturn(Collections.singletonList(carrierPackageName)).when(mTelephonyManager) .getCarrierPackageNamesForIntentAndPhone(any(), eq(phoneId)); ((MockContentResolver) mContext.getContentResolver()).addProvider( SubscriptionManager.CONTENT_URI.getAuthority(), new FakeSubscriptionContentProvider()); mUpdater.updateSubscriptionByCarrierConfig(mPhone.getPhoneId(), carrierPackageName, new PersistableBundle()); verify(mContentProvider, never()).update(any(), any(), any(), any()); verify(mSubscriptionController, never()).refreshCachedActiveSubscriptionInfoList(); verify(mSubscriptionController, never()).notifySubscriptionInfoChanged(); } @Test @SmallTest public void testUpdateFromCarrierConfigOpportunisticSetOpportunistic() throws Exception { final int phoneId = mPhone.getPhoneId(); PersistableBundle carrierConfig = getCarrierConfigForSubInfoUpdate( true, ""); String carrierPackageName = "FakeCarrierPackageName"; doReturn(FAKE_SUB_ID_1).when(mSubscriptionController).getSubIdUsingPhoneId(phoneId); doReturn(mSubInfo).when(mSubscriptionController).getSubscriptionInfo(eq(FAKE_SUB_ID_1)); doReturn(false).when(mSubInfo).isOpportunistic(); doReturn(Collections.singletonList(carrierPackageName)).when(mTelephonyManager) .getCarrierPackageNamesForIntentAndPhone(any(), eq(phoneId)); ((MockContentResolver) mContext.getContentResolver()).addProvider( SubscriptionManager.CONTENT_URI.getAuthority(), new FakeSubscriptionContentProvider()); mUpdater.updateSubscriptionByCarrierConfig(mPhone.getPhoneId(), carrierPackageName, carrierConfig); ArgumentCaptor<ContentValues> cvCaptor = ArgumentCaptor.forClass(ContentValues.class); verify(mContentProvider, times(1)).update( eq(SubscriptionManager.getUriForSubscriptionId(FAKE_SUB_ID_1)), cvCaptor.capture(), eq(null), eq(null)); assertEquals(1, cvCaptor.getValue().getAsInteger( SubscriptionManager.IS_OPPORTUNISTIC).intValue()); assertEquals(1, cvCaptor.getValue().size()); verify(mSubscriptionController, times(1)).refreshCachedActiveSubscriptionInfoList(); verify(mSubscriptionController, times(1)).notifySubscriptionInfoChanged(); } @Test @SmallTest public void testUpdateFromCarrierConfigOpportunisticAddToGroup() throws Exception { final int phoneId = mPhone.getPhoneId(); PersistableBundle carrierConfig = getCarrierConfigForSubInfoUpdate( true, "11111111-2222-3333-4444-555555555555"); String carrierPackageName = "FakeCarrierPackageName"; doReturn(FAKE_SUB_ID_1).when(mSubscriptionController).getSubIdUsingPhoneId(phoneId); doReturn(mSubInfo).when(mSubscriptionController).getSubscriptionInfo(eq(FAKE_SUB_ID_1)); doReturn(Collections.singletonList(carrierPackageName)).when(mTelephonyManager) .getCarrierPackageNamesForIntentAndPhone(any(), eq(phoneId)); ((MockContentResolver) mContext.getContentResolver()).addProvider( SubscriptionManager.CONTENT_URI.getAuthority(), new FakeSubscriptionContentProvider()); mUpdater.updateSubscriptionByCarrierConfig(mPhone.getPhoneId(), carrierPackageName, carrierConfig); ArgumentCaptor<ContentValues> cvCaptor = ArgumentCaptor.forClass(ContentValues.class); verify(mContentProvider, times(1)).update( eq(SubscriptionManager.getUriForSubscriptionId(FAKE_SUB_ID_1)), cvCaptor.capture(), eq(null), eq(null)); assertEquals(1, cvCaptor.getValue().getAsInteger( SubscriptionManager.IS_OPPORTUNISTIC).intValue()); assertEquals("11111111-2222-3333-4444-555555555555", cvCaptor.getValue().getAsString(SubscriptionManager.GROUP_UUID)); assertEquals(2, cvCaptor.getValue().size()); } @Test @SmallTest public void testUpdateFromCarrierConfigOpportunisticRemoveFromGroup() throws Exception { final int phoneId = mPhone.getPhoneId(); PersistableBundle carrierConfig = getCarrierConfigForSubInfoUpdate( true, "00000000-0000-0000-0000-000000000000"); String carrierPackageName = "FakeCarrierPackageName"; doReturn(FAKE_SUB_ID_1).when(mSubscriptionController).getSubIdUsingPhoneId(phoneId); doReturn(mSubInfo).when(mSubscriptionController).getSubscriptionInfo(eq(FAKE_SUB_ID_1)); doReturn(ParcelUuid.fromString("11111111-2222-3333-4444-555555555555")) .when(mSubInfo).getGroupUuid(); doReturn(Collections.singletonList(carrierPackageName)).when(mTelephonyManager) .getCarrierPackageNamesForIntentAndPhone(any(), eq(phoneId)); ((MockContentResolver) mContext.getContentResolver()).addProvider( SubscriptionManager.CONTENT_URI.getAuthority(), new FakeSubscriptionContentProvider()); mUpdater.updateSubscriptionByCarrierConfig(mPhone.getPhoneId(), carrierPackageName, carrierConfig); ArgumentCaptor<ContentValues> cvCaptor = ArgumentCaptor.forClass(ContentValues.class); verify(mContentProvider, times(1)).update( eq(SubscriptionManager.getUriForSubscriptionId(FAKE_SUB_ID_1)), cvCaptor.capture(), eq(null), eq(null)); assertEquals(1, cvCaptor.getValue().getAsInteger( SubscriptionManager.IS_OPPORTUNISTIC).intValue()); assertNull(cvCaptor.getValue().getAsString(SubscriptionManager.GROUP_UUID)); assertEquals(2, cvCaptor.getValue().size()); } } Loading
src/java/com/android/internal/telephony/SubscriptionController.java +55 −5 Original line number Diff line number Diff line Loading @@ -237,6 +237,12 @@ public class SubscriptionController extends ISub.Stub { mLastISubServiceRegTime = System.currentTimeMillis(); } /** * Switching between DSDS and single sim mode needs to clear the slot index of * subscription info. */ clearSlotIndexForSubInfoRecords(); if (DBG) logdl("[SubscriptionController] init by Context"); } Loading @@ -263,6 +269,25 @@ public class SubscriptionController extends ISub.Stub { return sSlotIndexToSubIds.size() > 0; } /** * Switching between DSDS and single sim mode needs to update the slot index of subscription * info into invalid if the slot index is not active. */ private void clearSlotIndexForSubInfoRecords() { if (mTelephonyManager == null || mContext == null) { logel("[clearSlotIndexForSubInfoRecords] TelephonyManager or mContext is null"); return; } int phoneCount = mTelephonyManager.getPhoneCount(); // Update simInfo db with invalid slot index ContentResolver resolver = mContext.getContentResolver(); ContentValues value = new ContentValues(1); value.put(SubscriptionManager.SIM_SLOT_INDEX, SubscriptionManager.INVALID_SIM_SLOT_INDEX); mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI, value, SubscriptionManager.SIM_SLOT_INDEX + ">=" + phoneCount, null); } private SubscriptionController(Phone phone) { mContext = phone.getContext(); mAppOps = mContext.getSystemService(AppOpsManager.class); Loading @@ -273,6 +298,12 @@ public class SubscriptionController extends ISub.Stub { migrateImsSettings(); /** * Switching between DSDS and single sim mode needs to clear the slot index of * subscription info. */ clearSlotIndexForSubInfoRecords(); if (DBG) logdl("[SubscriptionController] init by Phone"); } Loading Loading @@ -514,6 +545,20 @@ public class SubscriptionController extends ISub.Stub { return null; } /** * Get a single subscription info record for a given subscription. * * @param subId the subId to query. * * @hide */ public SubscriptionInfo getSubscriptionInfo(int subId) { List<SubscriptionInfo> subInfoList = getSubInfo( SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=" + subId, null); if (subInfoList == null || subInfoList.isEmpty()) return null; return subInfoList.get(0); } /** * Get the active SubscriptionInfo associated with the iccId * @param iccId the IccId of SIM card Loading Loading @@ -1060,9 +1105,6 @@ public class SubscriptionController extends ISub.Stub { if (value.size() > 0) { resolver.update(SubscriptionManager.getUriForSubscriptionId(subId), value, null, null); // Refresh the Cache of Active Subscription Info List refreshCachedActiveSubscriptionInfoList(); } if (DBG) logdl("[addSubInfoRecord] Record already exists"); Loading Loading @@ -1145,8 +1187,11 @@ public class SubscriptionController extends ISub.Stub { } } if (isSubscriptionForRemoteSim(subscriptionType)) { // Refresh the Cache of Active Subscription Info List. This should be done after // updating sSlotIndexToSubIds which is done through addToSubIdList() above. refreshCachedActiveSubscriptionInfoList(); if (isSubscriptionForRemoteSim(subscriptionType)) { notifySubscriptionInfoChanged(); } else { // Handle Local SIM devices // Set Display name after sub id is set above so as to get valid simCarrierName Loading Loading @@ -2344,7 +2389,12 @@ public class SubscriptionController extends ISub.Stub { simState = IccCardConstants.State.UNKNOWN; err = "invalid slotIndex"; } else { Phone phone = PhoneFactory.getPhone(slotIndex); Phone phone = null; try { phone = PhoneFactory.getPhone(slotIndex); } catch (IllegalStateException e) { // ignore } if (phone == null) { simState = IccCardConstants.State.UNKNOWN; err = "phone == null"; Loading
src/java/com/android/internal/telephony/SubscriptionInfoUpdater.java +112 −2 Original line number Diff line number Diff line Loading @@ -32,12 +32,15 @@ import android.os.Handler; import android.os.IRemoteCallback; import android.os.Looper; import android.os.Message; import android.os.ParcelUuid; import android.os.PersistableBundle; import android.os.RemoteException; import android.os.ServiceManager; import android.preference.PreferenceManager; import android.provider.Settings; import android.provider.Settings.Global; import android.provider.Settings.SettingNotFoundException; import android.service.carrier.CarrierService; import android.service.euicc.EuiccProfileInfo; import android.service.euicc.EuiccService; import android.service.euicc.GetEuiccProfileInfoListResult; Loading Loading @@ -90,6 +93,9 @@ public class SubscriptionInfoUpdater extends Handler { private static final String ICCID_STRING_FOR_NO_SIM = ""; private static final ParcelUuid REMOVE_GROUP_UUID = ParcelUuid.fromString(CarrierConfigManager.REMOVE_GROUP_UUID_STRING); // Key used to read/write the current IMSI. Updated on SIM_STATE_CHANGED - LOADED. public static final String CURR_SUBID = "curr_subid"; Loading @@ -111,6 +117,8 @@ public class SubscriptionInfoUpdater extends Handler { private int mCurrentlyActiveUserId; private CarrierServiceBindHelper mCarrierServiceBindHelper; // TODO: The SubscriptionController instance should be passed in here from PhoneFactory // rather than invoking the static getter all over the place. public SubscriptionInfoUpdater( Looper looper, Context context, Phone[] phone, CommandsInterface[] ci) { this(looper, context, phone, ci, Loading Loading @@ -512,8 +520,8 @@ public class SubscriptionInfoUpdater extends Handler { } private void updateCarrierServices(int slotId, String simState) { CarrierConfigManager configManager = (CarrierConfigManager) mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE); CarrierConfigManager configManager = (CarrierConfigManager) mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE); configManager.updateConfigForPhoneId(slotId, simState); mCarrierServiceBindHelper.updateForPhoneId(slotId, simState); } Loading Loading @@ -753,6 +761,108 @@ public class SubscriptionInfoUpdater extends Handler { return hasChanges; } /** * Called by CarrierConfigLoader to update the subscription before sending a broadcast. */ public void updateSubscriptionByCarrierConfigAndNotifyComplete(int phoneId, String configPackageName, PersistableBundle config, Message onComplete) { post(() -> { updateSubscriptionByCarrierConfig(phoneId, configPackageName, config); onComplete.sendToTarget(); }); } private String getDefaultCarrierServicePackageName() { CarrierConfigManager configManager = (CarrierConfigManager) mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE); return configManager.getDefaultCarrierServicePackageName(); } private boolean isCarrierServicePackage(int phoneId, String pkgName) { if (pkgName.equals(getDefaultCarrierServicePackageName())) return false; List<String> carrierPackageNames = TelephonyManager.from(mContext) .getCarrierPackageNamesForIntentAndPhone( new Intent(CarrierService.CARRIER_SERVICE_INTERFACE), phoneId); if (DBG) logd("Carrier Packages For Subscription = " + carrierPackageNames); return carrierPackageNames != null && carrierPackageNames.contains(pkgName); } /** * Update the currently active Subscription based on information from CarrierConfig */ @VisibleForTesting public void updateSubscriptionByCarrierConfig( int phoneId, String configPackageName, PersistableBundle config) { if (!SubscriptionManager.isValidPhoneId(phoneId) || TextUtils.isEmpty(configPackageName) || config == null) { if (DBG) { logd("In updateSubscriptionByCarrierConfig(): phoneId=" + phoneId + " configPackageName=" + configPackageName + " config=" + ((config == null) ? "null" : config.hashCode())); } return; } SubscriptionController sc = SubscriptionController.getInstance(); if (sc == null) { loge("SubscriptionController was null"); return; } int currentSubId = sc.getSubIdUsingPhoneId(phoneId); if (!SubscriptionManager.isValidSubscriptionId(currentSubId) || currentSubId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) { if (DBG) logd("No subscription is active for phone being updated"); return; } SubscriptionInfo currentSubInfo = sc.getSubscriptionInfo(currentSubId); if (currentSubInfo == null) { loge("Couldn't retrieve subscription info for current subscription"); return; } if (!isCarrierServicePackage(phoneId, configPackageName)) { loge("Cannot manage subId=" + currentSubId + ", carrierPackage=" + configPackageName); return; } ContentValues cv = new ContentValues(); boolean isOpportunistic = config.getBoolean( CarrierConfigManager.KEY_IS_OPPORTUNISTIC_SUBSCRIPTION_BOOL, false); if (currentSubInfo.isOpportunistic() != isOpportunistic) { if (DBG) logd("Set SubId=" + currentSubId + " isOpportunistic=" + isOpportunistic); cv.put(SubscriptionManager.IS_OPPORTUNISTIC, isOpportunistic ? "1" : "0"); } String groupUuidString = config.getString(CarrierConfigManager.KEY_SUBSCRIPTION_GROUP_UUID_STRING, ""); ParcelUuid groupId = null; if (!TextUtils.isEmpty(groupUuidString)) { try { // Update via a UUID Structure to ensure consistent formatting ParcelUuid groupUuid = ParcelUuid.fromString(groupUuidString); if (groupUuid.equals(REMOVE_GROUP_UUID) && currentSubInfo.getGroupUuid() != null) { cv.put(SubscriptionManager.GROUP_UUID, (String) null); if (DBG) logd("Group Removed for" + currentSubId); } else { // TODO: validate and update group owner information once feasible. cv.put(SubscriptionManager.GROUP_UUID, groupUuid.toString()); if (DBG) logd("Group Added for" + currentSubId); } } catch (IllegalArgumentException e) { loge("Invalid Group UUID=" + groupUuidString); } } if (cv.size() > 0 && mContext.getContentResolver().update(SubscriptionManager .getUriForSubscriptionId(currentSubId), cv, null, null) > 0) { sc.refreshCachedActiveSubscriptionInfoList(); sc.notifySubscriptionInfoChanged(); } } private static int findSubscriptionInfoForIccid(List<SubscriptionInfo> list, String iccid) { for (int i = 0; i < list.size(); i++) { if (TextUtils.equals(iccid, list.get(i).getIccId())) { Loading
src/java/com/android/internal/telephony/dataconnection/DataConnection.java +11 −0 Original line number Diff line number Diff line Loading @@ -233,6 +233,11 @@ public class DataConnection extends StateMachine { int mTag; public int mCid; /** * Indicate this data connection has been transferred to the other transport type during * IWLAN and WWAN handover. */ private boolean mHasTransferred; private final Map<ApnContext, ConnectionParams> mApnContexts = new ConcurrentHashMap<>(); PendingIntent mReconnectIntent = null; Loading Loading @@ -366,6 +371,10 @@ public class DataConnection extends StateMachine { return getCurrentState() == mActivatingState; } boolean hasBeenTransferred() { return mHasTransferred; } int getCid() { return mCid; } Loading Loading @@ -1706,6 +1715,7 @@ public class DataConnection extends StateMachine { mApnSetting != null ? (long) mApnSetting.getApnTypeBitmask() : 0L, mApnSetting != null ? mApnSetting.canHandleType(ApnSetting.TYPE_DEFAULT) : false); mHasTransferred = false; } @Override public boolean processMessage(Message msg) { Loading Loading @@ -2882,6 +2892,7 @@ public class DataConnection extends StateMachine { public DcNetworkAgent transferNetworkAgent(DataConnection dataConnection, @TransportType int transportType) { mNetworkAgent.acquireOwnership(dataConnection, transportType); this.mHasTransferred = true; return mNetworkAgent; } Loading
src/java/com/android/internal/telephony/dataconnection/DcTracker.java +11 −3 Original line number Diff line number Diff line Loading @@ -2919,9 +2919,17 @@ public class DcTracker extends Handler { private void onDisconnectDone(ApnContext apnContext) { if(DBG) log("onDisconnectDone: EVENT_DISCONNECT_DONE apnContext=" + apnContext); apnContext.setState(DctConstants.State.IDLE); mPhone.notifyDataConnection(apnContext.getApnType()); final DataConnection dc = apnContext.getDataConnection(); // when data connection is gone and not for handover, notify all apn types which // this data connection can handle. Note, this might not work if one apn type served for // multiple data connection. if (dc != null && dc.isInactive() && !dc.hasBeenTransferred()) { String[] types = ApnSetting.getApnTypesStringFromBitmask( apnContext.getApnSetting().getApnTypeBitmask()).split(","); for (String type : types) { mPhone.notifyDataConnection(type); } } // if all data connection are gone, check whether Airplane mode request was // pending. if (isDisconnected()) { Loading
tests/telephonytests/src/com/android/internal/telephony/SubscriptionInfoUpdaterTest.java +126 −0 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ import static com.android.internal.telephony.TelephonyTestUtils.waitForMs; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.any; Loading @@ -40,6 +41,8 @@ import android.content.pm.IPackageManager; import android.content.pm.UserInfo; import android.net.Uri; import android.os.HandlerThread; import android.os.ParcelUuid; import android.os.PersistableBundle; import android.service.euicc.EuiccProfileInfo; import android.service.euicc.EuiccService; import android.service.euicc.GetEuiccProfileInfoListResult; Loading @@ -64,6 +67,7 @@ import org.mockito.stubbing.Answer; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; Loading Loading @@ -571,4 +575,126 @@ public class SubscriptionInfoUpdaterTest extends TelephonyTest { eq(FAKE_SUB_ID_1)); verify(mSubscriptionController, times(0)).clearSubInfo(); } PersistableBundle getCarrierConfigForSubInfoUpdate( boolean isOpportunistic, String groupUuid) { PersistableBundle p = new PersistableBundle(); p.putBoolean(CarrierConfigManager.KEY_IS_OPPORTUNISTIC_SUBSCRIPTION_BOOL, isOpportunistic); p.putString(CarrierConfigManager.KEY_SUBSCRIPTION_GROUP_UUID_STRING, groupUuid); return p; } @Test @SmallTest public void testUpdateFromCarrierConfigOpportunisticUnchanged() throws Exception { final int phoneId = mPhone.getPhoneId(); String carrierPackageName = "FakeCarrierPackageName"; doReturn(FAKE_SUB_ID_1).when(mSubscriptionController).getSubIdUsingPhoneId(phoneId); doReturn(mSubInfo).when(mSubscriptionController).getSubscriptionInfo(eq(FAKE_SUB_ID_1)); doReturn(Collections.singletonList(carrierPackageName)).when(mTelephonyManager) .getCarrierPackageNamesForIntentAndPhone(any(), eq(phoneId)); ((MockContentResolver) mContext.getContentResolver()).addProvider( SubscriptionManager.CONTENT_URI.getAuthority(), new FakeSubscriptionContentProvider()); mUpdater.updateSubscriptionByCarrierConfig(mPhone.getPhoneId(), carrierPackageName, new PersistableBundle()); verify(mContentProvider, never()).update(any(), any(), any(), any()); verify(mSubscriptionController, never()).refreshCachedActiveSubscriptionInfoList(); verify(mSubscriptionController, never()).notifySubscriptionInfoChanged(); } @Test @SmallTest public void testUpdateFromCarrierConfigOpportunisticSetOpportunistic() throws Exception { final int phoneId = mPhone.getPhoneId(); PersistableBundle carrierConfig = getCarrierConfigForSubInfoUpdate( true, ""); String carrierPackageName = "FakeCarrierPackageName"; doReturn(FAKE_SUB_ID_1).when(mSubscriptionController).getSubIdUsingPhoneId(phoneId); doReturn(mSubInfo).when(mSubscriptionController).getSubscriptionInfo(eq(FAKE_SUB_ID_1)); doReturn(false).when(mSubInfo).isOpportunistic(); doReturn(Collections.singletonList(carrierPackageName)).when(mTelephonyManager) .getCarrierPackageNamesForIntentAndPhone(any(), eq(phoneId)); ((MockContentResolver) mContext.getContentResolver()).addProvider( SubscriptionManager.CONTENT_URI.getAuthority(), new FakeSubscriptionContentProvider()); mUpdater.updateSubscriptionByCarrierConfig(mPhone.getPhoneId(), carrierPackageName, carrierConfig); ArgumentCaptor<ContentValues> cvCaptor = ArgumentCaptor.forClass(ContentValues.class); verify(mContentProvider, times(1)).update( eq(SubscriptionManager.getUriForSubscriptionId(FAKE_SUB_ID_1)), cvCaptor.capture(), eq(null), eq(null)); assertEquals(1, cvCaptor.getValue().getAsInteger( SubscriptionManager.IS_OPPORTUNISTIC).intValue()); assertEquals(1, cvCaptor.getValue().size()); verify(mSubscriptionController, times(1)).refreshCachedActiveSubscriptionInfoList(); verify(mSubscriptionController, times(1)).notifySubscriptionInfoChanged(); } @Test @SmallTest public void testUpdateFromCarrierConfigOpportunisticAddToGroup() throws Exception { final int phoneId = mPhone.getPhoneId(); PersistableBundle carrierConfig = getCarrierConfigForSubInfoUpdate( true, "11111111-2222-3333-4444-555555555555"); String carrierPackageName = "FakeCarrierPackageName"; doReturn(FAKE_SUB_ID_1).when(mSubscriptionController).getSubIdUsingPhoneId(phoneId); doReturn(mSubInfo).when(mSubscriptionController).getSubscriptionInfo(eq(FAKE_SUB_ID_1)); doReturn(Collections.singletonList(carrierPackageName)).when(mTelephonyManager) .getCarrierPackageNamesForIntentAndPhone(any(), eq(phoneId)); ((MockContentResolver) mContext.getContentResolver()).addProvider( SubscriptionManager.CONTENT_URI.getAuthority(), new FakeSubscriptionContentProvider()); mUpdater.updateSubscriptionByCarrierConfig(mPhone.getPhoneId(), carrierPackageName, carrierConfig); ArgumentCaptor<ContentValues> cvCaptor = ArgumentCaptor.forClass(ContentValues.class); verify(mContentProvider, times(1)).update( eq(SubscriptionManager.getUriForSubscriptionId(FAKE_SUB_ID_1)), cvCaptor.capture(), eq(null), eq(null)); assertEquals(1, cvCaptor.getValue().getAsInteger( SubscriptionManager.IS_OPPORTUNISTIC).intValue()); assertEquals("11111111-2222-3333-4444-555555555555", cvCaptor.getValue().getAsString(SubscriptionManager.GROUP_UUID)); assertEquals(2, cvCaptor.getValue().size()); } @Test @SmallTest public void testUpdateFromCarrierConfigOpportunisticRemoveFromGroup() throws Exception { final int phoneId = mPhone.getPhoneId(); PersistableBundle carrierConfig = getCarrierConfigForSubInfoUpdate( true, "00000000-0000-0000-0000-000000000000"); String carrierPackageName = "FakeCarrierPackageName"; doReturn(FAKE_SUB_ID_1).when(mSubscriptionController).getSubIdUsingPhoneId(phoneId); doReturn(mSubInfo).when(mSubscriptionController).getSubscriptionInfo(eq(FAKE_SUB_ID_1)); doReturn(ParcelUuid.fromString("11111111-2222-3333-4444-555555555555")) .when(mSubInfo).getGroupUuid(); doReturn(Collections.singletonList(carrierPackageName)).when(mTelephonyManager) .getCarrierPackageNamesForIntentAndPhone(any(), eq(phoneId)); ((MockContentResolver) mContext.getContentResolver()).addProvider( SubscriptionManager.CONTENT_URI.getAuthority(), new FakeSubscriptionContentProvider()); mUpdater.updateSubscriptionByCarrierConfig(mPhone.getPhoneId(), carrierPackageName, carrierConfig); ArgumentCaptor<ContentValues> cvCaptor = ArgumentCaptor.forClass(ContentValues.class); verify(mContentProvider, times(1)).update( eq(SubscriptionManager.getUriForSubscriptionId(FAKE_SUB_ID_1)), cvCaptor.capture(), eq(null), eq(null)); assertEquals(1, cvCaptor.getValue().getAsInteger( SubscriptionManager.IS_OPPORTUNISTIC).intValue()); assertNull(cvCaptor.getValue().getAsString(SubscriptionManager.GROUP_UUID)); assertEquals(2, cvCaptor.getValue().size()); } }