diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2021-11-12 15:44:38 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2021-11-12 15:44:38 +0000 |
commit | aa86636e6bc3872aeb8ef214c2b3fbb519f6f3cd (patch) | |
tree | b7f8b73b10a414d0bc6b8036d00e8c74b3594bb8 | |
parent | e9c4c7306fd22a30d67bea2a186cc22c6d267f15 (diff) | |
parent | 8045eba07f71575f67f546599080b7482bf62a32 (diff) | |
download | telephony-android12-mainline-neuralnetworks-release.tar.gz |
Snap for 7910331 from 8045eba07f71575f67f546599080b7482bf62a32 to mainline-neuralnetworks-releaseandroid-mainline-12.0.0_r92android-mainline-12.0.0_r78android-mainline-12.0.0_r50android12-mainline-neuralnetworks-release
Change-Id: If90d12bdcd9036b78021864594780170b65f5f76
50 files changed, 1241 insertions, 299 deletions
@@ -2,14 +2,15 @@ amitmahajan@google.com breadley@google.com fionaxu@google.com jackyu@google.com -hallliu@google.com rgreenwalt@google.com tgunn@google.com jminjie@google.com shuoq@google.com -refuhoo@google.com nazaninb@google.com sarahchin@google.com -dbright@google.com xiaotonj@google.com +huiwang@google.com +jayachandranc@google.com +chinmayd@google.com +amruthr@google.com diff --git a/src/java/com/android/internal/telephony/GsmCdmaConnection.java b/src/java/com/android/internal/telephony/GsmCdmaConnection.java index e9ecb79874..b79bdef998 100644 --- a/src/java/com/android/internal/telephony/GsmCdmaConnection.java +++ b/src/java/com/android/internal/telephony/GsmCdmaConnection.java @@ -42,7 +42,7 @@ import com.android.internal.telephony.uicc.UiccCardApplication; import com.android.telephony.Rlog; import java.util.ArrayList; -import java.util.Arrays; +import java.util.Collections; /** * {@hide} @@ -141,7 +141,10 @@ public class GsmCdmaConnection extends Connection { mAddress = dc.number; setEmergencyCallInfo(mOwner); - mForwardedNumber = new ArrayList<String>(Arrays.asList(dc.forwardedNumber)); + String forwardedNumber = TextUtils.isEmpty(dc.forwardedNumber) ? null : dc.forwardedNumber; + Rlog.i(LOG_TAG, "create, forwardedNumber=" + Rlog.pii(LOG_TAG, forwardedNumber)); + mForwardedNumber = forwardedNumber == null ? null : + new ArrayList<>(Collections.singletonList(dc.forwardedNumber)); mIsIncoming = dc.isMT; mCreateTime = System.currentTimeMillis(); mCnapName = dc.name; @@ -710,11 +713,13 @@ public class GsmCdmaConnection extends Connection { mOwner.getPhone().getVoiceCallSessionStats().onAudioCodecChanged(this, dc.audioQuality); } - ArrayList<String> forwardedNumber = - new ArrayList<String>(Arrays.asList(dc.forwardedNumber)); - if (!equalsHandlesNulls(mForwardedNumber, forwardedNumber)) { - if (Phone.DEBUG_PHONE) log("update: mForwardedNumber, # changed!"); - mForwardedNumber = forwardedNumber; + String forwardedNumber = TextUtils.isEmpty(dc.forwardedNumber) ? null : dc.forwardedNumber; + Rlog.i(LOG_TAG, "update: forwardedNumber=" + Rlog.pii(LOG_TAG, forwardedNumber)); + ArrayList<String> forwardedNumbers = forwardedNumber == null ? null : + new ArrayList<>(Collections.singletonList(dc.forwardedNumber)); + if (!equalsHandlesNulls(mForwardedNumber, forwardedNumbers)) { + if (Phone.DEBUG_PHONE) log("update: mForwardedNumber, # changed"); + mForwardedNumber = forwardedNumbers; changed = true; } diff --git a/src/java/com/android/internal/telephony/GsmCdmaPhone.java b/src/java/com/android/internal/telephony/GsmCdmaPhone.java index 895c21318c..a034392cee 100644 --- a/src/java/com/android/internal/telephony/GsmCdmaPhone.java +++ b/src/java/com/android/internal/telephony/GsmCdmaPhone.java @@ -126,6 +126,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.Iterator; import java.util.List; +import java.util.function.Consumer; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -257,6 +258,7 @@ public class GsmCdmaPhone extends Phone { private IccSmsInterfaceManager mIccSmsInterfaceManager; private boolean mResetModemOnRadioTechnologyChange = false; + private boolean mSsOverCdmaSupported = false; private int mRilVersion; private boolean mBroadcastEmergencyCallStateChanges = false; @@ -1325,8 +1327,8 @@ public class GsmCdmaPhone extends Phone { } @Override - public Connection dial(String dialString, @NonNull DialArgs dialArgs) - throws CallStateException { + public Connection dial(String dialString, @NonNull DialArgs dialArgs, + Consumer<Phone> chosenPhoneConsumer) throws CallStateException { if (!isPhoneTypeGsm() && dialArgs.uusInfo != null) { throw new CallStateException("Sending UUS information NOT supported in CDMA!"); } @@ -1409,6 +1411,7 @@ public class GsmCdmaPhone extends Phone { || useImsForEmergency) { try { if (DBG) logd("Trying IMS PS call"); + chosenPhoneConsumer.accept(imsPhone); return imsPhone.dial(dialString, dialArgs); } catch (CallStateException e) { if (DBG) logd("IMS PS call exception " + e + @@ -1465,6 +1468,7 @@ public class GsmCdmaPhone extends Phone { mCi.testingEmergencyCall(); } + chosenPhoneConsumer.accept(this); return dialInternal(dialString, dialArgs); } @@ -2200,6 +2204,11 @@ public class GsmCdmaPhone extends Phone { return false; } + private void updateSsOverCdmaSupported(PersistableBundle b) { + if (b == null) return; + mSsOverCdmaSupported = b.getBoolean(CarrierConfigManager.KEY_SUPPORT_SS_OVER_CDMA_BOOL); + } + @Override public boolean useSsOverIms(Message onComplete) { boolean isUtEnabled = isUtEnabled(); @@ -2240,8 +2249,16 @@ public class GsmCdmaPhone extends Phone { mCi.queryCallForwardStatus(commandInterfaceCFReason, serviceClass, null, resp); } } else { - loge("getCallForwardingOption: not possible in CDMA, just return empty result"); - AsyncResult.forMessage(onComplete, makeEmptyCallForward(), null); + if (!mSsOverCdmaSupported) { + // If SS over CDMA is not supported and UT is not at the time, notify the user of + // the error and disable the option. + AsyncResult.forMessage(onComplete, null, + new CommandException(CommandException.Error.INVALID_STATE, + "Call Forwarding over CDMA unavailable")); + } else { + loge("getCallForwardingOption: not possible in CDMA, just return empty result"); + AsyncResult.forMessage(onComplete, makeEmptyCallForward(), null); + } onComplete.sendToTarget(); } } @@ -2289,7 +2306,7 @@ public class GsmCdmaPhone extends Phone { timerSeconds, resp); } - } else { + } else if (mSsOverCdmaSupported) { String formatNumber = GsmCdmaConnection.formatDialString(dialingNumber); String cfNumber = CdmaMmiCode.getCallForwardingPrefixAndNumber( commandInterfaceCFAction, commandInterfaceCFReason, formatNumber); @@ -2306,6 +2323,10 @@ public class GsmCdmaPhone extends Phone { AsyncResult.forMessage(onComplete, CommandsInterface.SS_STATUS_UNKNOWN, null); onComplete.sendToTarget(); + } else { + loge("setCallForwardingOption: SS over CDMA not supported, can not complete"); + AsyncResult.forMessage(onComplete, CommandsInterface.SS_STATUS_UNKNOWN, null); + onComplete.sendToTarget(); } } @@ -2429,8 +2450,17 @@ public class GsmCdmaPhone extends Phone { //class parameter in call waiting interrogation to network mCi.queryCallWaiting(CommandsInterface.SERVICE_CLASS_NONE, onComplete); } else { - int arr[] = {CommandsInterface.SS_STATUS_UNKNOWN, CommandsInterface.SERVICE_CLASS_NONE}; - AsyncResult.forMessage(onComplete, arr, null); + if (!mSsOverCdmaSupported) { + // If SS over CDMA is not supported and UT is not at the time, notify the user of + // the error and disable the option. + AsyncResult.forMessage(onComplete, null, + new CommandException(CommandException.Error.INVALID_STATE, + "Call Waiting over CDMA unavailable")); + } else { + int[] arr = + {CommandsInterface.SS_STATUS_UNKNOWN, CommandsInterface.SERVICE_CLASS_NONE}; + AsyncResult.forMessage(onComplete, arr, null); + } onComplete.sendToTarget(); } } @@ -2458,7 +2488,7 @@ public class GsmCdmaPhone extends Phone { if (isPhoneTypeGsm()) { mCi.setCallWaiting(enable, serviceClass, onComplete); - } else { + } else if (mSsOverCdmaSupported) { String cwPrefix = CdmaMmiCode.getCallWaitingPrefix(enable); Rlog.i(LOG_TAG, "setCallWaiting in CDMA : dial for set call waiting" + " prefix= " + cwPrefix); @@ -2472,6 +2502,10 @@ public class GsmCdmaPhone extends Phone { AsyncResult.forMessage(onComplete, CommandsInterface.SS_STATUS_UNKNOWN, null); onComplete.sendToTarget(); + } else { + loge("setCallWaiting: SS over CDMA not supported, can not complete"); + AsyncResult.forMessage(onComplete, CommandsInterface.SS_STATUS_UNKNOWN, null); + onComplete.sendToTarget(); } } @@ -2907,8 +2941,6 @@ public class GsmCdmaPhone extends Phone { break; case EVENT_CARRIER_CONFIG_CHANGED: - // Obtain new radio capabilities from the modem, since some are SIM-dependent - mCi.getRadioCapability(obtainMessage(EVENT_GET_RADIO_CAPABILITY)); // Only check for the voice radio tech if it not going to be updated by the voice // registration changes. if (!mContext.getResources().getBoolean( @@ -2926,8 +2958,10 @@ public class GsmCdmaPhone extends Phone { updateCdmaRoamingSettingsAfterCarrierConfigChanged(b); updateNrSettingsAfterCarrierConfigChanged(b); + updateSsOverCdmaSupported(b); loadAllowedNetworksFromSubscriptionDatabase(); - updateAllowedNetworkTypes(null); + // Obtain new radio capabilities from the modem, since some are SIM-dependent + mCi.getRadioCapability(obtainMessage(EVENT_GET_RADIO_CAPABILITY)); break; case EVENT_SET_ROAMING_PREFERENCE_DONE: diff --git a/src/java/com/android/internal/telephony/MultiSimSettingController.java b/src/java/com/android/internal/telephony/MultiSimSettingController.java index 26fcfd6e0b..ca5d962701 100644 --- a/src/java/com/android/internal/telephony/MultiSimSettingController.java +++ b/src/java/com/android/internal/telephony/MultiSimSettingController.java @@ -341,16 +341,18 @@ public class MultiSimSettingController extends Handler { } /** - * Upon initialization, update defaults and mobile data enabling. + * Upon initialization or radio available, update defaults and mobile data enabling. * Should only be triggered once. */ private void onAllSubscriptionsLoaded() { - if (DBG) log("onAllSubscriptionsLoaded"); - mSubInfoInitialized = true; - for (Phone phone : PhoneFactory.getPhones()) { - phone.mCi.registerForRadioStateChanged(this, EVENT_RADIO_STATE_CHANGED, null); + if (DBG) log("onAllSubscriptionsLoaded: mSubInfoInitialized=" + mSubInfoInitialized); + if (!mSubInfoInitialized) { + mSubInfoInitialized = true; + for (Phone phone : PhoneFactory.getPhones()) { + phone.mCi.registerForRadioStateChanged(this, EVENT_RADIO_STATE_CHANGED, null); + } + reEvaluateAll(); } - reEvaluateAll(); } /** @@ -445,11 +447,16 @@ public class MultiSimSettingController extends Handler { } /** - * Wait for subInfo initialization (after boot up) and carrier config load for all active - * subscriptions before re-evaluate multi SIM settings. + * Wait for subInfo initialization (after boot up or radio unavailable) and carrier config load + * for all active subscriptions before re-evaluate multi SIM settings. */ private boolean isReadyToReevaluate() { - return mSubInfoInitialized && isCarrierConfigLoadedForAllSub(); + boolean carrierConfigsLoaded = isCarrierConfigLoadedForAllSub(); + if (DBG) { + log("isReadyToReevaluate: subInfoInitialized=" + mSubInfoInitialized + + ", carrierConfigsLoaded=" + carrierConfigsLoaded); + } + return mSubInfoInitialized && carrierConfigsLoaded; } private void reEvaluateAll() { diff --git a/src/java/com/android/internal/telephony/NetworkTypeController.java b/src/java/com/android/internal/telephony/NetworkTypeController.java index 5d5fca0ee4..a1724a6822 100644 --- a/src/java/com/android/internal/telephony/NetworkTypeController.java +++ b/src/java/com/android/internal/telephony/NetworkTypeController.java @@ -150,6 +150,7 @@ public class NetworkTypeController extends StateMachine { private boolean mIsPhysicalChannelConfig16Supported; private Boolean mIsNrAdvancedAllowedByPco = false; private int mNrAdvancedCapablePcoId = 0; + private boolean mIsUsingUserDataForRrcDetection = false; /** * NetworkTypeController constructor. @@ -279,6 +280,13 @@ public class NetworkTypeController extends StateMachine { CarrierConfigManager.KEY_ADDITIONAL_NR_ADVANCED_BANDS_INT_ARRAY); mNrAdvancedCapablePcoId = b.getInt( CarrierConfigManager.KEY_NR_ADVANCED_CAPABLE_PCO_ID_INT); + mIsUsingUserDataForRrcDetection = b.getBoolean( + CarrierConfigManager.KEY_LTE_ENDC_USING_USER_DATA_FOR_RRC_DETECTION_BOOL); + if (mIsPhysicalChannelConfig16Supported && mIsUsingUserDataForRrcDetection) { + mPhone.getDcTracker(AccessNetworkConstants.TRANSPORT_TYPE_WWAN) + .registerForPhysicalLinkStateChanged(getHandler(), + EVENT_PHYSICAL_LINK_STATE_CHANGED); + } } } createTimerRules(nrIconConfiguration, overrideTimerRule, overrideSecondaryTimerRule); @@ -503,7 +511,7 @@ public class NetworkTypeController extends StateMachine { // ignored break; case EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED: - if (mIsPhysicalChannelConfig16Supported) { + if (isUsingPhysicalChannelConfigForRrcDetection()) { mPhysicalLinkState = getPhysicalLinkStateFromPhysicalChannelConfig(); } break; @@ -608,7 +616,7 @@ public class NetworkTypeController extends StateMachine { // ignored break; case EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED: - if (mIsPhysicalChannelConfig16Supported) { + if (isUsingPhysicalChannelConfigForRrcDetection()) { mPhysicalLinkState = getPhysicalLinkStateFromPhysicalChannelConfig(); if (mIsTimerResetEnabledForLegacyStateRRCIdle && !isPhysicalLinkActive()) { resetAllTimers(); @@ -680,7 +688,7 @@ public class NetworkTypeController extends StateMachine { // ignore break; case EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED: - if (mIsPhysicalChannelConfig16Supported) { + if (isUsingPhysicalChannelConfigForRrcDetection()) { mPhysicalLinkState = getPhysicalLinkStateFromPhysicalChannelConfig(); if (isNrNotRestricted()) { // NOT_RESTRICTED_RRC_IDLE -> NOT_RESTRICTED_RRC_CON @@ -764,7 +772,7 @@ public class NetworkTypeController extends StateMachine { // ignore break; case EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED: - if (mIsPhysicalChannelConfig16Supported) { + if (isUsingPhysicalChannelConfigForRrcDetection()) { mPhysicalLinkState = getPhysicalLinkStateFromPhysicalChannelConfig(); if (isNrNotRestricted()) { // NOT_RESTRICTED_RRC_CON -> NOT_RESTRICTED_RRC_IDLE @@ -856,7 +864,7 @@ public class NetworkTypeController extends StateMachine { break; case EVENT_NR_FREQUENCY_CHANGED: case EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED: - if (mIsPhysicalChannelConfig16Supported) { + if (isUsingPhysicalChannelConfigForRrcDetection()) { mPhysicalLinkState = getPhysicalLinkStateFromPhysicalChannelConfig(); } updateNrAdvancedState(); @@ -1188,6 +1196,10 @@ public class NetworkTypeController extends StateMachine { } } + private boolean isUsingPhysicalChannelConfigForRrcDetection() { + return mIsPhysicalChannelConfig16Supported && !mIsUsingUserDataForRrcDetection; + } + protected void log(String s) { Rlog.d(TAG, "[" + mPhone.getPhoneId() + "] " + s); } diff --git a/src/java/com/android/internal/telephony/Phone.java b/src/java/com/android/internal/telephony/Phone.java index ef032cd475..93a2e8e488 100644 --- a/src/java/com/android/internal/telephony/Phone.java +++ b/src/java/com/android/internal/telephony/Phone.java @@ -452,7 +452,7 @@ public abstract class Phone extends Handler implements PhoneInternalInterface { private static final String ALLOWED_NETWORK_TYPES_TEXT_ENABLE_2G = "enable_2g"; private static final int INVALID_ALLOWED_NETWORK_TYPES = -1; protected boolean mIsCarrierNrSupported = false; - + protected boolean mIsAllowedNetworkTypesLoadedFromDb = false; private boolean mUnitTestMode; private CarrierPrivilegesTracker mCarrierPrivilegesTracker = null; @@ -737,6 +737,8 @@ public abstract class Phone extends Handler implements PhoneInternalInterface { break; case EVENT_INITIATE_SILENT_REDIAL: + // This is an ImsPhone -> GsmCdmaPhone redial + // See ImsPhone#initiateSilentRedial Rlog.d(LOG_TAG, "Event EVENT_INITIATE_SILENT_REDIAL Received"); ar = (AsyncResult) msg.obj; if ((ar.exception == null) && (ar.result != null)) { @@ -747,6 +749,10 @@ public abstract class Phone extends Handler implements PhoneInternalInterface { if (TextUtils.isEmpty(dialString)) return; try { Connection cn = dialInternal(dialString, dialArgs); + // The ImsPhoneConnection that is owned by the ImsPhone is currently the + // one with a callback registered to TelephonyConnection. Notify the + // redial happened over that Phone so that it can be replaced with the + // new GSM/CDMA Connection. Rlog.d(LOG_TAG, "Notify redial connection changed cn: " + cn); if (mImsPhone != null) { // Don't care it is null or not. @@ -2317,6 +2323,8 @@ public abstract class Phone extends Handler implements PhoneInternalInterface { String result = SubscriptionController.getInstance().getSubscriptionProperty( getSubId(), SubscriptionManager.ALLOWED_NETWORK_TYPES); + // After fw load network type from DB, do unlock if subId is valid. + mIsAllowedNetworkTypesLoadedFromDb = SubscriptionManager.isValidSubscriptionId(getSubId()); if (result == null) { return; } @@ -2418,10 +2426,18 @@ public abstract class Phone extends Handler implements PhoneInternalInterface { int subId = getSubId(); if (!TelephonyManager.isValidAllowedNetworkTypesReason(reason)) { loge("setAllowedNetworkTypes: Invalid allowed network type reason: " + reason); + AsyncResult.forMessage(response, null, + new CommandException(CommandException.Error.INVALID_ARGUMENTS)); + response.sendToTarget(); return; } - if (!SubscriptionManager.isUsableSubscriptionId(subId)) { - loge("setAllowedNetworkTypes: Invalid subscriptionId: " + subId); + if (!SubscriptionManager.isUsableSubscriptionId(subId) + || !mIsAllowedNetworkTypesLoadedFromDb) { + loge("setAllowedNetworkTypes: no sim or network type is not loaded. SubscriptionId: " + + subId + ", isNetworkTypeLoaded" + mIsAllowedNetworkTypesLoadedFromDb); + AsyncResult.forMessage(response, null, + new CommandException(CommandException.Error.MISSING_RESOURCE)); + response.sendToTarget(); return; } String mapAsString = ""; @@ -4247,8 +4263,9 @@ public abstract class Phone extends Handler implements PhoneInternalInterface { Phone imsPhone = mImsPhone; if (imsPhone != null) { imsPhone.getImsRegistrationState(callback); + } else { + callback.accept(RegistrationManager.REGISTRATION_STATE_NOT_REGISTERED); } - callback.accept(RegistrationManager.REGISTRATION_STATE_NOT_REGISTERED); } @@ -4371,7 +4388,9 @@ public abstract class Phone extends Handler implements PhoneInternalInterface { public void sendSubscriptionSettings(boolean restoreNetworkSelection) { // Send settings down - updateAllowedNetworkTypes(null); + if (mIsAllowedNetworkTypesLoadedFromDb) { + updateAllowedNetworkTypes(null); + } if (restoreNetworkSelection) { restoreSavedNetworkSelection(null); @@ -5024,4 +5043,16 @@ public abstract class Phone extends Handler implements PhoneInternalInterface { private static String pii(String s) { return Rlog.pii(LOG_TAG, s); } + + /** + * Used in unit tests to set whether the AllowedNetworkTypes is loaded from Db. Should not + * be used otherwise. + * + * @return {@code true} if the AllowedNetworkTypes is loaded from Db, + * {@code false} otherwise. + */ + @VisibleForTesting + public boolean isAllowedNetworkTypesLoadedFromDb() { + return mIsAllowedNetworkTypesLoadedFromDb; + } } diff --git a/src/java/com/android/internal/telephony/PhoneInternalInterface.java b/src/java/com/android/internal/telephony/PhoneInternalInterface.java index a61954bcd8..437459f89e 100644 --- a/src/java/com/android/internal/telephony/PhoneInternalInterface.java +++ b/src/java/com/android/internal/telephony/PhoneInternalInterface.java @@ -38,6 +38,7 @@ import com.android.internal.telephony.PhoneConstants.DataState; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.List; +import java.util.function.Consumer; /** * Internal interface used to control the phone; SDK developers cannot @@ -489,12 +490,33 @@ public interface PhoneInternalInterface { * * @param dialString The dial string. * @param dialArgs Parameters to perform the dial with. + * @param chosenPhone The Phone (either GsmCdmaPhone or ImsPhone) that has been chosen to dial + * this number. This is used for any setup that should occur before dial + * actually occurs. * @exception CallStateException if a new outgoing call is not currently * possible because no more call slots exist or a call exists * that is dialing, alerting, ringing, or waiting. Other * errors are handled asynchronously. */ - Connection dial(String dialString, @NonNull DialArgs dialArgs) throws CallStateException; + Connection dial(String dialString, @NonNull DialArgs dialArgs, + Consumer<Phone> chosenPhone) throws CallStateException; + + /** + * Initiate a new voice connection. This happens asynchronously, so you + * cannot assume the audio path is connected (or a call index has been + * assigned) until PhoneStateChanged notification has occurred. + * + * @param dialString The dial string. + * @param dialArgs Parameters to perform the dial with. + * @exception CallStateException if a new outgoing call is not currently + * possible because no more call slots exist or a call exists + * that is dialing, alerting, ringing, or waiting. Other + * errors are handled asynchronously. + */ + default Connection dial(String dialString, @NonNull DialArgs dialArgs) + throws CallStateException { + return dial(dialString, dialArgs, (phone) -> {}); + } /** * Initiate a new conference connection. This happens asynchronously, so you diff --git a/src/java/com/android/internal/telephony/PhoneSubInfoController.java b/src/java/com/android/internal/telephony/PhoneSubInfoController.java index 8db71d99c0..36ed749600 100644 --- a/src/java/com/android/internal/telephony/PhoneSubInfoController.java +++ b/src/java/com/android/internal/telephony/PhoneSubInfoController.java @@ -23,6 +23,7 @@ import static android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE; import android.annotation.NonNull; import android.annotation.Nullable; +import android.app.AppOpsManager; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.pm.PackageManager; @@ -35,6 +36,7 @@ import android.telephony.ImsiEncryptionInfo; import android.telephony.PhoneNumberUtils; import android.telephony.SubscriptionManager; import android.telephony.TelephonyFrameworkInitializer; +import android.util.EventLog; import com.android.internal.telephony.uicc.IsimRecords; import com.android.internal.telephony.uicc.UiccCard; @@ -48,6 +50,7 @@ public class PhoneSubInfoController extends IPhoneSubInfo.Stub { @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) private final Context mContext; + private AppOpsManager mAppOps; public PhoneSubInfoController(Context context) { ServiceRegisterer phoneSubServiceRegisterer = TelephonyFrameworkInitializer @@ -56,6 +59,7 @@ public class PhoneSubInfoController extends IPhoneSubInfo.Stub { if (phoneSubServiceRegisterer.get() == null) { phoneSubServiceRegisterer.register(this); } + mAppOps = context.getSystemService(AppOpsManager.class); mContext = context; } @@ -71,6 +75,7 @@ public class PhoneSubInfoController extends IPhoneSubInfo.Stub { public String getDeviceIdForPhone(int phoneId, String callingPackage, String callingFeatureId) { + enforceCallingPackageUidMatched(callingPackage); return callPhoneMethodForPhoneIdWithReadDeviceIdentifiersCheck(phoneId, callingPackage, callingFeatureId, "getDeviceId", (phone) -> phone.getDeviceId()); } @@ -265,6 +270,15 @@ public class PhoneSubInfoController extends IPhoneSubInfo.Stub { return PhoneFactory.getPhone(phoneId); } + private void enforceCallingPackageUidMatched(String callingPackage) { + try { + mAppOps.checkPackage(Binder.getCallingUid(), callingPackage); + } catch (SecurityException se) { + EventLog.writeEvent(0x534e4554, "188677422", Binder.getCallingUid()); + throw se; + } + } + private boolean enforceIccSimChallengeResponsePermission(Context context, int subId, String callingPackage, String callingFeatureId, String message) { if (TelephonyPermissions.checkCallingOrSelfUseIccAuthWithDeviceIdentifier(context, diff --git a/src/java/com/android/internal/telephony/RadioIndication.java b/src/java/com/android/internal/telephony/RadioIndication.java index 72ba991347..d18efdcb9c 100644 --- a/src/java/com/android/internal/telephony/RadioIndication.java +++ b/src/java/com/android/internal/telephony/RadioIndication.java @@ -1132,8 +1132,8 @@ public class RadioIndication extends IRadioIndication.Stub { @NetworkRegistrationInfo.Domain int domain, int causeCode, int additionalCauseCode) { mRil.processIndication(indicationType); - - if (cellIdentity == null + CellIdentity ci = CellIdentity.create(cellIdentity); + if (ci == null || TextUtils.isEmpty(chosenPlmn) || (domain & NetworkRegistrationInfo.DOMAIN_CS_PS) == 0 || (domain & ~NetworkRegistrationInfo.DOMAIN_CS_PS) != 0 @@ -1147,8 +1147,6 @@ public class RadioIndication extends IRadioIndication.Stub { return; } - CellIdentity ci = CellIdentity.create(cellIdentity); - mRil.mRegistrationFailedRegistrant.notifyRegistrant( new AsyncResult( null, diff --git a/src/java/com/android/internal/telephony/SmsController.java b/src/java/com/android/internal/telephony/SmsController.java index e7feaf4cad..9f791619d8 100644 --- a/src/java/com/android/internal/telephony/SmsController.java +++ b/src/java/com/android/internal/telephony/SmsController.java @@ -515,6 +515,10 @@ public class SmsController extends ISmsImplBase { Uri messageUri, String scAddress, PendingIntent sentIntent, PendingIntent deliveryIntent) { IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId); + if (!getCallingPackage().equals(callingPkg)) { + throw new SecurityException("sendStoredText: Package " + callingPkg + + "does not belong to " + Binder.getCallingUid()); + } if (iccSmsIntMgr != null) { iccSmsIntMgr.sendStoredText(callingPkg, callingAttributionTag, messageUri, scAddress, sentIntent, deliveryIntent); @@ -529,6 +533,10 @@ public class SmsController extends ISmsImplBase { Uri messageUri, String scAddress, List<PendingIntent> sentIntents, List<PendingIntent> deliveryIntents) { IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId); + if (!getCallingPackage().equals(callingPkg)) { + throw new SecurityException("sendStoredMultipartText: Package " + callingPkg + + " does not belong to " + Binder.getCallingUid()); + } if (iccSmsIntMgr != null) { iccSmsIntMgr.sendStoredMultipartText(callingPkg, callingAttributionTag, messageUri, scAddress, sentIntents, deliveryIntents); diff --git a/src/java/com/android/internal/telephony/SubscriptionController.java b/src/java/com/android/internal/telephony/SubscriptionController.java index 39a5f51bd1..49db160450 100644 --- a/src/java/com/android/internal/telephony/SubscriptionController.java +++ b/src/java/com/android/internal/telephony/SubscriptionController.java @@ -905,6 +905,19 @@ public class SubscriptionController extends ISub.Stub { @Override public List<SubscriptionInfo> getAllSubInfoList(String callingPackage, String callingFeatureId) { + return getAllSubInfoList(callingPackage, callingFeatureId, false); + } + + /** + * @param callingPackage The package making the IPC. + * @param callingFeatureId The feature in the package + * @param skipConditionallyRemoveIdentifier if set, skip removing identifier conditionally + * @return List of all SubscriptionInfo records in database, + * include those that were inserted before, maybe empty but not null. + * @hide + */ + public List<SubscriptionInfo> getAllSubInfoList(String callingPackage, + String callingFeatureId, boolean skipConditionallyRemoveIdentifier) { if (VDBG) logd("[getAllSubInfoList]+"); // This API isn't public, so no need to provide a valid subscription ID - we're not worried @@ -917,22 +930,22 @@ public class SubscriptionController extends ISub.Stub { // Now that all security checks passes, perform the operation as ourselves. final long identity = Binder.clearCallingIdentity(); + List<SubscriptionInfo> subList; try { - List<SubscriptionInfo> subList = null; subList = getSubInfo(null, null); - if (subList != null) { - if (VDBG) logd("[getAllSubInfoList]- " + subList.size() + " infos return"); - subList.stream().map( - subscriptionInfo -> conditionallyRemoveIdentifiers(subscriptionInfo, - callingPackage, callingFeatureId, "getAllSubInfoList")) - .collect(Collectors.toList()); - } else { - if (VDBG) logd("[getAllSubInfoList]- no info return"); - } - return subList; } finally { Binder.restoreCallingIdentity(identity); } + if (subList != null && !skipConditionallyRemoveIdentifier) { + if (VDBG) logd("[getAllSubInfoList]- " + subList.size() + " infos return"); + subList = subList.stream().map( + subscriptionInfo -> conditionallyRemoveIdentifiers(subscriptionInfo, + callingPackage, callingFeatureId, "getAllSubInfoList")) + .collect(Collectors.toList()); + } else { + if (VDBG) logd("[getAllSubInfoList]- no info return"); + } + return subList; } private List<SubscriptionInfo> makeCacheListCopyWithLock(List<SubscriptionInfo> cacheSubList) { @@ -3890,8 +3903,10 @@ public class SubscriptionController extends ISub.Stub { List<SubscriptionInfo> subInfoList; try { + // need to bypass removing identifier check because that will remove the subList without + // group id. subInfoList = getAllSubInfoList(mContext.getOpPackageName(), - mContext.getAttributionTag()); + mContext.getAttributionTag(), true); if (groupUuid == null || subInfoList == null || subInfoList.isEmpty()) { return new ArrayList<>(); } diff --git a/src/java/com/android/internal/telephony/SubscriptionInfoUpdater.java b/src/java/com/android/internal/telephony/SubscriptionInfoUpdater.java index 9132e5e2b2..36751a3389 100644 --- a/src/java/com/android/internal/telephony/SubscriptionInfoUpdater.java +++ b/src/java/com/android/internal/telephony/SubscriptionInfoUpdater.java @@ -419,13 +419,8 @@ public class SubscriptionInfoUpdater extends Handler { UiccSlot uiccSlot = UiccController.getInstance().getUiccSlotForPhone(phoneId); String iccId = (uiccSlot != null) ? IccUtils.stripTrailingFs(uiccSlot.getIccId()) : null; if (!TextUtils.isEmpty(iccId)) { - // Call updateSubscriptionInfoByIccId() only if was - // not done earlier from SIM Locked event - if (sIccId[phoneId] == null) { - sIccId[phoneId] = iccId; - - updateSubscriptionInfoByIccId(phoneId, true /* updateEmbeddedSubs */); - } + sIccId[phoneId] = iccId; + updateSubscriptionInfoByIccId(phoneId, true /* updateEmbeddedSubs */); } cardIds.add(getCardIdFromPhoneId(phoneId)); @@ -457,10 +452,10 @@ public class SubscriptionInfoUpdater extends Handler { // At this phase, the subscription list is accessible. Treating NOT_READY // as equivalent to ABSENT, once the rest of the system can handle it. sIccId[phoneId] = ICCID_STRING_FOR_NO_SIM; + updateSubscriptionInfoByIccId(phoneId, false /* updateEmbeddedSubs */); } else { sIccId[phoneId] = null; } - updateSubscriptionInfoByIccId(phoneId, false /* updateEmbeddedSubs */); broadcastSimStateChanged(phoneId, IccCardConstants.INTENT_VALUE_ICC_NOT_READY, null); @@ -792,8 +787,8 @@ public class SubscriptionInfoUpdater extends Handler { if (DBG) logd("SubInfo Initialized"); sIsSubInfoInitialized = true; mSubscriptionController.notifySubInfoReady(); - MultiSimSettingController.getInstance().notifyAllSubscriptionLoaded(); } + MultiSimSettingController.getInstance().notifyAllSubscriptionLoaded(); } /** diff --git a/src/java/com/android/internal/telephony/dataconnection/ApnContext.java b/src/java/com/android/internal/telephony/dataconnection/ApnContext.java index 8d7bc0657e..49eb7c5db4 100644 --- a/src/java/com/android/internal/telephony/dataconnection/ApnContext.java +++ b/src/java/com/android/internal/telephony/dataconnection/ApnContext.java @@ -430,9 +430,7 @@ public class ApnContext { public void releaseNetwork(NetworkRequest networkRequest, @ReleaseNetworkType int type) { synchronized (mRefCountLock) { - if (mNetworkRequests.contains(networkRequest) == false) { - logl("releaseNetwork can't find this request (" + networkRequest + ")"); - } else { + if (mNetworkRequests.contains(networkRequest)) { mNetworkRequests.remove(networkRequest); if (mDataConnection != null) { // New network request added. Should re-evaluate properties of diff --git a/src/java/com/android/internal/telephony/dataconnection/DataConnection.java b/src/java/com/android/internal/telephony/dataconnection/DataConnection.java index 8a892222f2..6ddc6fec82 100644 --- a/src/java/com/android/internal/telephony/dataconnection/DataConnection.java +++ b/src/java/com/android/internal/telephony/dataconnection/DataConnection.java @@ -314,6 +314,10 @@ public class DataConnection extends StateMachine { private boolean mUnmeteredOverride; private int mRilRat = ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN; private int mDataRegState = Integer.MAX_VALUE; + // Indicating data connection is suspended due to temporary reasons, for example, out of + // service, concurrency voice/data not supported, etc.. Note this flag is only meaningful when + // data is in active state. When data is in inactive, connecting, or disconnecting, this flag + // is unmeaningful. private boolean mIsSuspended; private int mDownlinkBandwidth = 14; private int mUplinkBandwidth = 14; @@ -487,28 +491,6 @@ public class DataConnection extends StateMachine { return new LinkProperties(mLinkProperties); } - boolean isSuspended() { - // Data can only be (temporarily) suspended while data is in active state - if (getCurrentState() != mActiveState) return false; - - // never set suspend for emergency apn - if (mApnSetting != null && mApnSetting.isEmergencyApn()) { - return false; - } - - // if we are not in-service change to SUSPENDED - if (mDataRegState != ServiceState.STATE_IN_SERVICE) { - return true; - } - - // check for voice call and concurrency issues - if (!mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) { - return mPhone.getCallTracker().getState() != PhoneConstants.State.IDLE; - } - - return false; - } - boolean isDisconnecting() { return getCurrentState() == mDisconnectingState || getCurrentState() == mDisconnectingErrorCreatingConnection; @@ -1055,6 +1037,8 @@ public class DataConnection extends StateMachine { } if (srcDc == null) { + loge("requestHandover: Cannot find source data connection."); + onRquestHandoverFailed(cp); return; } @@ -1066,8 +1050,7 @@ public class DataConnection extends StateMachine { mHandoverSourceNetworkAgent = srcDc.getNetworkAgent(); if (mHandoverSourceNetworkAgent == null) { loge("requestHandover: Cannot get network agent from the source dc " + srcDc.getName()); - notifyConnectCompleted(cp, DataFailCause.UNKNOWN, - DataCallResponse.HANDOVER_FAILURE_MODE_UNKNOWN, false); + onRquestHandoverFailed(cp); return; } @@ -1107,7 +1090,8 @@ public class DataConnection extends StateMachine { /** * Called on the source data connection from the target data connection. */ - private void startHandover(Consumer<Integer> onTargetDcComplete) { + @VisibleForTesting + public void startHandover(Consumer<Integer> onTargetDcComplete) { logd("startHandover: " + toStringSimple()); // Set the handover state to being transferred on "this" data connection which is the src. setHandoverState(HANDOVER_STATE_BEING_TRANSFERRED); @@ -1340,7 +1324,7 @@ public class DataConnection extends StateMachine { /** * Clear all settings called when entering mInactiveState. */ - private void clearSettings() { + private synchronized void clearSettings() { if (DBG) log("clearSettings"); mCreateTime = -1; @@ -1417,6 +1401,7 @@ public class DataConnection extends StateMachine { } else if (cp.mApnContext.getApnTypeBitmask() == ApnSetting.TYPE_ENTERPRISE && !mDcController.isDefaultDataActive()) { if (DBG) log("No default data connection currently active"); + mCid = response.getId(); result = SetupResult.ERROR_NO_DEFAULT_CONNECTION; result.mFailCause = DataFailCause.NO_DEFAULT_DATA; } else { @@ -2338,8 +2323,8 @@ public class DataConnection extends StateMachine { mRilRat = drsRatPair.second; if (DBG) { log("DcDefaultState: EVENT_DATA_CONNECTION_DRS_OR_RAT_CHANGED" - + " drs=" + mDataRegState - + " mRilRat=" + mRilRat); + + " regState=" + ServiceState.rilServiceStateToString(mDataRegState) + + " RAT=" + ServiceState.rilRadioTechnologyToString(mRilRat)); } mDataCallSessionStats.onDrsOrRatChanged(mRilRat); break; @@ -2422,12 +2407,28 @@ public class DataConnection extends StateMachine { Rlog.d(getName(), "Setting suspend state without a NetworkAgent"); } - boolean suspended = isSuspended(); - if (mIsSuspended != suspended) { - mIsSuspended = suspended; + boolean newSuspendedState = false; + // Data can only be (temporarily) suspended while data is in active state + if (getCurrentState() == mActiveState) { + // Never set suspended for emergency apn. Emergency data connection + // can work while device is not in service. + if (mApnSetting != null && mApnSetting.isEmergencyApn()) { + newSuspendedState = false; + // If we are not in service, change to suspended. + } else if (mDataRegState != ServiceState.STATE_IN_SERVICE) { + newSuspendedState = true; + // Check voice/data concurrency. + } else if (!mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) { + newSuspendedState = mPhone.getCallTracker().getState() != PhoneConstants.State.IDLE; + } + } + + // Only notify when there is a change. + if (mIsSuspended != newSuspendedState) { + mIsSuspended = newSuspendedState; // If data connection is active, we need to notify the new data connection state - // changed event. + // changed event reflecting the latest suspended state. if (isActive()) { notifyDataConnectionState(); } @@ -3829,7 +3830,7 @@ public class DataConnection extends StateMachine { } /** Doesn't print mApnList of ApnContext's which would be recursive */ - public String toStringSimple() { + public synchronized String toStringSimple() { return getName() + ": State=" + getCurrentState().getName() + " mApnSetting=" + mApnSetting + " RefCount=" + mApnContexts.size() + " mCid=" + mCid + " mCreateTime=" + mCreateTime @@ -3940,7 +3941,7 @@ public class DataConnection extends StateMachine { return TelephonyManager.DATA_CONNECTING; } else if (isActive()) { // The data connection can only be suspended when it's in active state. - if (isSuspended()) { + if (mIsSuspended) { return TelephonyManager.DATA_SUSPENDED; } return TelephonyManager.DATA_CONNECTED; diff --git a/src/java/com/android/internal/telephony/dataconnection/DcController.java b/src/java/com/android/internal/telephony/dataconnection/DcController.java index 8d60477004..2156e663cd 100644 --- a/src/java/com/android/internal/telephony/dataconnection/DcController.java +++ b/src/java/com/android/internal/telephony/dataconnection/DcController.java @@ -29,6 +29,7 @@ import android.telephony.DataFailCause; import android.telephony.data.ApnSetting; import android.telephony.data.DataCallResponse; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.telephony.DctConstants; import com.android.internal.telephony.Phone; import com.android.internal.telephony.dataconnection.DataConnection.UpdateLinkPropertyResult; @@ -89,8 +90,8 @@ public class DcController extends Handler { /** * Aggregated physical link state from all data connections. This reflects the device's RRC * connection state. - * // TODO: Instead of tracking the RRC state here, we should make PhysicalChannelConfig work in - * S. + * If {@link CarrierConfigManager.KEY_LTE_ENDC_USING_USER_DATA_FOR_RRC_DETECTION_BOOL} is true, + * then This reflects "internet data connection" instead of RRC state. */ private @PhysicalLinkState int mPhysicalLinkState = PHYSICAL_LINK_UNKNOWN; @@ -229,6 +230,7 @@ public class DcController extends Handler { boolean isAnyDataCallDormant = false; boolean isAnyDataCallActive = false; + boolean isInternetDataCallActive = false; for (DataCallResponse newState : dcsList) { @@ -249,6 +251,11 @@ public class DcController extends Handler { log("onDataStateChanged: Found ConnId=" + newState.getId() + " newState=" + newState.toString()); } + if (apnContexts.stream().anyMatch( + i -> ApnSetting.TYPE_DEFAULT_STRING.equals(i.getApnType())) + && newState.getLinkStatus() == DataConnActiveStatus.ACTIVE) { + isInternetDataCallActive = true; + } if (newState.getLinkStatus() == DataConnActiveStatus.INACTIVE) { if (mDct.isCleanupRequired.get()) { apnsToCleanup.addAll(apnContexts); @@ -360,8 +367,12 @@ public class DcController extends Handler { if (mDataServiceManager.getTransportType() == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) { - int physicalLinkState = isAnyDataCallActive - ? PHYSICAL_LINK_ACTIVE : PHYSICAL_LINK_NOT_ACTIVE; + boolean isPhysicalLinkStateFocusingOnInternetData = + mDct.getLteEndcUsingUserDataForIdleDetection(); + int physicalLinkState = + (isPhysicalLinkStateFocusingOnInternetData + ? isInternetDataCallActive : isAnyDataCallActive) + ? PHYSICAL_LINK_ACTIVE : PHYSICAL_LINK_NOT_ACTIVE; if (mPhysicalLinkState != physicalLinkState) { mPhysicalLinkState = physicalLinkState; mPhysicalLinkStateChangedRegistrants.notifyResult(mPhysicalLinkState); @@ -409,11 +420,13 @@ public class DcController extends Handler { /** * Register for physical link state (i.e. RRC state) changed event. - * + * if {@link CarrierConfigManager.KEY_LTE_ENDC_USING_USER_DATA_FOR_RRC_DETECTION_BOOL} is true, + * then physical link state is focusing on "internet data connection" instead of RRC state. * @param h The handler * @param what The event */ - void registerForPhysicalLinkStateChanged(Handler h, int what) { + @VisibleForTesting + public void registerForPhysicalLinkStateChanged(Handler h, int what) { mPhysicalLinkStateChangedRegistrants.addUnique(h, what, null); } diff --git a/src/java/com/android/internal/telephony/dataconnection/DcNetworkAgent.java b/src/java/com/android/internal/telephony/dataconnection/DcNetworkAgent.java index d7d7edc41f..ab04bc1302 100644 --- a/src/java/com/android/internal/telephony/dataconnection/DcNetworkAgent.java +++ b/src/java/com/android/internal/telephony/dataconnection/DcNetworkAgent.java @@ -86,7 +86,7 @@ public class DcNetworkAgent extends NetworkAgent { public final DcKeepaliveTracker keepaliveTracker = new DcKeepaliveTracker(); - private final QosCallbackTracker mQosCallbackTracker = new QosCallbackTracker(this); + private final QosCallbackTracker mQosCallbackTracker; private final Executor mQosCallbackExecutor = Executors.newSingleThreadExecutor(); @@ -119,6 +119,7 @@ public class DcNetworkAgent extends NetworkAgent { } else { loge("The connection does not have a valid link properties."); } + mQosCallbackTracker = new QosCallbackTracker(this); } private @NetworkType int getNetworkType() { diff --git a/src/java/com/android/internal/telephony/dataconnection/DcTracker.java b/src/java/com/android/internal/telephony/dataconnection/DcTracker.java index 8def434c69..5bfe15bca7 100644 --- a/src/java/com/android/internal/telephony/dataconnection/DcTracker.java +++ b/src/java/com/android/internal/telephony/dataconnection/DcTracker.java @@ -352,6 +352,9 @@ public class DcTracker extends Handler { private boolean mNrSaSub6Unmetered = false; private boolean mNrNsaRoamingUnmetered = false; + // it effect the PhysicalLinkStateChanged + private boolean mLteEndcUsingUserDataForRrcDetection = false; + // stats per data call recovery event private DataStallRecoveryStats mDataStallRecoveryStats; @@ -1647,7 +1650,12 @@ public class DcTracker extends Handler { if (DBG) log(str.toString()); apnContext.requestLog(str.toString()); if (requestType == REQUEST_TYPE_HANDOVER) { - sendHandoverCompleteMessages(apnContext.getApnTypeBitmask(), false, false); + // If fails due to latest preference already changed back to source transport, then + // just fallback (will not attempt handover anymore, and will not tear down the + // data connection on source transport. + boolean fallback = dataConnectionReasons.contains( + DataDisallowedReasonType.ON_OTHER_TRANSPORT); + sendHandoverCompleteMessages(apnContext.getApnTypeBitmask(), false, fallback); } return; } @@ -2311,6 +2319,7 @@ public class DcTracker extends Handler { // TODO: It'd be nice to only do this if the changed entrie(s) // match the current operator. if (DBG) log("onApnChanged: createAllApnList and cleanUpAllConnections"); + mDataThrottler.reset(); setDefaultPreferredApnIfNeeded(); createAllApnList(); setDataProfilesAsNeeded(); @@ -2538,6 +2547,7 @@ public class DcTracker extends Handler { if (mSimState == TelephonyManager.SIM_STATE_ABSENT) { onSimAbsent(); } else if (mSimState == TelephonyManager.SIM_STATE_LOADED) { + mDataThrottler.reset(); if (mConfigReady) { createAllApnList(); setDataProfilesAsNeeded(); @@ -3676,7 +3686,15 @@ public class DcTracker extends Handler { if (mPreferredApn.getOperatorNumeric().equals(operator)) { if (mPreferredApn.canSupportNetworkType( ServiceState.rilRadioTechnologyToNetworkType(radioTech))) { - apnList.add(mPreferredApn); + // Create a new instance of ApnSetting for ENTERPRISE because each + // DataConnection should have its own ApnSetting. ENTERPRISE uses the same + // APN as DEFAULT but is a separate DataConnection + if (ApnSetting.getApnTypesBitmaskFromString(requestedApnType) + == ApnSetting.TYPE_ENTERPRISE) { + apnList.add(ApnSetting.makeApnSetting(mPreferredApn)); + } else { + apnList.add(mPreferredApn); + } if (DBG) log("buildWaitingApns: X added preferred apnList=" + apnList); return apnList; } @@ -3695,7 +3713,15 @@ public class DcTracker extends Handler { if (apn.getApnSetId() == Telephony.Carriers.MATCH_ALL_APN_SET_ID || preferredApnSetId == apn.getApnSetId()) { if (VDBG) log("buildWaitingApns: adding apn=" + apn); - apnList.add(apn); + // Create a new instance of ApnSetting for ENTERPRISE because each + // DataConnection should have its own ApnSetting. ENTERPRISE uses the same + // APN as DEFAULT but is a separate DataConnection + if (ApnSetting.getApnTypesBitmaskFromString(requestedApnType) + == ApnSetting.TYPE_ENTERPRISE) { + apnList.add(ApnSetting.makeApnSetting(apn)); + } else { + apnList.add(apn); + } } else { log("buildWaitingApns: APN set id " + apn.getApnSetId() + " does not match the preferred set id " + preferredApnSetId); @@ -5575,13 +5601,21 @@ public class DcTracker extends Handler { CarrierConfigManager.KEY_UNMETERED_NR_SA_SUB6_BOOL); mNrNsaRoamingUnmetered = b.getBoolean( CarrierConfigManager.KEY_UNMETERED_NR_NSA_WHEN_ROAMING_BOOL); + mLteEndcUsingUserDataForRrcDetection = b.getBoolean( + CarrierConfigManager.KEY_LTE_ENDC_USING_USER_DATA_FOR_RRC_DETECTION_BOOL); } } updateLinkBandwidths(bandwidths, useLte); } + public boolean getLteEndcUsingUserDataForIdleDetection() { + return mLteEndcUsingUserDataForRrcDetection; + } + /** * Register for physical link state (i.e. RRC state) changed event. + * if {@link CarrierConfigManager.KEY_LTE_ENDC_USING_USER_DATA_FOR_RRC_DETECTION_BOOL} is true, + * then physical link state is focusing on "internet data connection" instead of RRC state. * * @param h The handler * @param what The event diff --git a/src/java/com/android/internal/telephony/dataconnection/QosCallbackTracker.java b/src/java/com/android/internal/telephony/dataconnection/QosCallbackTracker.java index d1021fea28..795ed147b4 100644 --- a/src/java/com/android/internal/telephony/dataconnection/QosCallbackTracker.java +++ b/src/java/com/android/internal/telephony/dataconnection/QosCallbackTracker.java @@ -44,7 +44,7 @@ import java.util.Map; * {@hide} */ public class QosCallbackTracker { - private static final String LOG_TAG = QosCallbackTracker.class.getSimpleName(); + @NonNull private final String mTag; @NonNull private final DcNetworkAgent mDcNetworkAgent; @NonNull private final Map<Integer, QosBearerSession> mQosBearerSessions; @@ -59,6 +59,7 @@ public class QosCallbackTracker { mQosBearerSessions = new HashMap<>(); mCallbacksToFilter = new HashMap<>(); mDcNetworkAgent = dcNetworkAgent; + mTag = "QosCallbackTracker" + "-" + mDcNetworkAgent.getNetwork().getNetId(); } /** @@ -206,15 +207,19 @@ public class QosCallbackTracker { for (final QosBearerFilter sessionFilter : qosBearerSession.getQosBearerFilterList()) { if (!sessionFilter.getLocalAddresses().isEmpty() - && !sessionFilter.getRemoteAddresses().isEmpty()) { + && !sessionFilter.getRemoteAddresses().isEmpty() + && sessionFilter.getLocalPortRange().isValid() + && sessionFilter.getRemotePortRange().isValid()) { if (matchesByRemoteAndLocalAddress(sessionFilter, filter)) { qosFilter = getFilterByPrecedence(qosFilter, sessionFilter); } - } else if (!sessionFilter.getRemoteAddresses().isEmpty()) { + } else if (!sessionFilter.getRemoteAddresses().isEmpty() + && sessionFilter.getRemotePortRange().isValid()) { if (matchesByRemoteAddress(sessionFilter, filter)) { qosFilter = getFilterByPrecedence(qosFilter, sessionFilter); } - } else if (!sessionFilter.getLocalAddresses().isEmpty()) { + } else if (!sessionFilter.getLocalAddresses().isEmpty() + && sessionFilter.getLocalPortRange().isValid()) { if (matchesByLocalAddress(sessionFilter, filter)) { qosFilter = getFilterByPrecedence(qosFilter, sessionFilter); } @@ -256,12 +261,15 @@ public class QosCallbackTracker { mDcNetworkAgent.notifyQosSessionAvailable( callbackId, session.getQosBearerSessionId(), nrQosAttr); } + + logd("sendSessionAvailable, callbackId=" + callbackId); } private void sendSessionLost(final int callbackId, @NonNull final QosBearerSession session) { mDcNetworkAgent.notifyQosSessionLost(callbackId, session.getQosBearerSessionId(), session.getQos() instanceof EpsQos ? QosSession.TYPE_EPS_BEARER : QosSession.TYPE_NR_BEARER); + logd("sendSessionLost, callbackId=" + callbackId); } public interface IFilter { @@ -275,6 +283,6 @@ public class QosCallbackTracker { * @param s is string log */ private void logd(String s) { - Rlog.d(LOG_TAG, s); + Rlog.d(mTag, s); } } diff --git a/src/java/com/android/internal/telephony/dataconnection/TelephonyNetworkFactory.java b/src/java/com/android/internal/telephony/dataconnection/TelephonyNetworkFactory.java index 2889e7aa6c..279da92b67 100644 --- a/src/java/com/android/internal/telephony/dataconnection/TelephonyNetworkFactory.java +++ b/src/java/com/android/internal/telephony/dataconnection/TelephonyNetworkFactory.java @@ -338,8 +338,16 @@ public class TelephonyNetworkFactory extends NetworkFactory { logl("onReleaseNetworkFor " + networkRequest + " applied " + applied); if (applied) { - int transport = getTransportTypeFromNetworkRequest(networkRequest); - releaseNetworkInternal(networkRequest, DcTracker.RELEASE_TYPE_NORMAL, transport); + // Most of the time, the network request only exists in one of the DcTracker, but in the + // middle of handover, the network request temporarily exists in both DcTrackers. If + // connectivity service releases the network request while handover is ongoing, we need + // to remove network requests from both DcTrackers. + // Note that this part will be refactored in T, where we won't even have DcTracker at + // all. + releaseNetworkInternal(networkRequest, DcTracker.RELEASE_TYPE_NORMAL, + AccessNetworkConstants.TRANSPORT_TYPE_WWAN); + releaseNetworkInternal(networkRequest, DcTracker.RELEASE_TYPE_NORMAL, + AccessNetworkConstants.TRANSPORT_TYPE_WLAN); } } @@ -442,12 +450,6 @@ public class TelephonyNetworkFactory extends NetworkFactory { if (mNetworkRequests.containsKey(networkRequest)) { // Update it with the target transport. mNetworkRequests.put(networkRequest, targetTransport); - } else { - log("Network request was released before handover is completed. Now" - + " we need to release this network request. " - + networkRequest); - releaseNetworkInternal(networkRequest, DcTracker.RELEASE_TYPE_NORMAL, - targetTransport); } } else { // If handover fails and requires to fallback, the context of target transport needs to diff --git a/src/java/com/android/internal/telephony/emergency/EmergencyNumberTracker.java b/src/java/com/android/internal/telephony/emergency/EmergencyNumberTracker.java index 0226b072c5..fb892e4fd4 100644 --- a/src/java/com/android/internal/telephony/emergency/EmergencyNumberTracker.java +++ b/src/java/com/android/internal/telephony/emergency/EmergencyNumberTracker.java @@ -457,7 +457,7 @@ public class EmergencyNumberTracker extends Handler { } EmergencyNumber.mergeSameNumbersInEmergencyNumberList(updatedAssetEmergencyNumberList); } catch (IOException ex) { - loge("Cache asset emergency database failure: " + ex); + logw("Cache asset emergency database failure: " + ex); } finally { // close quietly by catching non-runtime exceptions. if (inputStream != null) { @@ -1143,6 +1143,10 @@ public class EmergencyNumberTracker extends Handler { Rlog.d(TAG, str); } + private static void logw(String str) { + Rlog.w(TAG, str); + } + private static void loge(String str) { Rlog.e(TAG, str); } diff --git a/src/java/com/android/internal/telephony/gsm/GsmMmiCode.java b/src/java/com/android/internal/telephony/gsm/GsmMmiCode.java index d2f0387d55..dec246836c 100644 --- a/src/java/com/android/internal/telephony/gsm/GsmMmiCode.java +++ b/src/java/com/android/internal/telephony/gsm/GsmMmiCode.java @@ -1228,7 +1228,7 @@ public final class GsmMmiCode extends Handler implements MmiCode { onUssdFinishedError() { if (mState == State.PENDING) { mState = State.FAILED; - if (mMessage.length() == 0) { + if (TextUtils.isEmpty(mMessage)) { mMessage = mContext.getText(com.android.internal.R.string.mmiError); } Rlog.d(LOG_TAG, "onUssdFinishedError"); diff --git a/src/java/com/android/internal/telephony/ims/ImsResolver.java b/src/java/com/android/internal/telephony/ims/ImsResolver.java index d990da494d..188e69582c 100644 --- a/src/java/com/android/internal/telephony/ims/ImsResolver.java +++ b/src/java/com/android/internal/telephony/ims/ImsResolver.java @@ -274,15 +274,18 @@ public class ImsResolver implements ImsServiceController.ImsServiceControllerCal SubscriptionManager.INVALID_SUBSCRIPTION_ID); int slotSimState = mTelephonyManagerProxy.getSimState(mContext, slotId); if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID - && slotSimState != TelephonyManager.SIM_STATE_ABSENT) { + && (slotSimState != TelephonyManager.SIM_STATE_ABSENT + && slotSimState != TelephonyManager.SIM_STATE_NOT_READY)) { // We only care about carrier config updates that happen when a slot is known to be - // absent or populated and the carrier config has been loaded. + // absent, the subscription is disabled (not ready), or populated and the carrier + // config has been loaded. Log.i(TAG, "Received CCC for slot " + slotId + " and sim state " + slotSimState + ", ignoring."); return; } - Log.i(TAG, "Received Carrier Config Changed for SlotId: " + slotId); + Log.i(TAG, "Received Carrier Config Changed for SlotId: " + slotId + + ", sim state: " + slotSimState); mHandler.obtainMessage(HANDLER_CONFIG_CHANGED, slotId).sendToTarget(); } diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhone.java b/src/java/com/android/internal/telephony/imsphone/ImsPhone.java index 028f3c2500..786bbcfea2 100644 --- a/src/java/com/android/internal/telephony/imsphone/ImsPhone.java +++ b/src/java/com/android/internal/telephony/imsphone/ImsPhone.java @@ -919,7 +919,9 @@ public class ImsPhone extends ImsPhoneBase { } @Override - public Connection dial(String dialString, DialArgs dialArgs) throws CallStateException { + public Connection dial(String dialString, DialArgs dialArgs, + Consumer<Phone> chosenPhoneConsumer) throws CallStateException { + chosenPhoneConsumer.accept(this); return dialInternal(dialString, dialArgs, null); } @@ -1580,7 +1582,31 @@ public class ImsPhone extends ImsPhoneBase { new SilentRedialParam(mLastDialString, cause, dialArgs), null); if (ar != null) { - mSilentRedialRegistrants.notifyRegistrants(ar); + // There is a race condition that can happen in some cases: + // (Main thread) dial start + // (Binder Thread) onCallSessionFailed + // (Binder Thread) schedule a redial for CS on the main thread + // (Main Thread) dial finish + // (Main Thread) schedule to associate ImsPhoneConnection with + // GsmConnection on the main thread + // If scheduling the CS redial occurs before the command to schedule the + // ImsPhoneConnection to be associated with the GsmConnection, the CS redial will occur + // before GsmConnection has had callbacks to ImsPhone correctly updated. This will cause + // Callbacks back to GsmCdmaPhone to never be set up correctly and we will lose track of + // the instance. + // Instead, schedule this redial to happen on the main thread, so that we know dial has + // finished before scheduling a redial: + // (Main thread) dial start + // (Binder Thread) onCallSessionFailed -> move notify registrants to main thread + // (Main Thread) dial finish + // (Main Thread) schedule on main thread to associate ImsPhoneConnection with + // GsmConnection + // (Main Thread) schedule a redial for CS + mContext.getMainExecutor().execute(() -> { + logd("initiateSilentRedial: notifying registrants, isEmergency=" + isEmergency + + ", eccCategory=" + eccCategory); + mSilentRedialRegistrants.notifyRegistrants(ar); + }); } } @@ -1888,6 +1914,7 @@ public class ImsPhone extends ImsPhoneBase { } break; case EVENT_INITIATE_VOLTE_SILENT_REDIAL: { + // This is a CS -> IMS redial if (VDBG) logd("EVENT_INITIATE_VOLTE_SILENT_REDIAL"); ar = (AsyncResult) msg.obj; if (ar.exception == null && ar.result != null) { @@ -1900,6 +1927,10 @@ public class ImsPhone extends ImsPhoneBase { try { Connection cn = dial(dialString, updateDialArgsForVolteSilentRedial(dialArgs, causeCode)); + // The GSM/CDMA Connection that is owned by the GsmCdmaPhone is currently + // the one with a callback registered to TelephonyConnection. Notify the + // redial happened over that Phone so that it can be replaced with the + // new ImsPhoneConnection. Rlog.d(LOG_TAG, "Notify volte redial connection changed cn: " + cn); if (mDefaultPhone != null) { // don't care it is null or not. diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java index 7b1f020169..f67082a3da 100755 --- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java +++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java @@ -2715,6 +2715,7 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall { case ImsReasonInfo.CODE_LOCAL_CALL_DECLINE: case ImsReasonInfo.CODE_REMOTE_CALL_DECLINE: + case ImsReasonInfo.CODE_REJECTED_ELSEWHERE: // If the call has been declined locally (on this device), or on remotely (on // another device using multiendpoint functionality), mark it as rejected. return DisconnectCause.INCOMING_REJECTED; diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneMmiCode.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneMmiCode.java index 87790a3a6d..359079d560 100644 --- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneMmiCode.java +++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneMmiCode.java @@ -346,8 +346,8 @@ public final class ImsPhoneMmiCode extends Handler implements MmiCode { return dialString; } - static ImsPhoneMmiCode - newNetworkInitiatedUssd(String ussdMessage, boolean isUssdRequest, ImsPhone phone) { + public static ImsPhoneMmiCode newNetworkInitiatedUssd(String ussdMessage, + boolean isUssdRequest, ImsPhone phone) { ImsPhoneMmiCode ret; ret = new ImsPhoneMmiCode(phone); @@ -1194,12 +1194,10 @@ public final class ImsPhoneMmiCode extends Handler implements MmiCode { * * The radio has reset, and this is still pending */ - - void - onUssdFinishedError() { + public void onUssdFinishedError() { if (mState == State.PENDING) { mState = State.FAILED; - if (mMessage.length() == 0) { + if (TextUtils.isEmpty(mMessage)) { mMessage = mContext.getText(com.android.internal.R.string.mmiError); } Rlog.d(LOG_TAG, "onUssdFinishedError: mmi=" + this); diff --git a/src/java/com/android/internal/telephony/sip/SipPhone.java b/src/java/com/android/internal/telephony/sip/SipPhone.java index c88d01f9bb..4ac1ddf3ae 100755 --- a/src/java/com/android/internal/telephony/sip/SipPhone.java +++ b/src/java/com/android/internal/telephony/sip/SipPhone.java @@ -43,6 +43,7 @@ import com.android.internal.telephony.PhoneNotifier; import com.android.telephony.Rlog; import java.text.ParseException; +import java.util.function.Consumer; import java.util.regex.Pattern; /** @@ -192,7 +193,9 @@ public class SipPhone extends SipPhoneBase { } @Override - public Connection dial(String dialString, DialArgs dialArgs) throws CallStateException { + public Connection dial(String dialString, DialArgs dialArgs, + Consumer<Phone> chosenPhoneConsumer) throws CallStateException { + chosenPhoneConsumer.accept(this); synchronized (SipPhone.class) { return dialInternal(dialString, dialArgs.videoState); } diff --git a/src/java/com/android/internal/telephony/uicc/PinStorage.java b/src/java/com/android/internal/telephony/uicc/PinStorage.java index b348c61286..28851247c2 100644 --- a/src/java/com/android/internal/telephony/uicc/PinStorage.java +++ b/src/java/com/android/internal/telephony/uicc/PinStorage.java @@ -250,14 +250,14 @@ public class PinStorage extends Handler { } /** - * Return the cached pin for the {@code slotId}, or an empty string if it is not available. + * Return the cached pin for the SIM card identified by {@code slotId} and {@code iccid}, or + * an empty string if it is not available. * * The method returns the PIN only if the state is VERIFICATION_READY. If the PIN is found, * its state changes to AVAILABLE, so that it cannot be retrieved a second time during the * same boot cycle. If the PIN verification fails, it will be removed after the failed attempt. */ - public synchronized String getPin(int slotId) { - String iccid = getIccid(slotId); + public synchronized String getPin(int slotId, String iccid) { if (!validateSlotId(slotId) || !validateIccid(iccid)) { return ""; } @@ -874,7 +874,7 @@ public class PinStorage extends Handler { private void verifyPendingPin(int slotId) { // We intentionally invoke getPin() here, as it updates the status and makes sure that // same PIN is not used more than once - String pin = getPin(slotId); + String pin = getPin(slotId, getIccid(slotId)); if (pin.isEmpty()) { // PIN is not available for verification: return. return; diff --git a/src/java/com/android/internal/telephony/uicc/UiccController.java b/src/java/com/android/internal/telephony/uicc/UiccController.java index 36cad5f442..fb64b6a3cc 100644 --- a/src/java/com/android/internal/telephony/uicc/UiccController.java +++ b/src/java/com/android/internal/telephony/uicc/UiccController.java @@ -1103,12 +1103,16 @@ public class UiccController extends Handler { options.toBundle()); } - private boolean slotStatusChanged(ArrayList<IccSlotStatus> slotStatusList) { + /** + * Check if slot status has changed from the last received one + */ + @VisibleForTesting + public boolean slotStatusChanged(ArrayList<IccSlotStatus> slotStatusList) { if (sLastSlotStatus == null || sLastSlotStatus.size() != slotStatusList.size()) { return true; } - for (IccSlotStatus iccSlotStatus : slotStatusList) { - if (!sLastSlotStatus.contains(iccSlotStatus)) { + for (int i = 0; i < slotStatusList.size(); i++) { + if (!sLastSlotStatus.get(i).equals(slotStatusList.get(i))) { return true; } } diff --git a/src/java/com/android/internal/telephony/uicc/UiccPkcs15.java b/src/java/com/android/internal/telephony/uicc/UiccPkcs15.java index 12540672ea..9543908e71 100644 --- a/src/java/com/android/internal/telephony/uicc/UiccPkcs15.java +++ b/src/java/com/android/internal/telephony/uicc/UiccPkcs15.java @@ -90,7 +90,7 @@ public class UiccPkcs15 extends Handler { private void readBinary() { if (mChannelId >=0 ) { mUiccProfile.iccTransmitApduLogicalChannel(mChannelId, 0x00, 0xB0, 0x00, 0x00, 0x00, - mFileId, obtainMessage(EVENT_READ_BINARY_DONE)); + "", obtainMessage(EVENT_READ_BINARY_DONE)); } else { log("EF based"); } diff --git a/src/java/com/android/internal/telephony/uicc/UiccProfile.java b/src/java/com/android/internal/telephony/uicc/UiccProfile.java index 6d5357f74b..9b601853f3 100644 --- a/src/java/com/android/internal/telephony/uicc/UiccProfile.java +++ b/src/java/com/android/internal/telephony/uicc/UiccProfile.java @@ -121,6 +121,7 @@ public class UiccProfile extends IccCard { private RegistrantList mOperatorBrandOverrideRegistrants = new RegistrantList(); private final int mPhoneId; + private final PinStorage mPinStorage; private static final int EVENT_RADIO_OFF_OR_UNAVAILABLE = 1; private static final int EVENT_ICC_LOCKED = 2; @@ -280,7 +281,7 @@ public class UiccProfile extends IccCard { // An error occurred during automatic PIN verification. At this point, // clear the cache and propagate the state. loge("An error occurred during internal PIN verification"); - UiccController.getInstance().getPinStorage().clearPin(mPhoneId); + mPinStorage.clearPin(mPhoneId); updateExternalState(); } else { log("Internal PIN verification was successful!"); @@ -320,6 +321,7 @@ public class UiccProfile extends IccCard { // for RadioConfig<1.2 eid is not known when the EuiccCard is constructed ((EuiccCard) mUiccCard).registerForEidReady(mHandler, EVENT_EID_READY, null); } + mPinStorage = UiccController.getInstance().getPinStorage(); update(c, ci, ics); ci.registerForOffOrNotAvailable(mHandler, EVENT_RADIO_OFF_OR_UNAVAILABLE, null); @@ -521,6 +523,9 @@ public class UiccProfile extends IccCard { } } + /** + * ICC availability/state changed. Update corresponding fields and external state if needed. + */ private void updateIccAvailability(boolean allAppsChanged) { synchronized (mLock) { UiccCardApplication newApp; @@ -625,7 +630,7 @@ public class UiccProfile extends IccCard { // If the PIN code is required and an available cached PIN is available, intercept // the update of external state and perform an internal PIN verification. if (lockedState == IccCardConstants.State.PIN_REQUIRED) { - String pin = UiccController.getInstance().getPinStorage().getPin(mPhoneId); + String pin = mPinStorage.getPin(mPhoneId, mIccRecords.getFullIccId()); if (!pin.isEmpty()) { log("PIN_REQUIRED[" + mPhoneId + "] - Cache present"); mCi.supplyIccPin(pin, mHandler.obtainMessage(EVENT_SUPPLY_ICC_PIN_DONE)); diff --git a/tests/telephonytests/src/android/telephony/ims/SipTransportImplBaseTest.java b/tests/telephonytests/src/android/telephony/ims/SipTransportImplBaseTest.java new file mode 100644 index 0000000000..11b1d22ae6 --- /dev/null +++ b/tests/telephonytests/src/android/telephony/ims/SipTransportImplBaseTest.java @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony.ims; + +import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertNotNull; +import static junit.framework.Assert.assertTrue; + +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +import android.os.IBinder; +import android.telephony.ims.aidl.ISipDelegate; +import android.telephony.ims.aidl.ISipDelegateMessageCallback; +import android.telephony.ims.aidl.ISipDelegateStateCallback; +import android.telephony.ims.aidl.ISipTransport; +import android.telephony.ims.stub.SipDelegate; +import android.telephony.ims.stub.SipTransportImplBase; +import android.util.ArraySet; + +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; + +import java.util.Collections; +import java.util.Set; +import java.util.concurrent.Executor; + +@RunWith(AndroidJUnit4.class) +public class SipTransportImplBaseTest { + + private static final int TEST_SUB_ID = 1; + + private static class TestSipTransport extends SipTransportImplBase { + + private static class SipDelegateContainer { + public final int subId; + public final DelegateRequest delegateRequest; + public final DelegateStateCallback delegateStateCallback; + public final DelegateMessageCallback delegateMessageCallback; + public final SipDelegate sipDelegate; + + SipDelegateContainer(int subId, DelegateRequest request, + DelegateStateCallback dc, DelegateMessageCallback mc, SipDelegate delegate) { + this.subId = subId; + delegateRequest = request; + delegateStateCallback = dc; + delegateMessageCallback = mc; + sipDelegate = delegate; + } + } + + private final Set<SipDelegateContainer> mDelegates = new ArraySet<>(); + + TestSipTransport(Executor executor) { + super(executor); + } + + @Override + public void createSipDelegate(int subscriptionId, DelegateRequest request, + DelegateStateCallback dc, DelegateMessageCallback mc) { + SipDelegate mockDelegate = mock(SipDelegate.class); + SipDelegateContainer container = new SipDelegateContainer(subscriptionId, request, dc, + mc, mockDelegate); + mDelegates.add(container); + dc.onCreated(mockDelegate, Collections.emptySet()); + } + + @Override + public void destroySipDelegate(SipDelegate delegate, int reason) { + mDelegates.removeIf(candidate -> { + if (delegate.equals(candidate.sipDelegate)) { + candidate.delegateStateCallback.onDestroyed(reason); + return true; + } + return false; + }); + } + + public boolean isTrackedDelegateSetEmpty() { + return mDelegates.isEmpty(); + } + } + + @Test + public void createDestroyDelegate() throws Exception { + // Set up the executor to simply run inline + TestSipTransport t = new TestSipTransport(Runnable::run); + + ISipDelegateStateCallback stateCb = mock(ISipDelegateStateCallback.class); + IBinder stateBinder = mock(IBinder.class); + doReturn(stateBinder).when(stateCb).asBinder(); + ISipDelegateMessageCallback messageCb = mock(ISipDelegateMessageCallback.class); + + ISipDelegate delegate = createSipDelegate(t, stateCb, messageCb); + assertFalse(t.isTrackedDelegateSetEmpty()); + ArgumentCaptor<IBinder.DeathRecipient> captor = + ArgumentCaptor.forClass(IBinder.DeathRecipient.class); + verify(stateBinder).linkToDeath(captor.capture(), anyInt()); + assertNotNull(captor.getValue()); + + destroySipDelegate(t, delegate, + SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP); + verify(stateBinder).unlinkToDeath(eq(captor.getValue()), anyInt()); + verify(stateCb).onDestroyed( + eq(SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP)); + } + + + @Test + public void testPhoneProcessCrash() throws Exception { + // Set up the executor to simply run inline + TestSipTransport t = new TestSipTransport(Runnable::run); + + ISipDelegateStateCallback stateCb = mock(ISipDelegateStateCallback.class); + IBinder stateBinder = mock(IBinder.class); + doReturn(stateBinder).when(stateCb).asBinder(); + ISipDelegateMessageCallback messageCb = mock(ISipDelegateMessageCallback.class); + + createSipDelegate(t, stateCb, messageCb); + assertFalse(t.isTrackedDelegateSetEmpty()); + ArgumentCaptor<IBinder.DeathRecipient> captor = + ArgumentCaptor.forClass(IBinder.DeathRecipient.class); + verify(stateBinder).linkToDeath(captor.capture(), anyInt()); + assertNotNull(captor.getValue()); + IBinder.DeathRecipient recipient = captor.getValue(); + + // simulate phone process crash + recipient.binderDied(stateBinder); + verify(stateCb).onDestroyed(SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_SERVICE_DEAD); + assertTrue(t.isTrackedDelegateSetEmpty()); + } + + private ISipDelegate createSipDelegate(TestSipTransport transport, + ISipDelegateStateCallback stateCb, + ISipDelegateMessageCallback messageCb) throws Exception { + ISipTransport transportBinder = transport.getBinder(); + transportBinder.createSipDelegate(TEST_SUB_ID, new DelegateRequest(Collections.emptySet()), + stateCb, messageCb); + ArgumentCaptor<ISipDelegate> captor = ArgumentCaptor.forClass(ISipDelegate.class); + verify(stateCb).onCreated(captor.capture(), anyList()); + assertNotNull(captor.getValue()); + return captor.getValue(); + } + + private void destroySipDelegate(TestSipTransport transport, ISipDelegate delegate, + int reason) throws Exception { + ISipTransport transportBinder = transport.getBinder(); + transportBinder.destroySipDelegate(delegate, reason); + } +} diff --git a/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java b/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java index f04e69f321..14f5aa14cf 100644 --- a/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java +++ b/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java @@ -424,6 +424,12 @@ public class ContextFixture implements TestFixture<Context> { } @Override + public void sendBroadcast(Intent intent, String receiverPermission, Bundle initialExtras) { + logd("sendBroadcast called for " + intent.getAction()); + sendBroadcast(intent); + } + + @Override public void sendOrderedBroadcast(Intent intent, String receiverPermission) { logd("sendOrderedBroadcast called for " + intent.getAction()); sendBroadcast(intent); diff --git a/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaConnectionTest.java b/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaConnectionTest.java index a5d0b4d790..a078642b1a 100644 --- a/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaConnectionTest.java +++ b/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaConnectionTest.java @@ -278,4 +278,22 @@ public class GsmCdmaConnectionTest extends TelephonyTest { assertEquals(new ArrayList<String>(Arrays.asList(forwardedNumber)), connection.getForwardedNumber()); } + + @Test @SmallTest + public void testForwardedNumberEmptyNull() { + mDC.state = DriverCall.State.INCOMING; + mDC.forwardedNumber = ""; + connection = new GsmCdmaConnection(mPhone, mDC, mCT, 0); + assertNull(connection.getForwardedNumber()); + mDC.forwardedNumber = null; + connection.update(mDC); + assertNull(connection.getForwardedNumber()); + + mDC.forwardedNumber = null; + connection = new GsmCdmaConnection(mPhone, mDC, mCT, 0); + assertNull(connection.getForwardedNumber()); + mDC.forwardedNumber = ""; + connection.update(mDC); + assertNull(connection.getForwardedNumber()); + } } diff --git a/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java b/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java index e56ed3e58f..2f98164b68 100644 --- a/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java +++ b/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java @@ -1622,4 +1622,33 @@ public class GsmCdmaPhoneTest extends TelephonyTest { assertEquals(LinkCapacityEstimate.INVALID, lce3.getUplinkCapacityKbps()); assertEquals(LinkCapacityEstimate.LCE_TYPE_COMBINED, lce3.getType()); } + + @Test + @SmallTest + public void testLoadAllowedNetworksFromSubscriptionDatabase_loadTheNullValue_isLoadedTrue() { + int subId = 1; + doReturn(subId).when(mSubscriptionController).getSubIdUsingPhoneId(anyInt()); + + doReturn(null).when(mSubscriptionController).getSubscriptionProperty(anyInt(), + eq(SubscriptionManager.ALLOWED_NETWORK_TYPES)); + + mPhoneUT.loadAllowedNetworksFromSubscriptionDatabase(); + + assertEquals(true, mPhoneUT.isAllowedNetworkTypesLoadedFromDb()); + } + + @Test + @SmallTest + public void testLoadAllowedNetworksFromSubscriptionDatabase_subIdNotValid_isLoadedFalse() { + int subId = -1; + doReturn(subId).when(mSubscriptionController).getSubIdUsingPhoneId(anyInt()); + + when(mSubscriptionController.getSubscriptionProperty(anyInt(), + eq(SubscriptionManager.ALLOWED_NETWORK_TYPES))).thenReturn(null); + + + mPhoneUT.loadAllowedNetworksFromSubscriptionDatabase(); + + assertEquals(false, mPhoneUT.isAllowedNetworkTypesLoadedFromDb()); + } } diff --git a/tests/telephonytests/src/com/android/internal/telephony/MultiSimSettingControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/MultiSimSettingControllerTest.java index 2551678818..56689f4fc2 100644 --- a/tests/telephonytests/src/com/android/internal/telephony/MultiSimSettingControllerTest.java +++ b/tests/telephonytests/src/com/android/internal/telephony/MultiSimSettingControllerTest.java @@ -210,6 +210,14 @@ public class MultiSimSettingControllerTest extends TelephonyTest { mMultiSimSettingControllerUT.notifyCarrierConfigChanged(1, 2); processAllMessages(); + // Ensure all subscription loaded only updates state once + clearInvocations(mSubControllerMock); + mMultiSimSettingControllerUT.notifyAllSubscriptionLoaded(); + processAllMessages(); + verify(mSubControllerMock, never()).setDefaultDataSubId(anyInt()); + verify(mSubControllerMock, never()).setDefaultVoiceSubId(anyInt()); + verify(mSubControllerMock, never()).setDefaultSmsSubId(anyInt()); + // Notify radio unavailable. replaceInstance(BaseCommands.class, "mState", mSimulatedCommands, TelephonyManager.RADIO_POWER_UNAVAILABLE); diff --git a/tests/telephonytests/src/com/android/internal/telephony/NetworkTypeControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/NetworkTypeControllerTest.java index 1fc5d37e89..be23df8ba1 100644 --- a/tests/telephonytests/src/com/android/internal/telephony/NetworkTypeControllerTest.java +++ b/tests/telephonytests/src/com/android/internal/telephony/NetworkTypeControllerTest.java @@ -302,6 +302,27 @@ public class NetworkTypeControllerTest extends TelephonyTest { } @Test + public void testTransitionToCurrentStateIdle_usingUserDataForRrcDetection() throws Exception { + mBundle.putBoolean( + CarrierConfigManager.KEY_LTE_ENDC_USING_USER_DATA_FOR_RRC_DETECTION_BOOL, true); + doReturn(true).when(mTelephonyManager).isRadioInterfaceCapabilitySupported( + TelephonyManager.CAPABILITY_PHYSICAL_CHANNEL_CONFIG_1_6_SUPPORTED); + mNetworkTypeController = new NetworkTypeController(mPhone, mDisplayInfoController); + broadcastCarrierConfigs(); + processAllMessages(); + assertEquals("DefaultState", getCurrentState().getName()); + doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType(); + doReturn(NetworkRegistrationInfo.NR_STATE_NOT_RESTRICTED).when(mServiceState).getNrState(); + mNetworkTypeController.sendMessage(EVENT_PHYSICAL_LINK_STATE_CHANGED, + new AsyncResult(null, DcController.PHYSICAL_LINK_NOT_ACTIVE, null)); + mNetworkTypeController.sendMessage(NetworkTypeController.EVENT_UPDATE); + + processAllMessages(); + + assertEquals("not_restricted_rrc_idle", getCurrentState().getName()); + } + + @Test public void testTransitionToCurrentStateLteConnected() throws Exception { assertEquals("DefaultState", getCurrentState().getName()); doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType(); @@ -319,6 +340,7 @@ public class NetworkTypeControllerTest extends TelephonyTest { doReturn(true).when(mTelephonyManager).isRadioInterfaceCapabilitySupported( TelephonyManager.CAPABILITY_PHYSICAL_CHANNEL_CONFIG_1_6_SUPPORTED); mNetworkTypeController = new NetworkTypeController(mPhone, mDisplayInfoController); + broadcastCarrierConfigs(); processAllMessages(); assertEquals("DefaultState", getCurrentState().getName()); doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType(); @@ -331,6 +353,28 @@ public class NetworkTypeControllerTest extends TelephonyTest { } @Test + public void testTransitionToCurrentStateLteConnected_usingUserDataForRrcDetection() + throws Exception { + mBundle.putBoolean( + CarrierConfigManager.KEY_LTE_ENDC_USING_USER_DATA_FOR_RRC_DETECTION_BOOL, true); + doReturn(true).when(mTelephonyManager).isRadioInterfaceCapabilitySupported( + TelephonyManager.CAPABILITY_PHYSICAL_CHANNEL_CONFIG_1_6_SUPPORTED); + mNetworkTypeController = new NetworkTypeController(mPhone, mDisplayInfoController); + broadcastCarrierConfigs(); + processAllMessages(); + assertEquals("DefaultState", getCurrentState().getName()); + doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType(); + doReturn(NetworkRegistrationInfo.NR_STATE_NOT_RESTRICTED).when(mServiceState).getNrState(); + mNetworkTypeController.sendMessage(EVENT_PHYSICAL_LINK_STATE_CHANGED, + new AsyncResult(null, DcController.PHYSICAL_LINK_ACTIVE, null)); + mNetworkTypeController.sendMessage(NetworkTypeController.EVENT_UPDATE); + + processAllMessages(); + + assertEquals("not_restricted_rrc_con", getCurrentState().getName()); + } + + @Test public void testTransitionToCurrentStateNrConnected() throws Exception { assertEquals("DefaultState", getCurrentState().getName()); doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType(); @@ -564,6 +608,30 @@ public class NetworkTypeControllerTest extends TelephonyTest { assertEquals("not_restricted_rrc_con", getCurrentState().getName()); } + + @Test + public void testUsingUserDataForRrcDetection_FromNrConnectedMmwaveToLteConnected() + throws Exception { + mBundle.putBoolean( + CarrierConfigManager.KEY_LTE_ENDC_USING_USER_DATA_FOR_RRC_DETECTION_BOOL, true); + doReturn(true).when(mTelephonyManager).isRadioInterfaceCapabilitySupported( + TelephonyManager.CAPABILITY_PHYSICAL_CHANNEL_CONFIG_1_6_SUPPORTED); + mNetworkTypeController = new NetworkTypeController(mPhone, mDisplayInfoController); + broadcastCarrierConfigs(); + processAllMessages(); + testTransitionToCurrentStateNrConnectedMmwave(); + doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType(); + doReturn(NetworkRegistrationInfo.NR_STATE_NOT_RESTRICTED).when(mServiceState).getNrState(); + mNetworkTypeController.sendMessage(EVENT_PHYSICAL_LINK_STATE_CHANGED, + new AsyncResult(null, DcController.PHYSICAL_LINK_ACTIVE, null)); + mNetworkTypeController.sendMessage(EVENT_NR_FREQUENCY_CHANGED); + mNetworkTypeController.sendMessage(EVENT_NR_STATE_CHANGED); + + processAllMessages(); + + assertEquals("not_restricted_rrc_con", getCurrentState().getName()); + } + @Test public void testEventPhysicalChannelChangeFromLteToLteCaInLegacyState() throws Exception { testTransitionToCurrentStateLegacy(); @@ -669,6 +737,26 @@ public class NetworkTypeControllerTest extends TelephonyTest { } @Test + public void testEventPhysicalLinkStateChanged_UsingUserDataForRrcDetection() + throws Exception { + mBundle.putBoolean( + CarrierConfigManager.KEY_LTE_ENDC_USING_USER_DATA_FOR_RRC_DETECTION_BOOL, true); + doReturn(true).when(mTelephonyManager).isRadioInterfaceCapabilitySupported( + TelephonyManager.CAPABILITY_PHYSICAL_CHANNEL_CONFIG_1_6_SUPPORTED); + mNetworkTypeController = new NetworkTypeController(mPhone, mDisplayInfoController); + broadcastCarrierConfigs(); + processAllMessages(); + testTransitionToCurrentStateLteConnected_usingUserDataForRrcDetection(); + doReturn(ServiceState.FREQUENCY_RANGE_MMWAVE).when(mServiceState).getNrFrequencyRange(); + mNetworkTypeController.sendMessage(EVENT_PHYSICAL_LINK_STATE_CHANGED, + new AsyncResult(null, DcController.PHYSICAL_LINK_NOT_ACTIVE, null)); + + processAllMessages(); + + assertEquals("not_restricted_rrc_idle", getCurrentState().getName()); + } + + @Test public void testEventPhysicalChannelConfigNotifChanged() throws Exception { testTransitionToCurrentStateNrConnected(); assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA, diff --git a/tests/telephonytests/src/com/android/internal/telephony/PhysicalChannelConfigTest.java b/tests/telephonytests/src/com/android/internal/telephony/PhysicalChannelConfigTest.java index 1811073f5d..853162a03b 100644 --- a/tests/telephonytests/src/com/android/internal/telephony/PhysicalChannelConfigTest.java +++ b/tests/telephonytests/src/com/android/internal/telephony/PhysicalChannelConfigTest.java @@ -39,6 +39,7 @@ public class PhysicalChannelConfigTest { private static final int CELL_BANDWIDTH = 12345; private static final int FREQUENCY_RANGE = 1; private static final int CHANNEL_NUMBER = 1234; + private static final int CHANNEL_NUMBER_UNKNOWN = PhysicalChannelConfig.CHANNEL_NUMBER_UNKNOWN; private static final int[] CONTEXT_IDS = new int[] {123, 555, 1, 0}; private static final int PHYSICAL_CELL_ID = 502; private static final int BAND = 1; @@ -141,6 +142,25 @@ public class PhysicalChannelConfigTest { } @Test + public void testUplinkFrequencyForNrArfcnWithUnknownChannelNumber(){ + setUpPhysicalChannelConfig(NETWORK_TYPE_NR, AccessNetworkConstants.NgranBands.BAND_1, + CHANNEL_NUMBER, CHANNEL_NUMBER_UNKNOWN, ServiceState.FREQUENCY_RANGE_MID); + + assertThat(mPhysicalChannelConfig.getUplinkFrequencyKhz()).isEqualTo(INVALID_FREQUENCY); + } + + @Test + public void testUplinkFrequencyForNrArfcn(){ + setUpPhysicalChannelConfig(NETWORK_TYPE_NR, AccessNetworkConstants.NgranBands.BAND_1, + CHANNEL_NUMBER, CHANNEL_NUMBER, ServiceState.FREQUENCY_RANGE_MID); + + // 3GPP TS 38.104 Table 5.4.2.1-1, {@link AccessNetworkUtils#getFrequencyFromNrArfcn}. + // Formula of NR-ARFCN convert to actual frequency: + // Actual frequency(kHz) = (RANGE_OFFSET + GLOBAL_KHZ * (ARFCN - ARFCN_OFFSET)) + assertThat(mPhysicalChannelConfig.getUplinkFrequencyKhz()).isEqualTo(6170); + } + + @Test public void testBuilder() { setUpPhysicalChannelConfig(NETWORK_TYPE_LTE, BAND, CHANNEL_NUMBER, CHANNEL_NUMBER, FREQUENCY_RANGE); diff --git a/tests/telephonytests/src/com/android/internal/telephony/SubscriptionInfoUpdaterTest.java b/tests/telephonytests/src/com/android/internal/telephony/SubscriptionInfoUpdaterTest.java index c92d4f9d95..1161792685 100644 --- a/tests/telephonytests/src/com/android/internal/telephony/SubscriptionInfoUpdaterTest.java +++ b/tests/telephonytests/src/com/android/internal/telephony/SubscriptionInfoUpdaterTest.java @@ -246,8 +246,8 @@ public class SubscriptionInfoUpdaterTest extends TelephonyTest { mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE); verify(mConfigManager, never()).updateConfigForPhoneId(eq(FAKE_PHONE_ID_1), eq(IccCardConstants.INTENT_VALUE_ICC_NOT_READY)); - verify(mSubscriptionController).clearSubInfoRecord(FAKE_PHONE_ID_1); - verify(mSubscriptionController).notifySubscriptionInfoChanged(); + verify(mSubscriptionController, never()).clearSubInfoRecord(FAKE_PHONE_ID_1); + verify(mSubscriptionController, never()).notifySubscriptionInfoChanged(); } @Test diff --git a/tests/telephonytests/src/com/android/internal/telephony/TelephonyRegistryTest.java b/tests/telephonytests/src/com/android/internal/telephony/TelephonyRegistryTest.java index 1d5bce64db..d14bbd0801 100644 --- a/tests/telephonytests/src/com/android/internal/telephony/TelephonyRegistryTest.java +++ b/tests/telephonytests/src/com/android/internal/telephony/TelephonyRegistryTest.java @@ -30,6 +30,7 @@ import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -273,7 +274,6 @@ public class TelephonyRegistryTest extends TelephonyTest { assertEquals(phoneCapability, mPhoneCapability); } - @Test @SmallTest public void testActiveDataSubChanged() { // mTelephonyRegistry.listen with notifyNow = true should trigger callback immediately. @@ -616,9 +616,13 @@ public class TelephonyRegistryTest extends TelephonyTest { int[] events = {TelephonyCallback.EVENT_DISPLAY_INFO_CHANGED}; mTelephonyRegistry.listenWithEventList(2, mContext.getOpPackageName(), mContext.getAttributionTag(), mTelephonyCallback.callback, events, false); + when(mMockConfigurationProvider.isDisplayInfoNrAdvancedSupported( + anyString(), any())).thenReturn(true); + TelephonyDisplayInfo displayInfo = new TelephonyDisplayInfo( + TelephonyManager.NETWORK_TYPE_LTE, + TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED); // Notify with invalid subId on default phone. Should NOT trigger callback. - TelephonyDisplayInfo displayInfo = new TelephonyDisplayInfo(0, 0); mTelephonyRegistry.notifyDisplayInfoChanged(0, INVALID_SUBSCRIPTION_ID, displayInfo); processAllMessages(); assertEquals(null, mTelephonyDisplayInfo); @@ -630,6 +634,82 @@ public class TelephonyRegistryTest extends TelephonyTest { } @Test + public void testDisplayInfoCompatibility() { + mContext.sendBroadcast(new Intent(ACTION_DEFAULT_SUBSCRIPTION_CHANGED) + .putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, 12) + .putExtra(SubscriptionManager.EXTRA_SLOT_INDEX, 0)); + processAllMessages(); + int[] events = {TelephonyCallback.EVENT_DISPLAY_INFO_CHANGED}; + mTelephonyRegistry.listenWithEventList(2, mContext.getOpPackageName(), + mContext.getAttributionTag(), mTelephonyCallback.callback, events, false); + when(mMockConfigurationProvider.isDisplayInfoNrAdvancedSupported( + anyString(), any())).thenReturn(false); + TelephonyDisplayInfo displayInfo = new TelephonyDisplayInfo( + TelephonyManager.NETWORK_TYPE_LTE, + TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED); + TelephonyDisplayInfo expectDisplayInfo = new TelephonyDisplayInfo( + TelephonyManager.NETWORK_TYPE_LTE, + TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE); + + // Notify with invalid subId on default phone. Should NOT trigger callback. + mTelephonyRegistry.notifyDisplayInfoChanged(0, INVALID_SUBSCRIPTION_ID, displayInfo); + processAllMessages(); + assertEquals(null, mTelephonyDisplayInfo); + + // Notify with the matching subId on default phone. Should trigger callback. + mTelephonyRegistry.notifyDisplayInfoChanged(0, 2, displayInfo); + processAllMessages(); + assertEquals(expectDisplayInfo, mTelephonyDisplayInfo); + } + + @Test + public void testDisplayInfoCompatibility_moreCallingPackages() { + mContext.sendBroadcast(new Intent(ACTION_DEFAULT_SUBSCRIPTION_CHANGED) + .putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, 12) + .putExtra(SubscriptionManager.EXTRA_SLOT_INDEX, 0)); + processAllMessages(); + int[] events = {TelephonyCallback.EVENT_DISPLAY_INFO_CHANGED}; + TelephonyDisplayInfo displayInfo = new TelephonyDisplayInfo( + TelephonyManager.NETWORK_TYPE_LTE, + TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED); + TelephonyDisplayInfo expectDisplayInfo = new TelephonyDisplayInfo( + TelephonyManager.NETWORK_TYPE_LTE, + TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE); + TelephonyCallback telephonyCallback2 = new TelephonyCallbackWrapper() { + @Override + public void onDisplayInfoChanged(TelephonyDisplayInfo displayInfoNotify) { + assertEquals(displayInfo, displayInfoNotify); + } + }; + Executor mSimpleExecutor2 = new Executor() { + @Override + public void execute(Runnable r) { + r.run(); + } + }; + telephonyCallback2.init(mSimpleExecutor2); + mTelephonyRegistry.listenWithEventList(2, "pkg1", + mContext.getAttributionTag(), mTelephonyCallback.callback, events, false); + mTelephonyRegistry.listenWithEventList(2, "pkg2", + mContext.getAttributionTag(), telephonyCallback2.callback, events, false); + when(mMockConfigurationProvider.isDisplayInfoNrAdvancedSupported( + eq("pkg1"), any())).thenReturn(false); + when(mMockConfigurationProvider.isDisplayInfoNrAdvancedSupported( + eq("pkg2"), any())).thenReturn(true); + + + // Notify with invalid subId on default phone. Should NOT trigger callback. + mTelephonyRegistry.notifyDisplayInfoChanged(0, INVALID_SUBSCRIPTION_ID, displayInfo); + processAllMessages(); + assertEquals(null, mTelephonyDisplayInfo); + + // Notify with the matching subId on default phone. Should trigger callback. + mTelephonyRegistry.notifyDisplayInfoChanged(0, 2, displayInfo); + processAllMessages(); + assertEquals(expectDisplayInfo, mTelephonyDisplayInfo); + } + + @Test public void testNotifyCellLocationForSubscriberByUserSwitched() throws RemoteException { final int phoneId = 0; final int subId = 1; diff --git a/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java b/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java index 7e807fa967..2242d55cb9 100644 --- a/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java +++ b/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java @@ -456,7 +456,7 @@ public abstract class TelephonyTest { TelephonyManager.disableServiceHandleCaching(); SubscriptionController.disableCaching(); // For testing do not allow Log.WTF as it can cause test process to crash - Log.setWtfHandler((tagString, what, system) -> logd("WTF captured, ignoring. Tag: " + Log.setWtfHandler((tagString, what, system) -> Log.d(TAG, "WTF captured, ignoring. Tag: " + tagString + ", exception: " + what)); mPhones = new Phone[] {mPhone}; diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataConnectionTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataConnectionTest.java index 9562fad633..ca8abeb603 100644 --- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataConnectionTest.java +++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataConnectionTest.java @@ -30,6 +30,7 @@ import static com.android.internal.telephony.dataconnection.DcTrackerTest.FAKE_P import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Matchers.argThat; import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyBoolean; @@ -38,6 +39,7 @@ import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -46,6 +48,7 @@ import android.net.KeepalivePacketData; import android.net.LinkAddress; import android.net.LinkProperties; import android.net.NattKeepalivePacketData; +import android.net.Network; import android.net.NetworkCapabilities; import android.os.AsyncResult; import android.os.Handler; @@ -55,6 +58,8 @@ import android.telephony.AccessNetworkConstants; import android.telephony.AccessNetworkConstants.AccessNetworkType; import android.telephony.CarrierConfigManager; import android.telephony.ServiceState; +import android.telephony.ServiceState.RegState; +import android.telephony.ServiceState.RilRadioTechnology; import android.telephony.data.ApnSetting; import android.telephony.data.DataCallResponse; import android.telephony.data.DataProfile; @@ -63,6 +68,7 @@ import android.telephony.data.DataServiceCallback; import android.telephony.data.TrafficDescriptor; import android.test.suitebuilder.annotation.MediumTest; import android.test.suitebuilder.annotation.SmallTest; +import android.util.Pair; import com.android.internal.R; import com.android.internal.telephony.PhoneConstants; @@ -84,6 +90,7 @@ import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.function.Consumer; public class DataConnectionTest extends TelephonyTest { private static final int DEFAULT_DC_CID = 10; @@ -429,9 +436,9 @@ public class DataConnectionTest extends TelephonyTest { } private boolean isSuspended() throws Exception { - Method method = DataConnection.class.getDeclaredMethod("isSuspended"); - method.setAccessible(true); - return (boolean) method.invoke(mDc); + Field field = DataConnection.class.getDeclaredField("mIsSuspended"); + field.setAccessible(true); + return field.getBoolean(mDc); } private SetupResult setLinkProperties(DataCallResponse response, LinkProperties linkProperties) @@ -926,6 +933,12 @@ public class DataConnectionTest extends TelephonyTest { assertTrue(mDc.isInactive()); } + private void serviceStateChangedEvent(@RegState int dataRegState, @RilRadioTechnology int rat) { + mDc.obtainMessage(DataConnection.EVENT_DATA_CONNECTION_DRS_OR_RAT_CHANGED, + new AsyncResult(null, new Pair<>(dataRegState, rat), null)).sendToTarget(); + waitForMs(100); + } + @Test @SmallTest public void testIsIpAddress() { @@ -1281,24 +1294,56 @@ public class DataConnectionTest extends TelephonyTest { assertTrue(mDc.isInactive()); doReturn(mApn1).when(mApnContext).getApnSetting(); doReturn(ApnSetting.TYPE_DEFAULT).when(mApnContext).getApnTypeBitmask(); + doReturn(true).when(mSST).isConcurrentVoiceAndDataAllowed(); connectEvent(true); + // Before getting any service state event, the connection should not be suspended. + assertFalse(isSuspended()); + // Return true if combined reg state is not in service - doReturn(ServiceState.STATE_OUT_OF_SERVICE).when(mServiceState).getDataRegistrationState(); + serviceStateChangedEvent(ServiceState.STATE_OUT_OF_SERVICE, + ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN); assertTrue(isSuspended()); // Return false if in service and concurrent voice and data is allowed - doReturn(ServiceState.STATE_IN_SERVICE).when(mServiceState).getDataRegistrationState(); - doReturn(true).when(mSST).isConcurrentVoiceAndDataAllowed(); + serviceStateChangedEvent(ServiceState.STATE_IN_SERVICE, + ServiceState.RIL_RADIO_TECHNOLOGY_LTE); assertFalse(isSuspended()); // Return false if in service and concurrent voice/data not allowed but call state is idle doReturn(false).when(mSST).isConcurrentVoiceAndDataAllowed(); doReturn(PhoneConstants.State.IDLE).when(mCT).getState(); + mDc.sendMessage(DataConnection.EVENT_DATA_CONNECTION_VOICE_CALL_STARTED); + waitForMs(100); assertFalse(isSuspended()); // Return true if in service, concurrent voice/data not allowed, and call state not idle doReturn(PhoneConstants.State.RINGING).when(mCT).getState(); + mDc.sendMessage(DataConnection.EVENT_DATA_CONNECTION_VOICE_CALL_STARTED); + waitForMs(100); + assertTrue(isSuspended()); + } + + @Test + public void testDataCreatedWhenOutOfService() throws Exception { + serviceStateChangedEvent(ServiceState.STATE_OUT_OF_SERVICE, + ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN); + ArgumentCaptor<NetworkCapabilities> ncCaptor = + ArgumentCaptor.forClass(NetworkCapabilities.class); + doReturn(mock(Network.class)).when(mConnectivityManager).registerNetworkAgent( + any(), any(), any(), ncCaptor.capture(), any(), any(), anyInt()); + + doReturn(mApn1).when(mApnContext).getApnSetting(); + doReturn(ApnSetting.TYPE_DEFAULT).when(mApnContext).getApnTypeBitmask(); + doReturn(true).when(mSST).isConcurrentVoiceAndDataAllowed(); + connectEvent(true); + waitForMs(100); + + NetworkCapabilities nc = ncCaptor.getValue(); + // The network must be created with NOT_SUSPENDED capability. + assertTrue(nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED)); + + // But it's final state must be suspended. assertTrue(isSuspended()); } @@ -1316,4 +1361,23 @@ public class DataConnectionTest extends TelephonyTest { verify(mDataThrottler).setRetryTime(eq(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_SUPL), eq(RetryManager.NO_SUGGESTED_RETRY_DELAY), eq(DcTracker.REQUEST_TYPE_NORMAL)); } + + @Test + public void testDataHandoverFailed() throws Exception { + doReturn(mDefaultDc).when(mDcTracker).getDataConnectionByApnType(anyString()); + + doAnswer(invocation -> { + final Consumer<Integer> consumer = (Consumer<Integer>) invocation.getArguments()[0]; + consumer.accept(DataServiceCallback.RESULT_SUCCESS); + return null; + }).when(mDefaultDc).startHandover(any(Consumer.class)); + + replaceInstance(ConnectionParams.class, "mRequestType", mCp, + DcTracker.REQUEST_TYPE_HANDOVER); + assertTrue(mDc.isInactive()); + connectEvent(false); + + // Make sure the data connection is still in inactive state + assertTrue(mDc.isInactive()); + } } diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcControllerTest.java index 3c8e7ab29c..003f74eef4 100644 --- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcControllerTest.java +++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcControllerTest.java @@ -22,7 +22,9 @@ import static com.android.internal.telephony.dataconnection.DcTrackerTest.FAKE_G import static com.android.internal.telephony.dataconnection.DcTrackerTest.FAKE_IFNAME; import static com.android.internal.telephony.dataconnection.DcTrackerTest.FAKE_PCSCF_ADDRESS; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.Mockito.any; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.eq; @@ -33,7 +35,9 @@ import android.net.InetAddresses; import android.net.LinkAddress; import android.net.LinkProperties; import android.os.AsyncResult; +import android.os.Handler; import android.os.Looper; +import android.os.Message; import android.telephony.AccessNetworkConstants; import android.telephony.data.ApnSetting; import android.telephony.data.DataCallResponse; @@ -51,6 +55,7 @@ import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; import org.mockito.Mock; import java.lang.reflect.Method; @@ -63,7 +68,10 @@ import java.util.List; public class DcControllerTest extends TelephonyTest { private static final int DATA_CONNECTION_ACTIVE_PH_LINK_DORMANT = 1; + private static final int DATA_CONNECTION_ACTIVE_PH_LINK_ACTIVE = 2; + private static final int EVENT_DATA_STATE_CHANGED = 0x00040007; + private static final int EVENT_PHYSICAL_LINK_STATE_CHANGED = 1; @Mock private DataConnection mDc; @@ -71,6 +79,8 @@ public class DcControllerTest extends TelephonyTest { private List<ApnContext> mApnContexts; @Mock private DataServiceManager mDataServiceManager; + @Mock + private Handler mTestHandler; UpdateLinkPropertyResult mResult; @@ -142,4 +152,131 @@ public class DcControllerTest extends TelephonyTest { verify(mDcTracker, times(1)).sendStopNetStatPoll(eq(DctConstants.Activity.DORMANT)); } + + @Test + @SmallTest + public void testPhysicalLinkStateChanged_defaultApnTypeAndDormant_registrantNotifyResult() + throws Exception { + ArrayList<DataCallResponse> l = new ArrayList<>(); + DataCallResponse dcResponse = new DataCallResponse.Builder() + .setCause(0) + .setRetryDurationMillis(-1) + .setId(1) + .setLinkStatus(DATA_CONNECTION_ACTIVE_PH_LINK_DORMANT) + .setProtocolType(ApnSetting.PROTOCOL_IP) + .setInterfaceName(FAKE_IFNAME) + .setAddresses(Arrays.asList( + new LinkAddress(InetAddresses.parseNumericAddress(FAKE_ADDRESS), 0))) + .setDnsAddresses(Arrays.asList(InetAddresses.parseNumericAddress(FAKE_DNS))) + .setGatewayAddresses(Arrays.asList(InetAddresses.parseNumericAddress(FAKE_GATEWAY))) + .setPcscfAddresses( + Arrays.asList(InetAddresses.parseNumericAddress(FAKE_PCSCF_ADDRESS))) + .setMtuV4(1440) + .setMtuV6(1440) + .build(); + l.add(dcResponse); + mDc.mCid = 1; + mDcc.addActiveDcByCid(mDc); + ApnContext apnContext = new ApnContext(mPhone, ApnSetting.TYPE_DEFAULT, TAG, mDcTracker, 1); + List<ApnContext> apnContextList = new ArrayList<>(); + apnContextList.add(apnContext); + doReturn(apnContextList).when(mDc).getApnContexts(); + doReturn(true).when(mDcTracker).getLteEndcUsingUserDataForIdleDetection(); + mDcc.registerForPhysicalLinkStateChanged(mTestHandler, EVENT_PHYSICAL_LINK_STATE_CHANGED); + + mDcc.sendMessage(mDcc.obtainMessage(EVENT_DATA_STATE_CHANGED, + new AsyncResult(null, l, null))); + processAllMessages(); + + ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class); + verify(mTestHandler, times(1)).sendMessageDelayed(messageCaptor.capture(), anyLong()); + Message message = messageCaptor.getValue(); + assertEquals(EVENT_PHYSICAL_LINK_STATE_CHANGED, message.what); + AsyncResult ar = (AsyncResult) message.obj; + assertEquals(DcController.PHYSICAL_LINK_NOT_ACTIVE, (int) ar.result); + } + + @Test + @SmallTest + public void testPhysicalLinkStateChanged_imsApnTypeAndDormant_NoNotifyResult() + throws Exception { + testPhysicalLinkStateChanged_defaultApnTypeAndDormant_registrantNotifyResult(); + + ArrayList<DataCallResponse> l = new ArrayList<>(); + DataCallResponse dcResponse = new DataCallResponse.Builder() + .setCause(0) + .setRetryDurationMillis(-1) + .setId(1) + .setLinkStatus(DATA_CONNECTION_ACTIVE_PH_LINK_ACTIVE) + .setProtocolType(ApnSetting.PROTOCOL_IP) + .setInterfaceName(FAKE_IFNAME) + .setAddresses(Arrays.asList( + new LinkAddress(InetAddresses.parseNumericAddress(FAKE_ADDRESS), 0))) + .setDnsAddresses(Arrays.asList(InetAddresses.parseNumericAddress(FAKE_DNS))) + .setGatewayAddresses(Arrays.asList(InetAddresses.parseNumericAddress(FAKE_GATEWAY))) + .setPcscfAddresses( + Arrays.asList(InetAddresses.parseNumericAddress(FAKE_PCSCF_ADDRESS))) + .setMtuV4(1440) + .setMtuV6(1440) + .build(); + l.add(dcResponse); + mDc.mCid = 1; + mDcc.addActiveDcByCid(mDc); + ApnContext apnContext = new ApnContext(mPhone, ApnSetting.TYPE_IMS, TAG, mDcTracker, 1); + List<ApnContext> apnContextList = new ArrayList<>(); + apnContextList.add(apnContext); + doReturn(apnContextList).when(mDc).getApnContexts(); + doReturn(true).when(mDcTracker).getLteEndcUsingUserDataForIdleDetection(); + + mDcc.sendMessage(mDcc.obtainMessage(EVENT_DATA_STATE_CHANGED, + new AsyncResult(null, l, null))); + processAllMessages(); + + ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class); + verify(mTestHandler, times(1)).sendMessageDelayed(messageCaptor.capture(), anyLong()); + } + + @Test + @SmallTest + public void testPhysicalLinkStateChanged_defaultApnTypeAndStateChanged_registrantNotifyResult() + throws Exception { + testPhysicalLinkStateChanged_imsApnTypeAndDormant_NoNotifyResult(); + + ArrayList<DataCallResponse> l = new ArrayList<>(); + DataCallResponse dcResponse = new DataCallResponse.Builder() + .setCause(0) + .setRetryDurationMillis(-1) + .setId(1) + .setLinkStatus(DATA_CONNECTION_ACTIVE_PH_LINK_ACTIVE) + .setProtocolType(ApnSetting.PROTOCOL_IP) + .setInterfaceName(FAKE_IFNAME) + .setAddresses(Arrays.asList( + new LinkAddress(InetAddresses.parseNumericAddress(FAKE_ADDRESS), 0))) + .setDnsAddresses(Arrays.asList(InetAddresses.parseNumericAddress(FAKE_DNS))) + .setGatewayAddresses(Arrays.asList(InetAddresses.parseNumericAddress(FAKE_GATEWAY))) + .setPcscfAddresses( + Arrays.asList(InetAddresses.parseNumericAddress(FAKE_PCSCF_ADDRESS))) + .setMtuV4(1440) + .setMtuV6(1440) + .build(); + l.add(dcResponse); + mDc.mCid = 1; + mDcc.addActiveDcByCid(mDc); + ApnContext apnContext = new ApnContext(mPhone, ApnSetting.TYPE_DEFAULT, TAG, mDcTracker, 1); + List<ApnContext> apnContextList = new ArrayList<>(); + apnContextList.add(apnContext); + doReturn(apnContextList).when(mDc).getApnContexts(); + doReturn(true).when(mDcTracker).getLteEndcUsingUserDataForIdleDetection(); + + mDcc.sendMessage(mDcc.obtainMessage(EVENT_DATA_STATE_CHANGED, + new AsyncResult(null, l, null))); + processAllMessages(); + + ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class); + verify(mTestHandler, times(2)).sendMessageDelayed(messageCaptor.capture(), anyLong()); + Message message = messageCaptor.getValue(); + assertEquals(EVENT_PHYSICAL_LINK_STATE_CHANGED, message.what); + AsyncResult ar = (AsyncResult) message.obj; + assertEquals(DcController.PHYSICAL_LINK_ACTIVE, (int) ar.result); + } }
\ No newline at end of file diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcTrackerTest.java index 951439f6d3..89a59b412f 100644 --- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcTrackerTest.java +++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcTrackerTest.java @@ -58,6 +58,7 @@ import android.net.NetworkPolicyManager; import android.net.NetworkRequest; import android.net.Uri; import android.os.AsyncResult; +import android.os.Bundle; import android.os.Handler; import android.os.HandlerThread; import android.os.IBinder; @@ -2896,6 +2897,33 @@ public class DcTrackerTest extends TelephonyTest { } @Test + public void testDataUnthrottledAfterAPNChanged() throws Exception { + initApns(ApnSetting.TYPE_IMS_STRING, new String[]{ApnSetting.TYPE_IMS_STRING}); + replaceInstance(DcTracker.class, "mDataThrottler", mDct, mDataThrottler); + + mDct.enableApn(ApnSetting.TYPE_IMS, DcTracker.REQUEST_TYPE_NORMAL, null); + sendInitializationEvents(); + mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_APN_CHANGED, null)); + waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler()); + + // Verify unthrottling + verify(mDataThrottler, times(2)).reset(); + } + + @Test + public void testDataUnthrottledOnSimStateChanged() throws Exception { + initApns(ApnSetting.TYPE_IMS_STRING, new String[]{ApnSetting.TYPE_IMS_STRING}); + replaceInstance(DcTracker.class, "mDataThrottler", mDct, mDataThrottler); + + mDct.enableApn(ApnSetting.TYPE_IMS, DcTracker.REQUEST_TYPE_NORMAL, null); + sendInitializationEvents(); + sendSimStateUpdated("testDataUnthrottledOnSimStateChanged"); + + // Verify unthrottling + verify(mDataThrottler, times(2)).reset(); + } + + @Test public void testHandlingSecondHandoverRequest() throws Exception { initApns(ApnSetting.TYPE_IMS_STRING, new String[]{ApnSetting.TYPE_IMS_STRING}); setUpDataConnection(); @@ -2982,4 +3010,19 @@ public class DcTrackerTest extends TelephonyTest { // Ensure handover is not completed yet verify(handler, never()).sendMessageDelayed(any(), anyLong()); } + + @Test + public void testPreferenceChangedFallback() { + Handler handler = Mockito.mock(Handler.class); + doReturn(AccessNetworkConstants.TRANSPORT_TYPE_WLAN).when(mTransportManager) + .getPreferredTransport(anyInt()); + Message handoverCompleteMessage = Message.obtain(handler); + addHandoverCompleteMsg(handoverCompleteMessage, ApnSetting.TYPE_IMS); + initApns(ApnSetting.TYPE_IMS_STRING, new String[]{ApnSetting.TYPE_IMS_STRING}); + mDct.enableApn(ApnSetting.TYPE_IMS, DcTracker.REQUEST_TYPE_HANDOVER, + handoverCompleteMessage); + waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler()); + Bundle bundle = handoverCompleteMessage.getData(); + assertTrue(bundle.getBoolean("extra_handover_failure_fallback")); + } } diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/QosCallbackTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/QosCallbackTrackerTest.java index e7c4db43bb..5a8b54028a 100644 --- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/QosCallbackTrackerTest.java +++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/QosCallbackTrackerTest.java @@ -20,6 +20,7 @@ import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.any; import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; @@ -94,12 +95,16 @@ public class QosCallbackTrackerTest extends TelephonyTest { @Mock private DcNetworkAgent mDcNetworkAgent; + @Mock + private Network mNetwork; private QosCallbackTracker mQosCallbackTracker; @Before public void setUp() throws Exception { super.setUp(getClass().getSimpleName()); + doReturn(mNetwork).when(mDcNetworkAgent).getNetwork(); + doReturn(100).when(mNetwork).getNetId(); mQosCallbackTracker = new QosCallbackTracker(mDcNetworkAgent); processAllMessages(); } @@ -452,5 +457,34 @@ public class QosCallbackTrackerTest extends TelephonyTest { verify(mDcNetworkAgent, times(1)).notifyQosSessionLost(eq(1), eq(1234), eq(1)); verify(mDcNetworkAgent, times(1)).notifyQosSessionLost(eq(2), eq(1235), eq(1)); } + + @Test + @SmallTest + public void testQosSessionWithInvalidPortRange() throws Exception { + // Non-matching QosBearerFilter + ArrayList<QosBearerFilter> qosFilters1 = new ArrayList<>(); + qosFilters1.add(createIpv4QosFilter("155.55.55.55", + new QosBearerFilter.PortRange(0,0), 45)); + + ArrayList<QosBearerSession> qosSessions = new ArrayList<>(); + qosSessions.add(new QosBearerSession(1234, createEpsQos(5, 6, 7, 8), qosFilters1)); + + // Matching QosBearerFilter + ArrayList<QosBearerFilter> qosFilters2 = new ArrayList<>(); + qosFilters2.add(createIpv4QosFilter("122.22.22.22", + new QosBearerFilter.PortRange(-1, 1), 45)); + qosSessions.add(new QosBearerSession(1235, createEpsQos(5, 6, 7, 8), qosFilters2)); + + mQosCallbackTracker.updateSessions(qosSessions); + + // Add filter after updateSessions + Filter filter = new Filter(new InetSocketAddress( + InetAddresses.parseNumericAddress("122.22.22.22"), 2222)); + mQosCallbackTracker.addFilter(1, filter); + + verify(mDcNetworkAgent, never()).notifyQosSessionAvailable(eq(1), + eq(1235), any(EpsBearerQosSessionAttributes.class)); + + } } diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/TelephonyNetworkFactoryTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/TelephonyNetworkFactoryTest.java index 1dfd48584f..30bd8b27ff 100644 --- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/TelephonyNetworkFactoryTest.java +++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/TelephonyNetworkFactoryTest.java @@ -36,7 +36,6 @@ import android.net.NetworkProvider; import android.net.NetworkRequest; import android.net.TelephonyNetworkSpecifier; import android.os.AsyncResult; -import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.os.Message; @@ -465,16 +464,6 @@ public class TelephonyNetworkFactoryTest extends TelephonyTest { mTelephonyNetworkFactoryUT.releaseNetworkFor(mmsNetworkRequest); processAllMessages(); - Message msg = mNetworkRequestMessageMap.get(mmsNetworkRequest); - - Bundle bundle = msg.getData(); - bundle.putParcelable("extra_network_request", mmsNetworkRequest); - bundle.putBoolean("extra_success", true); - bundle.putInt("extra_transport_type", AccessNetworkConstants.TRANSPORT_TYPE_WLAN); - bundle.putBoolean("extra_handover_failure_fallback", false); - h.sendMessage(msg); - processAllMessages(); - // Ensure the release is called one more time after the normal release verify(mDcTracker, times(2)).releaseNetwork(any(), eq(1)); } diff --git a/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmMmiCodeTest.java b/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmMmiCodeTest.java index 08d5b13d64..ee4f6e5a26 100644 --- a/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmMmiCodeTest.java +++ b/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmMmiCodeTest.java @@ -16,6 +16,8 @@ package com.android.internal.telephony.gsm; +import static junit.framework.Assert.fail; + import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.doReturn; @@ -95,6 +97,16 @@ public class GsmMmiCodeTest extends TelephonyTest { assertTrue(mGsmMmiCode == null); } + @Test + public void testNoCrashOnEmptyMessage() { + GsmMmiCode mmi = GsmMmiCode.newNetworkInitiatedUssd(null, true, mGsmCdmaPhoneUT, null); + try { + mmi.onUssdFinishedError(); + } catch (Exception e) { + fail("Shouldn't crash!!!"); + } + } + private void setCarrierSupportsCallerIdVerticalServiceCodesCarrierConfig() { final PersistableBundle bundle = new PersistableBundle(); bundle.putBoolean(CarrierConfigManager diff --git a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneMmiCodeTest.java b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneMmiCodeTest.java index 220e7ce845..aee46b728c 100644 --- a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneMmiCodeTest.java +++ b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneMmiCodeTest.java @@ -16,6 +16,8 @@ package com.android.internal.telephony.imsphone; +import static junit.framework.Assert.fail; + import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.doReturn; @@ -101,6 +103,16 @@ public class ImsPhoneMmiCodeTest extends TelephonyTest { assertTrue(mImsPhoneMmiCode == null); } + @Test + public void testNoCrashOnEmptyMessage() { + ImsPhoneMmiCode mmi = ImsPhoneMmiCode.newNetworkInitiatedUssd(null, true, mImsPhoneUT); + try { + mmi.onUssdFinishedError(); + } catch (Exception e) { + fail("Shouldn't crash!!!"); + } + } + private void setCarrierSupportsCallerIdVerticalServiceCodesCarrierConfig() { final PersistableBundle bundle = new PersistableBundle(); bundle.putBoolean(CarrierConfigManager diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/PinStorageTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/PinStorageTest.java index f165a9ef4d..a26c0f905a 100644 --- a/tests/telephonytests/src/com/android/internal/telephony/uicc/PinStorageTest.java +++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/PinStorageTest.java @@ -108,7 +108,7 @@ public class PinStorageTest extends TelephonyTest { public void storePin_withoutReboot_pinCannotBeRetrieved() { mPinStorage.storePin("1234", 0); - assertThat(mPinStorage.getPin(0)).isEqualTo(""); + assertThat(mPinStorage.getPin(0, ICCID_1)).isEqualTo(""); } @Test @@ -118,7 +118,7 @@ public class PinStorageTest extends TelephonyTest { simulateReboot(); - assertThat(mPinStorage.getPin(0)).isEqualTo(""); + assertThat(mPinStorage.getPin(0, ICCID_1)).isEqualTo(""); } @Test @@ -130,7 +130,7 @@ public class PinStorageTest extends TelephonyTest { mPinStorage = new PinStorage(mContext); mPinStorage.mShortTermSecretKeyDurationMinutes = 0; - assertThat(mPinStorage.getPin(0)).isEqualTo(""); + assertThat(mPinStorage.getPin(0, ICCID_1)).isEqualTo(""); } @Test @@ -144,8 +144,8 @@ public class PinStorageTest extends TelephonyTest { simulateReboot(); // PIN can be retrieved only once after unattended reboot - assertThat(mPinStorage.getPin(0)).isEqualTo("1234"); - assertThat(mPinStorage.getPin(0)).isEqualTo(""); + assertThat(mPinStorage.getPin(0, ICCID_1)).isEqualTo("1234"); + assertThat(mPinStorage.getPin(0, ICCID_1)).isEqualTo(""); } @Test @@ -164,7 +164,7 @@ public class PinStorageTest extends TelephonyTest { simulateReboot(); // PIN cannot be retrieved - assertThat(mPinStorage.getPin(0)).isEqualTo(""); + assertThat(mPinStorage.getPin(0, ICCID_1)).isEqualTo(""); } @Test @@ -181,7 +181,7 @@ public class PinStorageTest extends TelephonyTest { moveTimeForward(60000); processAllMessages(); - assertThat(mPinStorage.getPin(0)).isEqualTo(""); + assertThat(mPinStorage.getPin(0, ICCID_1)).isEqualTo(""); // Simulate a second unattended reboot to make sure that PIN was deleted. result = mPinStorage.prepareUnattendedReboot(); @@ -189,7 +189,7 @@ public class PinStorageTest extends TelephonyTest { simulateReboot(); - assertThat(mPinStorage.getPin(0)).isEqualTo(""); + assertThat(mPinStorage.getPin(0, ICCID_1)).isEqualTo(""); } @Test @@ -205,7 +205,7 @@ public class PinStorageTest extends TelephonyTest { processAllMessages(); simulateReboot(); - assertThat(mPinStorage.getPin(0)).isEqualTo(""); + assertThat(mPinStorage.getPin(0, ICCID_1)).isEqualTo(""); } @Test @@ -221,12 +221,12 @@ public class PinStorageTest extends TelephonyTest { // Switch to a different ICCID in the device after the reboot doReturn(ICCID_2).when(mPhone).getFullIccSerialNumber(); - assertThat(mPinStorage.getPin(0)).isEqualTo(""); + assertThat(mPinStorage.getPin(0, ICCID_2)).isEqualTo(""); // Switch back to the initial ICCID to make sure that PIN was deleted. doReturn(ICCID_1).when(mPhone).getFullIccSerialNumber(); - assertThat(mPinStorage.getPin(0)).isEqualTo(""); + assertThat(mPinStorage.getPin(0, ICCID_1)).isEqualTo(""); } @Test @@ -240,7 +240,7 @@ public class PinStorageTest extends TelephonyTest { simulateReboot(); - assertThat(mPinStorage.getPin(0)).isEqualTo(""); + assertThat(mPinStorage.getPin(0, ICCID_1)).isEqualTo(""); } @Test @@ -254,7 +254,7 @@ public class PinStorageTest extends TelephonyTest { simulateReboot(); - assertThat(mPinStorage.getPin(0)).isEqualTo("5678"); + assertThat(mPinStorage.getPin(0, ICCID_1)).isEqualTo("5678"); } @Test @@ -267,7 +267,7 @@ public class PinStorageTest extends TelephonyTest { simulateReboot(); - assertThat(mPinStorage.getPin(0)).isEqualTo(""); + assertThat(mPinStorage.getPin(0, ICCID_1)).isEqualTo(""); } @Test @@ -280,7 +280,7 @@ public class PinStorageTest extends TelephonyTest { simulateReboot(); - assertThat(mPinStorage.getPin(0)).isEqualTo(""); + assertThat(mPinStorage.getPin(0, ICCID_1)).isEqualTo(""); } @Test @@ -293,7 +293,7 @@ public class PinStorageTest extends TelephonyTest { simulateReboot(); - assertThat(mPinStorage.getPin(0)).isEqualTo(""); + assertThat(mPinStorage.getPin(0, ICCID_INVALID)).isEqualTo(""); } @Test @@ -309,7 +309,7 @@ public class PinStorageTest extends TelephonyTest { simulateReboot(); - assertThat(mPinStorage.getPin(0)).isEqualTo(""); + assertThat(mPinStorage.getPin(0, ICCID_1)).isEqualTo(""); } @Test @@ -328,7 +328,7 @@ public class PinStorageTest extends TelephonyTest { simulateReboot(); - assertThat(mPinStorage.getPin(0)).isEqualTo(""); + assertThat(mPinStorage.getPin(0, ICCID_1)).isEqualTo(""); } @Test @@ -346,7 +346,7 @@ public class PinStorageTest extends TelephonyTest { simulateReboot(); - assertThat(mPinStorage.getPin(0)).isEqualTo(""); + assertThat(mPinStorage.getPin(0, ICCID_1)).isEqualTo(""); } @Test @@ -369,7 +369,7 @@ public class PinStorageTest extends TelephonyTest { simulateReboot(); - assertThat(mPinStorage.getPin(0)).isEqualTo(""); + assertThat(mPinStorage.getPin(0, ICCID_1)).isEqualTo(""); } @Test @@ -389,7 +389,7 @@ public class PinStorageTest extends TelephonyTest { simulateReboot(); - assertThat(mPinStorage.getPin(0)).isEqualTo(""); + assertThat(mPinStorage.getPin(0, ICCID_1)).isEqualTo(""); } @Test @@ -409,6 +409,6 @@ public class PinStorageTest extends TelephonyTest { mContext.sendBroadcast(intent); processAllMessages(); - assertThat(mPinStorage.getPin(0)).isEqualTo(""); + assertThat(mPinStorage.getPin(0, ICCID_1)).isEqualTo(""); } } diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRulesTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRulesTest.java index 39ec59dc63..7a16e5bf97 100644 --- a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRulesTest.java +++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRulesTest.java @@ -42,7 +42,10 @@ import org.mockito.Mock; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicReference; @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper @@ -561,9 +564,11 @@ public class UiccCarrierPrivilegeRulesTest extends TelephonyTest { }).when(mUiccProfile).iccOpenLogicalChannel(anyString(), anyInt(), any(Message.class)); // Select files + AtomicReference<String> currentFileId = new AtomicReference<>(); doAnswer(new Answer<Void>() { @Override public Void answer(InvocationOnMock invocation) throws Throwable { + currentFileId.set((String) invocation.getArguments()[6]); Message message = (Message) invocation.getArguments()[7]; AsyncResult ar = new AsyncResult(null, new int[]{2}, null); message.obj = ar; @@ -573,85 +578,44 @@ public class UiccCarrierPrivilegeRulesTest extends TelephonyTest { }).when(mUiccProfile).iccTransmitApduLogicalChannel(anyInt(), eq(0x00), eq(0xA4), eq(0x00), eq(0x04), eq(0x02), anyString(), any(Message.class)); - // Read binary - ODF - String odf = "A706300404025207"; + // Read binary - since params are identical across files, we need to keep track of which + // file was selected most recently and give back that content. + Map<String, String> binaryContent = + new HashMap<>() { + { + // ODF + put("5031", "A706300404025207"); + // DODF + put( + "5207", + "A1293000300F0C0D4750205345204163632043746CA1143012060A2A864886FC6B" + + "81480101300404024200"); + // ACMF + put("4200", "301004080102030405060708300404024300"); + // ACRF + put("4300", "3010A0080406FFFFFFFFFFFF300404024310"); + // ACCF + put( + "4310", + "30220420B9CFCE1C47A6AC713442718F15EF55B00B3A6D1A6D48CB46249FA8EB51" + + "465350302204204C36AF4A5BDAD97C1F3D8B283416D244496C2AC5EA" + + "FE8226079EF6F676FD1859"); + } + }; doAnswer(new Answer<Void>() { @Override public Void answer(InvocationOnMock invocation) throws Throwable { Message message = (Message) invocation.getArguments()[7]; - IccIoResult iir = new IccIoResult(0x90, 0x00, IccUtils.hexStringToBytes(odf)); + IccIoResult iir = + new IccIoResult(0x90, 0x00, + IccUtils.hexStringToBytes(binaryContent.get(currentFileId.get()))); AsyncResult ar = new AsyncResult(null, iir, null); message.obj = ar; message.sendToTarget(); return null; } }).when(mUiccProfile).iccTransmitApduLogicalChannel(anyInt(), eq(0x00), eq(0xB0), eq(0x00), - eq(0x00), eq(0x00), eq("5031"), any(Message.class)); - - // Read binary - DODF - String dodf = - "A1293000300F0C0D4750205345204163632043746CA11" - + "43012060A2A864886FC6B81480101300404024200"; - doAnswer(new Answer<Void>() { - @Override - public Void answer(InvocationOnMock invocation) throws Throwable { - Message message = (Message) invocation.getArguments()[7]; - IccIoResult iir = new IccIoResult(0x90, 0x00, IccUtils.hexStringToBytes(dodf)); - AsyncResult ar = new AsyncResult(null, iir, null); - message.obj = ar; - message.sendToTarget(); - return null; - } - }).when(mUiccProfile).iccTransmitApduLogicalChannel(anyInt(), eq(0x00), eq(0xB0), eq(0x00), - eq(0x00), eq(0x00), eq("5207"), any(Message.class)); - - // Read binary - ACMF - String acmf = "301004080102030405060708300404024300"; - doAnswer(new Answer<Void>() { - @Override - public Void answer(InvocationOnMock invocation) throws Throwable { - Message message = (Message) invocation.getArguments()[7]; - IccIoResult iir = new IccIoResult(0x90, 0x00, IccUtils.hexStringToBytes(acmf)); - AsyncResult ar = new AsyncResult(null, iir, null); - message.obj = ar; - message.sendToTarget(); - return null; - } - }).when(mUiccProfile).iccTransmitApduLogicalChannel(anyInt(), eq(0x00), eq(0xB0), eq(0x00), - eq(0x00), eq(0x00), eq("4200"), any(Message.class)); - - // Read binary - ACRF - String acrf = "3010A0080406FFFFFFFFFFFF300404024310"; - doAnswer(new Answer<Void>() { - @Override - public Void answer(InvocationOnMock invocation) throws Throwable { - Message message = (Message) invocation.getArguments()[7]; - IccIoResult iir = new IccIoResult(0x90, 0x00, IccUtils.hexStringToBytes(acrf)); - AsyncResult ar = new AsyncResult(null, iir, null); - message.obj = ar; - message.sendToTarget(); - return null; - } - }).when(mUiccProfile).iccTransmitApduLogicalChannel(anyInt(), eq(0x00), eq(0xB0), eq(0x00), - eq(0x00), eq(0x00), eq("4300"), any(Message.class)); - - // Read binary - ACCF - String accf = - "30220420B9CFCE1C47A6AC713442718F15EF55B00B3A6D1A6D48CB46249FA8EB514653503022042" - + "04C36AF4A5BDAD97C1F3D8B283416D244496C2AC5EAFE8226079EF6F676FD1859"; - doAnswer(new Answer<Void>() { - @Override - public Void answer(InvocationOnMock invocation) throws Throwable { - Message message = (Message) invocation.getArguments()[7]; - IccIoResult iir = new IccIoResult(0x90, 0x00, IccUtils.hexStringToBytes(accf)); - AsyncResult ar = new AsyncResult(null, iir, null); - message.obj = ar; - message.sendToTarget(); - return null; - } - }).when(mUiccProfile).iccTransmitApduLogicalChannel(anyInt(), eq(0x00), eq(0xB0), eq(0x00), - eq(0x00), eq(0x00), eq("4310"), any(Message.class)); - + eq(0x00), eq(0x00), eq(""), any(Message.class)); doAnswer(new Answer<Void>() { @Override @@ -706,9 +670,11 @@ public class UiccCarrierPrivilegeRulesTest extends TelephonyTest { }).when(mUiccProfile).iccOpenLogicalChannel(anyString(), anyInt(), any(Message.class)); // Select files + AtomicReference<String> currentFileId = new AtomicReference<>(); doAnswer(new Answer<Void>() { @Override public Void answer(InvocationOnMock invocation) throws Throwable { + currentFileId.set((String) invocation.getArguments()[6]); Message message = (Message) invocation.getArguments()[7]; AsyncResult ar = new AsyncResult(null, new int[]{2}, null); message.obj = ar; @@ -718,52 +684,37 @@ public class UiccCarrierPrivilegeRulesTest extends TelephonyTest { }).when(mUiccProfile).iccTransmitApduLogicalChannel(anyInt(), eq(0x00), eq(0xA4), eq(0x00), eq(0x04), eq(0x02), anyString(), any(Message.class)); - // Read binary ODF failed - doAnswer(new Answer<Void>() { - @Override - public Void answer(InvocationOnMock invocation) throws Throwable { - Message message = (Message) invocation.getArguments()[7]; - IccIoResult iir = new IccIoResult(0x90, 0x00, new byte[]{}); - AsyncResult ar = new AsyncResult(null, iir, null); - message.obj = ar; - message.sendToTarget(); - return null; - } - }).when(mUiccProfile).iccTransmitApduLogicalChannel(anyInt(), eq(0x00), eq(0xB0), eq(0x00), - eq(0x00), eq(0x00), eq("5031"), any(Message.class)); - - // Read binary - ACRF - String acrf = "3010A0080406FFFFFFFFFFFF300404024310"; + // Read binary - since params are identical across files, we need to keep track of which + // file was selected most recently and give back that content. + Map<String, String> binaryContent = + new HashMap<>() { + { + // ODF fails + put("5031", ""); + // ACRF + put("4300", "3010A0080406FFFFFFFFFFFF300404024310"); + // ACCF + put( + "4310", + "30220420B9CFCE1C47A6AC713442718F15EF55B00B3A6D1A6D48CB46249FA8EB51" + + "465350302204204C36AF4A5BDAD97C1F3D8B283416D244496C2AC5EA" + + "FE8226079EF6F676FD1859"); + } + }; doAnswer(new Answer<Void>() { @Override public Void answer(InvocationOnMock invocation) throws Throwable { Message message = (Message) invocation.getArguments()[7]; - IccIoResult iir = new IccIoResult(0x90, 0x00, IccUtils.hexStringToBytes(acrf)); + IccIoResult iir = + new IccIoResult(0x90, 0x00, + IccUtils.hexStringToBytes(binaryContent.get(currentFileId.get()))); AsyncResult ar = new AsyncResult(null, iir, null); message.obj = ar; message.sendToTarget(); return null; } }).when(mUiccProfile).iccTransmitApduLogicalChannel(anyInt(), eq(0x00), eq(0xB0), eq(0x00), - eq(0x00), eq(0x00), eq("4300"), any(Message.class)); - - // Read binary - ACCF - String accf = - "30220420B9CFCE1C47A6AC713442718F15EF55B00B3A6D1A6D48CB46249FA8EB514653503022042" - + "04C36AF4A5BDAD97C1F3D8B283416D244496C2AC5EAFE8226079EF6F676FD1859"; - doAnswer(new Answer<Void>() { - @Override - public Void answer(InvocationOnMock invocation) throws Throwable { - Message message = (Message) invocation.getArguments()[7]; - IccIoResult iir = new IccIoResult(0x90, 0x00, IccUtils.hexStringToBytes(accf)); - AsyncResult ar = new AsyncResult(null, iir, null); - message.obj = ar; - message.sendToTarget(); - return null; - } - }).when(mUiccProfile).iccTransmitApduLogicalChannel(anyInt(), eq(0x00), eq(0xB0), eq(0x00), - eq(0x00), eq(0x00), eq("4310"), any(Message.class)); - + eq(0x00), eq(0x00), eq(""), any(Message.class)); doAnswer(new Answer<Void>() { @Override diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccControllerTest.java index e518f3ec0f..a00e110f5a 100644 --- a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccControllerTest.java +++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccControllerTest.java @@ -18,6 +18,7 @@ package com.android.internal.telephony.uicc; import static junit.framework.Assert.fail; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; @@ -60,6 +61,7 @@ public class UiccControllerTest extends TelephonyTest { private static final int ICC_CHANGED_EVENT = 0; private static final int EVENT_GET_ICC_STATUS_DONE = 3; private static final int EVENT_GET_SLOT_STATUS_DONE = 4; + private static final int EVENT_SIM_REFRESH = 8; private static final int EVENT_EID_READY = 9; @Mock private Handler mMockedHandler; @@ -73,6 +75,8 @@ public class UiccControllerTest extends TelephonyTest { private UiccCard mMockCard; @Mock private EuiccCard mMockEuiccCard; + @Mock + private UiccProfile mMockProfile; private IccCardApplicationStatus composeUiccApplicationStatus( IccCardApplicationStatus.AppType appType, @@ -631,4 +635,32 @@ public class UiccControllerTest extends TelephonyTest { assertEquals(mUiccControllerUT.convertToPublicCardId(knownEidFromApdu), mUiccControllerUT.getCardIdForDefaultEuicc()); } + + @Test + public void testSlotStatusChanged() { + // simulate slot status loaded so that the UiccController sets the last slot status + IccSlotStatus iss1 = new IccSlotStatus(); + iss1.setSlotState(1 /* active */); + iss1.eid = "eid1"; + IccSlotStatus iss2 = new IccSlotStatus(); + iss2.setSlotState(1 /* active */); + iss2.eid = "eid2"; + ArrayList<IccSlotStatus> status = new ArrayList<IccSlotStatus>(); + status.add(iss1); + status.add(iss2); + AsyncResult ar = new AsyncResult(null, status, null); + Message msg = Message.obtain(mUiccControllerUT, EVENT_GET_SLOT_STATUS_DONE, ar); + mUiccControllerUT.handleMessage(msg); + processAllMessages(); + + assertFalse(mUiccControllerUT.slotStatusChanged(status)); + + // change the order of the IccSlotStatus in the list + status = new ArrayList<>(); + status.add(iss2); + status.add(iss1); + + // status should be treated different from last status + assertTrue(mUiccControllerUT.slotStatusChanged(status)); + } } |