Snap for 10453563 from 94ec5a14cea628292aa957a95d5fa325e015072f to mainline-adservices-release Change-Id: If3da7428500593bed6bb26f5bbc78998d67b3dd9
diff --git a/Android.bp b/Android.bp index fab53d1..4097571 100644 --- a/Android.bp +++ b/Android.bp
@@ -83,13 +83,14 @@ "android.hardware.radio-V1.4-java", "android.hardware.radio-V1.5-java", "android.hardware.radio-V1.6-java", - "android.hardware.radio.config-V1-java", - "android.hardware.radio.data-V1-java", - "android.hardware.radio.messaging-V1-java", - "android.hardware.radio.modem-V1-java", - "android.hardware.radio.network-V1-java", - "android.hardware.radio.sim-V1-java", - "android.hardware.radio.voice-V1-java", + "android.hardware.radio.config-V2-java", + "android.hardware.radio.data-V2-java", + "android.hardware.radio.ims-V1-java", + "android.hardware.radio.messaging-V2-java", + "android.hardware.radio.modem-V2-java", + "android.hardware.radio.network-V2-java", + "android.hardware.radio.sim-V2-java", + "android.hardware.radio.voice-V2-java", "voip-common", "ims-common", "unsupportedappusage",
diff --git a/OWNERS b/OWNERS index 94509fe..a061cf0 100644 --- a/OWNERS +++ b/OWNERS
@@ -8,15 +8,15 @@ jackyu@google.com jayachandranc@google.com linggm@google.com -mtelang@google.com +pmadapurmath@google.com rgreenwalt@google.com sarahchin@google.com sasindran@google.com tgunn@google.com tjstuart@google.com +tnd@google.com xiaotonj@google.com -
diff --git a/README.txt b/README.txt index 9e40b77..1a44beb 100644 --- a/README.txt +++ b/README.txt
@@ -15,7 +15,7 @@ implement in this directory and packages/services/Telephony. This IPC scheme allows us to run public API code in the calling process, while the telephony-related code runs in the privileged com.android.phone process. Such -implementations include PhoneInterfaceManager, SubscriptionController and +implementations include PhoneInterfaceManager, SubscriptionManagerService and others. The declaration of the com.android.phone process is in
diff --git a/proto/src/persist_atoms.proto b/proto/src/persist_atoms.proto index c10eeb5..61e44a3 100644 --- a/proto/src/persist_atoms.proto +++ b/proto/src/persist_atoms.proto
@@ -23,7 +23,7 @@ // Holds atoms to store on persist storage in case of power cycle or process crash. // NOTE: using int64 rather than google.protobuf.Timestamp for timestamps simplifies implementation. -// Next id: 50 +// Next id: 70 message PersistAtoms { /* Aggregated RAT usage during the call. */ repeated VoiceCallRatUsage voice_call_rat_usage = 1; @@ -177,6 +177,60 @@ /* Timestamp of last network_requests_v2 pull. */ optional int64 network_requests_v2_pull_timestamp_millis = 51; + + /* Unmetered networks information. */ + repeated UnmeteredNetworks unmetered_networks = 52; + + /* Outgoing Short Code SMS statistics and information. */ + repeated OutgoingShortCodeSms outgoing_short_code_sms = 53; + + /* Timestamp of last outgoing_short_code_sms pull. */ + optional int64 outgoing_short_code_sms_pull_timestamp_millis = 54; + + /* Number of time the user toggled the data switch feature since the last collection. */ + optional int32 auto_data_switch_toggle_count = 55; + + /** Snapshot of satellite controller. */ + repeated SatelliteController satellite_controller = 58; + + /* Timestamp of last satellite_controller pull. */ + optional int64 satellite_controller_pull_timestamp_millis = 59; + + /** Snapshot of satellite controller. */ + repeated SatelliteSession satellite_session = 60; + + /* Timestamp of last satellite_controller pull. */ + optional int64 satellite_session_pull_timestamp_millis = 61; + + /** Snapshot of satellite incoming datagram. */ + repeated SatelliteIncomingDatagram satellite_incoming_datagram = 62; + + /* Timestamp of last satellite_incoming_datagram pull. */ + optional int64 satellite_incoming_datagram_pull_timestamp_millis = 63; + + /** Snapshot of satellite outgoing datagram. */ + repeated SatelliteOutgoingDatagram satellite_outgoing_datagram = 64; + + /* Timestamp of last satellite_outgoing_datagram pull. */ + optional int64 satellite_outgoing_datagram_pull_timestamp_millis = 65; + + /** Snapshot of satellite provision datagram. */ + repeated SatelliteProvision satellite_provision = 66; + + /* Timestamp of last satellite_provision pull. */ + optional int64 satellite_provision_pull_timestamp_millis = 67; + + /** Snapshot of satellite SOS message recommender. */ + repeated SatelliteSosMessageRecommender satellite_sos_message_recommender = 68; + + /* Timestamp of last satellite_sos_message_recommender pull. */ + optional int64 satellite_sos_message_recommender_pull_timestamp_millis = 69; + + /* Consolidated emergency numbers list information. */ + repeated EmergencyNumbersInfo emergency_numbers_info = 56; + + /* Timestamp of last emergency number pull. */ + optional int64 emergency_number_pull_timestamp_millis = 57; } // The canonical versions of the following enums live in: @@ -187,10 +241,10 @@ // NOTE: StatsLog functions use int in place of enum message VoiceCallSession { + reserved 4; optional int32 bearer_at_start = 1; optional int32 bearer_at_end = 2; optional int32 direction = 3; - optional int32 setup_duration = 4; optional bool setup_failed = 5; optional int32 disconnect_reason_code = 6; optional int32 disconnect_extra_code = 7; @@ -219,6 +273,9 @@ optional int32 rat_at_connected = 30; optional bool is_multiparty = 31; optional int32 call_duration = 32; + optional int32 last_known_rat = 33; + optional int32 fold_state = 34; + // Internal use only optional int64 setup_begin_millis = 10001; } @@ -245,6 +302,11 @@ optional bool is_esim = 12; optional int32 carrier_id = 13; optional int64 message_id = 14; + optional int32 count = 15; + optional bool is_managed_profile = 16; + + // Internal use only + optional int32 hashCode = 10001; } message OutgoingSms { @@ -262,6 +324,13 @@ optional int64 message_id = 12; optional int32 retry_id = 13; optional int64 interval_millis = 14; + optional int32 count = 15; + optional int32 send_error_code = 16; + optional int32 network_error_code = 17; + optional bool is_managed_profile = 18; + + // Internal use only + optional int32 hashCode = 10001; } message CarrierIdMismatch { @@ -292,6 +361,8 @@ optional bool ongoing = 18; optional int32 band_at_end = 19; repeated int32 handover_failure_causes = 20; + repeated int32 handover_failure_rat = 21; + optional bool is_non_dds = 22; } message CellularServiceState { @@ -305,6 +376,8 @@ optional int32 carrier_id = 8; optional int64 total_time_millis = 9; // Duration needs to be rounded when pulled optional bool is_emergency_only = 10; + optional bool is_internet_pdn_up = 11; + optional int32 fold_state = 12; // Internal use only optional int64 last_used_millis = 10001; @@ -498,3 +571,104 @@ optional int32 failed_reason = 4; optional int32 count = 5; } + +message UnmeteredNetworks { + optional int32 phone_id = 1; + optional int32 carrier_id = 2; + optional int64 unmetered_networks_bitmask = 3; +} + +message OutgoingShortCodeSms { + optional int32 category = 1; + optional int32 xml_version = 2; + optional int32 short_code_sms_count = 3; +} + +message SatelliteController { + optional int32 count_of_satellite_service_enablements_success = 1; + optional int32 count_of_satellite_service_enablements_fail = 2; + optional int32 count_of_outgoing_datagram_success = 3; + optional int32 count_of_outgoing_datagram_fail = 4; + optional int32 count_of_incoming_datagram_success = 5; + optional int32 count_of_incoming_datagram_fail = 6; + optional int32 count_of_datagram_type_sos_sms_success = 7; + optional int32 count_of_datagram_type_sos_sms_fail = 8; + optional int32 count_of_datagram_type_location_sharing_success = 9; + optional int32 count_of_datagram_type_location_sharing_fail = 10; + optional int32 count_of_provision_success = 11; + optional int32 count_of_provision_fail = 12; + optional int32 count_of_deprovision_success = 13; + optional int32 count_of_deprovision_fail = 14; + optional int32 total_service_uptime_sec = 15; + optional int32 total_battery_consumption_percent = 16; + optional int32 total_battery_charged_time_sec = 17; +} + +message SatelliteSession { + optional int32 satellite_service_initialization_result = 1; + optional int32 satellite_technology = 2; + optional int32 count = 3; +} + +message SatelliteIncomingDatagram { + optional int32 result_code = 1; + optional int32 datagram_size_bytes = 2; + optional int64 datagram_transfer_time_millis = 3; +} + +message SatelliteOutgoingDatagram { + optional int32 datagram_type = 1; + optional int32 result_code = 2; + optional int32 datagram_size_bytes = 3; + optional int64 datagram_transfer_time_millis = 4; +} + +message SatelliteProvision { + optional int32 result_code = 1; + optional int32 provisioning_time_sec = 2; + optional bool is_provision_request = 3; + optional bool is_canceled = 4; +} + +message SatelliteSosMessageRecommender { + optional bool is_display_sos_message_sent = 1; + optional int32 count_of_timer_started = 2; + optional bool is_ims_registered = 3; + optional int32 cellular_service_state = 4; + optional int32 count = 5; +} + +message EmergencyNumbersInfo { + enum ServiceCategory { + EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED = 0; + EMERGENCY_SERVICE_CATEGORY_POLICE = 1; + EMERGENCY_SERVICE_CATEGORY_AMBULANCE = 2; + EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE = 3; + EMERGENCY_SERVICE_CATEGORY_MARINE_GUARD = 4; + EMERGENCY_SERVICE_CATEGORY_MOUNTAIN_RESCUE = 5; + EMERGENCY_SERVICE_CATEGORY_MIEC = 6; + EMERGENCY_SERVICE_CATEGORY_AIEC = 7; + } + enum Source { + EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING = 0; + EMERGENCY_NUMBER_SOURCE_SIM = 1; + EMERGENCY_NUMBER_SOURCE_DATABASE = 2; + EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG = 3; + EMERGENCY_NUMBER_SOURCE_DEFAULT = 4; + } + enum CallRoute { + EMERGENCY_CALL_ROUTE_UNKNOWN = 0; + EMERGENCY_CALL_ROUTE_EMERGENCY = 1; + EMERGENCY_CALL_ROUTE_NORMAL = 2; + } + optional bool is_db_version_ignored = 1; + optional int32 asset_version = 2; + optional int32 ota_version = 3; + optional string number = 4; + optional string country_iso = 5; + optional string mnc = 6; + optional CallRoute route = 7; + repeated string urns = 8; + repeated ServiceCategory service_categories = 9; + repeated Source sources = 10; +}
diff --git a/proto/src/telephony.proto b/proto/src/telephony.proto index 6d3d711..b87728b 100644 --- a/proto/src/telephony.proto +++ b/proto/src/telephony.proto
@@ -931,6 +931,9 @@ /** Data switch caused by CBRS switch. */ DATA_SWITCH_REASON_CBRS = 3; + + /** Data switch caused by non-default SIM having better availability(e.g.signal). */ + DATA_SWITCH_REASON_AUTO = 4; } /** The reason for data switch. */
diff --git a/src/java/com/android/internal/telephony/BaseCommands.java b/src/java/com/android/internal/telephony/BaseCommands.java index 972884a..b8de975 100644 --- a/src/java/com/android/internal/telephony/BaseCommands.java +++ b/src/java/com/android/internal/telephony/BaseCommands.java
@@ -17,6 +17,8 @@ package com.android.internal.telephony; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.AsyncResult; @@ -26,6 +28,7 @@ import android.os.Registrant; import android.os.RegistrantList; import android.telephony.Annotation.RadioPowerState; +import android.telephony.BarringInfo; import android.telephony.TelephonyManager; import android.telephony.emergency.EmergencyNumber; @@ -114,6 +117,17 @@ protected RegistrantList mBarringInfoChangedRegistrants = new RegistrantList(); protected RegistrantList mSimPhonebookChangedRegistrants = new RegistrantList(); protected RegistrantList mSimPhonebookRecordsReceivedRegistrants = new RegistrantList(); + protected RegistrantList mEmergencyNetworkScanRegistrants = new RegistrantList(); + protected RegistrantList mConnectionSetupFailureRegistrants = new RegistrantList(); + protected RegistrantList mNotifyAnbrRegistrants = new RegistrantList(); + protected RegistrantList mTriggerImsDeregistrationRegistrants = new RegistrantList(); + protected RegistrantList mPendingSatelliteMessageCountRegistrants = new RegistrantList(); + protected RegistrantList mNewSatelliteMessagesRegistrants = new RegistrantList(); + protected RegistrantList mSatelliteMessagesTransferCompleteRegistrants = new RegistrantList(); + protected RegistrantList mSatellitePointingInfoChangedRegistrants = new RegistrantList(); + protected RegistrantList mSatelliteModeChangedRegistrants = new RegistrantList(); + protected RegistrantList mSatelliteRadioTechnologyChangedRegistrants = new RegistrantList(); + protected RegistrantList mSatelliteProvisionStateChangedRegistrants = new RegistrantList(); @UnsupportedAppUsage protected Registrant mGsmSmsRegistrant; @@ -160,6 +174,8 @@ // Cache last emergency number list indication from radio private final List<EmergencyNumber> mLastEmergencyNumberListIndication = new ArrayList<>(); + // The last barring information received + protected BarringInfo mLastBarringInfo = new BarringInfo(); // Preferred network type received from PhoneFactory. // This is used when establishing a connection to the // vendor ril so it starts up in the correct mode. @@ -900,6 +916,7 @@ || mState == TelephonyManager.RADIO_POWER_UNAVAILABLE) && (oldState == TelephonyManager.RADIO_POWER_ON)) { mOffOrNotAvailRegistrants.notifyRegistrants(); + mLastBarringInfo = new BarringInfo(); } } } @@ -918,6 +935,12 @@ } } + /** {@inheritDoc} */ + @Override + public @NonNull BarringInfo getLastBarringInfo() { + return mLastBarringInfo; + } + /** * {@inheritDoc} */ @@ -1132,4 +1155,133 @@ @Override public void updateSimPhonebookRecord(SimPhonebookRecord phonebookRecord, Message result) { } + + /** + * Register for Emergency network scan result. + * + * @param h Handler for notification message. + * @param what User-defined message code. + * @param obj User object. + */ + @Override + public void registerForEmergencyNetworkScan(Handler h, int what, Object obj) { + mEmergencyNetworkScanRegistrants.add(h, what, obj); + } + + /** + * Unregister for Emergency network scan result. + * + * @param h Handler to be removed from the registrant list. + */ + @Override + public void unregisterForEmergencyNetworkScan(Handler h) { + mEmergencyNetworkScanRegistrants.remove(h); + } + + @Override + public void registerForConnectionSetupFailure(Handler h, int what, Object obj) { + mConnectionSetupFailureRegistrants.addUnique(h, what, obj); + } + + @Override + public void unregisterForConnectionSetupFailure(Handler h) { + mConnectionSetupFailureRegistrants.remove(h); + } + + @Override + public void registerForNotifyAnbr(Handler h, int what, Object obj) { + mNotifyAnbrRegistrants.addUnique(h, what, obj); + } + + @Override + public void unregisterForNotifyAnbr(Handler h) { + mNotifyAnbrRegistrants.remove(h); + } + + @Override + public void registerForTriggerImsDeregistration(Handler h, int what, Object obj) { + mTriggerImsDeregistrationRegistrants.add(h, what, obj); + } + + @Override + public void unregisterForTriggerImsDeregistration(Handler h) { + mTriggerImsDeregistrationRegistrants.remove(h); + } + + @Override + public void registerForPendingSatelliteMessageCount( + @NonNull Handler h, int what, @Nullable Object obj) { + mPendingSatelliteMessageCountRegistrants.add(h, what, obj); + } + + @Override + public void unregisterForPendingSatelliteMessageCount(@NonNull Handler h) { + mPendingSatelliteMessageCountRegistrants.remove(h); + } + + @Override + public void registerForNewSatelliteMessages( + @NonNull Handler h, int what, @Nullable Object obj) { + mNewSatelliteMessagesRegistrants.add(h, what, obj); + } + + @Override + public void unregisterForNewSatelliteMessages(@NonNull Handler h) { + mNewSatelliteMessagesRegistrants.remove(h); + } + + @Override + public void registerForSatelliteMessagesTransferComplete(@NonNull Handler h, + int what, @Nullable Object obj) { + mSatelliteMessagesTransferCompleteRegistrants.add(h, what, obj); + } + + @Override + public void unregisterForSatelliteMessagesTransferComplete(@NonNull Handler h) { + mSatelliteMessagesTransferCompleteRegistrants.remove(h); + } + + @Override + public void registerForSatellitePointingInfoChanged(@NonNull Handler h, + int what, @Nullable Object obj) { + mSatellitePointingInfoChangedRegistrants.add(h, what, obj); + } + + @Override + public void unregisterForSatellitePointingInfoChanged(@NonNull Handler h) { + mSatellitePointingInfoChangedRegistrants.remove(h); + } + + @Override + public void registerForSatelliteModeChanged(@NonNull Handler h, + int what, @Nullable Object obj) { + mSatelliteModeChangedRegistrants.add(h, what, obj); + } + + @Override + public void unregisterForSatelliteModeChanged(@NonNull Handler h) { + mSatelliteModeChangedRegistrants.remove(h); + } + + @Override + public void registerForSatelliteRadioTechnologyChanged(@NonNull Handler h, + int what, @Nullable Object obj) { + mSatelliteRadioTechnologyChangedRegistrants.add(h, what, obj); + } + + @Override + public void unregisterForSatelliteRadioTechnologyChanged(@NonNull Handler h) { + mSatelliteRadioTechnologyChangedRegistrants.remove(h); + } + + @Override + public void registerForSatelliteProvisionStateChanged(@NonNull Handler h, + int what, @Nullable Object obj) { + mSatelliteProvisionStateChangedRegistrants.add(h, what, obj); + } + + @Override + public void unregisterForSatelliteProvisionStateChanged(@NonNull Handler h) { + mSatelliteProvisionStateChangedRegistrants.remove(h); + } }
diff --git a/src/java/com/android/internal/telephony/CallStateException.java b/src/java/com/android/internal/telephony/CallStateException.java index 3fdc444..4de16cb 100644 --- a/src/java/com/android/internal/telephony/CallStateException.java +++ b/src/java/com/android/internal/telephony/CallStateException.java
@@ -35,6 +35,7 @@ public static final int ERROR_CALLING_DISABLED = 5; public static final int ERROR_TOO_MANY_CALLS = 6; public static final int ERROR_OTASP_PROVISIONING_IN_PROCESS = 7; + public static final int ERROR_FDN_BLOCKED = 8; public CallStateException()
diff --git a/src/java/com/android/internal/telephony/CallWaitingController.java b/src/java/com/android/internal/telephony/CallWaitingController.java new file mode 100644 index 0000000..49940fc --- /dev/null +++ b/src/java/com/android/internal/telephony/CallWaitingController.java
@@ -0,0 +1,684 @@ +/* + * 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 com.android.internal.telephony; + +import static android.telephony.CarrierConfigManager.ImsSs.CALL_WAITING_SYNC_FIRST_CHANGE; +import static android.telephony.CarrierConfigManager.ImsSs.CALL_WAITING_SYNC_FIRST_POWER_UP; +import static android.telephony.CarrierConfigManager.ImsSs.CALL_WAITING_SYNC_IMS_ONLY; +import static android.telephony.CarrierConfigManager.ImsSs.CALL_WAITING_SYNC_NONE; +import static android.telephony.CarrierConfigManager.ImsSs.CALL_WAITING_SYNC_USER_CHANGE; +import static android.telephony.CarrierConfigManager.ImsSs.KEY_TERMINAL_BASED_CALL_WAITING_DEFAULT_ENABLED_BOOL; +import static android.telephony.CarrierConfigManager.ImsSs.KEY_TERMINAL_BASED_CALL_WAITING_SYNC_TYPE_INT; +import static android.telephony.CarrierConfigManager.ImsSs.KEY_UT_TERMINAL_BASED_SERVICES_INT_ARRAY; +import static android.telephony.CarrierConfigManager.ImsSs.SUPPLEMENTARY_SERVICE_CW; + +import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_NONE; +import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_VOICE; + +import android.annotation.Nullable; +import android.content.Context; +import android.content.SharedPreferences; +import android.os.AsyncResult; +import android.os.Handler; +import android.os.Message; +import android.os.PersistableBundle; +import android.telephony.CarrierConfigManager; +import android.telephony.ServiceState; +import android.telephony.SubscriptionManager; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.IndentingPrintWriter; +import com.android.telephony.Rlog; + +import java.io.PrintWriter; + +/** + * Controls the change of the user setting of the call waiting service + */ +public class CallWaitingController extends Handler { + + public static final String LOG_TAG = "CallWaitingCtrl"; + private static final boolean DBG = false; /* STOPSHIP if true */ + + // Terminal-based call waiting is not supported. */ + public static final int TERMINAL_BASED_NOT_SUPPORTED = -1; + // Terminal-based call waiting is supported but not activated. */ + public static final int TERMINAL_BASED_NOT_ACTIVATED = 0; + // Terminal-based call waiting is supported and activated. */ + public static final int TERMINAL_BASED_ACTIVATED = 1; + + private static final int EVENT_SET_CALL_WAITING_DONE = 1; + private static final int EVENT_GET_CALL_WAITING_DONE = 2; + private static final int EVENT_REGISTERED_TO_NETWORK = 3; + + // Class to pack mOnComplete object passed by the caller + private static class Cw { + final boolean mEnable; + final Message mOnComplete; + final boolean mImsRegistered; + + Cw(boolean enable, boolean imsRegistered, Message onComplete) { + mEnable = enable; + mOnComplete = onComplete; + mImsRegistered = imsRegistered; + } + } + + @VisibleForTesting + public static final String PREFERENCE_TBCW = "terminal_based_call_waiting"; + @VisibleForTesting + public static final String KEY_SUB_ID = "subId"; + @VisibleForTesting + public static final String KEY_STATE = "state"; + @VisibleForTesting + public static final String KEY_CS_SYNC = "cs_sync"; + + private final CarrierConfigManager.CarrierConfigChangeListener mCarrierConfigChangeListener = + (slotIndex, subId, carrierId, specificCarrierId) -> onCarrierConfigurationChanged( + slotIndex); + + private boolean mSupportedByImsService = false; + private boolean mValidSubscription = false; + + // The user's last setting of terminal-based call waiting + private int mCallWaitingState = TERMINAL_BASED_NOT_SUPPORTED; + + private int mSyncPreference = CALL_WAITING_SYNC_NONE; + private int mLastSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; + + private boolean mCsEnabled = false; + private boolean mRegisteredForNetworkAttach = false; + private boolean mImsRegistered = false; + + private final GsmCdmaPhone mPhone; + private final ServiceStateTracker mSST; + private final Context mContext; + + // Constructors + public CallWaitingController(GsmCdmaPhone phone) { + mPhone = phone; + mSST = phone.getServiceStateTracker(); + mContext = phone.getContext(); + } + + private void initialize() { + CarrierConfigManager ccm = mContext.getSystemService(CarrierConfigManager.class); + if (ccm != null) { + // Callback directly handle carrier config change should be executed in handler thread + ccm.registerCarrierConfigChangeListener(this::post, mCarrierConfigChangeListener); + } else { + loge("CarrierConfigLoader is not available."); + } + + int phoneId = mPhone.getPhoneId(); + int subId = mPhone.getSubId(); + SharedPreferences sp = + mContext.getSharedPreferences(PREFERENCE_TBCW, Context.MODE_PRIVATE); + mLastSubId = sp.getInt(KEY_SUB_ID + phoneId, SubscriptionManager.INVALID_SUBSCRIPTION_ID); + mCallWaitingState = sp.getInt(KEY_STATE + subId, TERMINAL_BASED_NOT_SUPPORTED); + mSyncPreference = sp.getInt(KEY_CS_SYNC + phoneId, CALL_WAITING_SYNC_NONE); + + logi("initialize phoneId=" + phoneId + + ", lastSubId=" + mLastSubId + ", subId=" + subId + + ", state=" + mCallWaitingState + ", sync=" + mSyncPreference + + ", csEnabled=" + mCsEnabled); + } + + /** + * Returns the cached user setting. + * + * Possible values are + * {@link #TERMINAL_BASED_NOT_SUPPORTED}, + * {@link #TERMINAL_BASED_NOT_ACTIVATED}, and + * {@link #TERMINAL_BASED_ACTIVATED}. + * + * @param forCsOnly indicates the caller expects the result for CS calls only + */ + @VisibleForTesting + public synchronized int getTerminalBasedCallWaitingState(boolean forCsOnly) { + if (forCsOnly && (!mImsRegistered) && mSyncPreference == CALL_WAITING_SYNC_IMS_ONLY) { + return TERMINAL_BASED_NOT_SUPPORTED; + } + if (!mValidSubscription) return TERMINAL_BASED_NOT_SUPPORTED; + return mCallWaitingState; + } + + /** + * Serves the user's requests to interrogate the call waiting service + * + * @return true when terminal-based call waiting is supported, otherwise false + */ + @VisibleForTesting + public synchronized boolean getCallWaiting(@Nullable Message onComplete) { + if (mCallWaitingState == TERMINAL_BASED_NOT_SUPPORTED) return false; + + logi("getCallWaiting " + mCallWaitingState); + + if (mSyncPreference == CALL_WAITING_SYNC_FIRST_CHANGE) { + // Interrogate CW in CS network + if (!mCsEnabled) { + // skip interrogation if CS is not available and IMS is registered + if (isCircuitSwitchedNetworkAvailable() || !isImsRegistered()) { + Cw cw = new Cw(false, isImsRegistered(), onComplete); + Message resp = obtainMessage(EVENT_GET_CALL_WAITING_DONE, 0, 0, cw); + mPhone.mCi.queryCallWaiting(SERVICE_CLASS_NONE, resp); + return true; + } + } + } + + if (mSyncPreference == CALL_WAITING_SYNC_NONE + || mSyncPreference == CALL_WAITING_SYNC_FIRST_CHANGE + || mSyncPreference == CALL_WAITING_SYNC_FIRST_POWER_UP + || isSyncImsOnly()) { + sendGetCallWaitingResponse(onComplete); + return true; + } else if (mSyncPreference == CALL_WAITING_SYNC_USER_CHANGE + || mSyncPreference == CALL_WAITING_SYNC_IMS_ONLY) { + Cw cw = new Cw(false, isImsRegistered(), onComplete); + Message resp = obtainMessage(EVENT_GET_CALL_WAITING_DONE, 0, 0, cw); + mPhone.mCi.queryCallWaiting(SERVICE_CLASS_NONE, resp); + return true; + } + + return false; + } + + /** + * Serves the user's requests to set the call waiting service + * + * @param serviceClass the target service class. Values are CommandsInterface.SERVICE_CLASS_*. + * @return true when terminal-based call waiting is supported, otherwise false + */ + @VisibleForTesting + public synchronized boolean setCallWaiting(boolean enable, + int serviceClass, @Nullable Message onComplete) { + if (mCallWaitingState == TERMINAL_BASED_NOT_SUPPORTED) return false; + + if ((serviceClass & SERVICE_CLASS_VOICE) != SERVICE_CLASS_VOICE) return false; + + logi("setCallWaiting enable=" + enable + ", service=" + serviceClass); + + if (mSyncPreference == CALL_WAITING_SYNC_FIRST_CHANGE) { + // Enable CW in the CS network + if (!mCsEnabled && enable) { + if (isCircuitSwitchedNetworkAvailable() || !isImsRegistered()) { + Cw cw = new Cw(true, isImsRegistered(), onComplete); + Message resp = obtainMessage(EVENT_SET_CALL_WAITING_DONE, 0, 0, cw); + mPhone.mCi.setCallWaiting(true, serviceClass, resp); + return true; + } else { + // CS network is not available, however, IMS is registered. + // Enabling the service in the CS network will be delayed. + registerForNetworkAttached(); + } + } + } + + if (mSyncPreference == CALL_WAITING_SYNC_NONE + || mSyncPreference == CALL_WAITING_SYNC_FIRST_CHANGE + || mSyncPreference == CALL_WAITING_SYNC_FIRST_POWER_UP + || isSyncImsOnly()) { + updateState( + enable ? TERMINAL_BASED_ACTIVATED : TERMINAL_BASED_NOT_ACTIVATED); + + sendToTarget(onComplete, null, null); + return true; + } else if (mSyncPreference == CALL_WAITING_SYNC_USER_CHANGE + || mSyncPreference == CALL_WAITING_SYNC_IMS_ONLY) { + Cw cw = new Cw(enable, isImsRegistered(), onComplete); + Message resp = obtainMessage(EVENT_SET_CALL_WAITING_DONE, 0, 0, cw); + mPhone.mCi.setCallWaiting(enable, serviceClass, resp); + return true; + } + + return false; + } + + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case EVENT_SET_CALL_WAITING_DONE: + onSetCallWaitingDone((AsyncResult) msg.obj); + break; + case EVENT_GET_CALL_WAITING_DONE: + onGetCallWaitingDone((AsyncResult) msg.obj); + break; + case EVENT_REGISTERED_TO_NETWORK: + onRegisteredToNetwork(); + break; + default: + break; + } + } + + private synchronized void onSetCallWaitingDone(AsyncResult ar) { + if (ar.userObj == null) { + // For the case, CALL_WAITING_SYNC_FIRST_POWER_UP + if (DBG) logd("onSetCallWaitingDone to sync on network attached"); + if (ar.exception == null) { + updateSyncState(true); + } else { + loge("onSetCallWaitingDone e=" + ar.exception); + } + return; + } + + if (!(ar.userObj instanceof Cw)) { + // Unexpected state + if (DBG) logd("onSetCallWaitingDone unexpected result"); + return; + } + + if (DBG) logd("onSetCallWaitingDone"); + Cw cw = (Cw) ar.userObj; + + if (mSyncPreference == CALL_WAITING_SYNC_IMS_ONLY) { + // do not synchronize service state between CS and IMS + sendToTarget(cw.mOnComplete, ar.result, ar.exception); + return; + } + + if (ar.exception == null) { + if (mSyncPreference == CALL_WAITING_SYNC_FIRST_CHANGE) { + // SYNC_FIRST_CHANGE implies cw.mEnable is true. + updateSyncState(true); + } + updateState( + cw.mEnable ? TERMINAL_BASED_ACTIVATED : TERMINAL_BASED_NOT_ACTIVATED); + } else if (mSyncPreference == CALL_WAITING_SYNC_FIRST_CHANGE) { + if (cw.mImsRegistered) { + // IMS is registered. Do not notify error. + // SYNC_FIRST_CHANGE implies cw.mEnable is true. + updateState(TERMINAL_BASED_ACTIVATED); + sendToTarget(cw.mOnComplete, null, null); + return; + } + } + sendToTarget(cw.mOnComplete, ar.result, ar.exception); + } + + private synchronized void onGetCallWaitingDone(AsyncResult ar) { + if (ar.userObj == null) { + // For the case, CALL_WAITING_SYNC_FIRST_POWER_UP + if (DBG) logd("onGetCallWaitingDone to sync on network attached"); + boolean enabled = false; + if (ar.exception == null) { + //resp[0]: 1 if enabled, 0 otherwise + //resp[1]: bitwise ORs of SERVICE_CLASS_* constants + int[] resp = (int[]) ar.result; + if (resp != null && resp.length > 1) { + enabled = (resp[0] == 1) + && (resp[1] & SERVICE_CLASS_VOICE) == SERVICE_CLASS_VOICE; + } else { + loge("onGetCallWaitingDone unexpected response"); + } + } else { + loge("onGetCallWaitingDone e=" + ar.exception); + } + if (enabled) { + updateSyncState(true); + } else { + logi("onGetCallWaitingDone enabling CW service in CS network"); + mPhone.mCi.setCallWaiting(true, SERVICE_CLASS_VOICE, + obtainMessage(EVENT_SET_CALL_WAITING_DONE)); + } + unregisterForNetworkAttached(); + return; + } + + if (!(ar.userObj instanceof Cw)) { + // Unexpected state + if (DBG) logd("onGetCallWaitingDone unexpected result"); + return; + } + + if (DBG) logd("onGetCallWaitingDone"); + Cw cw = (Cw) ar.userObj; + + if (mSyncPreference == CALL_WAITING_SYNC_IMS_ONLY) { + // do not synchronize service state between CS and IMS + sendToTarget(cw.mOnComplete, ar.result, ar.exception); + return; + } + + if (ar.exception == null) { + int[] resp = (int[]) ar.result; + //resp[0]: 1 if enabled, 0 otherwise + //resp[1]: bitwise ORs of SERVICE_CLASS_ + if (resp == null || resp.length < 2) { + logi("onGetCallWaitingDone unexpected response"); + if (mSyncPreference == CALL_WAITING_SYNC_FIRST_CHANGE) { + // no exception but unexpected response, local setting is preferred. + sendGetCallWaitingResponse(cw.mOnComplete); + } else { + sendToTarget(cw.mOnComplete, ar.result, ar.exception); + } + return; + } + + boolean enabled = resp[0] == 1 + && (resp[1] & SERVICE_CLASS_VOICE) == SERVICE_CLASS_VOICE; + + if (mSyncPreference == CALL_WAITING_SYNC_FIRST_CHANGE) { + updateSyncState(enabled); + + if (!enabled && !cw.mImsRegistered) { + // IMS is not registered, change the local setting + logi("onGetCallWaitingDone CW in CS network is disabled."); + updateState(TERMINAL_BASED_NOT_ACTIVATED); + } + + // return the user setting saved + sendGetCallWaitingResponse(cw.mOnComplete); + return; + } + updateState(enabled ? TERMINAL_BASED_ACTIVATED : TERMINAL_BASED_NOT_ACTIVATED); + } else if (mSyncPreference == CALL_WAITING_SYNC_FIRST_CHANGE) { + // Got an exception + if (cw.mImsRegistered) { + // queryCallWaiting failed. However, IMS is registered. Do not notify error. + // return the user setting saved + logi("onGetCallWaitingDone get an exception, but IMS is registered"); + sendGetCallWaitingResponse(cw.mOnComplete); + return; + } + } + sendToTarget(cw.mOnComplete, ar.result, ar.exception); + } + + private void sendToTarget(Message onComplete, Object result, Throwable exception) { + if (onComplete != null) { + AsyncResult.forMessage(onComplete, result, exception); + onComplete.sendToTarget(); + } + } + + private void sendGetCallWaitingResponse(Message onComplete) { + if (onComplete != null) { + int serviceClass = SERVICE_CLASS_NONE; + if (mCallWaitingState == TERMINAL_BASED_ACTIVATED) { + serviceClass = SERVICE_CLASS_VOICE; + } + sendToTarget(onComplete, new int[] { mCallWaitingState, serviceClass }, null); + } + } + + private synchronized void onRegisteredToNetwork() { + if (mCsEnabled) return; + + if (DBG) logd("onRegisteredToNetwork"); + + mPhone.mCi.queryCallWaiting(SERVICE_CLASS_NONE, + obtainMessage(EVENT_GET_CALL_WAITING_DONE)); + } + + private synchronized void onCarrierConfigurationChanged(int slotIndex) { + if (slotIndex != mPhone.getPhoneId()) return; + + int subId = mPhone.getSubId(); + if (!SubscriptionManager.isValidSubscriptionId(subId)) { + logi("onCarrierConfigChanged invalid subId=" + subId); + + mValidSubscription = false; + unregisterForNetworkAttached(); + return; + } + + if (!updateCarrierConfig(subId, false /* ignoreSavedState */)) { + return; + } + + logi("onCarrierConfigChanged cs_enabled=" + mCsEnabled); + + if (mSyncPreference == CALL_WAITING_SYNC_FIRST_POWER_UP) { + if (!mCsEnabled) { + registerForNetworkAttached(); + } + } + } + + /** + * @param ignoreSavedState only used for test + * @return true when succeeded. + */ + @VisibleForTesting + public boolean updateCarrierConfig(int subId, boolean ignoreSavedState) { + mValidSubscription = true; + + PersistableBundle b = + CarrierConfigManager.getCarrierConfigSubset( + mContext, + subId, + KEY_UT_TERMINAL_BASED_SERVICES_INT_ARRAY, + KEY_TERMINAL_BASED_CALL_WAITING_SYNC_TYPE_INT, + KEY_TERMINAL_BASED_CALL_WAITING_DEFAULT_ENABLED_BOOL); + if (b.isEmpty()) return false; + + boolean supportsTerminalBased = false; + int[] services = b.getIntArray(KEY_UT_TERMINAL_BASED_SERVICES_INT_ARRAY); + if (services != null) { + for (int service : services) { + if (service == SUPPLEMENTARY_SERVICE_CW) { + supportsTerminalBased = true; + } + } + } + int syncPreference = b.getInt(KEY_TERMINAL_BASED_CALL_WAITING_SYNC_TYPE_INT, + CALL_WAITING_SYNC_FIRST_CHANGE); + boolean activated = b.getBoolean(KEY_TERMINAL_BASED_CALL_WAITING_DEFAULT_ENABLED_BOOL); + int defaultState = supportsTerminalBased + ? (activated ? TERMINAL_BASED_ACTIVATED : TERMINAL_BASED_NOT_ACTIVATED) + : TERMINAL_BASED_NOT_SUPPORTED; + int savedState = getSavedState(subId); + + if (DBG) { + logd("updateCarrierConfig phoneId=" + mPhone.getPhoneId() + + ", subId=" + subId + ", support=" + supportsTerminalBased + + ", sync=" + syncPreference + ", default=" + defaultState + + ", savedState=" + savedState); + } + + int desiredState = savedState; + + if (ignoreSavedState) { + desiredState = defaultState; + } else if ((mLastSubId != subId) + && (syncPreference == CALL_WAITING_SYNC_FIRST_POWER_UP + || syncPreference == CALL_WAITING_SYNC_FIRST_CHANGE)) { + desiredState = defaultState; + } else { + if (defaultState == TERMINAL_BASED_NOT_SUPPORTED) { + desiredState = TERMINAL_BASED_NOT_SUPPORTED; + } else if (savedState == TERMINAL_BASED_NOT_SUPPORTED) { + desiredState = defaultState; + } + } + + updateState(desiredState, syncPreference, ignoreSavedState); + return true; + } + + private void updateState(int state) { + updateState(state, mSyncPreference, false); + } + + private void updateState(int state, int syncPreference, boolean ignoreSavedState) { + int subId = mPhone.getSubId(); + + if (mLastSubId == subId + && mCallWaitingState == state + && mSyncPreference == syncPreference + && (!ignoreSavedState)) { + return; + } + + int phoneId = mPhone.getPhoneId(); + + logi("updateState phoneId=" + phoneId + + ", subId=" + subId + ", state=" + state + + ", sync=" + syncPreference + ", ignoreSavedState=" + ignoreSavedState); + + SharedPreferences sp = + mContext.getSharedPreferences(PREFERENCE_TBCW, Context.MODE_PRIVATE); + + SharedPreferences.Editor editor = sp.edit(); + editor.putInt(KEY_SUB_ID + phoneId, subId); + editor.putInt(KEY_STATE + subId, state); + editor.putInt(KEY_CS_SYNC + phoneId, syncPreference); + editor.apply(); + + mCallWaitingState = state; + mLastSubId = subId; + mSyncPreference = syncPreference; + if (mLastSubId != subId) { + mCsEnabled = false; + } + + mPhone.setTerminalBasedCallWaitingStatus(mCallWaitingState); + } + + private int getSavedState(int subId) { + SharedPreferences sp = + mContext.getSharedPreferences(PREFERENCE_TBCW, Context.MODE_PRIVATE); + int state = sp.getInt(KEY_STATE + subId, TERMINAL_BASED_NOT_SUPPORTED); + + logi("getSavedState subId=" + subId + ", state=" + state); + + return state; + } + + private void updateSyncState(boolean enabled) { + int phoneId = mPhone.getPhoneId(); + + logi("updateSyncState phoneId=" + phoneId + ", enabled=" + enabled); + + mCsEnabled = enabled; + } + + /** + * @return whether the service is enabled in the CS network + */ + @VisibleForTesting + public boolean getSyncState() { + return mCsEnabled; + } + + private boolean isCircuitSwitchedNetworkAvailable() { + logi("isCircuitSwitchedNetworkAvailable=" + + (mSST.getServiceState().getState() == ServiceState.STATE_IN_SERVICE)); + return mSST.getServiceState().getState() == ServiceState.STATE_IN_SERVICE; + } + + private boolean isImsRegistered() { + logi("isImsRegistered " + mImsRegistered); + return mImsRegistered; + } + + /** + * Sets the registration state of IMS service. + */ + public synchronized void setImsRegistrationState(boolean registered) { + logi("setImsRegistrationState prev=" + mImsRegistered + + ", new=" + registered); + mImsRegistered = registered; + } + + private void registerForNetworkAttached() { + logi("registerForNetworkAttached"); + if (mRegisteredForNetworkAttach) return; + + mSST.registerForNetworkAttached(this, EVENT_REGISTERED_TO_NETWORK, null); + mRegisteredForNetworkAttach = true; + } + + private void unregisterForNetworkAttached() { + logi("unregisterForNetworkAttached"); + if (!mRegisteredForNetworkAttach) return; + + mSST.unregisterForNetworkAttached(this); + removeMessages(EVENT_REGISTERED_TO_NETWORK); + mRegisteredForNetworkAttach = false; + } + + /** + * Sets whether the device supports the terminal-based call waiting. + * Only for test + */ + @VisibleForTesting + public synchronized void setTerminalBasedCallWaitingSupported(boolean supported) { + if (mSupportedByImsService == supported) return; + + logi("setTerminalBasedCallWaitingSupported " + supported); + + mSupportedByImsService = supported; + + if (supported) { + initialize(); + onCarrierConfigurationChanged(mPhone.getPhoneId()); + } else { + CarrierConfigManager ccm = mContext.getSystemService(CarrierConfigManager.class); + if (ccm != null && mCarrierConfigChangeListener != null) { + ccm.unregisterCarrierConfigChangeListener(mCarrierConfigChangeListener); + } + updateState(TERMINAL_BASED_NOT_SUPPORTED); + } + } + + /** + * Notifies that the UE has attached to the network + * Only for test + */ + @VisibleForTesting + public void notifyRegisteredToNetwork() { + sendEmptyMessage(EVENT_REGISTERED_TO_NETWORK); + } + + private boolean isSyncImsOnly() { + return (mSyncPreference == CALL_WAITING_SYNC_IMS_ONLY && mImsRegistered); + } + + /** + * Dump this instance into a readable format for dumpsys usage. + */ + public void dump(PrintWriter printWriter) { + IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, " "); + pw.increaseIndent(); + pw.println("CallWaitingController:"); + pw.println(" mSupportedByImsService=" + mSupportedByImsService); + pw.println(" mValidSubscription=" + mValidSubscription); + pw.println(" mCallWaitingState=" + mCallWaitingState); + pw.println(" mSyncPreference=" + mSyncPreference); + pw.println(" mLastSubId=" + mLastSubId); + pw.println(" mCsEnabled=" + mCsEnabled); + pw.println(" mRegisteredForNetworkAttach=" + mRegisteredForNetworkAttach); + pw.println(" mImsRegistered=" + mImsRegistered); + pw.decreaseIndent(); + } + + private void loge(String msg) { + Rlog.e(LOG_TAG, "[" + mPhone.getPhoneId() + "] " + msg); + } + + private void logi(String msg) { + Rlog.i(LOG_TAG, "[" + mPhone.getPhoneId() + "] " + msg); + } + + private void logd(String msg) { + Rlog.d(LOG_TAG, "[" + mPhone.getPhoneId() + "] " + msg); + } +}
diff --git a/src/java/com/android/internal/telephony/CarrierActionAgent.java b/src/java/com/android/internal/telephony/CarrierActionAgent.java index 42a4f23..6d74c18 100644 --- a/src/java/com/android/internal/telephony/CarrierActionAgent.java +++ b/src/java/com/android/internal/telephony/CarrierActionAgent.java
@@ -27,6 +27,7 @@ import android.os.RegistrantList; import android.provider.Settings; import android.provider.Telephony; +import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import android.util.LocalLog; import android.util.Log; @@ -93,7 +94,11 @@ // ignore rebroadcast since carrier apps are direct boot aware. return; } - sendMessage(obtainMessage(EVENT_SIM_STATE_CHANGED, iccState)); + final int phoneId = intent.getIntExtra(PhoneConstants.PHONE_KEY, + SubscriptionManager.INVALID_PHONE_INDEX); + if (mPhone.getPhoneId() == phoneId) { + sendMessage(obtainMessage(EVENT_SIM_STATE_CHANGED, iccState)); + } } } };
diff --git a/src/java/com/android/internal/telephony/CarrierInfoManager.java b/src/java/com/android/internal/telephony/CarrierInfoManager.java index 3e2baa5..863db93 100644 --- a/src/java/com/android/internal/telephony/CarrierInfoManager.java +++ b/src/java/com/android/internal/telephony/CarrierInfoManager.java
@@ -287,16 +287,17 @@ return; } mLastAccessResetCarrierKey = now; - int[] subIds = context.getSystemService(SubscriptionManager.class) - .getSubscriptionIds(mPhoneId); - if (subIds == null || subIds.length < 1) { + + int subId = SubscriptionManager.getSubscriptionId(mPhoneId); + if (!SubscriptionManager.isValidSubscriptionId(subId)) { Log.e(LOG_TAG, "Could not reset carrier keys, subscription for mPhoneId=" + mPhoneId); return; } + final TelephonyManager telephonyManager = context.getSystemService(TelephonyManager.class) - .createForSubscriptionId(subIds[0]); + .createForSubscriptionId(subId); int carrierId = telephonyManager.getSimCarrierId(); - deleteCarrierInfoForImsiEncryption(context, subIds[0], carrierId); + deleteCarrierInfoForImsiEncryption(context, subId, carrierId); Intent resetIntent = new Intent(TelephonyIntents.ACTION_CARRIER_CERTIFICATE_DOWNLOAD); SubscriptionManager.putPhoneIdAndSubIdExtra(resetIntent, mPhoneId); context.sendBroadcastAsUser(resetIntent, UserHandle.ALL);
diff --git a/src/java/com/android/internal/telephony/CarrierKeyDownloadManager.java b/src/java/com/android/internal/telephony/CarrierKeyDownloadManager.java index edf3d5f..beb6b26 100644 --- a/src/java/com/android/internal/telephony/CarrierKeyDownloadManager.java +++ b/src/java/com/android/internal/telephony/CarrierKeyDownloadManager.java
@@ -16,8 +16,6 @@ package com.android.internal.telephony; -import static android.telephony.CarrierConfigManager.KEY_ALLOW_METERED_NETWORK_FOR_CERT_DOWNLOAD_BOOL; - import static java.nio.charset.StandardCharsets.UTF_8; import android.app.AlarmManager; @@ -32,7 +30,6 @@ import android.os.Handler; import android.os.Message; import android.os.PersistableBundle; -import android.provider.Telephony; import android.telephony.CarrierConfigManager; import android.telephony.ImsiEncryptionInfo; import android.telephony.SubscriptionManager; @@ -122,13 +119,22 @@ mPhone = phone; mContext = phone.getContext(); IntentFilter filter = new IntentFilter(); - filter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED); filter.addAction(INTENT_KEY_RENEWAL_ALARM_PREFIX); filter.addAction(TelephonyIntents.ACTION_CARRIER_CERTIFICATE_DOWNLOAD); mContext.registerReceiver(mBroadcastReceiver, filter, null, phone); mDownloadManager = (DownloadManager) mContext.getSystemService(Context.DOWNLOAD_SERVICE); mTelephonyManager = mContext.getSystemService(TelephonyManager.class) .createForSubscriptionId(mPhone.getSubId()); + CarrierConfigManager carrierConfigManager = mContext.getSystemService( + CarrierConfigManager.class); + // Callback which directly handle config change should be executed on handler thread + carrierConfigManager.registerCarrierConfigChangeListener(this::post, + (slotIndex, subId, carrierId, specificCarrierId) -> { + if (slotIndex == mPhone.getPhoneId()) { + Log.d(LOG_TAG, "Carrier Config changed: slotIndex=" + slotIndex); + handleAlarmOrConfigChange(); + } + }); } private final BroadcastReceiver mDownloadReceiver = new BroadcastReceiver() { @@ -161,12 +167,6 @@ Log.d(LOG_TAG, "Handling reset intent: " + action); sendEmptyMessage(EVENT_ALARM_OR_CONFIG_CHANGE); } - } else if (action.equals(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)) { - if (phoneId == intent.getIntExtra(PhoneConstants.PHONE_KEY, - SubscriptionManager.INVALID_SIM_SLOT_INDEX)) { - Log.d(LOG_TAG, "Carrier Config changed: " + action); - sendEmptyMessage(EVENT_ALARM_OR_CONFIG_CHANGE); - } } } }; @@ -389,14 +389,23 @@ return false; } int subId = mPhone.getSubId(); - PersistableBundle b = carrierConfigManager.getConfigForSubId(subId); - if (b == null) { + PersistableBundle b = null; + try { + b = carrierConfigManager.getConfigForSubId(subId, + CarrierConfigManager.IMSI_KEY_AVAILABILITY_INT, + CarrierConfigManager.IMSI_KEY_DOWNLOAD_URL_STRING, + CarrierConfigManager.KEY_ALLOW_METERED_NETWORK_FOR_CERT_DOWNLOAD_BOOL); + } catch (RuntimeException e) { + Log.e(LOG_TAG, "CarrierConfigLoader is not available."); + } + if (b == null || b.isEmpty()) { return false; } + mKeyAvailability = b.getInt(CarrierConfigManager.IMSI_KEY_AVAILABILITY_INT); mURL = b.getString(CarrierConfigManager.IMSI_KEY_DOWNLOAD_URL_STRING); mAllowedOverMeteredNetwork = b.getBoolean( - KEY_ALLOW_METERED_NETWORK_FOR_CERT_DOWNLOAD_BOOL); + CarrierConfigManager.KEY_ALLOW_METERED_NETWORK_FOR_CERT_DOWNLOAD_BOOL); if (mKeyAvailability == 0 || TextUtils.isEmpty(mURL)) { Log.d(LOG_TAG, "Carrier not enabled or invalid values. mKeyAvailability=" + mKeyAvailability
diff --git a/src/java/com/android/internal/telephony/CarrierPrivilegesTracker.java b/src/java/com/android/internal/telephony/CarrierPrivilegesTracker.java index 1e184bb..ab7ebc4 100644 --- a/src/java/com/android/internal/telephony/CarrierPrivilegesTracker.java +++ b/src/java/com/android/internal/telephony/CarrierPrivilegesTracker.java
@@ -16,11 +16,8 @@ package com.android.internal.telephony; -import static android.telephony.CarrierConfigManager.EXTRA_SLOT_INDEX; -import static android.telephony.CarrierConfigManager.EXTRA_SUBSCRIPTION_INDEX; import static android.telephony.CarrierConfigManager.KEY_CARRIER_CERTIFICATE_STRING_ARRAY; import static android.telephony.SubscriptionManager.INVALID_SIM_SLOT_INDEX; -import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; import static android.telephony.TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS; import static android.telephony.TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS; import static android.telephony.TelephonyManager.CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED; @@ -31,8 +28,6 @@ import static android.telephony.TelephonyManager.SIM_STATE_READY; import static android.telephony.TelephonyManager.SIM_STATE_UNKNOWN; -import static com.android.internal.telephony.SubscriptionInfoUpdater.simStateString; - import android.annotation.ElapsedRealtimeLong; import android.annotation.NonNull; import android.annotation.Nullable; @@ -54,8 +49,6 @@ import android.os.Message; import android.os.PersistableBundle; import android.os.Process; -import android.os.Registrant; -import android.os.RegistrantList; import android.os.SystemClock; import android.os.UserHandle; import android.os.UserManager; @@ -69,7 +62,6 @@ import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; -import android.util.IntArray; import android.util.LocalLog; import android.util.Pair; @@ -86,6 +78,7 @@ import java.util.Collection; import java.util.Collections; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Objects; import java.util.Set; @@ -111,6 +104,7 @@ private static final int PACKAGE_NOT_PRIVILEGED = 0; private static final int PACKAGE_PRIVILEGED_FROM_CARRIER_CONFIG = 1; private static final int PACKAGE_PRIVILEGED_FROM_SIM = 2; + private static final int PACKAGE_PRIVILEGED_FROM_CARRIER_SERVICE_TEST_OVERRIDE = 3; // TODO(b/232273884): Turn feature on when find solution to handle the inter-carriers switching /** @@ -133,25 +127,6 @@ | PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS; /** - * Action to register a Registrant with this Tracker. - * obj: Registrant that will be notified of Carrier Privileged UID changes. - */ - private static final int ACTION_REGISTER_LISTENER = 1; - - /** - * Action to unregister a Registrant with this Tracker. - * obj: Handler used by the Registrant that will be removed. - */ - private static final int ACTION_UNREGISTER_LISTENER = 2; - - /** - * Action for tracking when Carrier Configs are updated. - * arg1: Subscription Id for the Carrier Configs update being broadcast - * arg2: Slot Index for the Carrier Configs update being broadcast - */ - private static final int ACTION_CARRIER_CONFIG_CERTS_UPDATED = 3; - - /** * Action for tracking when the Phone's SIM state changes. * arg1: slotId that this Action applies to * arg2: simState reported by this Broadcast @@ -192,6 +167,14 @@ */ private static final int ACTION_UICC_ACCESS_RULES_LOADED = 10; + /** + * Action to set the test override rule through {@link + * TelephonyManager#setCarrierServicePackageOverride}. + * + * <p>obj: String of the carrierServicePackage from method setCarrierServicePackageOverride. + */ + private static final int ACTION_SET_TEST_OVERRIDE_CARRIER_SERVICE_PACKAGE = 11; + private final Context mContext; private final Phone mPhone; private final PackageManager mPackageManager; @@ -201,7 +184,6 @@ private final TelephonyRegistryManager mTelephonyRegistryManager; @NonNull private final LocalLog mLocalLog = new LocalLog(64); - @NonNull private final RegistrantList mRegistrantList = new RegistrantList(); // Stores rules for Carrier Config-loaded rules @NonNull private final List<UiccAccessRule> mCarrierConfigRules = new ArrayList<>(); // Stores rules for SIM-loaded rules. @@ -211,6 +193,7 @@ // - Empty list indicates test override to simulate no rules (CC and UICC rules are ignored) // - Non-empty list indicates test override with specific rules (CC and UICC rules are ignored) @Nullable private List<UiccAccessRule> mTestOverrideRules = null; + @Nullable private String mTestOverrideCarrierServicePackage = null; // Map of PackageName -> Certificate hashes for that Package @NonNull private final Map<String, Set<String>> mInstalledPackageCerts = new ArrayMap<>(); // Map of PackageName -> UIDs for that Package @@ -300,19 +283,6 @@ if (action == null) return; switch (action) { - case CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED: { - Bundle extras = intent.getExtras(); - int slotIndex = extras.getInt(EXTRA_SLOT_INDEX); - int subId = - extras.getInt( - EXTRA_SUBSCRIPTION_INDEX, INVALID_SUBSCRIPTION_ID); - sendMessage( - obtainMessage( - ACTION_CARRIER_CONFIG_CERTS_UPDATED, - subId, - slotIndex)); - break; - } case TelephonyManager.ACTION_SIM_CARD_STATE_CHANGED: // fall through case TelephonyManager.ACTION_SIM_APPLICATION_STATE_CHANGED: { Bundle extras = intent.getExtras(); @@ -375,13 +345,16 @@ mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE); mCarrierConfigManager = (CarrierConfigManager) mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE); + // Callback is executed in handler thread and directly handles carrier config update + mCarrierConfigManager.registerCarrierConfigChangeListener(this::post, + (slotIndex, subId, carrierId, specificCarrierId) -> handleCarrierConfigUpdated( + subId, slotIndex)); mTelephonyManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE); mTelephonyRegistryManager = (TelephonyRegistryManager) mContext.getSystemService(Context.TELEPHONY_REGISTRY_SERVICE); IntentFilter certFilter = new IntentFilter(); - certFilter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED); certFilter.addAction(TelephonyManager.ACTION_SIM_CARD_STATE_CHANGED); certFilter.addAction(TelephonyManager.ACTION_SIM_APPLICATION_STATE_CHANGED); mContext.registerReceiver(mIntentReceiver, certFilter); @@ -403,20 +376,6 @@ @Override public void handleMessage(Message msg) { switch (msg.what) { - case ACTION_REGISTER_LISTENER: { - handleRegisterListener((Registrant) msg.obj); - break; - } - case ACTION_UNREGISTER_LISTENER: { - handleUnregisterListener((Handler) msg.obj); - break; - } - case ACTION_CARRIER_CONFIG_CERTS_UPDATED: { - int subId = msg.arg1; - int slotIndex = msg.arg2; - handleCarrierConfigUpdated(subId, slotIndex); - break; - } case ACTION_SIM_STATE_UPDATED: { handleSimStateChanged(msg.arg1, msg.arg2); break; @@ -448,6 +407,11 @@ handleUiccAccessRulesLoaded(); break; } + case ACTION_SET_TEST_OVERRIDE_CARRIER_SERVICE_PACKAGE: { + String carrierServicePackage = (String) msg.obj; + handleSetTestOverrideCarrierServicePackage(carrierServicePackage); + break; + } default: { Rlog.e(TAG, "Received unknown msg type: " + msg.what); break; @@ -455,23 +419,6 @@ } } - private void handleRegisterListener(@NonNull Registrant registrant) { - mRegistrantList.add(registrant); - mPrivilegedPackageInfoLock.readLock().lock(); - try { - // Old registrant callback still takes int[] as parameter, need conversion here - int[] uids = intSetToArray(mPrivilegedPackageInfo.mUids); - registrant.notifyResult( - Arrays.copyOf(uids, uids.length)); - } finally { - mPrivilegedPackageInfoLock.readLock().unlock(); - } - } - - private void handleUnregisterListener(@NonNull Handler handler) { - mRegistrantList.remove(handler); - } - private void handleCarrierConfigUpdated(int subId, int slotIndex) { if (slotIndex != mPhone.getPhoneId()) return; @@ -493,7 +440,10 @@ @NonNull private List<UiccAccessRule> getCarrierConfigRules(int subId) { - PersistableBundle carrierConfigs = mCarrierConfigManager.getConfigForSubId(subId); + PersistableBundle carrierConfigs = + CarrierConfigManager.getCarrierConfigSubset( + mContext, subId, KEY_CARRIER_CERTIFICATE_STRING_ARRAY); + // CarrierConfigManager#isConfigForIdentifiedCarrier can handle null or empty bundle if (!mCarrierConfigManager.isConfigForIdentifiedCarrier(carrierConfigs)) { return Collections.EMPTY_LIST; } @@ -529,7 +479,7 @@ SystemClock.uptimeMillis() + CLEAR_UICC_RULES_DELAY_MILLIS; sendMessageAtTime(obtainMessage(ACTION_CLEAR_UICC_RULES), mClearUiccRulesUptimeMillis); - mLocalLog.log("SIM is gone, simState=" + simStateString(simState) + mLocalLog.log("SIM is gone, simState=" + TelephonyManager.simStateToString(simState) + ". Delay " + TimeUnit.MILLISECONDS.toSeconds( CLEAR_UICC_RULES_DELAY_MILLIS) + " seconds to clear UICC rules."); } else { @@ -614,10 +564,10 @@ List<Signature> signatures = UiccAccessRule.getSignatures(pkg); for (Signature signature : signatures) { byte[] sha1 = UiccAccessRule.getCertHash(signature, SHA_1); - certs.add(IccUtils.bytesToHexString(sha1).toUpperCase()); + certs.add(IccUtils.bytesToHexString(sha1).toUpperCase(Locale.ROOT)); byte[] sha256 = UiccAccessRule.getCertHash(signature, SHA_256); - certs.add(IccUtils.bytesToHexString(sha256).toUpperCase()); + certs.add(IccUtils.bytesToHexString(sha256).toUpperCase(Locale.ROOT)); } mInstalledPackageCerts.put(pkg.packageName, certs); @@ -731,12 +681,6 @@ mPrivilegedPackageInfoLock.readLock().lock(); try { - // The obsoleted callback only care about UIDs - if (carrierPrivilegesUidsChanged) { - int[] uids = intSetToArray(mPrivilegedPackageInfo.mUids); - mRegistrantList.notifyResult(Arrays.copyOf(uids, uids.length)); - } - if (carrierPrivilegesPackageNamesChanged || carrierPrivilegesUidsChanged) { mTelephonyRegistryManager.notifyCarrierPrivilegesChanged( mPhone.getPhoneId(), @@ -768,6 +712,7 @@ final int priv = getPackagePrivilegedStatus(e.getKey(), e.getValue()); switch (priv) { case PACKAGE_PRIVILEGED_FROM_SIM: + case PACKAGE_PRIVILEGED_FROM_CARRIER_SERVICE_TEST_OVERRIDE: // fallthrough carrierServiceEligiblePackages.add(e.getKey()); // fallthrough case PACKAGE_PRIVILEGED_FROM_CARRIER_CONFIG: @@ -809,7 +754,9 @@ } for (UiccAccessRule rule : mCarrierConfigRules) { if (rule.matches(cert, pkgName)) { - return PACKAGE_PRIVILEGED_FROM_CARRIER_CONFIG; + return pkgName.equals(mTestOverrideCarrierServicePackage) + ? PACKAGE_PRIVILEGED_FROM_CARRIER_SERVICE_TEST_OVERRIDE + : PACKAGE_PRIVILEGED_FROM_CARRIER_CONFIG; } } } @@ -882,33 +829,6 @@ } /** - * Registers the given Registrant with this tracker. - * - * <p>After being registered, the Registrant will be notified with the current Carrier - * Privileged UIDs for this Phone. - * - * @deprecated Use {@link TelephonyManager#addCarrierPrivilegesListener} instead, which also - * provides package names - * <p>TODO(b/211658797) migrate callers, then delete all Registrant logic from CPT - */ - @Deprecated - public void registerCarrierPrivilegesListener(@NonNull Handler h, int what, - @Nullable Object obj) { - sendMessage(obtainMessage(ACTION_REGISTER_LISTENER, new Registrant(h, what, obj))); - } - - /** - * Unregisters the given listener with this tracker. - * - * @deprecated Use {@link TelephonyManager#removeCarrierPrivilegesListener} instead - * <p>TODO(b/211658797) migrate callers, then delete all Registrant logic from CPT - */ - @Deprecated - public void unregisterCarrierPrivilegesListener(@NonNull Handler handler) { - sendMessage(obtainMessage(ACTION_UNREGISTER_LISTENER, handler)); - } - - /** * Set test carrier privilege rules which will override the actual rules on both Carrier Config * and SIM. * @@ -921,6 +841,30 @@ sendMessage(obtainMessage(ACTION_SET_TEST_OVERRIDE_RULE, carrierPrivilegeRules)); } + /** + * Override the carrier provisioning package, if it exists. + * + * <p>This API is to be used ONLY for testing, and requires the provided package to be carrier + * privileged. While this override is set, ONLY the specified package will be considered + * eligible to be bound as the carrier provisioning package, and any existing bindings will be + * terminated. + * + * @param carrierServicePackage the package to be used as the overridden carrier service + * package, or {@code null} to reset override + * @see TelephonyManager#setCarrierServicePackageOverride + */ + public void setTestOverrideCarrierServicePackage(@Nullable String carrierServicePackage) { + sendMessage(obtainMessage( + ACTION_SET_TEST_OVERRIDE_CARRIER_SERVICE_PACKAGE, carrierServicePackage)); + } + + private void handleSetTestOverrideCarrierServicePackage( + @Nullable String carrierServicePackage) { + mTestOverrideCarrierServicePackage = carrierServicePackage; + refreshInstalledPackageCache(); + maybeUpdatePrivilegedPackagesAndNotifyRegistrants(); + } + private void handleSetTestOverrideRules(@Nullable String carrierPrivilegeRules) { if (carrierPrivilegeRules == null) { mTestOverrideRules = null; @@ -1089,6 +1033,11 @@ String carrierServicePackageName = null; for (ResolveInfo resolveInfo : carrierServiceResolveInfos) { String packageName = getPackageName(resolveInfo); + if (mTestOverrideCarrierServicePackage != null + && !mTestOverrideCarrierServicePackage.equals(packageName)) { + continue; + } + if (simPrivilegedPackages.contains(packageName)) { carrierServicePackageName = packageName; break; @@ -1098,11 +1047,4 @@ ? new Pair<>(null, Process.INVALID_UID) : new Pair<>(carrierServicePackageName, getPackageUid(carrierServicePackageName)); } - - @NonNull - private static int[] intSetToArray(@NonNull Set<Integer> intSet) { - IntArray converter = new IntArray(intSet.size()); - intSet.forEach(converter::add); - return converter.toArray(); - } }
diff --git a/src/java/com/android/internal/telephony/CarrierResolver.java b/src/java/com/android/internal/telephony/CarrierResolver.java index ec6a5a0..8a9b3e3 100644 --- a/src/java/com/android/internal/telephony/CarrierResolver.java +++ b/src/java/com/android/internal/telephony/CarrierResolver.java
@@ -43,6 +43,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.telephony.metrics.CarrierIdMatchStats; import com.android.internal.telephony.metrics.TelephonyMetrics; +import com.android.internal.telephony.subscription.SubscriptionManagerService; import com.android.internal.telephony.uicc.IccRecords; import com.android.internal.telephony.uicc.UiccController; import com.android.internal.telephony.util.TelephonyUtils; @@ -54,6 +55,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Locale; /** * CarrierResolver identifies the subscription carrier and returns a canonical carrier Id @@ -191,14 +193,14 @@ } /** - * This is triggered from SubscriptionInfoUpdater after sim state change. + * This is triggered from UiccController after sim state change. * The sequence of sim loading would be - * 1. ACTION_SUBINFO_CONTENT_CHANGE + * 1. OnSubscriptionsChangedListener * 2. ACTION_SIM_STATE_CHANGED/ACTION_SIM_CARD_STATE_CHANGED * /ACTION_SIM_APPLICATION_STATE_CHANGED * 3. ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED * - * For SIM refresh either reset or init refresh type, SubscriptionInfoUpdater will re-trigger + * For SIM refresh either reset or init refresh type, UiccController will re-trigger * carrier identification with sim loaded state. Framework today silently handle single file * refresh type. * TODO: check fileId from single file refresh, if the refresh file is IMSI, gid1 or other @@ -547,7 +549,7 @@ // subscriptioninfo db to make sure we have correct carrier id set. if (SubscriptionManager.isValidSubscriptionId(mPhone.getSubId()) && !isSimOverride) { // only persist carrier id to simInfo db when subId is valid. - SubscriptionController.getInstance().setCarrierId(mCarrierId, mPhone.getSubId()); + SubscriptionManagerService.getInstance().setCarrierId(mPhone.getSubId(), mCarrierId); } } @@ -751,7 +753,8 @@ // Ideally we should do full string match. However due to SIM manufacture issues // gid from some SIM might has garbage tail. private boolean gidMatch(String gidFromSim, String gid) { - return (gidFromSim != null) && gidFromSim.toLowerCase().startsWith(gid.toLowerCase()); + return (gidFromSim != null) && gidFromSim.toLowerCase(Locale.ROOT) + .startsWith(gid.toLowerCase(Locale.ROOT)); } private boolean carrierPrivilegeRulesMatch(List<String> certsFromSubscription,
diff --git a/src/java/com/android/internal/telephony/CarrierServiceBindHelper.java b/src/java/com/android/internal/telephony/CarrierServiceBindHelper.java index dfa53b3..960d794 100644 --- a/src/java/com/android/internal/telephony/CarrierServiceBindHelper.java +++ b/src/java/com/android/internal/telephony/CarrierServiceBindHelper.java
@@ -16,6 +16,7 @@ package com.android.internal.telephony; +import android.annotation.NonNull; import android.compat.annotation.UnsupportedAppUsage; import android.content.BroadcastReceiver; import android.content.ComponentName; @@ -200,7 +201,13 @@ } } - void updateForPhoneId(int phoneId, String simState) { + /** + * Update SIM state. + * + * @param phoneId The phone id. + * @param simState The legacy SIM state. + */ + public void updateForPhoneId(int phoneId, @NonNull String simState) { logdWithLocalLog("update binding for phoneId: " + phoneId + " simState: " + simState); if (!SubscriptionManager.isValidPhoneId(phoneId)) { return;
diff --git a/src/java/com/android/internal/telephony/CarrierServiceStateTracker.java b/src/java/com/android/internal/telephony/CarrierServiceStateTracker.java index 64dc7ec..6b44998 100644 --- a/src/java/com/android/internal/telephony/CarrierServiceStateTracker.java +++ b/src/java/com/android/internal/telephony/CarrierServiceStateTracker.java
@@ -19,10 +19,8 @@ import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; -import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; -import android.content.IntentFilter; import android.content.res.Resources; import android.os.Handler; import android.os.HandlerExecutor; @@ -102,8 +100,34 @@ this.mSST = sst; mTelephonyManager = mPhone.getContext().getSystemService( TelephonyManager.class).createForSubscriptionId(mPhone.getSubId()); - phone.getContext().registerReceiver(mBroadcastReceiver, new IntentFilter( - CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)); + CarrierConfigManager ccm = mPhone.getContext().getSystemService(CarrierConfigManager.class); + ccm.registerCarrierConfigChangeListener( + mPhone.getContext().getMainExecutor(), + (slotIndex, subId, carrierId, specificCarrierId) -> { + if (slotIndex != mPhone.getPhoneId()) return; + + Rlog.d(LOG_TAG, "onCarrierConfigChanged: slotIndex=" + slotIndex + + ", subId=" + subId + ", carrierId=" + carrierId); + + // Only get carrier configs used for EmergencyNetworkNotification + // and PrefNetworkNotification + PersistableBundle b = + CarrierConfigManager.getCarrierConfigSubset( + mPhone.getContext(), + mPhone.getSubId(), + CarrierConfigManager.KEY_EMERGENCY_NOTIFICATION_DELAY_INT, + CarrierConfigManager + .KEY_PREF_NETWORK_NOTIFICATION_DELAY_INT); + if (b.isEmpty()) return; + + for (Map.Entry<Integer, NotificationType> entry : + mNotificationTypeMap.entrySet()) { + NotificationType notificationType = entry.getValue(); + notificationType.setDelay(b); + } + handleConfigChanges(); + }); + // Listen for subscriber changes SubscriptionManager.from(mPhone.getContext()).addOnSubscriptionsChangedListener( new OnSubscriptionsChangedListener(this.getLooper()) { @@ -246,7 +270,7 @@ TelephonyManager tm = ((TelephonyManager) context.getSystemService( Context.TELEPHONY_SERVICE)).createForSubscriptionId(mPhone.getSubId()); - boolean isCarrierConfigEnabled = isCarrierConfigEnableNr(context); + boolean isCarrierConfigEnabled = isCarrierConfigEnableNr(); boolean isRadioAccessFamilySupported = checkSupportedBitmask( tm.getSupportedRadioAccessFamily(), TelephonyManager.NETWORK_TYPE_BITMASK_NR); boolean isNrNetworkTypeAllowed = checkSupportedBitmask( @@ -261,15 +285,13 @@ return (isCarrierConfigEnabled && isRadioAccessFamilySupported && isNrNetworkTypeAllowed); } - private boolean isCarrierConfigEnableNr(Context context) { - CarrierConfigManager carrierConfigManager = (CarrierConfigManager) - context.getSystemService(Context.CARRIER_CONFIG_SERVICE); - if (carrierConfigManager == null) { - Rlog.e(LOG_TAG, "isCarrierConfigEnableNr: CarrierConfigManager is null"); - return false; - } - PersistableBundle config = carrierConfigManager.getConfigForSubId(mPhone.getSubId()); - if (config == null) { + private boolean isCarrierConfigEnableNr() { + PersistableBundle config = + CarrierConfigManager.getCarrierConfigSubset( + mPhone.getContext(), + mPhone.getSubId(), + CarrierConfigManager.KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY); + if (config.isEmpty()) { Rlog.e(LOG_TAG, "isCarrierConfigEnableNr: Cannot get config " + mPhone.getSubId()); return false; } @@ -348,21 +370,6 @@ return (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); } - private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - CarrierConfigManager carrierConfigManager = (CarrierConfigManager) - context.getSystemService(Context.CARRIER_CONFIG_SERVICE); - PersistableBundle b = carrierConfigManager.getConfigForSubId(mPhone.getSubId()); - - for (Map.Entry<Integer, NotificationType> entry : mNotificationTypeMap.entrySet()) { - NotificationType notificationType = entry.getValue(); - notificationType.setDelay(b); - } - handleConfigChanges(); - } - }; - /** * Post a notification to the NotificationManager for changing network type. */ @@ -376,6 +383,7 @@ Notification.Builder builder = getNotificationBuilder(notificationType); // set some common attributes builder.setWhen(System.currentTimeMillis()) + .setShowWhen(true) .setAutoCancel(true) .setSmallIcon(com.android.internal.R.drawable.stat_sys_warning) .setColor(context.getResources().getColor( @@ -590,7 +598,7 @@ .setContentTitle(title) .setStyle(new Notification.BigTextStyle().bigText(details)) .setContentText(details) - .setFlag(Notification.FLAG_NO_CLEAR, true) + .setOngoing(true) .setChannelId(NotificationChannelController.CHANNEL_ID_WFC); } }
diff --git a/src/java/com/android/internal/telephony/CarrierSignalAgent.java b/src/java/com/android/internal/telephony/CarrierSignalAgent.java index 2914ff9..3250cef 100644 --- a/src/java/com/android/internal/telephony/CarrierSignalAgent.java +++ b/src/java/com/android/internal/telephony/CarrierSignalAgent.java
@@ -20,11 +20,8 @@ import android.annotation.Nullable; import android.content.ActivityNotFoundException; -import android.content.BroadcastReceiver; import android.content.ComponentName; -import android.content.Context; import android.content.Intent; -import android.content.IntentFilter; import android.content.pm.PackageManager; import android.net.ConnectivityManager; import android.net.Network; @@ -48,7 +45,6 @@ import java.io.FileDescriptor; import java.io.PrintWriter; -import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.Map; @@ -102,51 +98,45 @@ /** * This is a list of supported signals from CarrierSignalAgent */ - private static final Set<String> VALID_CARRIER_SIGNAL_ACTIONS = new HashSet<>(Arrays.asList( + private static final Set<String> VALID_CARRIER_SIGNAL_ACTIONS = Set.of( TelephonyManager.ACTION_CARRIER_SIGNAL_PCO_VALUE, TelephonyManager.ACTION_CARRIER_SIGNAL_REDIRECTED, TelephonyManager.ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED, TelephonyManager.ACTION_CARRIER_SIGNAL_RESET, - TelephonyManager.ACTION_CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE)); + TelephonyManager.ACTION_CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE); - private static final Map<String, String> NEW_ACTION_TO_COMPAT_MAP = - new HashMap<String, String>() {{ - put(TelephonyManager.ACTION_CARRIER_SIGNAL_PCO_VALUE, - TelephonyIntents.ACTION_CARRIER_SIGNAL_PCO_VALUE); - put(TelephonyManager.ACTION_CARRIER_SIGNAL_REDIRECTED, - TelephonyIntents.ACTION_CARRIER_SIGNAL_REDIRECTED); - put(TelephonyManager.ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED, - TelephonyIntents.ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED); - put(TelephonyManager.ACTION_CARRIER_SIGNAL_RESET, - TelephonyIntents.ACTION_CARRIER_SIGNAL_RESET); - put(TelephonyManager.ACTION_CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE, - TelephonyIntents.ACTION_CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE); - }}; + private static final Map<String, String> NEW_ACTION_TO_COMPAT_MAP = Map.of( + TelephonyManager.ACTION_CARRIER_SIGNAL_PCO_VALUE, + TelephonyIntents.ACTION_CARRIER_SIGNAL_PCO_VALUE, + TelephonyManager.ACTION_CARRIER_SIGNAL_REDIRECTED, + TelephonyIntents.ACTION_CARRIER_SIGNAL_REDIRECTED, + TelephonyManager.ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED, + TelephonyIntents.ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED, + TelephonyManager.ACTION_CARRIER_SIGNAL_RESET, + TelephonyIntents.ACTION_CARRIER_SIGNAL_RESET, + TelephonyManager.ACTION_CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE, + TelephonyIntents.ACTION_CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE); private static final Map<String, String> COMPAT_ACTION_TO_NEW_MAP = NEW_ACTION_TO_COMPAT_MAP .entrySet().stream().collect(Collectors.toMap(Map.Entry::getValue, Map.Entry::getKey)); private final LocalLog mErrorLocalLog = new LocalLog(16); - private final BroadcastReceiver mReceiver = new BroadcastReceiver() { - public void onReceive(Context context, Intent intent) { - String action = intent.getAction(); - if (DBG) log("CarrierSignalAgent receiver action: " + action); - if (action.equals(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)) { - loadCarrierConfig(); - } - } - }; - private ConnectivityManager.NetworkCallback mNetworkCallback; /** Constructor */ public CarrierSignalAgent(Phone phone) { mPhone = phone; + CarrierConfigManager carrierConfigManager = mPhone.getContext().getSystemService( + CarrierConfigManager.class); loadCarrierConfig(); - // reload configurations on CARRIER_CONFIG_CHANGED - mPhone.getContext().registerReceiver(mReceiver, - new IntentFilter(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)); + carrierConfigManager.registerCarrierConfigChangeListener( + mPhone.getContext().getMainExecutor(), + (slotIndex, subId, carrierId, specificCarrierId) -> { + if (slotIndex == mPhone.getPhoneId()) { + loadCarrierConfig(); + } + }); mPhone.getCarrierActionAgent().registerForCarrierAction( CarrierActionAgent.CARRIER_ACTION_REPORT_DEFAULT_NETWORK_STATUS, this, EVENT_REGISTER_DEFAULT_NETWORK_AVAIL, null, false); @@ -208,45 +198,47 @@ * load carrier config and cached the results into a hashMap action -> array list of components. */ private void loadCarrierConfig() { - CarrierConfigManager configManager = (CarrierConfigManager) mPhone.getContext() - .getSystemService(Context.CARRIER_CONFIG_SERVICE); - PersistableBundle b = null; - if (configManager != null) { - b = configManager.getConfigForSubId(mPhone.getSubId()); + PersistableBundle b = + CarrierConfigManager.getCarrierConfigSubset( + mPhone.getContext(), + mPhone.getSubId(), + KEY_CARRIER_APP_WAKE_SIGNAL_CONFIG_STRING_ARRAY, + KEY_CARRIER_APP_NO_WAKE_SIGNAL_CONFIG_STRING_ARRAY); + if (b.isEmpty()) { + return; } - if (b != null) { - synchronized (mCachedWakeSignalConfigs) { - log("Loading carrier config: " + KEY_CARRIER_APP_WAKE_SIGNAL_CONFIG_STRING_ARRAY); - Map<String, Set<ComponentName>> config = parseAndCache( - b.getStringArray(KEY_CARRIER_APP_WAKE_SIGNAL_CONFIG_STRING_ARRAY)); - // In some rare cases, up-to-date config could be fetched with delay and all signals - // have already been delivered the receivers from the default carrier config. - // To handle this raciness, we should notify those receivers (from old configs) - // and reset carrier actions. This should be done before cached Config got purged - // and written with the up-to-date value, Otherwise those receivers from the - // old config might lingers without properly clean-up. - if (!mCachedWakeSignalConfigs.isEmpty() - && !config.equals(mCachedWakeSignalConfigs)) { - if (VDBG) log("carrier config changed, reset receivers from old config"); - mPhone.getCarrierActionAgent().sendEmptyMessage( - CarrierActionAgent.CARRIER_ACTION_RESET); - } - mCachedWakeSignalConfigs = config; - } - synchronized (mCachedNoWakeSignalConfigs) { - log("Loading carrier config: " - + KEY_CARRIER_APP_NO_WAKE_SIGNAL_CONFIG_STRING_ARRAY); - Map<String, Set<ComponentName>> config = parseAndCache( - b.getStringArray(KEY_CARRIER_APP_NO_WAKE_SIGNAL_CONFIG_STRING_ARRAY)); - if (!mCachedNoWakeSignalConfigs.isEmpty() - && !config.equals(mCachedNoWakeSignalConfigs)) { - if (VDBG) log("carrier config changed, reset receivers from old config"); - mPhone.getCarrierActionAgent().sendEmptyMessage( - CarrierActionAgent.CARRIER_ACTION_RESET); - } - mCachedNoWakeSignalConfigs = config; + synchronized (mCachedWakeSignalConfigs) { + log("Loading carrier config: " + KEY_CARRIER_APP_WAKE_SIGNAL_CONFIG_STRING_ARRAY); + Map<String, Set<ComponentName>> config = parseAndCache( + b.getStringArray(KEY_CARRIER_APP_WAKE_SIGNAL_CONFIG_STRING_ARRAY)); + // In some rare cases, up-to-date config could be fetched with delay and all signals + // have already been delivered the receivers from the default carrier config. + // To handle this raciness, we should notify those receivers (from old configs) + // and reset carrier actions. This should be done before cached Config got purged + // and written with the up-to-date value, Otherwise those receivers from the + // old config might lingers without properly clean-up. + if (!mCachedWakeSignalConfigs.isEmpty() + && !config.equals(mCachedWakeSignalConfigs)) { + if (VDBG) log("carrier config changed, reset receivers from old config"); + mPhone.getCarrierActionAgent().sendEmptyMessage( + CarrierActionAgent.CARRIER_ACTION_RESET); } + mCachedWakeSignalConfigs = config; + } + + synchronized (mCachedNoWakeSignalConfigs) { + log("Loading carrier config: " + + KEY_CARRIER_APP_NO_WAKE_SIGNAL_CONFIG_STRING_ARRAY); + Map<String, Set<ComponentName>> config = parseAndCache( + b.getStringArray(KEY_CARRIER_APP_NO_WAKE_SIGNAL_CONFIG_STRING_ARRAY)); + if (!mCachedNoWakeSignalConfigs.isEmpty() + && !config.equals(mCachedNoWakeSignalConfigs)) { + if (VDBG) log("carrier config changed, reset receivers from old config"); + mPhone.getCarrierActionAgent().sendEmptyMessage( + CarrierActionAgent.CARRIER_ACTION_RESET); + } + mCachedNoWakeSignalConfigs = config; } }
diff --git a/src/java/com/android/internal/telephony/CellBroadcastConfigTracker.java b/src/java/com/android/internal/telephony/CellBroadcastConfigTracker.java new file mode 100644 index 0000000..82d4409 --- /dev/null +++ b/src/java/com/android/internal/telephony/CellBroadcastConfigTracker.java
@@ -0,0 +1,534 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.telephony; + +import android.annotation.NonNull; +import android.os.AsyncResult; +import android.os.Build; +import android.os.Handler; +import android.os.HandlerExecutor; +import android.os.Message; +import android.telephony.CellBroadcastIdRange; +import android.telephony.SmsCbMessage; +import android.telephony.SubscriptionManager; +import android.telephony.TelephonyManager; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.telephony.cdma.CdmaSmsBroadcastConfigInfo; +import com.android.internal.telephony.gsm.SmsBroadcastConfigInfo; +import com.android.internal.util.State; +import com.android.internal.util.StateMachine; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.function.Consumer; + +/** + * This class is to track the state to set cell broadcast config + */ + +public final class CellBroadcastConfigTracker extends StateMachine { + private static final boolean DBG = Build.IS_DEBUGGABLE; + + private static final int EVENT_REQUEST = 1; + private static final int EVENT_CONFIGURATION_DONE = 2; + private static final int EVENT_ACTIVATION_DONE = 3; + private static final int EVENT_RADIO_OFF = 4; + private static final int EVENT_SUBSCRIPTION_CHANGED = 5; + + private static final int SMS_CB_CODE_SCHEME_MIN = 0; + private static final int SMS_CB_CODE_SCHEME_MAX = 255; + + // Cache of current cell broadcast id ranges of 3gpp + private List<CellBroadcastIdRange> mCbRanges3gpp = new CopyOnWriteArrayList<>(); + // Cache of current cell broadcast id ranges of 3gpp2 + private List<CellBroadcastIdRange> mCbRanges3gpp2 = new CopyOnWriteArrayList<>(); + private Phone mPhone; + @VisibleForTesting + public int mSubId; + @VisibleForTesting + public final SubscriptionManager.OnSubscriptionsChangedListener mSubChangedListener = + new SubscriptionManager.OnSubscriptionsChangedListener() { + @Override + public void onSubscriptionsChanged() { + sendMessage(EVENT_SUBSCRIPTION_CHANGED); + } + }; + + /** + * The class is to present the request to set cell broadcast id ranges + */ + private static class Request { + private final List<CellBroadcastIdRange> mCbRangesRequest3gpp = + new CopyOnWriteArrayList<>(); + private final List<CellBroadcastIdRange> mCbRangesRequest3gpp2 = + new CopyOnWriteArrayList<>(); + Consumer<Integer> mCallback; + + Request(@NonNull List<CellBroadcastIdRange> ranges, @NonNull Consumer<Integer> callback) { + ranges.forEach(r -> { + if (r.getType() == SmsCbMessage.MESSAGE_FORMAT_3GPP) { + mCbRangesRequest3gpp.add(r); + } else { + mCbRangesRequest3gpp2.add(r); + } + }); + mCallback = callback; + } + + List<CellBroadcastIdRange> get3gppRanges() { + return mCbRangesRequest3gpp; + } + + List<CellBroadcastIdRange> get3gpp2Ranges() { + return mCbRangesRequest3gpp2; + } + + Consumer<Integer> getCallback() { + return mCallback; + } + + @Override + public String toString() { + return "Request[mCbRangesRequest3gpp = " + mCbRangesRequest3gpp + ", " + + "mCbRangesRequest3gpp2 = " + mCbRangesRequest3gpp2 + ", " + + "mCallback = " + mCallback + "]"; + } + } + + /** + * The default state. + */ + private class DefaultState extends State { + @Override + public void enter() { + mPhone.registerForRadioOffOrNotAvailable(getHandler(), EVENT_RADIO_OFF, null); + mPhone.getContext().getSystemService(SubscriptionManager.class) + .addOnSubscriptionsChangedListener(new HandlerExecutor(getHandler()), + mSubChangedListener); + } + + @Override + public void exit() { + mPhone.unregisterForRadioOffOrNotAvailable(getHandler()); + mPhone.getContext().getSystemService(SubscriptionManager.class) + .removeOnSubscriptionsChangedListener(mSubChangedListener); + } + + @Override + public boolean processMessage(Message msg) { + boolean retVal = HANDLED; + if (DBG) { + logd("DefaultState message:" + msg.what); + } + switch (msg.what) { + case EVENT_RADIO_OFF: + resetConfig(); + break; + case EVENT_SUBSCRIPTION_CHANGED: + int subId = mPhone.getSubId(); + if (mSubId != subId) { + log("SubId changed from " + mSubId + " to " + subId); + mSubId = subId; + resetConfig(); + } + break; + default: + log("unexpected message!"); + break; + + } + + return retVal; + } + } + + private DefaultState mDefaultState = new DefaultState(); + + /* + * The idle state which does not have ongoing radio request. + */ + private class IdleState extends State { + @Override + public boolean processMessage(Message msg) { + boolean retVal = NOT_HANDLED; + if (DBG) { + logd("IdleState message:" + msg.what); + } + switch (msg.what) { + case EVENT_REQUEST: + Request request = (Request) msg.obj; + if (DBG) { + logd("IdleState handle EVENT_REQUEST with request:" + request); + } + if (!mCbRanges3gpp.equals(request.get3gppRanges())) { + // set gsm config if the config is changed + setGsmConfig(request.get3gppRanges(), request); + transitionTo(mGsmConfiguringState); + } else if (!mCbRanges3gpp2.equals(request.get3gpp2Ranges())) { + // set cdma config directly if no gsm config change but cdma config is + // changed + setCdmaConfig(request.get3gpp2Ranges(), request); + transitionTo(mCdmaConfiguringState); + } else { + logd("Do nothing as the requested ranges are same as now"); + request.getCallback().accept( + TelephonyManager.CELL_BROADCAST_RESULT_SUCCESS); + } + retVal = HANDLED; + break; + default: + break; + } + return retVal; + } + } + private IdleState mIdleState = new IdleState(); + + /* + * The state waiting for the result to set gsm config. + */ + private class GsmConfiguringState extends State { + @Override + public boolean processMessage(Message msg) { + boolean retVal = NOT_HANDLED; + if (DBG) { + logd("GsmConfiguringState message:" + msg.what); + } + switch (msg.what) { + case EVENT_REQUEST: + deferMessage(msg); + retVal = HANDLED; + break; + case EVENT_CONFIGURATION_DONE: + AsyncResult ar = (AsyncResult) msg.obj; + Request request = (Request) ar.userObj; + if (DBG) { + logd("GsmConfiguringState handle EVENT_CONFIGURATION_DONE with request:" + + request); + } + if (ar.exception == null) { + // set gsm activation and transit to gsm activating state + setActivation(SmsCbMessage.MESSAGE_FORMAT_3GPP, + !request.get3gppRanges().isEmpty(), request); + transitionTo(mGsmActivatingState); + } else { + logd("Failed to set gsm config"); + request.getCallback().accept( + TelephonyManager.CELL_BROADCAST_RESULT_FAIL_CONFIG); + // transit to idle state on the failure case + transitionTo(mIdleState); + } + retVal = HANDLED; + break; + default: + break; + } + return retVal; + } + } + private GsmConfiguringState mGsmConfiguringState = new GsmConfiguringState(); + + /* + * The state waiting for the result to set gsm activation. + */ + private class GsmActivatingState extends State { + @Override + public boolean processMessage(Message msg) { + boolean retVal = NOT_HANDLED; + if (DBG) { + logd("GsmActivatingState message:" + msg.what); + } + switch (msg.what) { + case EVENT_REQUEST: + deferMessage(msg); + retVal = HANDLED; + break; + case EVENT_ACTIVATION_DONE: + AsyncResult ar = (AsyncResult) msg.obj; + Request request = (Request) ar.userObj; + if (DBG) { + logd("GsmActivatingState handle EVENT_ACTIVATION_DONE with request:" + + request); + } + if (ar.exception == null) { + mCbRanges3gpp = request.get3gppRanges(); + if (!mCbRanges3gpp2.equals(request.get3gpp2Ranges())) { + // set cdma config and transit to cdma configuring state if the config + // is changed. + setCdmaConfig(request.get3gpp2Ranges(), request); + transitionTo(mCdmaConfiguringState); + } else { + logd("Done as no need to update ranges for 3gpp2"); + request.getCallback().accept( + TelephonyManager.CELL_BROADCAST_RESULT_SUCCESS); + // transit to idle state if there is no cdma config change + transitionTo(mIdleState); + } + } else { + logd("Failed to set gsm activation"); + request.getCallback().accept( + TelephonyManager.CELL_BROADCAST_RESULT_FAIL_ACTIVATION); + // transit to idle state on the failure case + transitionTo(mIdleState); + } + retVal = HANDLED; + break; + default: + break; + } + return retVal; + } + } + private GsmActivatingState mGsmActivatingState = new GsmActivatingState(); + + /* + * The state waiting for the result to set cdma config. + */ + private class CdmaConfiguringState extends State { + @Override + public boolean processMessage(Message msg) { + boolean retVal = NOT_HANDLED; + if (DBG) { + logd("CdmaConfiguringState message:" + msg.what); + } + switch (msg.what) { + case EVENT_REQUEST: + deferMessage(msg); + retVal = HANDLED; + break; + case EVENT_CONFIGURATION_DONE: + AsyncResult ar = (AsyncResult) msg.obj; + Request request = (Request) ar.userObj; + if (DBG) { + logd("CdmaConfiguringState handle EVENT_ACTIVATION_DONE with request:" + + request); + } + if (ar.exception == null) { + // set cdma activation and transit to cdma activating state + setActivation(SmsCbMessage.MESSAGE_FORMAT_3GPP2, + !request.get3gpp2Ranges().isEmpty(), request); + transitionTo(mCdmaActivatingState); + } else { + logd("Failed to set cdma config"); + request.getCallback().accept( + TelephonyManager.CELL_BROADCAST_RESULT_FAIL_CONFIG); + // transit to idle state on the failure case + transitionTo(mIdleState); + } + retVal = HANDLED; + break; + default: + break; + } + return retVal; + } + } + private CdmaConfiguringState mCdmaConfiguringState = new CdmaConfiguringState(); + + /* + * The state waiting for the result to set cdma activation. + */ + private class CdmaActivatingState extends State { + @Override + public boolean processMessage(Message msg) { + boolean retVal = NOT_HANDLED; + if (DBG) { + logd("CdmaActivatingState message:" + msg.what); + } + switch (msg.what) { + case EVENT_REQUEST: + deferMessage(msg); + retVal = HANDLED; + break; + case EVENT_ACTIVATION_DONE: + AsyncResult ar = (AsyncResult) msg.obj; + Request request = (Request) ar.userObj; + if (DBG) { + logd("CdmaActivatingState handle EVENT_ACTIVATION_DONE with request:" + + request); + } + if (ar.exception == null) { + mCbRanges3gpp2 = request.get3gpp2Ranges(); + request.getCallback().accept( + TelephonyManager.CELL_BROADCAST_RESULT_SUCCESS); + } else { + logd("Failed to set cdma activation"); + request.getCallback().accept( + TelephonyManager.CELL_BROADCAST_RESULT_FAIL_ACTIVATION); + } + // transit to idle state anyway + transitionTo(mIdleState); + retVal = HANDLED; + break; + default: + break; + } + return retVal; + } + } + private CdmaActivatingState mCdmaActivatingState = new CdmaActivatingState(); + + private CellBroadcastConfigTracker(Phone phone) { + super("CellBroadcastConfigTracker-" + phone.getPhoneId()); + init(phone); + } + + private CellBroadcastConfigTracker(Phone phone, Handler handler) { + super("CellBroadcastConfigTracker-" + phone.getPhoneId(), handler); + init(phone); + } + + private void init(Phone phone) { + logd("init"); + mPhone = phone; + mSubId = mPhone.getSubId(); + + addState(mDefaultState); + addState(mIdleState, mDefaultState); + addState(mGsmConfiguringState, mDefaultState); + addState(mGsmActivatingState, mDefaultState); + addState(mCdmaConfiguringState, mDefaultState); + addState(mCdmaActivatingState, mDefaultState); + setInitialState(mIdleState); + } + + /** + * create a CellBroadcastConfigTracker instance for the phone + */ + public static CellBroadcastConfigTracker make(Phone phone, Handler handler, + boolean shouldStart) { + CellBroadcastConfigTracker tracker = handler == null + ? new CellBroadcastConfigTracker(phone) + : new CellBroadcastConfigTracker(phone, handler); + if (shouldStart) { + tracker.start(); + } + return tracker; + } + + /** + * Return current cell broadcast ranges. + */ + @NonNull public List<CellBroadcastIdRange> getCellBroadcastIdRanges() { + List<CellBroadcastIdRange> ranges = new ArrayList<>(); + ranges.addAll(mCbRanges3gpp); + ranges.addAll(mCbRanges3gpp2); + return ranges; + } + + /** + * Set reception of cell broadcast messages with the list of the given ranges. + */ + public void setCellBroadcastIdRanges( + @NonNull List<CellBroadcastIdRange> ranges, @NonNull Consumer<Integer> callback) { + if (DBG) { + logd("setCellBroadcastIdRanges with ranges:" + ranges); + } + ranges = mergeRangesAsNeeded(ranges); + sendMessage(EVENT_REQUEST, new Request(ranges, callback)); + } + + /** + * Merge the overlapped CellBroadcastIdRanges in the list as needed + * @param ranges the list of CellBroadcastIdRanges + * @return the list of CellBroadcastIdRanges without overlapping + * + * @throws IllegalArgumentException if there is conflict of the ranges. For instance, + * the channel is enabled in some range, but disable in others. + */ + @VisibleForTesting + public static @NonNull List<CellBroadcastIdRange> mergeRangesAsNeeded( + @NonNull List<CellBroadcastIdRange> ranges) throws IllegalArgumentException { + ranges.sort((r1, r2) -> r1.getType() != r2.getType() ? r1.getType() - r2.getType() + : (r1.getStartId() != r2.getStartId() ? r1.getStartId() - r2.getStartId() + : r2.getEndId() - r1.getEndId())); + final List<CellBroadcastIdRange> newRanges = new ArrayList<>(); + ranges.forEach(r -> { + if (newRanges.isEmpty() || newRanges.get(newRanges.size() - 1).getType() != r.getType() + || newRanges.get(newRanges.size() - 1).getEndId() + 1 < r.getStartId() + || (newRanges.get(newRanges.size() - 1).getEndId() + 1 == r.getStartId() + && newRanges.get(newRanges.size() - 1).isEnabled() != r.isEnabled())) { + newRanges.add(new CellBroadcastIdRange(r.getStartId(), r.getEndId(), + r.getType(), r.isEnabled())); + } else { + if (newRanges.get(newRanges.size() - 1).isEnabled() != r.isEnabled()) { + throw new IllegalArgumentException("range conflict " + r); + } + if (r.getEndId() > newRanges.get(newRanges.size() - 1).getEndId()) { + CellBroadcastIdRange range = newRanges.get(newRanges.size() - 1); + newRanges.set(newRanges.size() - 1, new CellBroadcastIdRange( + range.getStartId(), r.getEndId(), range.getType(), range.isEnabled())); + } + } + }); + return newRanges; + } + + private void resetConfig() { + mCbRanges3gpp.clear(); + mCbRanges3gpp2.clear(); + } + + private void setGsmConfig(List<CellBroadcastIdRange> ranges, Request request) { + if (DBG) { + logd("setGsmConfig with " + ranges); + } + + SmsBroadcastConfigInfo[] configs = new SmsBroadcastConfigInfo[ranges.size()]; + for (int i = 0; i < configs.length; i++) { + CellBroadcastIdRange r = ranges.get(i); + configs[i] = new SmsBroadcastConfigInfo(r.getStartId(), r.getEndId(), + SMS_CB_CODE_SCHEME_MIN, SMS_CB_CODE_SCHEME_MAX, r.isEnabled()); + } + + Message response = obtainMessage(EVENT_CONFIGURATION_DONE, request); + mPhone.mCi.setGsmBroadcastConfig(configs, response); + } + + private void setCdmaConfig(List<CellBroadcastIdRange> ranges, Request request) { + if (DBG) { + logd("setCdmaConfig with " + ranges); + } + + CdmaSmsBroadcastConfigInfo[] configs = + new CdmaSmsBroadcastConfigInfo[ranges.size()]; + for (int i = 0; i < configs.length; i++) { + CellBroadcastIdRange r = ranges.get(i); + configs[i] = new CdmaSmsBroadcastConfigInfo( + r.getStartId(), r.getEndId(), 1, r.isEnabled()); + } + + Message response = obtainMessage(EVENT_CONFIGURATION_DONE, request); + mPhone.mCi.setCdmaBroadcastConfig(configs, response); + } + + private void setActivation(int type, boolean activate, Request request) { + if (DBG) { + logd("setActivation(" + type + "." + activate + ')'); + } + + Message response = obtainMessage(EVENT_ACTIVATION_DONE, request); + + if (type == SmsCbMessage.MESSAGE_FORMAT_3GPP) { + mPhone.mCi.setGsmBroadcastActivation(activate, response); + } else if (type == SmsCbMessage.MESSAGE_FORMAT_3GPP2) { + mPhone.mCi.setCdmaBroadcastActivation(activate, response); + } + } +}
diff --git a/src/java/com/android/internal/telephony/CellularNetworkService.java b/src/java/com/android/internal/telephony/CellularNetworkService.java index 19a585d..9cbd7a6 100644 --- a/src/java/com/android/internal/telephony/CellularNetworkService.java +++ b/src/java/com/android/internal/telephony/CellularNetworkService.java
@@ -18,9 +18,9 @@ import android.annotation.NonNull; import android.annotation.Nullable; -import android.hardware.radio.V1_0.RegState; import android.hardware.radio.V1_4.DataRegStateResult.VopsInfo.hidl_discriminator; import android.hardware.radio.V1_6.RegStateResult.AccessTechnologySpecificInfo; +import android.hardware.radio.network.RegState; import android.os.AsyncResult; import android.os.Handler; import android.os.Looper; @@ -35,6 +35,7 @@ import android.telephony.CellIdentityNr; import android.telephony.CellIdentityTdscdma; import android.telephony.CellIdentityWcdma; +import android.telephony.DataSpecificRegistrationInfo; import android.telephony.LteVopsSupportInfo; import android.telephony.NetworkRegistrationInfo; import android.telephony.NetworkService; @@ -190,6 +191,8 @@ return NetworkRegistrationInfo.REGISTRATION_STATE_UNKNOWN; case RegState.REG_ROAMING: return NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING; + case RegState.REG_EM: + return NetworkRegistrationInfo.REGISTRATION_STATE_EMERGENCY; default: return NetworkRegistrationInfo.REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING; } @@ -201,6 +204,7 @@ case RegState.NOT_REG_MT_SEARCHING_OP_EM: case RegState.REG_DENIED_EM: case RegState.UNKNOWN_EM: + case RegState.REG_EM: return true; case RegState.NOT_REG_MT_NOT_SEARCHING_OP: case RegState.REG_HOME: @@ -500,6 +504,14 @@ final String rplmn = regResult.registeredPlmn; final int reasonForDenial = regResult.reasonForDenial; + if (regState == NetworkRegistrationInfo.REGISTRATION_STATE_DENIED + && reasonForDenial + == android.hardware.radio.network.RegistrationFailCause.NONE) { + AnomalyReporter.reportAnomaly( + UUID.fromString("62ed270f-e139-418a-a427-8bcc1bca8f21"), + "RIL Missing Reg Fail Reason", mPhone.getCarrierId()); + } + int networkType = ServiceState.rilRadioTechnologyToNetworkType(regResult.rat); if (networkType == TelephonyManager.NETWORK_TYPE_LTE_CA) { networkType = TelephonyManager.NETWORK_TYPE_LTE; @@ -514,6 +526,8 @@ boolean isNrAvailable = false; boolean isDcNrRestricted = false; VopsSupportInfo vopsInfo = null; + int lteAttachResultType = 0; + int lteAttachExtraInfo = 0; android.hardware.radio.network.AccessTechnologySpecificInfo info = regResult.accessTechnologySpecificInfo; @@ -532,6 +546,8 @@ vopsInfo = convertHalLteVopsSupportInfo( info.getEutranInfo().lteVopsInfo.isVopsSupported, info.getEutranInfo().lteVopsInfo.isEmcBearerSupported); + lteAttachResultType = info.getEutranInfo().lteAttachResultType; + lteAttachExtraInfo = info.getEutranInfo().extraInfo; break; case android.hardware.radio.network.AccessTechnologySpecificInfo.ngranNrVopsInfo: vopsInfo = new NrVopsSupportInfo(info.getNgranNrVopsInfo().vopsSupported, @@ -557,10 +573,26 @@ loge("Unknown domain passed to CellularNetworkService= " + domain); // fall through case NetworkRegistrationInfo.DOMAIN_PS: - return new NetworkRegistrationInfo(domain, transportType, regState, networkType, - reasonForDenial, isEmergencyOnly, availableServices, cellIdentity, - rplmn, MAX_DATA_CALLS, isDcNrRestricted, isNrAvailable, isEndcAvailable, - vopsInfo); + return new NetworkRegistrationInfo.Builder() + .setDomain(domain) + .setTransportType(transportType) + .setRegistrationState(regState) + .setAccessNetworkTechnology(networkType) + .setRejectCause(reasonForDenial) + .setEmergencyOnly(isEmergencyOnly) + .setAvailableServices(availableServices) + .setCellIdentity(cellIdentity) + .setRegisteredPlmn(rplmn) + .setDataSpecificInfo( + new DataSpecificRegistrationInfo.Builder(MAX_DATA_CALLS) + .setDcNrRestricted(isDcNrRestricted) + .setNrAvailable(isNrAvailable) + .setEnDcAvailable(isEndcAvailable) + .setVopsSupportInfo(vopsInfo) + .setLteAttachResultType(lteAttachResultType) + .setLteAttachExtraInfo(lteAttachExtraInfo) + .build()) + .build(); } } @@ -582,6 +614,14 @@ networkType = getNetworkTypeForCellIdentity(networkType, cellIdentity, mPhone.getCarrierId()); + if (regState == NetworkRegistrationInfo.REGISTRATION_STATE_DENIED + && reasonForDenial + == android.hardware.radio.network.RegistrationFailCause.NONE) { + AnomalyReporter.reportAnomaly( + UUID.fromString("62ed270f-e139-418a-a427-8bcc1bca8f21"), + "RIL Missing Reg Fail Reason", mPhone.getCarrierId()); + } + // Conditional parameters for specific RANs boolean cssSupported = false; int roamingIndicator = 0;
diff --git a/src/java/com/android/internal/telephony/CommandException.java b/src/java/com/android/internal/telephony/CommandException.java index 72bb6a3..e068c1c 100644 --- a/src/java/com/android/internal/telephony/CommandException.java +++ b/src/java/com/android/internal/telephony/CommandException.java
@@ -341,7 +341,6 @@ return new CommandException(Error.RF_HARDWARE_ISSUE); case RILConstants.NO_RF_CALIBRATION_INFO: return new CommandException(Error.NO_RF_CALIBRATION_INFO); - default: Rlog.e("GSM", "Unrecognized RIL errno " + ril_errno); return new CommandException(Error.INVALID_RESPONSE);
diff --git a/src/java/com/android/internal/telephony/CommandsInterface.java b/src/java/com/android/internal/telephony/CommandsInterface.java index 27cedfe..971e051 100644 --- a/src/java/com/android/internal/telephony/CommandsInterface.java +++ b/src/java/com/android/internal/telephony/CommandsInterface.java
@@ -25,22 +25,31 @@ import android.os.Handler; import android.os.Message; import android.os.WorkSource; +import android.telephony.AccessNetworkConstants; import android.telephony.AccessNetworkConstants.AccessNetworkType; +import android.telephony.BarringInfo; import android.telephony.CarrierRestrictionRules; import android.telephony.ClientRequestStats; +import android.telephony.DomainSelectionService; import android.telephony.ImsiEncryptionInfo; import android.telephony.NetworkScanRequest; import android.telephony.RadioAccessSpecifier; import android.telephony.SignalThresholdInfo; import android.telephony.TelephonyManager; +import android.telephony.TelephonyManager.HalService; import android.telephony.data.DataCallResponse; import android.telephony.data.DataProfile; import android.telephony.data.NetworkSliceInfo; import android.telephony.data.TrafficDescriptor; import android.telephony.emergency.EmergencyNumber; +import android.telephony.ims.RegistrationManager; +import android.telephony.ims.feature.MmTelFeature; +import android.telephony.ims.stub.ImsRegistrationImplBase; import com.android.internal.telephony.cdma.CdmaSmsBroadcastConfigInfo; +import com.android.internal.telephony.emergency.EmergencyConstants; import com.android.internal.telephony.gsm.SmsBroadcastConfigInfo; +import com.android.internal.telephony.imsphone.ImsCallInfo; import com.android.internal.telephony.uicc.IccCardApplicationStatus.PersoSubState; import com.android.internal.telephony.uicc.IccCardStatus; import com.android.internal.telephony.uicc.SimPhonebookRecord; @@ -124,6 +133,15 @@ static final int CDMA_SMS_FAIL_CAUSE_OTHER_TERMINAL_PROBLEM = 39; static final int CDMA_SMS_FAIL_CAUSE_ENCODING_PROBLEM = 96; + /** IMS voice capability */ + int IMS_MMTEL_CAPABILITY_VOICE = 1 << 0; + /** IMS video capability */ + int IMS_MMTEL_CAPABILITY_VIDEO = 1 << 1; + /** IMS SMS capability */ + int IMS_MMTEL_CAPABILITY_SMS = 1 << 2; + /** IMS RCS capabilities */ + int IMS_RCS_CAPABILITIES = 1 << 3; + //***** Methods /** @@ -1773,6 +1791,17 @@ public void getDeviceIdentity(Message response); /** + * Request the device IMEI / IMEI type / IMEISV + * "response" is ImeiInfo object that contains + * [0] ImeiType Indicates whether IMEI is of primary or secondary type + * [1] IMEI if GSM subscription is available + * [2] IMEISV if GSM subscription is available + * + * @param response Message + */ + public void getImei(Message response); + + /** * Request the device MDN / H_SID / H_NID / MIN. * "response" is const char ** * [0] is MDN if CDMA subscription is available @@ -2086,10 +2115,15 @@ * * Input parameters equivalent to TS 27.007 AT+CCHC command. * + * Per spec SGP.22 V3.0, ES10 commands needs to be sent over command port of MEP-A. In order + * to close proper logical channel, should pass information about whether the logical channel + * was opened for sending ES10 commands or not. + * * @param channel Channel id. Id of the channel to be closed. + * @param isEs10 Whether the logical channel is opened to perform ES10 operations. * @param response Callback message. */ - public void iccCloseLogicalChannel(int channel, Message response); + public void iccCloseLogicalChannel(int channel, boolean isEs10, Message response); /** * Exchange APDUs with the SIM on a logical channel. @@ -2105,11 +2139,12 @@ * @param p3 P3 value of the APDU command. If p3 is negative a 4 byte APDU * is sent to the SIM. * @param data Data to be sent with the APDU. + * @param isEs10Command whether APDU command is an ES10 command or a regular APDU * @param response Callback message. response.obj.userObj will be * an IccIoResult on success. */ - public void iccTransmitApduLogicalChannel(int channel, int cla, int instruction, - int p1, int p2, int p3, String data, Message response); + void iccTransmitApduLogicalChannel(int channel, int cla, int instruction, + int p1, int p2, int p3, String data, boolean isEs10Command, Message response); /** * Exchange APDUs with the SIM on a basic channel. @@ -2186,11 +2221,21 @@ /** * @return the radio hal version + * @deprecated use {@link #getHalVersion(int)} */ + @Deprecated default HalVersion getHalVersion() { return HalVersion.UNKNOWN; } + /** + * @param service indicate the service id to query. + * @return the hal version of a specific service + */ + default HalVersion getHalVersion(@HalService int service) { + return HalVersion.UNKNOWN; + } + /** * Sets user selected subscription at Modem. * @@ -2619,6 +2664,15 @@ default void getBarringInfo(Message result) {}; /** + * Returns the last barring information received. + * + * @return the last barring information. + */ + default @Nullable BarringInfo getLastBarringInfo() { + return null; + }; + + /** * Allocates a pdu session id * * AsyncResult.result is the allocated pdu session id @@ -2747,6 +2801,54 @@ public void unregisterForSimPhonebookRecordsReceived(Handler h); /** + * Registers for notifications of connection setup failure. + * + * @param h Handler for notification message. + * @param what User-defined message code. + * @param obj User object. + */ + default void registerForConnectionSetupFailure(Handler h, int what, Object obj) {} + + /** + * Unregisters for notifications of connection setup failure. + * + * @param h Handler to be removed from the registrant list. + */ + default void unregisterForConnectionSetupFailure(Handler h) {} + + /** + * Registers for notifications when ANBR is received form the network. + * + * @param h Handler for notification message. + * @param what User-defined message code. + * @param obj User object. + */ + default void registerForNotifyAnbr(Handler h, int what, Object obj) {} + + /** + * Unregisters for notifications when ANBR is received form the network. + * + * @param h Handler to be removed from the registrant list. + */ + default void unregisterForNotifyAnbr(Handler h) {} + + /** + * Registers for IMS deregistration trigger from modem. + * + * @param h Handler for notification message. + * @param what User-defined message code. + * @param obj User object. + */ + default void registerForTriggerImsDeregistration(Handler h, int what, Object obj) {} + + /** + * Unregisters for IMS deregistration trigger from modem. + * + * @param h Handler to be removed from the registrant list. + */ + default void unregisterForTriggerImsDeregistration(Handler h) {} + + /** * Set the UE's usage setting. * * @param result Callback message containing the success or failure status. @@ -2761,4 +2863,417 @@ * @param result Callback message containing the usage setting (or a failure status). */ default void getUsageSetting(Message result) {} + + /** + * Sets the emergency mode. + * + * @param emcMode Defines the radio emergency mode type. + * @param result Callback message containing the success or failure status. + */ + default void setEmergencyMode(@EmergencyConstants.EmergencyMode int emcMode, + @Nullable Message result) {} + + /** + * Triggers an emergency network scan. + * + * @param accessNetwork Contains the list of access network types to be prioritized + * during emergency scan. The 1st entry has the highest priority. + * @param scanType Indicates the type of scans to be performed i.e. limited scan, + * full service scan or both. + * @param result Callback message containing the success or failure status. + */ + default void triggerEmergencyNetworkScan( + @NonNull @AccessNetworkConstants.RadioAccessNetworkType int[] accessNetwork, + @DomainSelectionService.EmergencyScanType int scanType, @Nullable Message result) {} + + /** + * Cancels ongoing emergency network scan. + * + * @param resetScan Indicates how the next {@link #triggerEmergencyNetworkScan} should work. + * If {@code true}, then the modem shall start the new scan from the beginning, + * otherwise the modem shall resume from the last search. + * @param result Callback message containing the success or failure status. + */ + default void cancelEmergencyNetworkScan(boolean resetScan, @Nullable Message result) {} + + /** + * Exits ongoing emergency mode. + * + * @param result Callback message containing the success or failure status. + */ + default void exitEmergencyMode(@Nullable Message result) {} + + /** + * Registers for emergency network scan result. + * + * @param h Handler for notification message. + * @param what User-defined message code. + * @param obj User object. + */ + default void registerForEmergencyNetworkScan(@NonNull Handler h, + int what, @Nullable Object obj) {} + + /** + * Unregisters for emergency network scan result. + * + * @param h Handler to be removed from the registrant list. + */ + default void unregisterForEmergencyNetworkScan(@NonNull Handler h) {} + + /** + * Provides a list of SRVCC call information to radio + * + * @param srvccConnections the list of connections. + */ + default void setSrvccCallInfo(SrvccConnection[] srvccConnections, Message result) {} + + /** + * Updates the IMS registration information to the radio. + * + * @param state The current IMS registration state. + * @param imsRadioTech The type of underlying radio access network used. + * @param suggestedAction The suggested action for the radio to perform. + * @param capabilities IMS capabilities such as VOICE, VIDEO and SMS. + */ + default void updateImsRegistrationInfo(int state, + @ImsRegistrationImplBase.ImsRegistrationTech int imsRadioTech, + @RegistrationManager.SuggestedAction int suggestedAction, + int capabilities, Message result) {} + + /** + * Notifies the NAS and RRC layers of the radio the type of upcoming IMS traffic. + * + * @param token A nonce to identify the request. + * @param trafficType IMS traffic type like registration, voice, video, SMS, emergency, and etc. + * @param accessNetworkType The type of underlying radio access network used. + * @param trafficDirection Indicates whether traffic is originated by mobile originated or + * mobile terminated use case eg. MO/MT call/SMS etc. + */ + default void startImsTraffic(int token, + @MmTelFeature.ImsTrafficType int trafficType, + @AccessNetworkConstants.RadioAccessNetworkType int accessNetworkType, + @MmTelFeature.ImsTrafficDirection int trafficDirection, + Message result) {} + + /** + * Notifies IMS traffic has been stopped. + * + * @param token The token assigned by startImsTraffic. + */ + default void stopImsTraffic(int token, Message result) {} + + /** + * Triggers the UE initiated EPS fallback procedure. + * + * @param reason Specifies the reason for EPS fallback. + */ + default void triggerEpsFallback(int reason, Message result) {} + + /** + * Triggers radio to send ANBRQ message to the network. + * + * @param mediaType Media type is used to identify media stream such as audio or video. + * @param direction Direction of this packet stream (e.g. uplink or downlink). + * @param bitsPerSecond The bit rate requested by the opponent UE. + * @param result Callback message to receive the result. + */ + default void sendAnbrQuery(int mediaType, int direction, int bitsPerSecond, Message result) {} + + /** + * Set the UE's ability to accept/reject null ciphered and/or null integrity-protected + * connections. + * + * @param enabled true to allow null ciphered and/or null integrity-protected connections, + * false to disallow. + * @param result Callback message containing the success or failure status. + */ + default void setNullCipherAndIntegrityEnabled(boolean enabled, Message result) {} + + /** + * Check whether null ciphering and/or null integrity-protected connections are allowed. + * + * @param result Callback message containing the success or failure status. + */ + default void isNullCipherAndIntegrityEnabled(Message result) {} + + /** + * Notifies the IMS call status to the modem. + * + * @param imsCallInfo The list of {@link ImsCallInfo}. + * @param result A callback to receive the response. + */ + default void updateImsCallStatus(@NonNull List<ImsCallInfo> imsCallInfo, Message result) {} + + /** + * Enables or disables N1 mode (access to 5G core network) in accordance with + * 3GPP TS 24.501 4.9. + * @param enable {@code true} to enable N1 mode, {@code false} to disable N1 mode. + * @param result Callback message to receive the result. + */ + default void setN1ModeEnabled(boolean enable, Message result) {} + + /** + * Check whether N1 mode (access to 5G core network) is enabled or not. + * @param result Callback message to receive the result. + */ + default void isN1ModeEnabled(Message result) {} + + /** + * Get feature capabilities supported by satellite. + * + * @param result Message that will be sent back to the requester + */ + default void getSatelliteCapabilities(Message result) {} + + /** + * Turn satellite modem on/off. + * + * @param result Message that will be sent back to the requester + * @param on {@code true} for turning on. + * {@code false} for turning off. + */ + default void setSatellitePower(Message result, boolean on) {} + + /** + * Get satellite modem state. + * + * @param result Message that will be sent back to the requester + */ + default void getSatellitePowerState(Message result) {} + + /** + * Get satellite provision state. + * + * @param result Message that will be sent back to the requester + */ + default void getSatelliteProvisionState(Message result) {} + + /** + * Check whether satellite modem is supported by the device. + * + * @param result Message that will be sent back to the requester + */ + default void isSatelliteSupported(Message result) {} + + /** + * Provision the subscription with a satellite provider. This is needed to register the + * subscription if the provider allows dynamic registration. + * + * @param result Message that will be sent back to the requester. + * @param imei IMEI of the SIM associated with the satellite modem. + * @param msisdn MSISDN of the SIM associated with the satellite modem. + * @param imsi IMSI of the SIM associated with the satellite modem. + * @param features List of features to be provisioned. + */ + default void provisionSatelliteService( + Message result, String imei, String msisdn, String imsi, int[] features) {} + + /** + * Add contacts that are allowed to be used for satellite communication. This is applicable for + * incoming messages as well. + * + * @param result Message that will be sent back to the requester. + * @param contacts List of allowed contacts to be added. + */ + default void addAllowedSatelliteContacts(Message result, String[] contacts) {} + + /** + * Remove contacts that are allowed to be used for satellite communication. This is applicable + * for incoming messages as well. + * + * @param result Message that will be sent back to the requester. + * @param contacts List of allowed contacts to be removed. + */ + default void removeAllowedSatelliteContacts(Message result, String[] contacts) {} + + /** + * Send text messages. + * + * @param result Message that will be sent back to the requester. + * @param messages List of messages in text format to be sent. + * @param destination The recipient of the message. + * @param latitude The current latitude of the device. + * @param longitude The current longitude of the device. The location (i.e., latitude and + * longitude) of the device will be filled for emergency messages. + */ + default void sendSatelliteMessages(Message result, String[] messages, String destination, + double latitude, double longitude) {} + + /** + * Get pending messages. + * + * @param result Message that will be sent back to the requester. + */ + default void getPendingSatelliteMessages(Message result) {} + + /** + * Get current satellite registration mode. + * + * @param result Message that will be sent back to the requester. + */ + default void getSatelliteMode(Message result) {} + + /** + * Set the filter for what type of indication framework want to receive from modem. + * + * @param result Message that will be sent back to the requester. + * @param filterBitmask The filter bitmask identifying what type of indication Telephony + * framework wants to receive from modem. + */ + default void setSatelliteIndicationFilter(Message result, int filterBitmask) {} + + /** + * User started pointing to the satellite. Modem should continue to update the ponting input + * as user moves device. + * + * @param result Message that will be sent back to the requester. + */ + default void startSendingSatellitePointingInfo(Message result) {} + + /** + * Stop sending satellite pointing info to the framework. + * + * @param result Message that will be sent back to the requester. + */ + default void stopSendingSatellitePointingInfo(Message result) {} + + /** + * Get max number of characters per text message. + * + * @param result Message that will be sent back to the requester. + */ + default void getMaxCharactersPerSatelliteTextMessage(Message result) {} + + /** + * Get whether satellite communication is allowed for the current location. + * + * @param result Message that will be sent back to the requester. + */ + default void isSatelliteCommunicationAllowedForCurrentLocation(Message result) {} + + /** + * Get the time after which the satellite will be visible. + * + * @param result Message that will be sent back to the requester. + */ + default void getTimeForNextSatelliteVisibility(Message result) {} + + /** + * Registers for pending message count from satellite modem. + * + * @param h Handler for notification message. + * @param what User-defined message code. + * @param obj User object. + */ + default void registerForPendingSatelliteMessageCount(@NonNull Handler h, + int what, @Nullable Object obj) {} + + /** + * Unregisters for pending message count from satellite modem. + * + * @param h Handler to be removed from the registrant list. + */ + default void unregisterForPendingSatelliteMessageCount(@NonNull Handler h) {} + + /** + * Registers for new messages from satellite modem. + * + * @param h Handler for notification message. + * @param what User-defined message code. + * @param obj User object. + */ + default void registerForNewSatelliteMessages(@NonNull Handler h, + int what, @Nullable Object obj) {} + + /** + * Unregisters for new messages from satellite modem. + * + * @param h Handler to be removed from the registrant list. + */ + default void unregisterForNewSatelliteMessages(@NonNull Handler h) {} + + /** + * Registers for messages transfer complete from satellite modem. + * + * @param h Handler for notification message. + * @param what User-defined message code. + * @param obj User object. + */ + default void registerForSatelliteMessagesTransferComplete(@NonNull Handler h, + int what, @Nullable Object obj) {} + + /** + * Unregisters for messages transfer complete from satellite modem. + * + * @param h Handler to be removed from the registrant list. + */ + default void unregisterForSatelliteMessagesTransferComplete(@NonNull Handler h) {} + + /** + * Registers for pointing info changed from satellite modem. + * + * @param h Handler for notification message. + * @param what User-defined message code. + * @param obj User object. + */ + default void registerForSatellitePointingInfoChanged(@NonNull Handler h, + int what, @Nullable Object obj) {} + + /** + * Unregisters for pointing info changed from satellite modem. + * + * @param h Handler to be removed from the registrant list. + */ + default void unregisterForSatellitePointingInfoChanged(@NonNull Handler h) {} + + /** + * Registers for mode changed from satellite modem. + * + * @param h Handler for notification message. + * @param what User-defined message code. + * @param obj User object. + */ + default void registerForSatelliteModeChanged(@NonNull Handler h, + int what, @Nullable Object obj) {} + + /** + * Unregisters for mode changed from satellite modem. + * + * @param h Handler to be removed from the registrant list. + */ + default void unregisterForSatelliteModeChanged(@NonNull Handler h) {} + + /** + * Registers for radio technology changed from satellite modem. + * + * @param h Handler for notification message. + * @param what User-defined message code. + * @param obj User object. + */ + default void registerForSatelliteRadioTechnologyChanged(@NonNull Handler h, + int what, @Nullable Object obj) {} + + /** + * Unregisters for radio technology changed from satellite modem. + * + * @param h Handler to be removed from the registrant list. + */ + default void unregisterForSatelliteRadioTechnologyChanged(@NonNull Handler h) {} + + /** + * Registers for provision state changed from satellite modem. + * + * @param h Handler for notification message. + * @param what User-defined message code. + * @param obj User object. + */ + default void registerForSatelliteProvisionStateChanged(@NonNull Handler h, + int what, @Nullable Object obj) {} + + /** + * Unregisters for provision state changed from satellite modem. + * + * @param h Handler to be removed from the registrant list. + */ + default void unregisterForSatelliteProvisionStateChanged(@NonNull Handler h) {} }
diff --git a/src/java/com/android/internal/telephony/Connection.java b/src/java/com/android/internal/telephony/Connection.java index c60e5df..68fd6ab 100644 --- a/src/java/com/android/internal/telephony/Connection.java +++ b/src/java/com/android/internal/telephony/Connection.java
@@ -27,6 +27,8 @@ import android.telephony.ServiceState.RilRadioTechnology; import android.telephony.emergency.EmergencyNumber; import android.telephony.ims.RtpHeaderExtension; +import android.telephony.ims.feature.MmTelFeature; +import android.telephony.ims.feature.MmTelFeature.ImsAudioHandler; import android.util.Log; import com.android.ims.internal.ConferenceParticipant; @@ -139,6 +141,13 @@ * @param extensionData The extension data. */ public void onReceivedRtpHeaderExtensions(@NonNull Set<RtpHeaderExtension> extensionData); + + /** + * Indicates that the audio handler for this connection is changed. + * + * @param imsAudioHandler {@link MmTelFeature#ImsAudioHandler}. + */ + void onAudioModeIsVoipChanged(@ImsAudioHandler int imsAudioHandler); } /** @@ -194,6 +203,8 @@ public void onReceivedDtmfDigit(char digit) {} @Override public void onReceivedRtpHeaderExtensions(@NonNull Set<RtpHeaderExtension> extensionData) {} + @Override + public void onAudioModeIsVoipChanged(@ImsAudioHandler int imsAudioHandler) {} } public static final int AUDIO_QUALITY_STANDARD = 1; @@ -328,6 +339,41 @@ /* Instance Methods */ /** + * PhoneFactory Dependencies for testing. + */ + @VisibleForTesting + public interface PhoneFactoryProxy { + Phone getPhone(int index); + Phone getDefaultPhone(); + Phone[] getPhones(); + } + + private PhoneFactoryProxy mPhoneFactoryProxy = new PhoneFactoryProxy() { + @Override + public Phone getPhone(int index) { + return PhoneFactory.getPhone(index); + } + + @Override + public Phone getDefaultPhone() { + return PhoneFactory.getDefaultPhone(); + } + + @Override + public Phone[] getPhones() { + return PhoneFactory.getPhones(); + } + }; + + /** + * Overrides PhoneFactory dependencies for testing. + */ + @VisibleForTesting + public void setPhoneFactoryProxy(PhoneFactoryProxy proxy) { + mPhoneFactoryProxy = proxy; + } + + /** * @return The telecom internal call ID associated with this connection. Only to be used for * debugging purposes. */ @@ -590,14 +636,35 @@ */ public void setEmergencyCallInfo(CallTracker ct) { if (ct != null) { - Phone phone = ct.getPhone(); - if (phone != null) { - EmergencyNumberTracker tracker = phone.getEmergencyNumberTracker(); + Phone currentPhone = ct.getPhone(); + if (currentPhone != null) { + EmergencyNumberTracker tracker = currentPhone.getEmergencyNumberTracker(); if (tracker != null) { EmergencyNumber num = tracker.getEmergencyNumber(mAddress); + Phone[] allPhones = mPhoneFactoryProxy.getPhones(); if (num != null) { mIsEmergencyCall = true; mEmergencyNumberInfo = num; + } else if (allPhones.length > 1) { + // If there are multiple active SIMs, check all instances: + boolean found = false; + for (Phone phone : allPhones) { + // If the current iteration was already checked, skip: + if (phone.getPhoneId() == currentPhone.getPhoneId()){ + continue; + } + num = phone.getEmergencyNumberTracker() + .getEmergencyNumber(mAddress); + if (num != null){ + found = true; + mIsEmergencyCall = true; + mEmergencyNumberInfo = num; + break; + } + } + if (!found){ + Rlog.e(TAG, "setEmergencyCallInfo: emergency number is null"); + } } else { Rlog.e(TAG, "setEmergencyCallInfo: emergency number is null"); } @@ -1519,6 +1586,25 @@ } /** + * Called to report audio mode changed for Voip. + * @param imsAudioHandler the received value to handle the audio for this IMS call. + */ + public void onAudioModeIsVoipChanged(@ImsAudioHandler int imsAudioHandler) { + Rlog.i(TAG, "onAudioModeIsVoipChanged: conn imsAudioHandler " + imsAudioHandler); + + boolean isVoip = imsAudioHandler == MmTelFeature.AUDIO_HANDLER_ANDROID; + if (isVoip == mAudioModeIsVoip) return; + mAudioModeIsVoip = isVoip; + + Rlog.i(TAG, "onAudioModeIsVoipChanged: isVoip: " + isVoip + + "mAudioModeIsVoip:" + mAudioModeIsVoip); + + for (Listener l : mListeners) { + l.onAudioModeIsVoipChanged(imsAudioHandler); + } + } + + /** * Called to report RTP header extensions received from the network. * @param extensionData the received extension data. */
diff --git a/src/java/com/android/internal/telephony/DataIndication.java b/src/java/com/android/internal/telephony/DataIndication.java index c16cbaa..205c4d8 100644 --- a/src/java/com/android/internal/telephony/DataIndication.java +++ b/src/java/com/android/internal/telephony/DataIndication.java
@@ -16,6 +16,8 @@ package com.android.internal.telephony; +import static android.telephony.TelephonyManager.HAL_SERVICE_DATA; + import static com.android.internal.telephony.RILConstants.RIL_UNSOL_DATA_CALL_LIST_CHANGED; import static com.android.internal.telephony.RILConstants.RIL_UNSOL_KEEPALIVE_STATUS; import static com.android.internal.telephony.RILConstants.RIL_UNSOL_PCO_DATA; @@ -53,9 +55,9 @@ */ public void dataCallListChanged(int indicationType, android.hardware.radio.data.SetupDataCallResult[] dcList) { - mRil.processIndication(RIL.DATA_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_DATA, indicationType); - if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_DATA_CALL_LIST_CHANGED, dcList); + if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_DATA_CALL_LIST_CHANGED, dcList); ArrayList<DataCallResponse> response = RILUtils.convertHalDataCallResultList(dcList); mRil.mDataCallListChangedRegistrants.notifyRegistrants( new AsyncResult(null, response, null)); @@ -68,11 +70,12 @@ */ public void keepaliveStatus(int indicationType, android.hardware.radio.data.KeepaliveStatus halStatus) { - mRil.processIndication(RIL.DATA_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_DATA, indicationType); - if (RIL.RILJ_LOGD) { - mRil.unsljLogRet(RIL_UNSOL_KEEPALIVE_STATUS, "handle=" + halStatus.sessionHandle - + " code=" + halStatus.code); + if (mRil.isLogOrTrace()) { + mRil.unsljLogRet( + RIL_UNSOL_KEEPALIVE_STATUS, + "handle=" + halStatus.sessionHandle + " code=" + halStatus.code); } KeepaliveStatus ks = new KeepaliveStatus( @@ -86,11 +89,11 @@ * @param pco New PcoData */ public void pcoData(int indicationType, android.hardware.radio.data.PcoDataInfo pco) { - mRil.processIndication(RIL.DATA_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_DATA, indicationType); PcoData response = new PcoData(pco.cid, pco.bearerProto, pco.pcoId, pco.contents); - if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_PCO_DATA, response); + if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_PCO_DATA, response); mRil.mPcoDataRegistrants.notifyRegistrants(new AsyncResult(null, response, null)); } @@ -103,10 +106,10 @@ */ public void unthrottleApn(int indicationType, android.hardware.radio.data.DataProfileInfo dpi) throws RemoteException { - mRil.processIndication(RIL.DATA_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_DATA, indicationType); DataProfile response = RILUtils.convertToDataProfile(dpi); - if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_UNTHROTTLE_APN, response); + if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_UNTHROTTLE_APN, response); mRil.mApnUnthrottledRegistrants.notifyRegistrants(new AsyncResult(null, response, null)); } @@ -119,8 +122,10 @@ */ public void slicingConfigChanged(int indicationType, android.hardware.radio.data.SlicingConfig slicingConfig) throws RemoteException { - mRil.processIndication(RIL.DATA_SERVICE, indicationType); - if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_SLICING_CONFIG_CHANGED, slicingConfig); + mRil.processIndication(HAL_SERVICE_DATA, indicationType); + if (mRil.isLogOrTrace()) { + mRil.unsljLogRet(RIL_UNSOL_SLICING_CONFIG_CHANGED, slicingConfig); + } NetworkSlicingConfig ret = RILUtils.convertHalSlicingConfig(slicingConfig); mRil.mSlicingConfigChangedRegistrants.notifyRegistrants( new AsyncResult(null, ret, null));
diff --git a/src/java/com/android/internal/telephony/DataResponse.java b/src/java/com/android/internal/telephony/DataResponse.java index 7cfe13b..bef1da7 100644 --- a/src/java/com/android/internal/telephony/DataResponse.java +++ b/src/java/com/android/internal/telephony/DataResponse.java
@@ -16,6 +16,8 @@ package com.android.internal.telephony; +import static android.telephony.TelephonyManager.HAL_SERVICE_DATA; + import android.hardware.radio.RadioError; import android.hardware.radio.RadioResponseInfo; import android.hardware.radio.data.IRadioDataResponse; @@ -51,7 +53,7 @@ * @param id The pdu session id allocated */ public void allocatePduSessionIdResponse(RadioResponseInfo responseInfo, int id) { - RILRequest rr = mRil.processResponse(RIL.DATA_SERVICE, responseInfo); + RILRequest rr = mRil.processResponse(HAL_SERVICE_DATA, responseInfo); if (rr != null) { if (responseInfo.error == RadioError.NONE) { RadioResponse.sendMessageResponse(rr.mResult, id); @@ -64,14 +66,14 @@ * @param responseInfo Response info struct containing response type, serial no. and error */ public void cancelHandoverResponse(RadioResponseInfo responseInfo) { - RadioResponse.responseVoid(RIL.DATA_SERVICE, mRil, responseInfo); + RadioResponse.responseVoid(HAL_SERVICE_DATA, mRil, responseInfo); } /** * @param responseInfo Response info struct containing response type, serial no. and error */ public void deactivateDataCallResponse(RadioResponseInfo responseInfo) { - RadioResponse.responseVoid(RIL.DATA_SERVICE, mRil, responseInfo); + RadioResponse.responseVoid(HAL_SERVICE_DATA, mRil, responseInfo); } /** @@ -80,7 +82,7 @@ */ public void getDataCallListResponse(RadioResponseInfo responseInfo, android.hardware.radio.data.SetupDataCallResult[] dataCallResultList) { - RILRequest rr = mRil.processResponse(RIL.DATA_SERVICE, responseInfo); + RILRequest rr = mRil.processResponse(HAL_SERVICE_DATA, responseInfo); if (rr != null) { ArrayList<DataCallResponse> response = @@ -98,7 +100,7 @@ */ public void getSlicingConfigResponse(RadioResponseInfo responseInfo, android.hardware.radio.data.SlicingConfig slicingConfig) { - RILRequest rr = mRil.processResponse(RIL.DATA_SERVICE, responseInfo); + RILRequest rr = mRil.processResponse(HAL_SERVICE_DATA, responseInfo); if (rr != null) { NetworkSlicingConfig ret = RILUtils.convertHalSlicingConfig(slicingConfig); @@ -113,35 +115,35 @@ * @param responseInfo Response info struct containing response type, serial no. and error */ public void releasePduSessionIdResponse(RadioResponseInfo responseInfo) { - RadioResponse.responseVoid(RIL.DATA_SERVICE, mRil, responseInfo); + RadioResponse.responseVoid(HAL_SERVICE_DATA, mRil, responseInfo); } /** * @param responseInfo Response info struct containing response type, serial no. and error */ public void setDataAllowedResponse(RadioResponseInfo responseInfo) { - RadioResponse.responseVoid(RIL.DATA_SERVICE, mRil, responseInfo); + RadioResponse.responseVoid(HAL_SERVICE_DATA, mRil, responseInfo); } /** * @param responseInfo Response info struct containing response type, serial no. and error */ public void setDataProfileResponse(RadioResponseInfo responseInfo) { - RadioResponse.responseVoid(RIL.DATA_SERVICE, mRil, responseInfo); + RadioResponse.responseVoid(HAL_SERVICE_DATA, mRil, responseInfo); } /** * @param responseInfo Response info struct containing response type, serial no. and error */ public void setDataThrottlingResponse(RadioResponseInfo responseInfo) { - RadioResponse.responseVoid(RIL.DATA_SERVICE, mRil, responseInfo); + RadioResponse.responseVoid(HAL_SERVICE_DATA, mRil, responseInfo); } /** * @param responseInfo Response info struct containing response type, serial no. and error */ public void setInitialAttachApnResponse(RadioResponseInfo responseInfo) { - RadioResponse.responseVoid(RIL.DATA_SERVICE, mRil, responseInfo); + RadioResponse.responseVoid(HAL_SERVICE_DATA, mRil, responseInfo); } /** @@ -150,7 +152,7 @@ */ public void setupDataCallResponse(RadioResponseInfo responseInfo, android.hardware.radio.data.SetupDataCallResult setupDataCallResult) { - RILRequest rr = mRil.processResponse(RIL.DATA_SERVICE, responseInfo); + RILRequest rr = mRil.processResponse(HAL_SERVICE_DATA, responseInfo); if (rr != null) { DataCallResponse response = RILUtils.convertHalDataCallResult(setupDataCallResult); @@ -165,7 +167,7 @@ * @param responseInfo Response info struct containing response type, serial no. and error */ public void startHandoverResponse(RadioResponseInfo responseInfo) { - RadioResponse.responseVoid(RIL.DATA_SERVICE, mRil, responseInfo); + RadioResponse.responseVoid(HAL_SERVICE_DATA, mRil, responseInfo); } /** @@ -175,7 +177,7 @@ public void startKeepaliveResponse(RadioResponseInfo responseInfo, android.hardware.radio.data.KeepaliveStatus keepaliveStatus) { - RILRequest rr = mRil.processResponse(RIL.DATA_SERVICE, responseInfo); + RILRequest rr = mRil.processResponse(HAL_SERVICE_DATA, responseInfo); if (rr == null) return; KeepaliveStatus ret = null; @@ -214,7 +216,7 @@ * @param responseInfo Response info struct containing response type, serial no. and error */ public void stopKeepaliveResponse(RadioResponseInfo responseInfo) { - RILRequest rr = mRil.processResponse(RIL.DATA_SERVICE, responseInfo); + RILRequest rr = mRil.processResponse(HAL_SERVICE_DATA, responseInfo); if (rr == null) return; try {
diff --git a/src/java/com/android/internal/telephony/DebugService.java b/src/java/com/android/internal/telephony/DebugService.java index 0b759b5..3341577 100644 --- a/src/java/com/android/internal/telephony/DebugService.java +++ b/src/java/com/android/internal/telephony/DebugService.java
@@ -16,6 +16,8 @@ package com.android.internal.telephony; +import android.os.Build; + import com.android.internal.telephony.metrics.TelephonyMetrics; import com.android.telephony.Rlog; @@ -50,8 +52,16 @@ TelephonyMetrics.getInstance().dump(fd, pw, args); return; case "--saveatoms": - log("Saving atoms.."); - PhoneFactory.getMetricsCollector().getAtomsStorage().flushAtoms(); + if (Build.IS_DEBUGGABLE) { + log("Saving atoms.."); + PhoneFactory.getMetricsCollector().flushAtomsStorage(); + } + return; + case "--clearatoms": + if (Build.IS_DEBUGGABLE) { + log("Clearing atoms.."); + PhoneFactory.getMetricsCollector().clearAtomsStorage(); + } return; } }
diff --git a/src/java/com/android/internal/telephony/DefaultPhoneNotifier.java b/src/java/com/android/internal/telephony/DefaultPhoneNotifier.java index e4aff4c..e5a5c8f 100644 --- a/src/java/com/android/internal/telephony/DefaultPhoneNotifier.java +++ b/src/java/com/android/internal/telephony/DefaultPhoneNotifier.java
@@ -18,6 +18,7 @@ import android.annotation.NonNull; import android.content.Context; +import android.telephony.Annotation; import android.telephony.Annotation.RadioPowerState; import android.telephony.Annotation.SrvccState; import android.telephony.BarringInfo; @@ -32,9 +33,13 @@ import android.telephony.ServiceState; import android.telephony.TelephonyDisplayInfo; import android.telephony.TelephonyManager.DataEnabledReason; +import android.telephony.TelephonyManager.EmergencyCallbackModeStopReason; +import android.telephony.TelephonyManager.EmergencyCallbackModeType; import android.telephony.TelephonyRegistryManager; import android.telephony.emergency.EmergencyNumber; +import android.telephony.ims.ImsCallSession; import android.telephony.ims.ImsReasonInfo; +import android.telephony.ims.MediaQualityStatus; import com.android.telephony.Rlog; @@ -142,15 +147,28 @@ mTelephonyRegistryMgr.notifyCellInfoChanged(subId, cellInfo); } - public void notifyPreciseCallState(Phone sender) { + /** + * Notify precise call state of foreground, background and ringing call states. + * + * @param imsCallIds Array of IMS call session ID{@link ImsCallSession#getCallId} for + * ringing, foreground & background calls. + * @param imsCallServiceTypes Array of IMS call service type for ringing, foreground & + * background calls. + * @param imsCallTypes Array of IMS call type for ringing, foreground & background calls. + */ + public void notifyPreciseCallState(Phone sender, String[] imsCallIds, + @Annotation.ImsCallServiceType int[] imsCallServiceTypes, + @Annotation.ImsCallType int[] imsCallTypes) { Call ringingCall = sender.getRingingCall(); Call foregroundCall = sender.getForegroundCall(); Call backgroundCall = sender.getBackgroundCall(); + if (ringingCall != null && foregroundCall != null && backgroundCall != null) { - mTelephonyRegistryMgr.notifyPreciseCallState(sender.getPhoneId(), sender.getSubId(), - convertPreciseCallState(ringingCall.getState()), + int[] callStates = {convertPreciseCallState(ringingCall.getState()), convertPreciseCallState(foregroundCall.getState()), - convertPreciseCallState(backgroundCall.getState())); + convertPreciseCallState(backgroundCall.getState())}; + mTelephonyRegistryMgr.notifyPreciseCallState(sender.getPhoneId(), sender.getSubId(), + callStates, imsCallIds, imsCallServiceTypes, imsCallTypes); } } @@ -223,6 +241,12 @@ } @Override + public void notifyMediaQualityStatusChanged(Phone sender, MediaQualityStatus status) { + mTelephonyRegistryMgr.notifyMediaQualityStatusChanged( + sender.getPhoneId(), sender.getSubId(), status); + } + + @Override public void notifyRegistrationFailed(Phone sender, @NonNull CellIdentity cellIdentity, @NonNull String chosenPlmn, int domain, int causeCode, int additionalCauseCode) { mTelephonyRegistryMgr.notifyRegistrationFailed(sender.getPhoneId(), sender.getSubId(), @@ -262,6 +286,18 @@ sender.getSubId(), linkCapacityEstimateList); } + @Override + public void notifyCallbackModeStarted(Phone sender, @EmergencyCallbackModeType int type) { + mTelephonyRegistryMgr.notifyCallBackModeStarted(sender.getPhoneId(), + sender.getSubId(), type); + } + + @Override + public void notifyCallbackModeStopped(Phone sender, @EmergencyCallbackModeType int type, + @EmergencyCallbackModeStopReason int reason) { + mTelephonyRegistryMgr.notifyCallbackModeStopped(sender.getPhoneId(), + sender.getSubId(), type, reason); + } /** * Convert the {@link Call.State} enum into the PreciseCallState.PRECISE_CALL_STATE_* constants * for the public API.
diff --git a/src/java/com/android/internal/telephony/DeviceStateMonitor.java b/src/java/com/android/internal/telephony/DeviceStateMonitor.java index 3d63a29..ecc6208 100644 --- a/src/java/com/android/internal/telephony/DeviceStateMonitor.java +++ b/src/java/com/android/internal/telephony/DeviceStateMonitor.java
@@ -20,6 +20,7 @@ import static android.hardware.radio.V1_0.DeviceStateType.CHARGING_STATE; import static android.hardware.radio.V1_0.DeviceStateType.LOW_DATA_EXPECTED; import static android.hardware.radio.V1_0.DeviceStateType.POWER_SAVE_MODE; +import static android.telephony.TelephonyManager.HAL_SERVICE_NETWORK; import android.app.UiModeManager; import android.content.BroadcastReceiver; @@ -669,7 +670,7 @@ LINK_CAPACITY_UPLINK_THRESHOLDS, AccessNetworkType.EUTRAN); mPhone.setLinkCapacityReportingCriteria(LINK_CAPACITY_DOWNLINK_THRESHOLDS, LINK_CAPACITY_UPLINK_THRESHOLDS, AccessNetworkType.CDMA2000); - if (mPhone.getHalVersion().greaterOrEqual(RIL.RADIO_HAL_VERSION_1_5)) { + if (mPhone.getHalVersion(HAL_SERVICE_NETWORK).greaterOrEqual(RIL.RADIO_HAL_VERSION_1_5)) { mPhone.setLinkCapacityReportingCriteria(LINK_CAPACITY_DOWNLINK_THRESHOLDS, LINK_CAPACITY_UPLINK_THRESHOLDS, AccessNetworkType.NGRAN); }
diff --git a/src/java/com/android/internal/telephony/DisplayInfoController.java b/src/java/com/android/internal/telephony/DisplayInfoController.java index 886a899..567331d 100644 --- a/src/java/com/android/internal/telephony/DisplayInfoController.java +++ b/src/java/com/android/internal/telephony/DisplayInfoController.java
@@ -18,11 +18,11 @@ import android.annotation.NonNull; import android.os.Handler; +import android.os.Message; import android.os.Registrant; import android.os.RegistrantList; -import android.telephony.AccessNetworkConstants; import android.telephony.AnomalyReporter; -import android.telephony.NetworkRegistrationInfo; +import android.telephony.ServiceState; import android.telephony.TelephonyDisplayInfo; import android.telephony.TelephonyManager; import android.util.IndentingPrintWriter; @@ -45,8 +45,6 @@ * TelephonyDisplayInfo via {@link #getTelephonyDisplayInfo}. */ public class DisplayInfoController extends Handler { - private static final String TAG = "DisplayInfoController"; - private final String mLogTag; private final LocalLog mLocalLog = new LocalLog(128); @@ -66,22 +64,36 @@ TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED) ); + /** Event for service state changed (roaming). */ + private static final int EVENT_SERVICE_STATE_CHANGED = 1; + private final Phone mPhone; private final NetworkTypeController mNetworkTypeController; private final RegistrantList mTelephonyDisplayInfoChangedRegistrants = new RegistrantList(); - private TelephonyDisplayInfo mTelephonyDisplayInfo; + private @NonNull TelephonyDisplayInfo mTelephonyDisplayInfo; + private @NonNull ServiceState mServiceState; public DisplayInfoController(Phone phone) { mPhone = phone; mLogTag = "DIC-" + mPhone.getPhoneId(); + mTelephonyDisplayInfo = new TelephonyDisplayInfo( + TelephonyManager.NETWORK_TYPE_UNKNOWN, + TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE); mNetworkTypeController = new NetworkTypeController(phone, this); mNetworkTypeController.sendMessage(NetworkTypeController.EVENT_UPDATE); + + mServiceState = mPhone.getServiceStateTracker().getServiceState(); + post(() -> { + mPhone.getServiceStateTracker() + .registerForServiceStateChanged(this, EVENT_SERVICE_STATE_CHANGED, null); + updateTelephonyDisplayInfo(); + }); } /** * @return the current TelephonyDisplayInfo */ - public TelephonyDisplayInfo getTelephonyDisplayInfo() { + public @NonNull TelephonyDisplayInfo getTelephonyDisplayInfo() { return mTelephonyDisplayInfo; } @@ -90,12 +102,10 @@ * NetworkTypeController. */ public void updateTelephonyDisplayInfo() { - NetworkRegistrationInfo nri = mPhone.getServiceState().getNetworkRegistrationInfo( - NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN); - int dataNetworkType = nri == null ? TelephonyManager.NETWORK_TYPE_UNKNOWN - : nri.getAccessNetworkTechnology(); - TelephonyDisplayInfo newDisplayInfo = new TelephonyDisplayInfo(dataNetworkType, - mNetworkTypeController.getOverrideNetworkType()); + TelephonyDisplayInfo newDisplayInfo = new TelephonyDisplayInfo( + mNetworkTypeController.getDataNetworkType(), + mNetworkTypeController.getOverrideNetworkType(), + mServiceState.getRoaming()); if (!newDisplayInfo.equals(mTelephonyDisplayInfo)) { logl("TelephonyDisplayInfo changed from " + mTelephonyDisplayInfo + " to " + newDisplayInfo); @@ -133,7 +143,7 @@ } } catch (InvalidArgumentException e) { logel(e.getMessage()); - AnomalyReporter.reportAnomaly(UUID.fromString("3aa92a2c-94ed-46a0-a744-d6b1dfec2a55"), + AnomalyReporter.reportAnomaly(UUID.fromString("3aa92a2c-94ed-46a0-a744-d6b1dfec2a56"), e.getMessage(), mPhone.getCarrierId()); } } @@ -157,6 +167,17 @@ mTelephonyDisplayInfoChangedRegistrants.remove(h); } + @Override + public void handleMessage(@NonNull Message msg) { + switch (msg.what) { + case EVENT_SERVICE_STATE_CHANGED: + mServiceState = mPhone.getServiceStateTracker().getServiceState(); + log("ServiceState updated, isRoaming=" + mServiceState.getRoaming()); + updateTelephonyDisplayInfo(); + break; + } + } + /** * Log debug messages. * @param s debug messages
diff --git a/src/java/com/android/internal/telephony/FdnUtils.java b/src/java/com/android/internal/telephony/FdnUtils.java new file mode 100644 index 0000000..aa2bcfd --- /dev/null +++ b/src/java/com/android/internal/telephony/FdnUtils.java
@@ -0,0 +1,190 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.telephony; + +import android.text.TextUtils; + +import com.android.i18n.phonenumbers.NumberParseException; +import com.android.i18n.phonenumbers.PhoneNumberUtil; +import com.android.i18n.phonenumbers.PhoneNumberUtil.PhoneNumberFormat; +import com.android.i18n.phonenumbers.Phonenumber.PhoneNumber; +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.telephony.uicc.AdnRecord; +import com.android.internal.telephony.uicc.AdnRecordCache; +import com.android.internal.telephony.uicc.IccConstants; +import com.android.internal.telephony.uicc.IccRecords; +import com.android.internal.telephony.uicc.UiccCardApplication; +import com.android.internal.telephony.uicc.UiccController; +import com.android.internal.telephony.uicc.UiccProfile; +import com.android.telephony.Rlog; + +import java.util.ArrayList; + +/** + * This is a basic utility class for common functions related to Fixed Dialing Numbers + * designed as per 3GPP 22.101. + */ +public class FdnUtils { + private static final boolean VDBG = false; + private static final String LOG_TAG = FdnUtils.class.getSimpleName(); + + /** + * The following function checks if dialed number is blocked due to FDN. + * + * @param phoneId The phone object id for which the FDN check is performed + * @param dialStr dialed phone number + * @param defaultCountryIso country ISO for the subscription associated with this phone + * @return {@code true} if dialStr is blocked due to FDN check. + */ + public static boolean isNumberBlockedByFDN(int phoneId, String dialStr, + String defaultCountryIso) { + if (!isFdnEnabled(phoneId)) { + return false; + } + + ArrayList<AdnRecord> fdnList = getFdnList(phoneId); + return !isFDN(dialStr, defaultCountryIso, fdnList); + } + + /** + * Checks if FDN is enabled + * @param phoneId The phone object id for which the FDN check is performed + * @return {@code true} if FDN is enabled + */ + public static boolean isFdnEnabled(int phoneId) { + UiccCardApplication app = getUiccCardApplication(phoneId); + if (app == null || (!app.getIccFdnAvailable())) { + return false; + } + + return app.getIccFdnEnabled(); + } + + /** + * If FDN is enabled, check to see if the given supplementary service control strings are + * blocked due to FDN. + * @param phoneId The phone object id for which the FDN check is performed + * @param controlStrings control strings associated with the supplementary service request + * @param defaultCountryIso country ISO for the subscription associated with this phone + * @return {@code true} if the FDN list does not contain any of the control strings. + */ + public static boolean isSuppServiceRequestBlockedByFdn(int phoneId, + ArrayList<String> controlStrings, String defaultCountryIso) { + if (!isFdnEnabled(phoneId)) { + return false; + } + + ArrayList<AdnRecord> fdnList = getFdnList(phoneId); + for(String controlString : controlStrings) { + if(isFDN(controlString, defaultCountryIso, fdnList)) { + return false; + } + } + return true; + } + + /** + * Checks if dialStr is part of FDN list. + * + * @param fdnList List of all FDN records associated with a sim card + * @param dialStr dialed phone number + * @param defaultCountryIso country ISO for the subscription associated with this phone + * @return {@code true} if dialStr is present in the fdnList. + */ + @VisibleForTesting + public static boolean isFDN(String dialStr, String defaultCountryIso, + ArrayList<AdnRecord> fdnList) { + if (fdnList == null || fdnList.isEmpty() || TextUtils.isEmpty(dialStr)) { + Rlog.w(LOG_TAG, "isFDN: unexpected null value"); + return false; + } + + // Parse the dialStr and convert it to E164 format + String dialStrE164 = null; + String dialStrNational = null; + final PhoneNumberUtil phoneNumberUtil = PhoneNumberUtil.getInstance(); + try { + PhoneNumber phoneNumber = phoneNumberUtil.parse(dialStr, defaultCountryIso); + dialStrE164 = phoneNumberUtil.format(phoneNumber, PhoneNumberFormat.E164); + dialStrNational = String.valueOf(phoneNumber.getNationalNumber()); + } catch (NumberParseException ignored) { + Rlog.w(LOG_TAG, "isFDN: could not parse dialStr"); + } + + /** + * Returns true if dialStrE164 or dialStrNational or dialStr starts with fdnNumber + * E.g.1: returns true if fdnNumber="123" and dialStr="12345" + * E.g.2: does not return true if fdnNumber="1123" and dialStr="12345" + */ + for (AdnRecord fdn: fdnList) { + String fdnNumber = fdn.getNumber(); + if (TextUtils.isEmpty(fdnNumber)) { + continue; + } + + if(!TextUtils.isEmpty(dialStrE164)) { + if(dialStrE164.startsWith(fdnNumber)) { + return true; + } + } + + if(!TextUtils.isEmpty(dialStrNational)) { + if (dialStrNational.startsWith(fdnNumber)) { + return true; + } + } + + if (dialStr.startsWith(fdnNumber)) { + return true; + } + } + + if (VDBG) { + Rlog.i(LOG_TAG, "isFDN: dialed number not present in FDN list"); + } + return false; + } + + private static ArrayList<AdnRecord> getFdnList(int phoneId) { + UiccCardApplication app = getUiccCardApplication(phoneId); + if (app == null) { + return null; + } + + IccRecords iccRecords = app.getIccRecords(); + if (iccRecords == null) { + return null; + } + + AdnRecordCache adnRecordCache = iccRecords.getAdnCache(); + if(adnRecordCache == null) { + return null; + } + + return adnRecordCache.getRecordsIfLoaded(IccConstants.EF_FDN); + } + + private static UiccCardApplication getUiccCardApplication(int phoneId) { + UiccProfile uiccProfile = UiccController.getInstance() + .getUiccProfileForPhone(phoneId); + if (uiccProfile == null) { + return null; + } + + return uiccProfile.getApplication(UiccController.APP_FAM_3GPP); + } +} \ No newline at end of file
diff --git a/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java b/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java old mode 100755 new mode 100644 index 3293558..d76ee19 --- a/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java +++ b/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java
@@ -45,6 +45,8 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.telephony.PhoneInternalInterface.DialArgs; import com.android.internal.telephony.cdma.CdmaCallWaitingNotification; +import com.android.internal.telephony.domainselection.DomainSelectionResolver; +import com.android.internal.telephony.emergency.EmergencyStateTracker; import com.android.internal.telephony.metrics.TelephonyMetrics; import com.android.telephony.Rlog; @@ -182,11 +184,6 @@ mCi.unregisterForCallWaitingInfo(this); // Prior to phone switch to GSM, if CDMA has any emergency call // data will be in disabled state, after switching to GSM enable data. - if (mIsInEmergencyCall) { - if (!mPhone.isUsingNewDataStack()) { - mPhone.getDataEnabledSettings().setInternalDataEnabled(true); - } - } } else { mConnections = new GsmCdmaConnection[MAX_CONNECTIONS_CDMA]; mPendingCallInEcm = false; @@ -400,9 +397,6 @@ //CDMA public void setIsInEmergencyCall() { mIsInEmergencyCall = true; - if (!mPhone.isUsingNewDataStack()) { - mPhone.getDataEnabledSettings().setInternalDataEnabled(false); - } mPhone.notifyEmergencyCallRegistrants(true); mPhone.sendEmergencyCallStateChange(true); } @@ -488,16 +482,29 @@ disableDataCallInEmergencyCall(dialString); // In Ecm mode, if another emergency call is dialed, Ecm mode will not exit. - if(!isPhoneInEcmMode || (isPhoneInEcmMode && isEmergencyCall)) { + if (!isPhoneInEcmMode || (isPhoneInEcmMode && isEmergencyCall)) { mCi.dial(mPendingMO.getAddress(), mPendingMO.isEmergencyCall(), mPendingMO.getEmergencyNumberInfo(), - mPendingMO.hasKnownUserIntentEmergency(), - clirMode, obtainCompleteMessage()); + mPendingMO.hasKnownUserIntentEmergency(), clirMode, + obtainCompleteMessage()); + } else if (DomainSelectionResolver.getInstance().isDomainSelectionSupported()) { + mPendingCallInEcm = true; + final int finalClirMode = clirMode; + Runnable onComplete = new Runnable() { + @Override + public void run() { + mCi.dial(mPendingMO.getAddress(), mPendingMO.isEmergencyCall(), + mPendingMO.getEmergencyNumberInfo(), + mPendingMO.hasKnownUserIntentEmergency(), finalClirMode, + obtainCompleteMessage()); + } + }; + EmergencyStateTracker.getInstance().exitEmergencyCallbackMode(onComplete); } else { mPhone.exitEmergencyCallbackMode(); - mPhone.setOnEcbModeExitResponse(this,EVENT_EXIT_ECM_RESPONSE_CDMA, null); - mPendingCallClirMode=clirMode; - mPendingCallInEcm=true; + mPhone.setOnEcbModeExitResponse(this, EVENT_EXIT_ECM_RESPONSE_CDMA, null); + mPendingCallClirMode = clirMode; + mPendingCallInEcm = true; } } @@ -967,6 +974,8 @@ } else { newUnknownConnectionCdma = mConnections[i]; } + } else if (hangupWaitingCallSilently(i)) { + return; } } } @@ -1018,6 +1027,9 @@ if (mConnections[i].getCall() == mRingingCall) { newRinging = mConnections[i]; + if (hangupWaitingCallSilently(i)) { + return; + } } // else something strange happened hasNonHangupStateChanged = true; } else if (conn != null && dc != null) { /* implicit conn.compareTo(dc) */ @@ -1754,9 +1766,6 @@ } if (!inEcm) { // Re-initiate data connection - if (!mPhone.isUsingNewDataStack()) { - mPhone.getDataEnabledSettings().setInternalDataEnabled(true); - } mPhone.notifyEmergencyCallRegistrants(false); } mPhone.sendEmergencyCallStateChange(false); @@ -1830,6 +1839,10 @@ } private boolean isEmcRetryCause(int causeCode) { + if (DomainSelectionResolver.getInstance().isDomainSelectionSupported()) { + log("isEmcRetryCause AP based domain selection ignores the cause"); + return false; + } if (causeCode == CallFailCause.EMC_REDIAL_ON_IMS || causeCode == CallFailCause.EMC_REDIAL_ON_VOWIFI) { return true; @@ -1909,4 +1922,22 @@ public void cleanupCalls() { pollCallsWhenSafe(); } + + private boolean hangupWaitingCallSilently(int index) { + if (index < 0 || index >= mConnections.length) return false; + + GsmCdmaConnection newRinging = mConnections[index]; + if (newRinging == null) return false; + + if ((mPhone.getTerminalBasedCallWaitingState(true) + == CallWaitingController.TERMINAL_BASED_NOT_ACTIVATED) + && (newRinging.getState() == Call.State.WAITING)) { + Rlog.d(LOG_TAG, "hangupWaitingCallSilently"); + newRinging.dispose(); + mConnections[index] = null; + mCi.hangupWaitingOrBackground(obtainCompleteMessage()); + return true; + } + return false; + } }
diff --git a/src/java/com/android/internal/telephony/GsmCdmaConnection.java b/src/java/com/android/internal/telephony/GsmCdmaConnection.java index b79bdef..e06520a 100644 --- a/src/java/com/android/internal/telephony/GsmCdmaConnection.java +++ b/src/java/com/android/internal/telephony/GsmCdmaConnection.java
@@ -478,7 +478,7 @@ * @return the corresponding value from {@link DisconnectCause} */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - int disconnectCauseFromCode(int causeCode) { + public int disconnectCauseFromCode(int causeCode) { /** * See 22.001 Annex F.4 for mapping of cause codes * to local tones @@ -565,6 +565,14 @@ case CallFailCause.RADIO_OFF: return DisconnectCause.POWER_OFF; + case CallFailCause.NO_VALID_SIM: + return DisconnectCause.ICC_ERROR; + + case CallFailCause.LOCAL_NETWORK_NO_SERVICE: + // fallthrough + case CallFailCause.LOCAL_SERVICE_UNAVAILABLE: + return DisconnectCause.OUT_OF_SERVICE; + case CallFailCause.ACCESS_CLASS_BLOCKED: case CallFailCause.ERROR_UNSPECIFIED: case CallFailCause.NORMAL_CLEARING:
diff --git a/src/java/com/android/internal/telephony/GsmCdmaPhone.java b/src/java/com/android/internal/telephony/GsmCdmaPhone.java index ff8412f..5eae061 100644 --- a/src/java/com/android/internal/telephony/GsmCdmaPhone.java +++ b/src/java/com/android/internal/telephony/GsmCdmaPhone.java
@@ -16,6 +16,11 @@ package com.android.internal.telephony; +import static android.telephony.NetworkRegistrationInfo.DOMAIN_CS; +import static android.telephony.NetworkRegistrationInfo.DOMAIN_CS_PS; +import static android.telephony.NetworkRegistrationInfo.DOMAIN_PS; +import static android.telephony.NetworkRegistrationInfo.DOMAIN_UNKNOWN; + import static com.android.internal.telephony.CommandException.Error.GENERIC_FAILURE; import static com.android.internal.telephony.CommandException.Error.SIM_BUSY; import static com.android.internal.telephony.CommandsInterface.CF_ACTION_DISABLE; @@ -32,6 +37,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; +import android.app.ActivityManager; import android.compat.annotation.UnsupportedAppUsage; import android.content.BroadcastReceiver; import android.content.ContentValues; @@ -40,6 +46,7 @@ import android.content.IntentFilter; import android.content.SharedPreferences; import android.database.SQLException; +import android.hardware.radio.modem.ImeiInfo; import android.net.Uri; import android.os.AsyncResult; import android.os.Build; @@ -57,6 +64,7 @@ import android.os.UserHandle; import android.os.WorkSource; import android.preference.PreferenceManager; +import android.provider.DeviceConfig; import android.provider.Settings; import android.provider.Telephony; import android.sysprop.TelephonyProperties; @@ -64,11 +72,13 @@ import android.telecom.PhoneAccountHandle; import android.telecom.TelecomManager; import android.telecom.VideoProfile; -import android.telephony.AccessNetworkConstants; +import android.telephony.AccessNetworkConstants.TransportType; import android.telephony.Annotation.DataActivityType; import android.telephony.Annotation.RadioPowerState; +import android.telephony.AnomalyReporter; import android.telephony.BarringInfo; import android.telephony.CarrierConfigManager; +import android.telephony.CellBroadcastIdRange; import android.telephony.CellIdentity; import android.telephony.ImsiEncryptionInfo; import android.telephony.LinkCapacityEstimate; @@ -82,7 +92,7 @@ import android.telephony.TelephonyManager; import android.telephony.UiccAccessRule; import android.telephony.UssdResponse; -import android.telephony.data.ApnSetting; +import android.telephony.ims.ImsCallProfile; import android.text.TextUtils; import android.util.Log; import android.util.Pair; @@ -94,17 +104,19 @@ import com.android.internal.telephony.data.AccessNetworksManager; import com.android.internal.telephony.data.DataNetworkController; import com.android.internal.telephony.data.LinkBandwidthEstimator; -import com.android.internal.telephony.dataconnection.DataEnabledSettings; -import com.android.internal.telephony.dataconnection.DcTracker; -import com.android.internal.telephony.dataconnection.TransportManager; +import com.android.internal.telephony.domainselection.DomainSelectionResolver; import com.android.internal.telephony.emergency.EmergencyNumberTracker; +import com.android.internal.telephony.emergency.EmergencyStateTracker; import com.android.internal.telephony.gsm.GsmMmiCode; +import com.android.internal.telephony.gsm.SsData; import com.android.internal.telephony.gsm.SuppServiceNotification; import com.android.internal.telephony.imsphone.ImsPhone; import com.android.internal.telephony.imsphone.ImsPhoneCallTracker; import com.android.internal.telephony.imsphone.ImsPhoneMmiCode; import com.android.internal.telephony.metrics.TelephonyMetrics; import com.android.internal.telephony.metrics.VoiceCallSessionStats; +import com.android.internal.telephony.subscription.SubscriptionInfoInternal; +import com.android.internal.telephony.subscription.SubscriptionManagerService.SubscriptionManagerServiceCallback; import com.android.internal.telephony.test.SimulatedRadioControl; import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppType; import com.android.internal.telephony.uicc.IccCardStatus; @@ -131,6 +143,9 @@ import java.util.Collections; import java.util.Iterator; import java.util.List; +import java.util.Locale; +import java.util.Set; +import java.util.UUID; import java.util.function.Consumer; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -157,6 +172,8 @@ // Key used to read/write the SIM IMSI used for storing the voice mail private static final String VM_SIM_IMSI = "vm_sim_imsi_key"; /** List of Registrants to receive Supplementary Service Notifications. */ + // Key used to read/write the current sub Id. Updated on SIM loaded. + public static final String CURR_SUBID = "curr_subid"; private RegistrantList mSsnRegistrants = new RegistrantList(); //CDMA @@ -227,10 +244,17 @@ private final RegistrantList mVolteSilentRedialRegistrants = new RegistrantList(); private DialArgs mDialArgs = null; - + private final RegistrantList mEmergencyDomainSelectedRegistrants = new RegistrantList(); private String mImei; private String mImeiSv; private String mVmNumber; + private int mImeiType = IMEI_TYPE_UNKNOWN; + + @VisibleForTesting + public CellBroadcastConfigTracker mCellBroadcastConfigTracker = + CellBroadcastConfigTracker.make(this, null, true); + + private boolean mIsNullCipherAndIntegritySupported = false; // Create Cfu (Call forward unconditional) so that dialing number & // mOnComplete (Message object passed by client) can be packed & @@ -271,12 +295,11 @@ private CarrierKeyDownloadManager mCDM; private CarrierInfoManager mCIM; - private final SettingsObserver mSettingsObserver; - private final ImsManagerFactory mImsManagerFactory; private final CarrierPrivilegesTracker mCarrierPrivilegesTracker; private final SubscriptionManager.OnSubscriptionsChangedListener mSubscriptionsChangedListener; + private final CallWaitingController mCallWaitingController; // Constructors @@ -316,10 +339,6 @@ mAccessNetworksManager = mTelephonyComponentFactory .inject(AccessNetworksManager.class.getName()) .makeAccessNetworksManager(this, getLooper()); - if (!isUsingNewDataStack()) { - mTransportManager = mTelephonyComponentFactory.inject(TransportManager.class.getName()) - .makeTransportManager(this); - } // SST/DSM depends on SSC, so SSC is instanced before SST/DSM mSignalStrengthController = mTelephonyComponentFactory.inject( SignalStrengthController.class.getName()).makeSignalStrengthController(this); @@ -328,10 +347,6 @@ mEmergencyNumberTracker = mTelephonyComponentFactory .inject(EmergencyNumberTracker.class.getName()).makeEmergencyNumberTracker( this, this.mCi); - if (!isUsingNewDataStack()) { - mDataEnabledSettings = mTelephonyComponentFactory - .inject(DataEnabledSettings.class.getName()).makeDataEnabledSettings(this); - } mDeviceStateMonitor = mTelephonyComponentFactory.inject(DeviceStateMonitor.class.getName()) .makeDeviceStateMonitor(this); @@ -340,20 +355,9 @@ mDisplayInfoController = mTelephonyComponentFactory.inject( DisplayInfoController.class.getName()).makeDisplayInfoController(this); - if (isUsingNewDataStack()) { - mDataNetworkController = mTelephonyComponentFactory.inject( - DataNetworkController.class.getName()) - .makeDataNetworkController(this, getLooper()); - } else { - // DcTracker uses ServiceStateTracker and DisplayInfoController so needs to be created - // after they are instantiated - for (int transport : mAccessNetworksManager.getAvailableTransports()) { - DcTracker dcTracker = mTelephonyComponentFactory.inject(DcTracker.class.getName()) - .makeDcTracker(this, transport); - mDcTrackers.put(transport, dcTracker); - mAccessNetworksManager.registerDataThrottler(dcTracker.getDataThrottler()); - } - } + mDataNetworkController = mTelephonyComponentFactory.inject( + DataNetworkController.class.getName()) + .makeDataNetworkController(this, getLooper()); mCarrierResolver = mTelephonyComponentFactory.inject(CarrierResolver.class.getName()) .makeCarrierResolver(this); @@ -365,23 +369,22 @@ mSST.registerForNetworkAttached(this, EVENT_REGISTERED_TO_NETWORK, null); mSST.registerForVoiceRegStateOrRatChanged(this, EVENT_VRS_OR_RAT_CHANGED, null); + mSST.getServiceStateStats().registerDataNetworkControllerCallback(); - // TODO: Remove SettingsObserver and provisioning events when DataEnabledSettings is removed - mSettingsObserver = new SettingsObserver(context, this); - mSettingsObserver.observe( - Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED), - EVENT_DEVICE_PROVISIONED_CHANGE); - mSettingsObserver.observe( - Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONING_MOBILE_DATA_ENABLED), - EVENT_DEVICE_PROVISIONING_DATA_SETTING_CHANGE); - - SubscriptionController.getInstance().registerForUiccAppsEnabled(this, - EVENT_UICC_APPS_ENABLEMENT_SETTING_CHANGED, null, false); + mSubscriptionManagerService.registerCallback(new SubscriptionManagerServiceCallback( + this::post) { + @Override + public void onUiccApplicationsEnabledChanged(int subId) { + reapplyUiccAppsEnablementIfNeeded(ENABLE_UICC_APPS_MAX_RETRIES); + } + }); mLinkBandwidthEstimator = mTelephonyComponentFactory .inject(LinkBandwidthEstimator.class.getName()) .makeLinkBandwidthEstimator(this); + mCallWaitingController = new CallWaitingController(this); + loadTtyMode(); CallManager.getInstance().registerPhone(this); @@ -419,6 +422,17 @@ int newPreferredTtyMode = intent.getIntExtra( TelecomManager.EXTRA_TTY_PREFERRED_MODE, TelecomManager.TTY_MODE_OFF); updateUiTtyMode(newPreferredTtyMode); + } else if (TelephonyManager.ACTION_SIM_APPLICATION_STATE_CHANGED.equals(action)) { + if (mPhoneId == intent.getIntExtra( + SubscriptionManager.EXTRA_SLOT_INDEX, + SubscriptionManager.INVALID_SIM_SLOT_INDEX)) { + int simState = intent.getIntExtra(TelephonyManager.EXTRA_SIM_STATE, + TelephonyManager.SIM_STATE_UNKNOWN); + if (simState == TelephonyManager.SIM_STATE_LOADED + && currentSlotSubIdChanged()) { + setNetworkSelectionModeAutomatic(null); + } + } } } }; @@ -475,15 +489,20 @@ mCi.registerForLceInfo(this, EVENT_LINK_CAPACITY_CHANGED, null); mCi.registerForCarrierInfoForImsiEncryption(this, EVENT_RESET_CARRIER_KEY_IMSI_ENCRYPTION, null); + mCi.registerForTriggerImsDeregistration(this, EVENT_IMS_DEREGISTRATION_TRIGGERED, null); + mCi.registerForNotifyAnbr(this, EVENT_TRIGGER_NOTIFY_ANBR, null); IntentFilter filter = new IntentFilter( CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED); filter.addAction(TelecomManager.ACTION_CURRENT_TTY_MODE_CHANGED); filter.addAction(TelecomManager.ACTION_TTY_PREFERRED_MODE_CHANGED); + filter.addAction(TelephonyManager.ACTION_SIM_APPLICATION_STATE_CHANGED); mContext.registerReceiver(mBroadcastReceiver, filter, android.Manifest.permission.MODIFY_PHONE_STATE, null, Context.RECEIVER_EXPORTED); mCDM = new CarrierKeyDownloadManager(this); mCIM = new CarrierInfoManager(); + + initializeCarrierApps(); } private void initRatSpecific(int precisePhoneType) { @@ -506,8 +525,12 @@ // This is needed to handle phone process crashes mIsPhoneInEcmState = getInEcmMode(); if (mIsPhoneInEcmState) { - // Send a message which will invoke handleExitEmergencyCallbackMode - mCi.exitEmergencyCallbackMode(null); + if (DomainSelectionResolver.getInstance().isDomainSelectionSupported()) { + EmergencyStateTracker.getInstance().exitEmergencyCallbackMode(); + } else { + // Send a message which will invoke handleExitEmergencyCallbackMode + mCi.exitEmergencyCallbackMode(null); + } } mCi.setPhoneType(PhoneConstants.PHONE_TYPE_CDMA); @@ -530,7 +553,7 @@ logd("update icc_operator_numeric=" + operatorNumeric); tm.setSimOperatorNumericForPhone(mPhoneId, operatorNumeric); - SubscriptionController.getInstance().setMccMnc(operatorNumeric, getSubId()); + mSubscriptionManagerService.setMccMnc(getSubId(), operatorNumeric); // Sets iso country property by retrieving from build-time system property String iso = ""; @@ -542,7 +565,7 @@ logd("init: set 'gsm.sim.operator.iso-country' to iso=" + iso); tm.setSimCountryIsoForPhone(mPhoneId, iso); - SubscriptionController.getInstance().setCountryIso(iso, getSubId()); + mSubscriptionManagerService.setCountryIso(getSubId(), iso); // Updates MCC MNC device configuration information logd("update mccmnc=" + operatorNumeric); @@ -554,6 +577,31 @@ } } + /** + * Initialize the carrier apps. + */ + private void initializeCarrierApps() { + // Only perform on the default phone. There is no need to do it twice on the DSDS device. + if (mPhoneId != 0) return; + + logd("initializeCarrierApps"); + mContext.registerReceiverForAllUsers(new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + // Remove this line after testing + if (Intent.ACTION_USER_FOREGROUND.equals(intent.getAction())) { + UserHandle userHandle = intent.getParcelableExtra(Intent.EXTRA_USER); + // If couldn't get current user ID, guess it's 0. + CarrierAppUtils.disableCarrierAppsUntilPrivileged(mContext.getOpPackageName(), + TelephonyManager.getDefault(), + userHandle != null ? userHandle.getIdentifier() : 0, mContext); + } + } + }, new IntentFilter(Intent.ACTION_USER_FOREGROUND), null, null); + CarrierAppUtils.disableCarrierAppsUntilPrivileged(mContext.getOpPackageName(), + TelephonyManager.getDefault(), ActivityManager.getCurrentUser(), mContext); + } + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public boolean isPhoneTypeGsm() { return mPrecisePhoneType == PhoneConstants.PHONE_TYPE_GSM; @@ -680,11 +728,6 @@ } @Override - public TransportManager getTransportManager() { - return mTransportManager; - } - - @Override public AccessNetworksManager getAccessNetworksManager() { return mAccessNetworksManager; } @@ -736,83 +779,8 @@ } @Override - public PhoneConstants.DataState getDataConnectionState(String apnType) { - PhoneConstants.DataState ret = PhoneConstants.DataState.DISCONNECTED; - - if (mSST == null) { - // Radio Technology Change is ongoing, dispose() and removeReferences() have - // already been called - - ret = PhoneConstants.DataState.DISCONNECTED; - } else if (mSST.getCurrentDataConnectionState() != ServiceState.STATE_IN_SERVICE - && (isPhoneTypeCdma() || isPhoneTypeCdmaLte() || - (isPhoneTypeGsm() && !apnType.equals(ApnSetting.TYPE_EMERGENCY_STRING)))) { - // If we're out of service, open TCP sockets may still work - // but no data will flow - - // Emergency APN is available even in Out Of Service - // Pass the actual State of EPDN - - ret = PhoneConstants.DataState.DISCONNECTED; - } else { /* mSST.gprsState == ServiceState.STATE_IN_SERVICE */ - int currentTransport = mAccessNetworksManager.getCurrentTransport( - ApnSetting.getApnTypesBitmaskFromString(apnType)); - if (getDcTracker(currentTransport) != null) { - switch (getDcTracker(currentTransport).getState(apnType)) { - case CONNECTED: - case DISCONNECTING: - if (isDataSuspended()) { - ret = PhoneConstants.DataState.SUSPENDED; - } else { - ret = PhoneConstants.DataState.CONNECTED; - } - break; - case CONNECTING: - ret = PhoneConstants.DataState.CONNECTING; - break; - default: - ret = PhoneConstants.DataState.DISCONNECTED; - } - } - } - - logd("getDataConnectionState apnType=" + apnType + " ret=" + ret); - return ret; - } - - @Override public @DataActivityType int getDataActivityState() { - if (isUsingNewDataStack()) { - return getDataNetworkController().getDataActivity(); - } - int ret = TelephonyManager.DATA_ACTIVITY_NONE; - - if (mSST.getCurrentDataConnectionState() == ServiceState.STATE_IN_SERVICE - && getDcTracker(AccessNetworkConstants.TRANSPORT_TYPE_WWAN) != null) { - switch (getDcTracker(AccessNetworkConstants.TRANSPORT_TYPE_WWAN).getActivity()) { - case DATAIN: - ret = TelephonyManager.DATA_ACTIVITY_IN; - break; - - case DATAOUT: - ret = TelephonyManager.DATA_ACTIVITY_OUT; - break; - - case DATAINANDOUT: - ret = TelephonyManager.DATA_ACTIVITY_INOUT; - break; - - case DORMANT: - ret = TelephonyManager.DATA_ACTIVITY_DORMANT; - break; - - default: - ret = TelephonyManager.DATA_ACTIVITY_NONE; - break; - } - } - - return ret; + return getDataNetworkController().getDataActivity(); } /** @@ -831,7 +799,10 @@ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public void notifyPreciseCallStateChanged() { /* we'd love it if this was package-scoped*/ - super.notifyPreciseCallStateChangedP(); + AsyncResult ar = new AsyncResult(null, this, null); + mPreciseCallStateRegistrants.notifyRegistrants(ar); + + mNotifier.notifyPreciseCallState(this, null, null, null); } public void notifyNewRingingConnection(Connection c) { @@ -1023,14 +994,6 @@ } @Override - public void dispose() { - // Note: this API is currently never called. We are defining actions here in case - // we need to dispose GsmCdmaPhone/Phone object. - super.dispose(); - SubscriptionController.getInstance().unregisterForUiccAppsEnabled(this); - } - - @Override public void enableEnhancedVoicePrivacy(boolean enable, Message onComplete) { if (isPhoneTypeGsm()) { loge("enableEnhancedVoicePrivacy: not expected on GSM"); @@ -1434,8 +1397,7 @@ // emergency number list on another SIM, but is not on theirs. In this case we will use the // emergency number list for this carrier's SIM only. if (useOnlyDialedSimEccList) { - isEmergency = getEmergencyNumberTracker().isEmergencyNumber(dialString, - true /* exactMatch */); + isEmergency = getEmergencyNumberTracker().isEmergencyNumber(dialString); logi("dial; isEmergency=" + isEmergency + " (based on this phone only); globalIsEmergency=" + tm.isEmergencyNumber(dialString)); @@ -1444,6 +1406,12 @@ logi("dial; isEmergency=" + isEmergency + " (based on all phones)"); } + // Undetectable emergeny number indicated by new domain selection service + if (dialArgs.isEmergency) { + logi("dial; isEmergency=" + isEmergency + " (domain selection module)"); + isEmergency = true; + } + /** Check if the call is Wireless Priority Service call */ boolean isWpsCall = dialString != null ? (dialString.startsWith(PREFIX_WPS) || dialString.startsWith(PREFIX_WPS_CLIR_ACTIVATE) @@ -1469,6 +1437,55 @@ boolean useImsForCall = useImsForCall(dialArgs) && (isWpsCall ? allowWpsOverIms : true); + Bundle extras = dialArgs.intentExtras; + if (extras != null && extras.containsKey(PhoneConstants.EXTRA_COMPARE_DOMAIN)) { + int domain = extras.getInt(PhoneConstants.EXTRA_DIAL_DOMAIN); + if (!isEmergency && (!isMmiCode || isPotentialUssdCode)) { + if ((domain == DOMAIN_PS && !useImsForCall) + || (domain == DOMAIN_CS && useImsForCall) + || domain == DOMAIN_UNKNOWN || domain == DOMAIN_CS_PS) { + loge("[Anomaly] legacy-useImsForCall:" + useImsForCall + + ", NCDS-domain:" + domain); + + AnomalyReporter.reportAnomaly( + UUID.fromString("bfae6c2e-ca2f-4121-b167-9cad26a3b353"), + "Domain selection results don't match. useImsForCall:" + + useImsForCall + ", NCDS-domain:" + domain); + } + } + extras.remove(PhoneConstants.EXTRA_COMPARE_DOMAIN); + } + + // Only when the domain selection service is supported, EXTRA_DIAL_DOMAIN extra shall exist. + if (extras != null && extras.containsKey(PhoneConstants.EXTRA_DIAL_DOMAIN)) { + int domain = extras.getInt(PhoneConstants.EXTRA_DIAL_DOMAIN); + logi("dial domain=" + domain); + useImsForCall = false; + useImsForUt = false; + useImsForEmergency = false; + if (domain == DOMAIN_PS) { + if (isEmergency) { + useImsForEmergency = true; + } else if (!isMmiCode || isPotentialUssdCode) { + useImsForCall = true; + } else { + // should not reach here + loge("dial unexpected Ut domain selection, ignored"); + } + } else if (domain == PhoneConstants.DOMAIN_NON_3GPP_PS) { + if (isEmergency) { + useImsForEmergency = true; + extras.putString(ImsCallProfile.EXTRA_CALL_RAT_TYPE, + String.valueOf(ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN)); + } else { + // should not reach here + loge("dial DOMAIN_NON_3GPP_PS should be used only for emergency calls"); + } + } + + extras.remove(PhoneConstants.EXTRA_DIAL_DOMAIN); + } + if (DBG) { logi("useImsForCall=" + useImsForCall + ", useOnlyDialedSimEccList=" + useOnlyDialedSimEccList @@ -1491,6 +1508,12 @@ + ((imsPhone != null) ? imsPhone.getServiceState().getState() : "N/A")); } + // Perform FDN check for non-emergency calls - shouldn't dial if number is blocked by FDN + if(!isEmergency && FdnUtils.isNumberBlockedByFDN(mPhoneId, dialString, getCountryIso())) { + throw new CallStateException(CallStateException.ERROR_FDN_BLOCKED, + "cannot dial number blocked by FDN"); + } + // Bypass WiFi Only WFC check if this is an emergency call - we should still try to // place over cellular if possible. if (!isEmergency) { @@ -1559,10 +1582,13 @@ } if (DBG) logd("Trying (non-IMS) CS call"); if (isDialedNumberSwapped && isEmergency) { - // Triggers ECM when CS call ends only for test emergency calls using - // ril.test.emergencynumber. - mIsTestingEmergencyCallbackMode = true; - mCi.testingEmergencyCall(); + // If domain selection is enabled, ECM testing is handled in EmergencyStateTracker + if (!DomainSelectionResolver.getInstance().isDomainSelectionSupported()) { + // Triggers ECM when CS call ends only for test emergency calls using + // ril.test.emergencynumber. + mIsTestingEmergencyCallbackMode = true; + mCi.testingEmergencyCall(); + } } chosenPhoneConsumer.accept(this); @@ -1688,6 +1714,13 @@ return true; } + // Perform FDN check + if(FdnUtils.isNumberBlockedByFDN(mPhoneId, ussdRequest, getCountryIso())) { + sendUssdResponse(ussdRequest, null, TelephonyManager.USSD_RETURN_FAILURE, + wrappedCallback ); + return true; + } + // Try over IMS if possible. Phone imsPhone = mImsPhone; if ((imsPhone != null) @@ -1787,7 +1820,7 @@ public void setRadioPower(boolean power, boolean forEmergencyCall, boolean isSelectedPhoneForEmergencyCall, boolean forceApply) { setRadioPowerForReason(power, forEmergencyCall, isSelectedPhoneForEmergencyCall, forceApply, - Phone.RADIO_POWER_REASON_USER); + TelephonyManager.RADIO_POWER_REASON_USER); } @Override @@ -1797,6 +1830,11 @@ forceApply, reason); } + @Override + public Set<Integer> getRadioPowerOffReasons() { + return mSST.getRadioPowerOffReasons(); + } + private void storeVoiceMailNumber(String number) { SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext()); SharedPreferences.Editor editor = sp.edit(); @@ -1946,6 +1984,11 @@ return mImei; } + @Override + public int getImeiType() { + return mImeiType; + } + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @Override public String getEsn() { @@ -2331,6 +2374,15 @@ @Override public void getCallForwardingOption(int commandInterfaceCFReason, int serviceClass, Message onComplete) { + // Perform FDN check + SsData.ServiceType serviceType = GsmMmiCode.cfReasonToServiceType(commandInterfaceCFReason); + if(isRequestBlockedByFDN(SsData.RequestType.SS_INTERROGATION, serviceType)) { + AsyncResult.forMessage(onComplete, null, + new CommandException(CommandException.Error.FDN_CHECK_FAILURE)); + onComplete.sendToTarget(); + return; + } + Phone imsPhone = mImsPhone; if (useSsOverIms(onComplete)) { imsPhone.getCallForwardingOption(commandInterfaceCFReason, serviceClass, onComplete); @@ -2380,6 +2432,16 @@ int serviceClass, int timerSeconds, Message onComplete) { + // Perform FDN check + SsData.RequestType requestType = GsmMmiCode.cfActionToRequestType(commandInterfaceCFAction); + SsData.ServiceType serviceType = GsmMmiCode.cfReasonToServiceType(commandInterfaceCFReason); + if(isRequestBlockedByFDN(requestType, serviceType)) { + AsyncResult.forMessage(onComplete, null, + new CommandException(CommandException.Error.FDN_CHECK_FAILURE)); + onComplete.sendToTarget(); + return; + } + Phone imsPhone = mImsPhone; if (useSsOverIms(onComplete)) { imsPhone.setCallForwardingOption(commandInterfaceCFAction, commandInterfaceCFReason, @@ -2433,6 +2495,15 @@ @Override public void getCallBarring(String facility, String password, Message onComplete, int serviceClass) { + // Perform FDN check + SsData.ServiceType serviceType = GsmMmiCode.cbFacilityToServiceType(facility); + if (isRequestBlockedByFDN(SsData.RequestType.SS_INTERROGATION, serviceType)) { + AsyncResult.forMessage(onComplete, null, + new CommandException(CommandException.Error.FDN_CHECK_FAILURE)); + onComplete.sendToTarget(); + return; + } + Phone imsPhone = mImsPhone; if (useSsOverIms(onComplete)) { imsPhone.getCallBarring(facility, password, onComplete, serviceClass); @@ -2449,6 +2520,17 @@ @Override public void setCallBarring(String facility, boolean lockState, String password, Message onComplete, int serviceClass) { + // Perform FDN check + SsData.RequestType requestType = lockState ? SsData.RequestType.SS_ACTIVATION : + SsData.RequestType.SS_DEACTIVATION; + SsData.ServiceType serviceType = GsmMmiCode.cbFacilityToServiceType(facility); + if (isRequestBlockedByFDN(requestType, serviceType)) { + AsyncResult.forMessage(onComplete, null, + new CommandException(CommandException.Error.FDN_CHECK_FAILURE)); + onComplete.sendToTarget(); + return; + } + Phone imsPhone = mImsPhone; if (useSsOverIms(onComplete)) { imsPhone.setCallBarring(facility, lockState, password, onComplete, serviceClass); @@ -2472,6 +2554,18 @@ */ public void changeCallBarringPassword(String facility, String oldPwd, String newPwd, Message onComplete) { + // Perform FDN check + SsData.ServiceType serviceType = GsmMmiCode.cbFacilityToServiceType(facility); + ArrayList<String> controlStrings = GsmMmiCode.getControlStringsForPwd( + SsData.RequestType.SS_REGISTRATION, + serviceType); + if(FdnUtils.isSuppServiceRequestBlockedByFdn(mPhoneId, controlStrings, getCountryIso())) { + AsyncResult.forMessage(onComplete, null, + new CommandException(CommandException.Error.FDN_CHECK_FAILURE)); + onComplete.sendToTarget(); + return; + } + if (isPhoneTypeGsm()) { mCi.changeBarringPassword(facility, oldPwd, newPwd, onComplete); } else { @@ -2481,7 +2575,16 @@ @Override public void getOutgoingCallerIdDisplay(Message onComplete) { + // Perform FDN check + if(isRequestBlockedByFDN(SsData.RequestType.SS_INTERROGATION, SsData.ServiceType.SS_CLIR)){ + AsyncResult.forMessage(onComplete, null, + new CommandException(CommandException.Error.FDN_CHECK_FAILURE)); + onComplete.sendToTarget(); + return; + } + Phone imsPhone = mImsPhone; + if (useSsOverIms(onComplete)) { imsPhone.getOutgoingCallerIdDisplay(onComplete); return; @@ -2499,6 +2602,15 @@ @Override public void setOutgoingCallerIdDisplay(int commandInterfaceCLIRMode, Message onComplete) { + // Perform FDN check + SsData.RequestType requestType = GsmMmiCode.clirModeToRequestType(commandInterfaceCLIRMode); + if (isRequestBlockedByFDN(requestType, SsData.ServiceType.SS_CLIR)) { + AsyncResult.forMessage(onComplete, null, + new CommandException(CommandException.Error.FDN_CHECK_FAILURE)); + onComplete.sendToTarget(); + return; + } + Phone imsPhone = mImsPhone; if (useSsOverIms(onComplete)) { imsPhone.setOutgoingCallerIdDisplay(commandInterfaceCLIRMode, onComplete); @@ -2521,6 +2633,14 @@ @Override public void queryCLIP(Message onComplete) { + // Perform FDN check + if(isRequestBlockedByFDN(SsData.RequestType.SS_INTERROGATION, SsData.ServiceType.SS_CLIP)){ + AsyncResult.forMessage(onComplete, null, + new CommandException(CommandException.Error.FDN_CHECK_FAILURE)); + onComplete.sendToTarget(); + return; + } + Phone imsPhone = mImsPhone; if (useSsOverIms(onComplete)) { imsPhone.queryCLIP(onComplete); @@ -2539,6 +2659,16 @@ @Override public void getCallWaiting(Message onComplete) { + // Perform FDN check + if(isRequestBlockedByFDN(SsData.RequestType.SS_INTERROGATION, SsData.ServiceType.SS_WAIT)){ + AsyncResult.forMessage(onComplete, null, + new CommandException(CommandException.Error.FDN_CHECK_FAILURE)); + onComplete.sendToTarget(); + return; + } + + if (mCallWaitingController.getCallWaiting(onComplete)) return; + Phone imsPhone = mImsPhone; if (useSsOverIms(onComplete)) { imsPhone.getCallWaiting(onComplete); @@ -2580,6 +2710,18 @@ @Override public void setCallWaiting(boolean enable, int serviceClass, Message onComplete) { + // Perform FDN check + SsData.RequestType requestType = enable ? SsData.RequestType.SS_ACTIVATION : + SsData.RequestType.SS_DEACTIVATION; + if (isRequestBlockedByFDN(requestType, SsData.ServiceType.SS_WAIT)) { + AsyncResult.forMessage(onComplete, null, + new CommandException(CommandException.Error.FDN_CHECK_FAILURE)); + onComplete.sendToTarget(); + return; + } + + if (mCallWaitingController.setCallWaiting(enable, serviceClass, onComplete)) return; + Phone imsPhone = mImsPhone; if (useSsOverIms(onComplete)) { imsPhone.setCallWaiting(enable, onComplete); @@ -2610,6 +2752,23 @@ } @Override + public int getTerminalBasedCallWaitingState(boolean forCsOnly) { + return mCallWaitingController.getTerminalBasedCallWaitingState(forCsOnly); + } + + @Override + public void setTerminalBasedCallWaitingStatus(int state) { + if (mImsPhone != null) { + mImsPhone.setTerminalBasedCallWaitingStatus(state); + } + } + + @Override + public void setTerminalBasedCallWaitingSupported(boolean supported) { + mCallWaitingController.setTerminalBasedCallWaitingSupported(supported); + } + + @Override public void getAvailableNetworks(Message response) { if (isPhoneTypeGsm() || isPhoneTypeCdmaLte()) { Message msg = obtainMessage(EVENT_GET_AVAILABLE_NETWORKS_DONE, response); @@ -2672,25 +2831,12 @@ @Override public boolean getDataRoamingEnabled() { - if (isUsingNewDataStack()) { - return getDataSettingsManager().isDataRoamingEnabled(); - } - if (getDcTracker(AccessNetworkConstants.TRANSPORT_TYPE_WWAN) != null) { - return getDcTracker(AccessNetworkConstants.TRANSPORT_TYPE_WWAN).getDataRoamingEnabled(); - } - return false; + return getDataSettingsManager().isDataRoamingEnabled(); } @Override public void setDataRoamingEnabled(boolean enable) { - if (isUsingNewDataStack()) { - getDataSettingsManager().setDataRoamingEnabled(enable); - return; - } - if (getDcTracker(AccessNetworkConstants.TRANSPORT_TYPE_WWAN) != null) { - getDcTracker(AccessNetworkConstants.TRANSPORT_TYPE_WWAN) - .setDataRoamingEnabledByUser(enable); - } + getDataSettingsManager().setDataRoamingEnabled(enable); } @Override @@ -2735,21 +2881,12 @@ } /** - * Whether data is enabled by user. Unlike isDataEnabled, this only - * checks user setting stored in {@link android.provider.Settings.Global#MOBILE_DATA} - * if not provisioning, or isProvisioningDataEnabled if provisioning. + * Whether data is enabled by user. */ @Override public boolean isUserDataEnabled() { - if (isUsingNewDataStack()) { - return getDataSettingsManager().isDataEnabledForReason( - TelephonyManager.DATA_ENABLED_REASON_USER); - } - if (mDataEnabledSettings.isProvisioning()) { - return mDataEnabledSettings.isProvisioningDataEnabled(); - } else { - return mDataEnabledSettings.isUserDataEnabled(); - } + return getDataSettingsManager().isDataEnabledForReason( + TelephonyManager.DATA_ENABLED_REASON_USER); } /** @@ -2901,11 +3038,12 @@ private void handleRadioAvailable() { mCi.getBasebandVersion(obtainMessage(EVENT_GET_BASEBAND_VERSION_DONE)); - + mCi.getImei(obtainMessage(EVENT_GET_DEVICE_IMEI_DONE)); mCi.getDeviceIdentity(obtainMessage(EVENT_GET_DEVICE_IDENTITY_DONE)); mCi.getRadioCapability(obtainMessage(EVENT_GET_RADIO_CAPABILITY)); mCi.areUiccApplicationsEnabled(obtainMessage(EVENT_GET_UICC_APPS_ENABLEMENT_DONE)); + handleNullCipherEnabledChange(); startLceAfterRadioIsAvailable(); } @@ -2951,7 +3089,21 @@ handleRadioAvailable(); } break; - + case EVENT_GET_DEVICE_IMEI_DONE : + ar = (AsyncResult)msg.obj; + if (ar.exception != null || ar.result == null) { + loge("Exception received : " + ar.exception); + break; + } + ImeiInfo imeiInfo = (ImeiInfo) ar.result; + if (!TextUtils.isEmpty(imeiInfo.imei)) { + mImeiType = imeiInfo.type; + mImei = imeiInfo.imei; + mImeiSv = imeiInfo.svn; + } else { + // TODO Report telephony anomaly + } + break; case EVENT_GET_DEVICE_IDENTITY_DONE:{ ar = (AsyncResult)msg.obj; @@ -2959,8 +3111,10 @@ break; } String[] respId = (String[])ar.result; - mImei = respId[0]; - mImeiSv = respId[1]; + if (TextUtils.isEmpty(mImei)) { + mImei = respId[0]; + mImeiSv = respId[1]; + } mEsn = respId[2]; mMeid = respId[3]; // some modems return all 0's instead of null/empty string when MEID is unavailable @@ -2985,12 +3139,16 @@ logd("Event EVENT_MODEM_RESET Received" + " isInEcm = " + isInEcm() + " isPhoneTypeGsm = " + isPhoneTypeGsm() + " mImsPhone = " + mImsPhone); if (isInEcm()) { - if (isPhoneTypeGsm()) { - if (mImsPhone != null) { - mImsPhone.handleExitEmergencyCallbackMode(); - } + if (DomainSelectionResolver.getInstance().isDomainSelectionSupported()) { + EmergencyStateTracker.getInstance().exitEmergencyCallbackMode(); } else { - handleExitEmergencyCallbackMode(msg); + if (isPhoneTypeGsm()) { + if (mImsPhone != null) { + mImsPhone.handleExitEmergencyCallbackMode(); + } + } else { + handleExitEmergencyCallbackMode(msg); + } } } } @@ -3283,24 +3441,9 @@ case EVENT_SET_CARRIER_DATA_ENABLED: ar = (AsyncResult) msg.obj; boolean enabled = (boolean) ar.result; - if (isUsingNewDataStack()) { - getDataSettingsManager().setDataEnabled( - TelephonyManager.DATA_ENABLED_REASON_CARRIER, enabled, - mContext.getOpPackageName()); - return; - } - mDataEnabledSettings.setDataEnabled(TelephonyManager.DATA_ENABLED_REASON_CARRIER, - enabled); - break; - case EVENT_DEVICE_PROVISIONED_CHANGE: - if (!isUsingNewDataStack()) { - mDataEnabledSettings.updateProvisionedChanged(); - } - break; - case EVENT_DEVICE_PROVISIONING_DATA_SETTING_CHANGE: - if (!isUsingNewDataStack()) { - mDataEnabledSettings.updateProvisioningDataEnabled(); - } + getDataSettingsManager().setDataEnabled( + TelephonyManager.DATA_ENABLED_REASON_CARRIER, enabled, + mContext.getOpPackageName()); break; case EVENT_GET_AVAILABLE_NETWORKS_DONE: ar = (AsyncResult) msg.obj; @@ -3375,12 +3518,64 @@ logd("EVENT_SUBSCRIPTIONS_CHANGED"); updateUsageSetting(); break; + case EVENT_SET_NULL_CIPHER_AND_INTEGRITY_DONE: + logd("EVENT_SET_NULL_CIPHER_AND_INTEGRITY_DONE"); + ar = (AsyncResult) msg.obj; + if (ar == null || ar.exception == null) { + mIsNullCipherAndIntegritySupported = true; + return; + } + CommandException.Error error = ((CommandException) ar.exception).getCommandError(); + mIsNullCipherAndIntegritySupported = !error.equals( + CommandException.Error.REQUEST_NOT_SUPPORTED); + break; + case EVENT_IMS_DEREGISTRATION_TRIGGERED: + logd("EVENT_IMS_DEREGISTRATION_TRIGGERED"); + ar = (AsyncResult) msg.obj; + if (ar.exception == null) { + mImsPhone.triggerImsDeregistration(((int[]) ar.result)[0]); + } else { + Rlog.e(LOG_TAG, "Unexpected unsol with exception", ar.exception); + } + break; + + case EVENT_TRIGGER_NOTIFY_ANBR: + logd("EVENT_TRIGGER_NOTIFY_ANBR"); + ar = (AsyncResult) msg.obj; + if (ar.exception == null) { + if (mImsPhone != null) { + mImsPhone.triggerNotifyAnbr(((int[]) ar.result)[0], ((int[]) ar.result)[1], + ((int[]) ar.result)[2]); + } + } + break; default: super.handleMessage(msg); } } + /** + * Check if a different SIM is inserted at this slot from the last time. Storing last subId + * in SharedPreference for now to detect SIM change. + * + * @return {@code true} if current slot mapping changed; {@code false} otherwise. + */ + private boolean currentSlotSubIdChanged() { + SharedPreferences sp = + PreferenceManager.getDefaultSharedPreferences(mContext); + int storedSubId = sp.getInt(CURR_SUBID + mPhoneId, -1); + boolean changed = storedSubId != getSubId(); + if (changed) { + // Update stored subId + SharedPreferences.Editor editor = sp.edit(); + editor.putInt(CURR_SUBID + mPhoneId, getSubId()); + editor.apply(); + } + Rlog.d(LOG_TAG, "currentSlotSubIdChanged: changed=" + changed); + return changed; + } + public UiccCardApplication getUiccCardApplication() { if (isPhoneTypeGsm()) { return mUiccController.getUiccCardApplication(mPhoneId, UiccController.APP_FAM_3GPP); @@ -3747,6 +3942,10 @@ //CDMA private void handleEnterEmergencyCallbackMode(Message msg) { + if (DomainSelectionResolver.getInstance().isDomainSelectionSupported()) { + Rlog.d(LOG_TAG, "DomainSelection enabled: ignore ECBM enter event."); + return; + } if (DBG) { Rlog.d(LOG_TAG, "handleEnterEmergencyCallbackMode, isInEcm()=" + isInEcm()); @@ -3770,6 +3969,10 @@ //CDMA private void handleExitEmergencyCallbackMode(Message msg) { + if (DomainSelectionResolver.getInstance().isDomainSelectionSupported()) { + Rlog.d(LOG_TAG, "DomainSelection enabled: ignore ECBM exit event."); + return; + } AsyncResult ar = (AsyncResult)msg.obj; if (DBG) { Rlog.d(LOG_TAG, "handleExitEmergencyCallbackMode,ar.exception , isInEcm=" @@ -3795,10 +3998,6 @@ // send an Intent sendEmergencyCallbackModeChange(); - // Re-initiate data connection - if (!isUsingNewDataStack()) { - mDataEnabledSettings.setInternalDataEnabled(true); - } notifyEmergencyCallRegistrants(false); } mIsTestingEmergencyCallbackMode = false; @@ -3816,6 +4015,7 @@ * otherwise, restart Ecm timer and notify apps the timer is restarted. */ public void handleTimerInEmergencyCallbackMode(int action) { + if (DomainSelectionResolver.getInstance().isDomainSelectionSupported()) return; switch(action) { case CANCEL_ECM_TIMER: removeCallbacks(mExitEcmRunnable); @@ -4229,6 +4429,7 @@ @Override public void setImsRegistrationState(boolean registered) { mSST.setImsRegistrationState(registered); + mCallWaitingController.setImsRegistrationState(registered); } @Override @@ -4290,6 +4491,13 @@ " mTelecomVoiceServiceStateOverride=" + mTelecomVoiceServiceStateOverride + "(" + ServiceState.rilServiceStateToString(mTelecomVoiceServiceStateOverride) + ")"); + pw.println(" mUiccApplicationsEnabled=" + mUiccApplicationsEnabled); + pw.flush(); + try { + mCallWaitingController.dump(pw); + } catch (Exception e) { + e.printStackTrace(); + } pw.flush(); } @@ -4409,7 +4617,7 @@ if (subInfo == null || TextUtils.isEmpty(subInfo.getCountryIso())) { return null; } - return subInfo.getCountryIso().toUpperCase(); + return subInfo.getCountryIso().toUpperCase(Locale.ROOT); } public void notifyEcbmTimerReset(Boolean flag) { @@ -4515,6 +4723,27 @@ mVolteSilentRedialRegistrants.notifyRegistrants(ar); } + /** {@inheritDoc} */ + @Override + public void registerForEmergencyDomainSelected( + @NonNull Handler h, int what, @Nullable Object obj) { + mEmergencyDomainSelectedRegistrants.addUnique(h, what, obj); + } + + /** {@inheritDoc} */ + @Override + public void unregisterForEmergencyDomainSelected(@NonNull Handler h) { + mEmergencyDomainSelectedRegistrants.remove(h); + } + + /** {@inheritDoc} */ + @Override + public void notifyEmergencyDomainSelected(@TransportType int transportType) { + logd("notifyEmergencyDomainSelected transportType=" + transportType); + mEmergencyDomainSelectedRegistrants.notifyRegistrants( + new AsyncResult(null, transportType, null)); + } + /** * Sets the SIM voice message waiting indicator records. * @param line GSM Subscriber Profile Number, one-based. Only '1' is supported @@ -4675,6 +4904,8 @@ // If no card is present or we don't have mUiccApplicationsEnabled yet, do nothing. if (slot == null || slot.getCardState() != IccCardStatus.CardState.CARDSTATE_PRESENT || mUiccApplicationsEnabled == null) { + loge("reapplyUiccAppsEnablementIfNeeded: slot state=" + + (slot != null ? slot.getCardState() : null)); return; } @@ -4687,18 +4918,24 @@ return; } - SubscriptionInfo info = SubscriptionController.getInstance().getSubInfoForIccId( - IccUtils.stripTrailingFs(iccId)); + SubscriptionInfo info = mSubscriptionManagerService + .getAllSubInfoList(mContext.getOpPackageName(), mContext.getAttributionTag()) + .stream() + .filter(subInfo -> subInfo.getIccId().equals(IccUtils.stripTrailingFs(iccId))) + .findFirst() + .orElse(null); + + logd("reapplyUiccAppsEnablementIfNeeded: retries=" + retries + ", subInfo=" + info); // If info is null, it could be a new subscription. By default we enable it. - boolean expectedValue = info == null ? true : info.areUiccApplicationsEnabled(); + boolean expectedValue = info == null || info.areUiccApplicationsEnabled(); // If for any reason current state is different from configured state, re-apply the // configured state. if (expectedValue != mUiccApplicationsEnabled) { mCi.enableUiccApplications(expectedValue, Message.obtain( this, EVENT_REAPPLY_UICC_APPS_ENABLEMENT_DONE, - new Pair<Boolean, Integer>(expectedValue, retries))); + new Pair<>(expectedValue, retries))); } } @@ -4745,24 +4982,7 @@ * @return Currently bound data service package names. */ public @NonNull List<String> getDataServicePackages() { - if (isUsingNewDataStack()) { - return getDataNetworkController().getDataServicePackages(); - } - List<String> packages = new ArrayList<>(); - int[] transports = new int[]{AccessNetworkConstants.TRANSPORT_TYPE_WWAN, - AccessNetworkConstants.TRANSPORT_TYPE_WLAN}; - - for (int transport : transports) { - DcTracker dct = getDcTracker(transport); - if (dct != null) { - String pkg = dct.getDataServiceManager().getDataServicePackageName(); - if (!TextUtils.isEmpty(pkg)) { - packages.add(pkg); - } - } - } - - return packages; + return getDataNetworkController().getDataServicePackages(); } private void updateBroadcastEmergencyCallStateChangesAfterCarrierConfigChanged( @@ -4804,28 +5024,26 @@ boolean mIsVonrEnabledByCarrier = config.getBoolean(CarrierConfigManager.KEY_VONR_ENABLED_BOOL); - - String result = SubscriptionController.getInstance().getSubscriptionProperty( - getSubId(), - SubscriptionManager.NR_ADVANCED_CALLING_ENABLED); + boolean mDefaultVonr = + config.getBoolean(CarrierConfigManager.KEY_VONR_ON_BY_DEFAULT_BOOL); int setting = -1; - if (result != null) { - setting = Integer.parseInt(result); + SubscriptionInfoInternal subInfo = mSubscriptionManagerService + .getSubscriptionInfoInternal(getSubId()); + if (subInfo != null) { + setting = subInfo.getNrAdvancedCallingEnabled(); } logd("VoNR setting from telephony.db:" + setting + " ,vonr_enabled_bool:" - + mIsVonrEnabledByCarrier); + + mIsVonrEnabledByCarrier + + " ,vonr_on_by_default_bool:" + + mDefaultVonr); - if (!mIsVonrEnabledByCarrier) { - mCi.setVoNrEnabled(false, obtainMessage(EVENT_SET_VONR_ENABLED_DONE), null); - } else if (setting == 1 || setting == -1) { - mCi.setVoNrEnabled(true, obtainMessage(EVENT_SET_VONR_ENABLED_DONE), null); - } else if (setting == 0) { - mCi.setVoNrEnabled(false, obtainMessage(EVENT_SET_VONR_ENABLED_DONE), null); - } + boolean enbleVonr = mIsVonrEnabledByCarrier + && (setting == 1 || (setting == -1 && mDefaultVonr)); + mCi.setVoNrEnabled(enbleVonr, obtainMessage(EVENT_SET_VONR_ENABLED_DONE), null); } private void updateCdmaRoamingSettingsAfterCarrierConfigChanged(PersistableBundle config) { @@ -4887,4 +5105,49 @@ public InboundSmsHandler getInboundSmsHandler(boolean is3gpp2) { return mIccSmsInterfaceManager.getInboundSmsHandler(is3gpp2); } + + /** + * Return current cell broadcast ranges. + */ + public List<CellBroadcastIdRange> getCellBroadcastIdRanges() { + return mCellBroadcastConfigTracker.getCellBroadcastIdRanges(); + } + + /** + * Set reception of cell broadcast messages with the list of the given ranges. + */ + @Override + public void setCellBroadcastIdRanges( + @NonNull List<CellBroadcastIdRange> ranges, Consumer<Integer> callback) { + mCellBroadcastConfigTracker.setCellBroadcastIdRanges(ranges, callback); + } + + /** + * The following function checks if supplementary service request is blocked due to FDN. + * @param requestType request type associated with the supplementary service + * @param serviceType supplementary service type + * @return {@code true} if request is blocked due to FDN. + */ + private boolean isRequestBlockedByFDN(SsData.RequestType requestType, + SsData.ServiceType serviceType) { + ArrayList<String> controlStrings = GsmMmiCode.getControlStrings(requestType, serviceType); + return FdnUtils.isSuppServiceRequestBlockedByFdn(mPhoneId, controlStrings, getCountryIso()); + } + + @Override + public void handleNullCipherEnabledChange() { + if (!DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_CELLULAR_SECURITY, + TelephonyManager.PROPERTY_ENABLE_NULL_CIPHER_TOGGLE, true)) { + logi("Not handling null cipher update. Feature disabled by DeviceConfig."); + return; + } + mCi.setNullCipherAndIntegrityEnabled( + getNullCipherAndIntegrityEnabledPreference(), + obtainMessage(EVENT_SET_NULL_CIPHER_AND_INTEGRITY_DONE)); + } + + @Override + public boolean isNullCipherAndIntegritySupported() { + return mIsNullCipherAndIntegritySupported; + } }
diff --git a/src/java/com/android/internal/telephony/HalVersion.java b/src/java/com/android/internal/telephony/HalVersion.java index f83d790..c05111b 100644 --- a/src/java/com/android/internal/telephony/HalVersion.java +++ b/src/java/com/android/internal/telephony/HalVersion.java
@@ -25,6 +25,9 @@ */ public class HalVersion implements Comparable<HalVersion> { + /** The HAL Version indicating that the version is unsupported */ + public static final HalVersion UNSUPPORTED = new HalVersion(-2, -2); + /** The HAL Version indicating that the version is unknown or invalid */ public static final HalVersion UNKNOWN = new HalVersion(-1, -1);
diff --git a/src/java/com/android/internal/telephony/IccPhoneBookInterfaceManager.java b/src/java/com/android/internal/telephony/IccPhoneBookInterfaceManager.java index 742cc90..ab62aa4 100644 --- a/src/java/com/android/internal/telephony/IccPhoneBookInterfaceManager.java +++ b/src/java/com/android/internal/telephony/IccPhoneBookInterfaceManager.java
@@ -39,6 +39,7 @@ import com.android.telephony.Rlog; import java.util.List; +import java.util.Locale; import java.util.concurrent.atomic.AtomicBoolean; /** @@ -337,7 +338,10 @@ } efid = updateEfForIccType(efid); - if (DBG) logd("getAdnRecordsInEF: efid=0x" + Integer.toHexString(efid).toUpperCase()); + if (DBG) { + logd("getAdnRecordsInEF: efid=0x" + Integer.toHexString(efid) + .toUpperCase(Locale.ROOT)); + } checkThread(); Request loadRequest = new Request();
diff --git a/src/java/com/android/internal/telephony/IccProvider.java b/src/java/com/android/internal/telephony/IccProvider.java index 7a128c0..b7c7e7b 100644 --- a/src/java/com/android/internal/telephony/IccProvider.java +++ b/src/java/com/android/internal/telephony/IccProvider.java
@@ -37,6 +37,7 @@ import com.android.telephony.Rlog; import java.util.List; +import java.util.Locale; /** * {@hide} @@ -424,7 +425,7 @@ private MatrixCursor loadFromEf(int efType, int subId) { if (DBG) log("loadFromEf: efType=0x" + - Integer.toHexString(efType).toUpperCase() + ", subscription=" + subId); + Integer.toHexString(efType).toUpperCase(Locale.ROOT) + ", subscription=" + subId); List<AdnRecord> adnRecords = null; try {
diff --git a/src/java/com/android/internal/telephony/IccSmsInterfaceManager.java b/src/java/com/android/internal/telephony/IccSmsInterfaceManager.java index a329771..2d77631 100644 --- a/src/java/com/android/internal/telephony/IccSmsInterfaceManager.java +++ b/src/java/com/android/internal/telephony/IccSmsInterfaceManager.java
@@ -176,6 +176,41 @@ mSmsPermissions = smsPermissions; } + /** + * PhoneFactory Dependencies for testing. + */ + @VisibleForTesting + public interface PhoneFactoryProxy { + Phone getPhone(int index); + Phone getDefaultPhone(); + Phone[] getPhones(); + } + + private PhoneFactoryProxy mPhoneFactoryProxy = new PhoneFactoryProxy() { + @Override + public Phone getPhone(int index) { + return PhoneFactory.getPhone(index); + } + + @Override + public Phone getDefaultPhone() { + return PhoneFactory.getDefaultPhone(); + } + + @Override + public Phone[] getPhones() { + return PhoneFactory.getPhones(); + } + }; + + /** + * Overrides PhoneFactory dependencies for testing. + */ + @VisibleForTesting + public void setPhoneFactoryProxy(PhoneFactoryProxy proxy) { + mPhoneFactoryProxy = proxy; + } + private void enforceNotOnHandlerThread(String methodName) { if (Looper.myLooper() == mHandler.getLooper()) { throw new RuntimeException("This method " + methodName + " will deadlock if called from" @@ -457,11 +492,11 @@ */ public void sendText(String callingPackage, String destAddr, String scAddr, String text, PendingIntent sentIntent, PendingIntent deliveryIntent, - boolean persistMessageForNonDefaultSmsApp, long messageId) { + boolean persistMessageForNonDefaultSmsApp, long messageId, boolean skipShortCodeCheck) { sendTextInternal(callingPackage, destAddr, scAddr, text, sentIntent, deliveryIntent, persistMessageForNonDefaultSmsApp, SMS_MESSAGE_PRIORITY_NOT_SPECIFIED, false /* expectMore */, SMS_MESSAGE_PERIOD_NOT_SPECIFIED, false /* isForVvm */, - messageId); + messageId, skipShortCodeCheck); } /** @@ -481,6 +516,16 @@ SMS_MESSAGE_PERIOD_NOT_SPECIFIED, isForVvm, 0L /* messageId */); } + + private void sendTextInternal(String callingPackage, String destAddr, String scAddr, + String text, PendingIntent sentIntent, PendingIntent deliveryIntent, + boolean persistMessageForNonDefaultSmsApp, int priority, boolean expectMore, + int validityPeriod, boolean isForVvm, long messageId) { + sendTextInternal(callingPackage, destAddr, scAddr, text, sentIntent, deliveryIntent, + persistMessageForNonDefaultSmsApp, priority, expectMore, validityPeriod, isForVvm, + messageId, false); + } + /** * Send a text based SMS. * @@ -527,12 +572,13 @@ * Any Other values including negative considered as Invalid Validity Period of the message. * @param messageId An id that uniquely identifies the message requested to be sent. * Used for logging and diagnostics purposes. The id may be 0. + * @param skipShortCodeCheck Skip check for short code type destination address. */ private void sendTextInternal(String callingPackage, String destAddr, String scAddr, String text, PendingIntent sentIntent, PendingIntent deliveryIntent, boolean persistMessageForNonDefaultSmsApp, int priority, boolean expectMore, - int validityPeriod, boolean isForVvm, long messageId) { + int validityPeriod, boolean isForVvm, long messageId, boolean skipShortCodeCheck) { if (Rlog.isLoggable("SMS", Log.VERBOSE)) { log("sendText: destAddr=" + destAddr + " scAddr=" + scAddr + " text='" + text + "' sentIntent=" + sentIntent + " deliveryIntent=" @@ -544,7 +590,7 @@ destAddr = filterDestAddress(destAddr); mDispatchersController.sendText(destAddr, scAddr, text, sentIntent, deliveryIntent, null/*messageUri*/, callingPackage, persistMessageForNonDefaultSmsApp, - priority, expectMore, validityPeriod, isForVvm, messageId); + priority, expectMore, validityPeriod, isForVvm, messageId, skipShortCodeCheck); } /** @@ -625,9 +671,9 @@ } if (Rlog.isLoggable("SMS", Log.VERBOSE)) { - log("pdu: " + pdu + - "\n format=" + format + - "\n receivedIntent=" + receivedIntent); + log("pdu: " + IccUtils.bytesToHexString(pdu) + + "\n format=" + format + + "\n receivedIntent=" + receivedIntent); } mDispatchersController.injectSmsPdu(pdu, format, false /* isOverIms */, result -> { @@ -862,6 +908,7 @@ public String getSmscAddressFromIccEf(String callingPackage) { if (!mSmsPermissions.checkCallingOrSelfCanGetSmscAddress( callingPackage, "getSmscAddressFromIccEf")) { + loge("Caller do not have permission to call GetSmscAddress"); return null; } enforceNotOnHandlerThread("getSmscAddressFromIccEf"); @@ -883,6 +930,7 @@ public boolean setSmscAddressOnIccEf(String callingPackage, String smsc) { if (!mSmsPermissions.checkCallingOrSelfCanSetSmscAddress( callingPackage, "setSmscAddressOnIccEf")) { + loge("Caller do not have permission to call SetSmscAddress"); return false; } enforceNotOnHandlerThread("setSmscAddressOnIccEf"); @@ -1452,11 +1500,27 @@ return null; } - private void notifyIfOutgoingEmergencySms(String destAddr) { + @VisibleForTesting + public void notifyIfOutgoingEmergencySms(String destAddr) { + Phone[] allPhones = mPhoneFactoryProxy.getPhones(); EmergencyNumber emergencyNumber = mPhone.getEmergencyNumberTracker().getEmergencyNumber( destAddr); if (emergencyNumber != null) { mPhone.notifyOutgoingEmergencySms(emergencyNumber); + } else if (allPhones.length > 1) { + // If there are multiple active SIMs, check all instances: + for (Phone phone : allPhones) { + // If the current iteration was already checked, skip: + if (phone.getPhoneId() == mPhone.getPhoneId()) { + continue; + } + emergencyNumber = phone.getEmergencyNumberTracker() + .getEmergencyNumber(destAddr); + if (emergencyNumber != null) { + mPhone.notifyOutgoingEmergencySms(emergencyNumber); + break; + } + } } }
diff --git a/src/java/com/android/internal/telephony/ImsIndication.java b/src/java/com/android/internal/telephony/ImsIndication.java new file mode 100644 index 0000000..00652f8 --- /dev/null +++ b/src/java/com/android/internal/telephony/ImsIndication.java
@@ -0,0 +1,117 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.telephony; + +import static android.telephony.TelephonyManager.HAL_SERVICE_IMS; + +import static com.android.internal.telephony.RILConstants.RIL_UNSOL_CONNECTION_SETUP_FAILURE; +import static com.android.internal.telephony.RILConstants.RIL_UNSOL_NOTIFY_ANBR; +import static com.android.internal.telephony.RILConstants.RIL_UNSOL_TRIGGER_IMS_DEREGISTRATION; + +import android.hardware.radio.ims.IRadioImsIndication; +import android.os.AsyncResult; +import android.telephony.ims.feature.ConnectionFailureInfo; + +/** + * Interface declaring unsolicited radio indications for IMS APIs. + */ +public class ImsIndication extends IRadioImsIndication.Stub { + private final RIL mRil; + + public ImsIndication(RIL ril) { + mRil = ril; + } + + @Override + public String getInterfaceHash() { + return IRadioImsIndication.HASH; + } + + @Override + public int getInterfaceVersion() { + return IRadioImsIndication.VERSION; + } + + /** + * Fired by radio when any IMS traffic is not sent to network due to any failure + * on cellular networks. + * + * @param indicationType Type of radio indication. + * @param token The token provided by {@link #startImsTraffic}. + * @param failureInfo Connection failure information. + */ + public void onConnectionSetupFailure(int indicationType, int token, + android.hardware.radio.ims.ConnectionFailureInfo failureInfo) { + mRil.processIndication(HAL_SERVICE_IMS, indicationType); + + Object[] response = new Object[2]; + response[0] = token; + response[1] = new ConnectionFailureInfo( + RILUtils.convertHalConnectionFailureReason(failureInfo.failureReason), + failureInfo.causeCode, failureInfo.waitTimeMillis); + + if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_CONNECTION_SETUP_FAILURE, response); + + mRil.mConnectionSetupFailureRegistrants.notifyRegistrants( + new AsyncResult(null, response, null)); + } + + /** + * Fired by radio when ANBR is received form the network. + * + * @param indicationType Type of radio indication. + * @param qosSessionId QoS session ID is used to identify media stream such as audio or video. + * @param imsdirection Direction of this packet stream (e.g. uplink or downlink). + * @param bitsPerSecond The recommended bit rate for the UE + * for a specific logical channel and a specific direction by the network. + */ + public void notifyAnbr(int indicationType, int qosSessionId, int imsdirection, + int bitsPerSecond) { + mRil.processIndication(HAL_SERVICE_IMS, indicationType); + + int[] response = new int[3]; + response[0] = qosSessionId; + response[1] = imsdirection; + response[2] = bitsPerSecond; + + if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_NOTIFY_ANBR, response); + + mRil.mNotifyAnbrRegistrants.notifyRegistrants(new AsyncResult(null, response, null)); + } + + /** + * Requests IMS stack to perform graceful IMS deregistration before radio performing + * network detach in the events of SIM remove, refresh or and so on. The radio waits for + * the IMS deregistration, which will be notified by telephony via + * {@link IRadioIms#updateImsRegistrationInfo()}, or a certain timeout interval to start + * the network detach procedure. + * + * @param indicationType Type of radio indication. + * @param reason the reason why the deregistration is triggered. + */ + public void triggerImsDeregistration(int indicationType, int reason) { + mRil.processIndication(HAL_SERVICE_IMS, indicationType); + + if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_TRIGGER_IMS_DEREGISTRATION, reason); + + int[] response = new int[1]; + response[0] = RILUtils.convertHalDeregistrationReason(reason); + + mRil.mTriggerImsDeregistrationRegistrants.notifyRegistrants( + new AsyncResult(null, response, null)); + } +}
diff --git a/src/java/com/android/internal/telephony/ImsResponse.java b/src/java/com/android/internal/telephony/ImsResponse.java new file mode 100644 index 0000000..1adc000 --- /dev/null +++ b/src/java/com/android/internal/telephony/ImsResponse.java
@@ -0,0 +1,110 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.telephony; + +import static android.telephony.TelephonyManager.HAL_SERVICE_IMS; +import static android.telephony.ims.feature.MmTelFeature.ImsTrafficSessionCallbackWrapper.INVALID_TOKEN; + +import android.hardware.radio.RadioError; +import android.hardware.radio.RadioResponseInfo; +import android.hardware.radio.ims.IRadioImsResponse; +import android.telephony.ims.feature.ConnectionFailureInfo; + +/** + * Interface declaring response functions to solicited radio requests for IMS APIs. + */ +public class ImsResponse extends IRadioImsResponse.Stub { + private final RIL mRil; + + public ImsResponse(RIL ril) { + mRil = ril; + } + + @Override + public String getInterfaceHash() { + return IRadioImsResponse.HASH; + } + + @Override + public int getInterfaceVersion() { + return IRadioImsResponse.VERSION; + } + + /** + * @param info Response info struct containing response type, serial no. and error. + */ + public void setSrvccCallInfoResponse(RadioResponseInfo info) { + RadioResponse.responseVoid(HAL_SERVICE_IMS, mRil, info); + } + + /** + * @param info Response info struct containing response type, serial no. and error. + */ + public void updateImsRegistrationInfoResponse(RadioResponseInfo info) { + RadioResponse.responseVoid(HAL_SERVICE_IMS, mRil, info); + } + + /** + * @param responseInfo Response info struct containing response type, serial no. and error. + * @param failureInfo Failure information. + */ + public void startImsTrafficResponse(RadioResponseInfo responseInfo, + android.hardware.radio.ims.ConnectionFailureInfo failureInfo) { + RILRequest rr = mRil.processResponse(HAL_SERVICE_IMS, responseInfo); + + if (rr != null) { + Object[] response = { new Integer(INVALID_TOKEN), null }; + if (responseInfo.error == RadioError.NONE) { + if (failureInfo != null) { + response[1] = new ConnectionFailureInfo( + RILUtils.convertHalConnectionFailureReason(failureInfo.failureReason), + failureInfo.causeCode, failureInfo.waitTimeMillis); + } + RadioResponse.sendMessageResponse(rr.mResult, response); + } + mRil.processResponseDone(rr, responseInfo, response); + } + } + + /** + * @param info Response info struct containing response type, serial no. and error. + */ + public void stopImsTrafficResponse(RadioResponseInfo info) { + RadioResponse.responseVoid(HAL_SERVICE_IMS, mRil, info); + } + + /** + * @param info Response info struct containing response type, serial no. and error. + */ + public void triggerEpsFallbackResponse(RadioResponseInfo info) { + RadioResponse.responseVoid(HAL_SERVICE_IMS, mRil, info); + } + + /** + * @param info Response info struct containing response type, serial no. and error. + */ + public void sendAnbrQueryResponse(RadioResponseInfo info) { + RadioResponse.responseVoid(HAL_SERVICE_IMS, mRil, info); + } + + /** + * @param info Response info struct containing response type, serial no. and error. + */ + public void updateImsCallStatusResponse(RadioResponseInfo info) { + RadioResponse.responseVoid(HAL_SERVICE_IMS, mRil, info); + } +}
diff --git a/src/java/com/android/internal/telephony/ImsSmsDispatcher.java b/src/java/com/android/internal/telephony/ImsSmsDispatcher.java index 42fe7a7..90885fe 100644 --- a/src/java/com/android/internal/telephony/ImsSmsDispatcher.java +++ b/src/java/com/android/internal/telephony/ImsSmsDispatcher.java
@@ -16,6 +16,7 @@ package com.android.internal.telephony; +import android.app.Activity; import android.content.Context; import android.os.Binder; import android.os.Message; @@ -43,7 +44,9 @@ import com.android.internal.telephony.util.SMSDispatcherUtil; import com.android.telephony.Rlog; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executor; @@ -58,6 +61,7 @@ private static final String TAG = "ImsSmsDispatcher"; private static final int CONNECT_DELAY_MS = 5000; // 5 seconds; + public static final int MAX_SEND_RETRIES_OVER_IMS = MAX_SEND_RETRIES; /** * Creates FeatureConnector instances for ImsManager, used during testing to inject mock @@ -72,6 +76,7 @@ FeatureConnector.Listener<ImsManager> listener, Executor executor); } + public List<Integer> mMemoryAvailableNotifierList = new ArrayList<Integer>(); @VisibleForTesting public Map<Integer, SmsTracker> mTrackers = new ConcurrentHashMap<>(); @VisibleForTesting @@ -140,6 +145,37 @@ private final IImsSmsListener mImsSmsListener = new IImsSmsListener.Stub() { @Override + public void onMemoryAvailableResult(int token, @SendStatusResult int status, + int networkReasonCode) { + final long identity = Binder.clearCallingIdentity(); + try { + logd("onMemoryAvailableResult token=" + token + " status=" + status + + " networkReasonCode=" + networkReasonCode); + if (!mMemoryAvailableNotifierList.contains(token)) { + loge("onMemoryAvailableResult Invalid token"); + return; + } + mMemoryAvailableNotifierList.remove(Integer.valueOf(token)); + + /** + * The Retrans flag is set and reset As per section 6.3.3.1.2 in TS 124011 + * Note: Assuming that SEND_STATUS_ERROR_RETRY is sent in case of temporary failure + */ + if (status == ImsSmsImplBase.SEND_STATUS_ERROR_RETRY) { + if (!mRPSmmaRetried) { + sendMessageDelayed(obtainMessage(EVENT_RETRY_SMMA), SEND_RETRY_DELAY); + mRPSmmaRetried = true; + } else { + mRPSmmaRetried = false; + } + } else { + mRPSmmaRetried = false; + } + } finally { + Binder.restoreCallingIdentity(identity); + } + } + @Override public void onSendSmsResult(int token, int messageRef, @SendStatusResult int status, @SmsManager.Result int reason, int networkReasonCode) { final long identity = Binder.clearCallingIdentity(); @@ -170,10 +206,19 @@ mTrackers.remove(token); break; case ImsSmsImplBase.SEND_STATUS_ERROR_RETRY: - if (tracker.mRetryCount < MAX_SEND_RETRIES) { + int maxRetryCountOverIms = getMaxRetryCountOverIms(); + if (tracker.mRetryCount < getMaxSmsRetryCount()) { + if (maxRetryCountOverIms < getMaxSmsRetryCount() + && tracker.mRetryCount >= maxRetryCountOverIms) { + tracker.mRetryCount += 1; + mTrackers.remove(token); + fallbackToPstn(tracker); + break; + } tracker.mRetryCount += 1; sendMessageDelayed( - obtainMessage(EVENT_SEND_RETRY, tracker), SEND_RETRY_DELAY); + obtainMessage(EVENT_SEND_RETRY, tracker), + getSmsRetryDelayValue()); } else { tracker.onFailed(mContext, reason, networkReasonCode); mTrackers.remove(token); @@ -193,6 +238,7 @@ SmsConstants.FORMAT_3GPP2.equals(getFormat()), status == ImsSmsImplBase.SEND_STATUS_ERROR_FALLBACK, reason, + networkReasonCode, tracker.mMessageId, tracker.isFromDefaultSmsApplication(mContext), tracker.getInterval()); @@ -248,6 +294,10 @@ mappedResult = ImsSmsImplBase.DELIVER_STATUS_ERROR_REQUEST_NOT_SUPPORTED; break; + case Activity.RESULT_OK: + // class2 message saving to SIM operation is in progress, defer ack + // until saving to SIM is success/failure + return; default: mappedResult = ImsSmsImplBase.DELIVER_STATUS_ERROR_GENERIC; break; @@ -263,7 +313,7 @@ } catch (ImsException e) { loge("Failed to acknowledgeSms(). Error: " + e.getMessage()); } - }, true /* ignoreClass */, true /* isOverIms */); + }, true /* ignoreClass */, true /* isOverIms */, token); } finally { Binder.restoreCallingIdentity(identity); } @@ -277,6 +327,10 @@ logd("SMS retry.."); sendSms((SmsTracker) msg.obj); break; + case EVENT_RETRY_SMMA: + logd("SMMA Notification retry.."); + onMemoryAvailable(); + break; default: super.handleMessage(msg); } @@ -295,6 +349,9 @@ mImsManager = manager; setListeners(); mIsImsServiceUp = true; + + /* set ImsManager */ + mSmsDispatchersController.setImsManager(mImsManager); } } @@ -309,6 +366,9 @@ synchronized (mLock) { mImsManager = null; mIsImsServiceUp = false; + + /* unset ImsManager */ + mSmsDispatchersController.setImsManager(null); } } }, this::post); @@ -394,6 +454,81 @@ } @Override + public int getMaxSmsRetryCount() { + int retryCount = MAX_SEND_RETRIES; + CarrierConfigManager mConfigManager; + + mConfigManager = (CarrierConfigManager) mContext.getSystemService( + Context.CARRIER_CONFIG_SERVICE); + + if (mConfigManager != null) { + PersistableBundle carrierConfig = mConfigManager.getConfigForSubId( + getSubId()); + + if (carrierConfig != null) { + retryCount = carrierConfig.getInt( + CarrierConfigManager.ImsSms.KEY_SMS_MAX_RETRY_COUNT_INT); + } + } + + Rlog.d(TAG, "Retry Count: " + retryCount); + + return retryCount; + } + + /** + * Returns the number of times SMS can be sent over IMS + * + * @return retry count over Ims from carrier configuration + */ + @VisibleForTesting + public int getMaxRetryCountOverIms() { + int retryCountOverIms = MAX_SEND_RETRIES_OVER_IMS; + CarrierConfigManager mConfigManager; + + mConfigManager = (CarrierConfigManager) mContext.getSystemService(Context + .CARRIER_CONFIG_SERVICE); + + if (mConfigManager != null) { + PersistableBundle carrierConfig = mConfigManager.getConfigForSubId( + getSubId()); + + + if (carrierConfig != null) { + retryCountOverIms = carrierConfig.getInt( + CarrierConfigManager.ImsSms.KEY_SMS_MAX_RETRY_OVER_IMS_COUNT_INT); + } + } + + Rlog.d(TAG, "Retry Count Over Ims: " + retryCountOverIms); + + return retryCountOverIms; + } + + @Override + public int getSmsRetryDelayValue() { + int retryDelay = SEND_RETRY_DELAY; + CarrierConfigManager mConfigManager; + + mConfigManager = (CarrierConfigManager) mContext.getSystemService( + Context.CARRIER_CONFIG_SERVICE); + + if (mConfigManager != null) { + PersistableBundle carrierConfig = mConfigManager.getConfigForSubId( + getSubId()); + + if (carrierConfig != null) { + retryDelay = carrierConfig.getInt( + CarrierConfigManager.ImsSms.KEY_SMS_OVER_IMS_SEND_RETRY_DELAY_MILLIS_INT); + } + } + + Rlog.d(TAG, "Retry delay: " + retryDelay); + + return retryDelay; + } + + @Override protected boolean shouldBlockSmsForEcbm() { // We should not block outgoing SMS during ECM on IMS. It only applies to outgoing CDMA // SMS. @@ -416,10 +551,43 @@ } @Override + protected SmsMessageBase.SubmitPduBase getSubmitPdu(String scAddr, String destAddr, + String message, boolean statusReportRequested, SmsHeader smsHeader, int priority, + int validityPeriod, int messageRef) { + return SMSDispatcherUtil.getSubmitPdu(isCdmaMo(), scAddr, destAddr, message, + statusReportRequested, smsHeader, priority, validityPeriod, messageRef); + } + + @Override + protected SmsMessageBase.SubmitPduBase getSubmitPdu(String scAddr, String destAddr, + int destPort, byte[] message, boolean statusReportRequested, int messageRef) { + return SMSDispatcherUtil.getSubmitPdu(isCdmaMo(), scAddr, destAddr, destPort, message, + statusReportRequested, messageRef); + } + + @Override protected TextEncodingDetails calculateLength(CharSequence messageBody, boolean use7bitOnly) { return SMSDispatcherUtil.calculateLength(isCdmaMo(), messageBody, use7bitOnly); } + /** + * Send the Memory Available Event to the ImsService + */ + public void onMemoryAvailable() { + logd("onMemoryAvailable "); + int token = mNextToken.incrementAndGet(); + try { + logd("onMemoryAvailable: token = " + token); + mMemoryAvailableNotifierList.add(token); + getImsManager().onMemoryAvailable(token); + } catch (ImsException e) { + loge("onMemoryAvailable failed: " + e.getMessage()); + if (mMemoryAvailableNotifierList.contains(token)) { + mMemoryAvailableNotifierList.remove(Integer.valueOf(token)); + } + } + } + @Override public void sendSms(SmsTracker tracker) { logd("sendSms: " @@ -489,6 +657,7 @@ @VisibleForTesting public void fallbackToPstn(SmsTracker tracker) { + tracker.mMessageRef = nextMessageRef(); mSmsDispatchersController.sendRetrySms(tracker); }
diff --git a/src/java/com/android/internal/telephony/InboundSmsHandler.java b/src/java/com/android/internal/telephony/InboundSmsHandler.java index eeda1f9..9166719 100644 --- a/src/java/com/android/internal/telephony/InboundSmsHandler.java +++ b/src/java/com/android/internal/telephony/InboundSmsHandler.java
@@ -45,7 +45,6 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.content.pm.IPackageManager; import android.content.pm.PackageManager; import android.database.Cursor; import android.database.SQLException; @@ -53,11 +52,10 @@ import android.os.AsyncResult; import android.os.Build; import android.os.Bundle; +import android.os.Looper; import android.os.Message; import android.os.PowerManager; import android.os.PowerWhitelistManager; -import android.os.RemoteException; -import android.os.ServiceManager; import android.os.UserHandle; import android.os.UserManager; import android.os.storage.StorageManager; @@ -89,7 +87,6 @@ import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Arrays; -import java.util.HashMap; import java.util.List; import java.util.ListIterator; import java.util.Map; @@ -116,38 +113,6 @@ protected static final boolean DBG = true; protected static final boolean VDBG = false; // STOPSHIP if true, logs user data - /** Query projection for checking for duplicate message segments. */ - private static final String[] PDU_DELETED_FLAG_PROJECTION = { - "pdu", - "deleted" - }; - - /** Mapping from DB COLUMN to PDU_SEQUENCE_PORT PROJECTION index */ - private static final Map<Integer, Integer> PDU_DELETED_FLAG_PROJECTION_INDEX_MAPPING = - new HashMap<Integer, Integer>() {{ - put(PDU_COLUMN, 0); - put(DELETED_FLAG_COLUMN, 1); - }}; - - /** Query projection for combining concatenated message segments. */ - private static final String[] PDU_SEQUENCE_PORT_PROJECTION = { - "pdu", - "sequence", - "destination_port", - "display_originating_addr", - "date" - }; - - /** Mapping from DB COLUMN to PDU_SEQUENCE_PORT PROJECTION index */ - private static final Map<Integer, Integer> PDU_SEQUENCE_PORT_PROJECTION_INDEX_MAPPING = - new HashMap<Integer, Integer>() {{ - put(PDU_COLUMN, 0); - put(SEQUENCE_COLUMN, 1); - put(DESTINATION_PORT_COLUMN, 2); - put(DISPLAY_ADDRESS_COLUMN, 3); - put(DATE_COLUMN, 4); - }}; - public static final int PDU_COLUMN = 0; public static final int SEQUENCE_COLUMN = 1; public static final int DESTINATION_PORT_COLUMN = 2; @@ -161,6 +126,34 @@ public static final int DELETED_FLAG_COLUMN = 10; public static final int SUBID_COLUMN = 11; + /** Query projection for checking for duplicate message segments. */ + private static final String[] PDU_DELETED_FLAG_PROJECTION = { + "pdu", + "deleted" + }; + + /** Mapping from DB COLUMN to PDU_SEQUENCE_PORT PROJECTION index */ + private static final Map<Integer, Integer> PDU_DELETED_FLAG_PROJECTION_INDEX_MAPPING = Map.of( + PDU_COLUMN, 0, + DELETED_FLAG_COLUMN, 1); + + /** Query projection for combining concatenated message segments. */ + private static final String[] PDU_SEQUENCE_PORT_PROJECTION = { + "pdu", + "sequence", + "destination_port", + "display_originating_addr", + "date" + }; + + /** Mapping from DB COLUMN to PDU_SEQUENCE_PORT PROJECTION index */ + private static final Map<Integer, Integer> PDU_SEQUENCE_PORT_PROJECTION_INDEX_MAPPING = Map.of( + PDU_COLUMN, 0, + SEQUENCE_COLUMN, 1, + DESTINATION_PORT_COLUMN, 2, + DISPLAY_ADDRESS_COLUMN, 3, + DATE_COLUMN, 4); + public static final String SELECT_BY_ID = "_id=?"; /** New SMS received as an AsyncResult. */ @@ -190,6 +183,7 @@ /** BroadcastReceiver timed out waiting for an intent */ public static final int EVENT_RECEIVER_TIMEOUT = 9; + /** Wakelock release delay when returning to idle state. */ private static final int WAKELOCK_TIMEOUT = 3000; @@ -294,8 +288,8 @@ * @param storageMonitor the SmsStorageMonitor to check for storage availability */ protected InboundSmsHandler(String name, Context context, SmsStorageMonitor storageMonitor, - Phone phone) { - super(name); + Phone phone, Looper looper) { + super(name, looper); mContext = context; mStorageMonitor = storageMonitor; @@ -506,7 +500,6 @@ case EVENT_RETURN_TO_IDLE: // already in idle state; ignore return HANDLED; - case EVENT_BROADCAST_COMPLETE: case EVENT_START_ACCEPTING_SMS: default: @@ -545,7 +538,8 @@ case EVENT_INJECT_SMS: // handle new injected SMS - handleInjectSms((AsyncResult) msg.obj, msg.arg1 == 1 /* isOverIms */); + handleInjectSms((AsyncResult) msg.obj, msg.arg1 == 1 /* isOverIms */, + msg.arg2 /* token */); sendMessage(EVENT_RETURN_TO_IDLE); return HANDLED; @@ -668,7 +662,6 @@ } } } - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) private void handleNewSms(AsyncResult ar) { if (ar.exception != null) { @@ -679,7 +672,7 @@ int result; try { SmsMessage sms = (SmsMessage) ar.result; - result = dispatchMessage(sms.mWrappedSmsMessage, SOURCE_NOT_INJECTED); + result = dispatchMessage(sms.mWrappedSmsMessage, SOURCE_NOT_INJECTED, 0 /*unused*/); } catch (RuntimeException ex) { loge("Exception dispatching message", ex); result = RESULT_SMS_DISPATCH_FAILURE; @@ -698,7 +691,7 @@ * @param ar is the AsyncResult that has the SMS PDU to be injected. */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - private void handleInjectSms(AsyncResult ar, boolean isOverIms) { + private void handleInjectSms(AsyncResult ar, boolean isOverIms, int token) { int result; SmsDispatchersController.SmsInjectionCallback callback = null; try { @@ -710,7 +703,7 @@ } else { @SmsSource int smsSource = isOverIms ? SOURCE_INJECTED_FROM_IMS : SOURCE_INJECTED_FROM_UNKNOWN; - result = dispatchMessage(sms.mWrappedSmsMessage, smsSource); + result = dispatchMessage(sms.mWrappedSmsMessage, smsSource, token); } } catch (RuntimeException ex) { loge("Exception dispatching message", ex); @@ -731,7 +724,7 @@ * @return a result code from {@link android.provider.Telephony.Sms.Intents}, * or {@link Activity#RESULT_OK} for delayed acknowledgment to SMSC */ - private int dispatchMessage(SmsMessageBase smsb, @SmsSource int smsSource) { + private int dispatchMessage(SmsMessageBase smsb, @SmsSource int smsSource, int token) { // If sms is null, there was a parsing error. if (smsb == null) { loge("dispatchSmsMessage: message is null"); @@ -745,20 +738,7 @@ return Intents.RESULT_SMS_HANDLED; } - // onlyCore indicates if the device is in cryptkeeper - boolean onlyCore = false; - try { - onlyCore = IPackageManager.Stub.asInterface(ServiceManager.getService("package")) - .isOnlyCoreApps(); - } catch (RemoteException e) { - } - if (onlyCore) { - // Device is unable to receive SMS in encrypted state - log("Received a short message in encrypted state. Rejecting."); - return Intents.RESULT_SMS_RECEIVED_WHILE_ENCRYPTED; - } - - int result = dispatchMessageRadioSpecific(smsb, smsSource); + int result = dispatchMessageRadioSpecific(smsb, smsSource, token); // In case of error, add to metrics. This is not required in case of success, as the // data will be tracked when the message is processed (processMessagePart). @@ -780,7 +760,7 @@ * or {@link Activity#RESULT_OK} for delayed acknowledgment to SMSC */ protected abstract int dispatchMessageRadioSpecific(SmsMessageBase smsb, - @SmsSource int smsSource); + @SmsSource int smsSource, int token); /** * Send an acknowledge message to the SMSC. @@ -880,7 +860,6 @@ * <code>RESULT_SMS_DISPATCH_FAILURE</code><br> * <code>RESULT_SMS_NULL_PDU</code><br> * <code>RESULT_SMS_NULL_MESSAGE</code><br> - * <code>RESULT_SMS_RECEIVED_WHILE_ENCRYPTED</code><br> * <code>RESULT_SMS_DATABASE_ERROR</code><br> * <code>RESULT_SMS_INVALID_URI</code><br> */ @@ -1164,7 +1143,7 @@ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) private void showNewMessageNotification() { // Do not show the notification on non-FBE devices. - if (!StorageManager.isFileEncryptedNativeOrEmulated()) { + if (!StorageManager.isFileEncrypted()) { return; } log("Show new message notification."); @@ -1458,12 +1437,14 @@ intent.putExtra("messageId", messageId); } + UserHandle userHandle = null; if (destPort == -1) { intent.setAction(Intents.SMS_DELIVER_ACTION); // Direct the intent to only the default SMS app. If we can't find a default SMS app // then sent it to all broadcast receivers. - // We are deliberately delivering to the primary user's default SMS App. - ComponentName componentName = SmsApplication.getDefaultSmsApplication(mContext, true); + userHandle = TelephonyUtils.getSubscriptionUserHandle(mContext, subId); + ComponentName componentName = SmsApplication.getDefaultSmsApplicationAsUser(mContext, + true, userHandle); if (componentName != null) { // Deliver SMS message only to this receiver. intent.setComponent(componentName); @@ -1487,9 +1468,12 @@ intent.setComponent(null); } + if (userHandle == null) { + userHandle = UserHandle.SYSTEM; + } Bundle options = handleSmsWhitelisting(intent.getComponent(), isClass0); dispatchIntent(intent, android.Manifest.permission.RECEIVE_SMS, - AppOpsManager.OPSTR_RECEIVE_SMS, options, resultReceiver, UserHandle.SYSTEM, subId); + AppOpsManager.OPSTR_RECEIVE_SMS, options, resultReceiver, userHandle, subId); } /**
diff --git a/src/java/com/android/internal/telephony/LocaleTracker.java b/src/java/com/android/internal/telephony/LocaleTracker.java old mode 100755 new mode 100644 index 4e0f452..de854fa --- a/src/java/com/android/internal/telephony/LocaleTracker.java +++ b/src/java/com/android/internal/telephony/LocaleTracker.java
@@ -30,6 +30,7 @@ import android.os.Handler; import android.os.Looper; import android.os.Message; +import android.os.UserHandle; import android.sysprop.TelephonyProperties; import android.telephony.CellInfo; import android.telephony.ServiceState; @@ -486,20 +487,12 @@ String countryIso = ""; String countryIsoDebugInfo = "empty as default"; - // For time zone detection we want the best geographical match we can get, which may differ - // from the countryIso. - String timeZoneCountryIso = null; - String timeZoneCountryIsoDebugInfo = null; - if (!TextUtils.isEmpty(mOperatorNumeric)) { MccMnc mccMnc = MccMnc.fromOperatorNumeric(mOperatorNumeric); if (mccMnc != null) { - countryIso = MccTable.countryCodeForMcc(mccMnc.mcc); + countryIso = MccTable.geoCountryCodeForMccMnc(mccMnc); countryIsoDebugInfo = "OperatorNumeric(" + mOperatorNumeric - + "): MccTable.countryCodeForMcc(\"" + mccMnc.mcc + "\")"; - timeZoneCountryIso = MccTable.geoCountryCodeForMccMnc(mccMnc); - timeZoneCountryIsoDebugInfo = - "OperatorNumeric: MccTable.geoCountryCodeForMccMnc(" + mccMnc + ")"; + + "): MccTable.geoCountryCodeForMccMnc(\"" + mccMnc + "\")"; } else { loge("updateLocale: Can't get country from operator numeric. mOperatorNumeric = " + mOperatorNumeric); @@ -509,15 +502,19 @@ // If for any reason we can't get country from operator numeric, try to get it from cell // info. if (TextUtils.isEmpty(countryIso)) { + // Find the most prevalent MCC from surrounding cell towers. String mcc = getMccFromCellInfo(); if (mcc != null) { countryIso = MccTable.countryCodeForMcc(mcc); countryIsoDebugInfo = "CellInfo: MccTable.countryCodeForMcc(\"" + mcc + "\")"; + // Some MCC+MNC combinations are known to be used in countries other than those + // that the MCC alone would suggest. Do a second pass of nearby cells that match + // the most frequently observed MCC to see if this could be one of those cases. MccMnc mccMnc = getMccMncFromCellInfo(mcc); if (mccMnc != null) { - timeZoneCountryIso = MccTable.geoCountryCodeForMccMnc(mccMnc); - timeZoneCountryIsoDebugInfo = + countryIso = MccTable.geoCountryCodeForMccMnc(mccMnc); + countryIsoDebugInfo = "CellInfo: MccTable.geoCountryCodeForMccMnc(" + mccMnc + ")"; } } @@ -526,8 +523,6 @@ if (mCountryOverride != null) { countryIso = mCountryOverride; countryIsoDebugInfo = "mCountryOverride = \"" + mCountryOverride + "\""; - timeZoneCountryIso = countryIso; - timeZoneCountryIsoDebugInfo = countryIsoDebugInfo; } if (!mPhone.isRadioOn()) { @@ -564,11 +559,13 @@ intent.putExtra(TelephonyManager.EXTRA_LAST_KNOWN_NETWORK_COUNTRY, getLastKnownCountryIso()); SubscriptionManager.putPhoneIdAndSubIdExtra(intent, mPhone.getPhoneId()); - mPhone.getContext().sendBroadcast(intent); + mPhone.getContext().sendBroadcastAsUser(intent, UserHandle.ALL); } // Pass the geographical country information to the telephony time zone detection code. + String timeZoneCountryIso = countryIso; + String timeZoneCountryIsoDebugInfo = countryIsoDebugInfo; boolean isTestMcc = false; if (!TextUtils.isEmpty(mOperatorNumeric)) { // For a test cell (MCC 001), the NitzStateMachine requires handleCountryDetected("") in @@ -579,11 +576,6 @@ timeZoneCountryIsoDebugInfo = "Test cell: " + mOperatorNumeric; } } - if (timeZoneCountryIso == null) { - // After this timeZoneCountryIso may still be null. - timeZoneCountryIso = countryIso; - timeZoneCountryIsoDebugInfo = "Defaulted: " + countryIsoDebugInfo; - } log("updateLocale: timeZoneCountryIso = " + timeZoneCountryIso + ", timeZoneCountryIsoDebugInfo = " + timeZoneCountryIsoDebugInfo);
diff --git a/src/java/com/android/internal/telephony/MessagingIndication.java b/src/java/com/android/internal/telephony/MessagingIndication.java index 86e30b0..5814e3d 100644 --- a/src/java/com/android/internal/telephony/MessagingIndication.java +++ b/src/java/com/android/internal/telephony/MessagingIndication.java
@@ -16,6 +16,8 @@ package com.android.internal.telephony; +import static android.telephony.TelephonyManager.HAL_SERVICE_MESSAGING; + import static com.android.internal.telephony.RILConstants.RIL_UNSOL_CDMA_RUIM_SMS_STORAGE_FULL; import static com.android.internal.telephony.RILConstants.RIL_UNSOL_RESPONSE_CDMA_NEW_SMS; import static com.android.internal.telephony.RILConstants.RIL_UNSOL_RESPONSE_NEW_BROADCAST_SMS; @@ -47,9 +49,9 @@ */ public void cdmaNewSms(int indicationType, android.hardware.radio.messaging.CdmaSmsMessage msg) { - mRil.processIndication(RIL.MESSAGING_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_MESSAGING, indicationType); - if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_RESPONSE_CDMA_NEW_SMS); + if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_RESPONSE_CDMA_NEW_SMS); SmsMessage sms = new SmsMessage(RILUtils.convertHalCdmaSmsMessage(msg)); if (mRil.mCdmaSmsRegistrant != null) { @@ -63,9 +65,9 @@ * @param indicationType Type of radio indication */ public void cdmaRuimSmsStorageFull(int indicationType) { - mRil.processIndication(RIL.MESSAGING_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_MESSAGING, indicationType); - if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_CDMA_RUIM_SMS_STORAGE_FULL); + if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_CDMA_RUIM_SMS_STORAGE_FULL); if (mRil.mIccSmsFullRegistrant != null) { mRil.mIccSmsFullRegistrant.notifyRegistrant(); @@ -82,11 +84,11 @@ * BTS as coded in 3GPP 23.041 Section 9.4.2.2 */ public void newBroadcastSms(int indicationType, byte[] data) { - mRil.processIndication(RIL.MESSAGING_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_MESSAGING, indicationType); - if (RIL.RILJ_LOGD) { - mRil.unsljLogvRet(RIL_UNSOL_RESPONSE_NEW_BROADCAST_SMS, - IccUtils.bytesToHexString(data)); + if (mRil.isLogOrTrace()) { + mRil.unsljLogvRet( + RIL_UNSOL_RESPONSE_NEW_BROADCAST_SMS, IccUtils.bytesToHexString(data)); } if (mRil.mGsmBroadcastSmsRegistrant != null) { @@ -101,8 +103,8 @@ * The PDU starts with the SMSC address per TS 27.005 (+CMT:) */ public void newSms(int indicationType, byte[] pdu) { - mRil.processIndication(RIL.MESSAGING_SERVICE, indicationType); - if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_RESPONSE_NEW_SMS); + mRil.processIndication(HAL_SERVICE_MESSAGING, indicationType); + if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_RESPONSE_NEW_SMS); SmsMessageBase smsb = com.android.internal.telephony.gsm.SmsMessage.createFromPdu(pdu); if (mRil.mGsmSmsRegistrant != null) { @@ -117,9 +119,9 @@ * @param recordNumber Record number on the SIM */ public void newSmsOnSim(int indicationType, int recordNumber) { - mRil.processIndication(RIL.MESSAGING_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_MESSAGING, indicationType); - if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_RESPONSE_NEW_SMS_ON_SIM); + if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_RESPONSE_NEW_SMS_ON_SIM); if (mRil.mSmsOnSimRegistrant != null) { mRil.mSmsOnSimRegistrant.notifyRegistrant(new AsyncResult(null, recordNumber, null)); @@ -133,9 +135,9 @@ * The PDU starts with the SMSC address per TS 27.005 (+CMT:) */ public void newSmsStatusReport(int indicationType, byte[] pdu) { - mRil.processIndication(RIL.MESSAGING_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_MESSAGING, indicationType); - if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_RESPONSE_NEW_SMS_STATUS_REPORT); + if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_RESPONSE_NEW_SMS_STATUS_REPORT); if (mRil.mSmsStatusRegistrant != null) { mRil.mSmsStatusRegistrant.notifyRegistrant(new AsyncResult(null, pdu, null)); @@ -149,9 +151,9 @@ * @param indicationType Type of radio indication */ public void simSmsStorageFull(int indicationType) { - mRil.processIndication(RIL.MESSAGING_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_MESSAGING, indicationType); - if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_SIM_SMS_STORAGE_FULL); + if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_SIM_SMS_STORAGE_FULL); if (mRil.mIccSmsFullRegistrant != null) { mRil.mIccSmsFullRegistrant.notifyRegistrant();
diff --git a/src/java/com/android/internal/telephony/MessagingResponse.java b/src/java/com/android/internal/telephony/MessagingResponse.java index 3dc1d1a..19211e1 100644 --- a/src/java/com/android/internal/telephony/MessagingResponse.java +++ b/src/java/com/android/internal/telephony/MessagingResponse.java
@@ -16,6 +16,8 @@ package com.android.internal.telephony; +import static android.telephony.TelephonyManager.HAL_SERVICE_MESSAGING; + import android.hardware.radio.RadioError; import android.hardware.radio.RadioResponseInfo; import android.hardware.radio.messaging.IRadioMessagingResponse; @@ -36,7 +38,7 @@ private void responseSms(RadioResponseInfo responseInfo, android.hardware.radio.messaging.SendSmsResult sms) { - RILRequest rr = mRil.processResponse(RIL.MESSAGING_SERVICE, responseInfo); + RILRequest rr = mRil.processResponse(HAL_SERVICE_MESSAGING, responseInfo); if (rr != null) { long messageId = RIL.getOutgoingSmsMessageId(rr.mResult); @@ -62,35 +64,35 @@ * @param responseInfo Response info struct containing response type, serial no. and error */ public void acknowledgeIncomingGsmSmsWithPduResponse(RadioResponseInfo responseInfo) { - RadioResponse.responseVoid(RIL.MESSAGING_SERVICE, mRil, responseInfo); + RadioResponse.responseVoid(HAL_SERVICE_MESSAGING, mRil, responseInfo); } /** * @param responseInfo Response info struct containing response type, serial no. and error */ public void acknowledgeLastIncomingCdmaSmsResponse(RadioResponseInfo responseInfo) { - RadioResponse.responseVoid(RIL.MESSAGING_SERVICE, mRil, responseInfo); + RadioResponse.responseVoid(HAL_SERVICE_MESSAGING, mRil, responseInfo); } /** * @param responseInfo Response info struct containing response type, serial no. and error */ public void acknowledgeLastIncomingGsmSmsResponse(RadioResponseInfo responseInfo) { - RadioResponse.responseVoid(RIL.MESSAGING_SERVICE, mRil, responseInfo); + RadioResponse.responseVoid(HAL_SERVICE_MESSAGING, mRil, responseInfo); } /** * @param responseInfo Response info struct containing response type, serial no. and error */ public void deleteSmsOnRuimResponse(RadioResponseInfo responseInfo) { - RadioResponse.responseVoid(RIL.MESSAGING_SERVICE, mRil, responseInfo); + RadioResponse.responseVoid(HAL_SERVICE_MESSAGING, mRil, responseInfo); } /** * @param responseInfo Response info struct containing response type, serial no. and error */ public void deleteSmsOnSimResponse(RadioResponseInfo responseInfo) { - RadioResponse.responseVoid(RIL.MESSAGING_SERVICE, mRil, responseInfo); + RadioResponse.responseVoid(HAL_SERVICE_MESSAGING, mRil, responseInfo); } /** @@ -99,7 +101,7 @@ */ public void getCdmaBroadcastConfigResponse(RadioResponseInfo responseInfo, android.hardware.radio.messaging.CdmaBroadcastSmsConfigInfo[] configs) { - RILRequest rr = mRil.processResponse(RIL.MESSAGING_SERVICE, responseInfo); + RILRequest rr = mRil.processResponse(HAL_SERVICE_MESSAGING, responseInfo); if (rr != null) { int[] ret; @@ -148,7 +150,7 @@ */ public void getGsmBroadcastConfigResponse(RadioResponseInfo responseInfo, android.hardware.radio.messaging.GsmBroadcastSmsConfigInfo[] configs) { - RILRequest rr = mRil.processResponse(RIL.MESSAGING_SERVICE, responseInfo); + RILRequest rr = mRil.processResponse(HAL_SERVICE_MESSAGING, responseInfo); if (rr != null) { ArrayList<SmsBroadcastConfigInfo> ret = new ArrayList<>(); @@ -168,14 +170,14 @@ * @param smsc Short Message Service Center address on the device */ public void getSmscAddressResponse(RadioResponseInfo responseInfo, String smsc) { - RadioResponse.responseString(RIL.MESSAGING_SERVICE, mRil, responseInfo, smsc); + RadioResponse.responseString(HAL_SERVICE_MESSAGING, mRil, responseInfo, smsc); } /** * @param responseInfo Response info struct containing response type, serial no. and error */ public void reportSmsMemoryStatusResponse(RadioResponseInfo responseInfo) { - RadioResponse.responseVoid(RIL.MESSAGING_SERVICE, mRil, responseInfo); + RadioResponse.responseVoid(HAL_SERVICE_MESSAGING, mRil, responseInfo); } /** @@ -227,35 +229,35 @@ * @param responseInfo Response info struct containing response type, serial no. and error */ public void setCdmaBroadcastActivationResponse(RadioResponseInfo responseInfo) { - RadioResponse.responseVoid(RIL.MESSAGING_SERVICE, mRil, responseInfo); + RadioResponse.responseVoid(HAL_SERVICE_MESSAGING, mRil, responseInfo); } /** * @param responseInfo Response info struct containing response type, serial no. and error */ public void setCdmaBroadcastConfigResponse(RadioResponseInfo responseInfo) { - RadioResponse.responseVoid(RIL.MESSAGING_SERVICE, mRil, responseInfo); + RadioResponse.responseVoid(HAL_SERVICE_MESSAGING, mRil, responseInfo); } /** * @param responseInfo Response info struct containing response type, serial no. and error */ public void setGsmBroadcastActivationResponse(RadioResponseInfo responseInfo) { - RadioResponse.responseVoid(RIL.MESSAGING_SERVICE, mRil, responseInfo); + RadioResponse.responseVoid(HAL_SERVICE_MESSAGING, mRil, responseInfo); } /** * @param responseInfo Response info struct containing response type, serial no. and error */ public void setGsmBroadcastConfigResponse(RadioResponseInfo responseInfo) { - RadioResponse.responseVoid(RIL.MESSAGING_SERVICE, mRil, responseInfo); + RadioResponse.responseVoid(HAL_SERVICE_MESSAGING, mRil, responseInfo); } /** * @param responseInfo Response info struct containing response type, serial no. and error */ public void setSmscAddressResponse(RadioResponseInfo responseInfo) { - RadioResponse.responseVoid(RIL.MESSAGING_SERVICE, mRil, responseInfo); + RadioResponse.responseVoid(HAL_SERVICE_MESSAGING, mRil, responseInfo); } /** @@ -263,7 +265,7 @@ * @param index record index where the CDMA SMS message is stored */ public void writeSmsToRuimResponse(RadioResponseInfo responseInfo, int index) { - RadioResponse.responseInts(RIL.MESSAGING_SERVICE, mRil, responseInfo, index); + RadioResponse.responseInts(HAL_SERVICE_MESSAGING, mRil, responseInfo, index); } /** @@ -271,7 +273,7 @@ * @param index record index where the message is stored */ public void writeSmsToSimResponse(RadioResponseInfo responseInfo, int index) { - RadioResponse.responseInts(RIL.MESSAGING_SERVICE, mRil, responseInfo, index); + RadioResponse.responseInts(HAL_SERVICE_MESSAGING, mRil, responseInfo, index); } @Override
diff --git a/src/java/com/android/internal/telephony/MissedIncomingCallSmsFilter.java b/src/java/com/android/internal/telephony/MissedIncomingCallSmsFilter.java index 5932f9e..dce65af 100644 --- a/src/java/com/android/internal/telephony/MissedIncomingCallSmsFilter.java +++ b/src/java/com/android/internal/telephony/MissedIncomingCallSmsFilter.java
@@ -23,12 +23,14 @@ import android.net.Uri; import android.os.Bundle; import android.os.PersistableBundle; +import android.os.UserHandle; import android.telecom.PhoneAccount; import android.telecom.PhoneAccountHandle; import android.telecom.TelecomManager; import android.telephony.CarrierConfigManager; import android.telephony.Rlog; import android.telephony.SmsMessage; +import android.telephony.SubscriptionManager; import android.text.TextUtils; import java.time.Instant; @@ -152,9 +154,6 @@ * @return {@code true} if the SMS message has been processed as a missed incoming call SMS. */ private boolean processSms(@NonNull SmsMessage message) { - long missedCallTime = 0; - String callerId = null; - String[] smsPatterns = mCarrierConfig.getStringArray(CarrierConfigManager .KEY_MISSED_INCOMING_CALL_SMS_PATTERN_STRING_ARRAY); if (smsPatterns == null || smsPatterns.length == 0) { @@ -162,76 +161,91 @@ return false; } - for (String smsPattern : smsPatterns) { - Pattern pattern; - try { - pattern = Pattern.compile(smsPattern, Pattern.DOTALL | Pattern.UNIX_LINES); - } catch (PatternSyntaxException e) { - Rlog.w(TAG, "Configuration error. Unexpected missed incoming call sms " - + "pattern: " + smsPattern + ", e=" + e); - continue; - } - - Matcher matcher = pattern.matcher(message.getMessageBody()); - String year = null, month = null, day = null, hour = null, minute = null; - if (matcher.find()) { - try { - month = matcher.group(SMS_MONTH_TAG); - day = matcher.group(SMS_DAY_TAG); - hour = matcher.group(SMS_HOUR_TAG); - minute = matcher.group(SMS_MINUTE_TAG); - if (VDBG) { - Rlog.v(TAG, "month=" + month + ", day=" + day + ", hour=" + hour - + ", minute=" + minute); + boolean result = false; + String[] missedCallMsgs = splitCalls(message.getMessageBody()); + if (missedCallMsgs != null && missedCallMsgs.length > 0) { + for (String parsedMsg : missedCallMsgs) { + long missedCallTime = 0; + String callerId = null; + for (String smsPattern : smsPatterns) { + Pattern pattern; + try { + pattern = Pattern.compile(smsPattern, Pattern.DOTALL | Pattern.UNIX_LINES); + } catch (PatternSyntaxException e) { + Rlog.w(TAG, "Configuration error. Unexpected missed incoming call sms " + + "pattern: " + smsPattern + ", e=" + e); + continue; } - } catch (IllegalArgumentException e) { - if (VDBG) { - Rlog.v(TAG, "One of the critical date field is missing. Using the " - + "current time for missed incoming call."); - } - missedCallTime = System.currentTimeMillis(); - } - // Year is an optional field. - try { - year = matcher.group(SMS_YEAR_TAG); - } catch (IllegalArgumentException e) { - if (VDBG) Rlog.v(TAG, "Year is missing."); - } - - try { - if (missedCallTime == 0) { - missedCallTime = getEpochTime(year, month, day, hour, minute); - if (missedCallTime == 0) { - Rlog.e(TAG, "Can't get the time. Use the current time."); + Matcher matcher = pattern.matcher(parsedMsg); + String year = null, month = null, day = null, hour = null, minute = null; + if (matcher.find()) { + try { + month = matcher.group(SMS_MONTH_TAG); + day = matcher.group(SMS_DAY_TAG); + hour = matcher.group(SMS_HOUR_TAG); + minute = matcher.group(SMS_MINUTE_TAG); + if (VDBG) { + Rlog.v(TAG, "month=" + month + ", day=" + day + ", hour=" + hour + + ", minute=" + minute); + } + } catch (IllegalArgumentException e) { + if (VDBG) { + Rlog.v(TAG, "One of the critical date field is missing. Using the " + + "current time for missed incoming call."); + } missedCallTime = System.currentTimeMillis(); } + + // Year is an optional field. + try { + year = matcher.group(SMS_YEAR_TAG); + } catch (IllegalArgumentException e) { + if (VDBG) Rlog.v(TAG, "Year is missing."); + } + + try { + if (missedCallTime == 0) { + missedCallTime = getEpochTime(year, month, day, hour, minute); + if (missedCallTime == 0) { + Rlog.e(TAG, "Can't get the time. Use the current time."); + missedCallTime = System.currentTimeMillis(); + } + } + + if (VDBG) Rlog.v(TAG, "missedCallTime=" + missedCallTime); + } catch (Exception e) { + Rlog.e(TAG, "Can't get the time for missed incoming call"); + } + + try { + callerId = matcher.group(SMS_CALLER_ID_TAG); + if (VDBG) Rlog.v(TAG, "caller id=" + callerId); + } catch (IllegalArgumentException e) { + Rlog.d(TAG, "Caller id is not provided or can't be parsed."); + } + createMissedIncomingCallEvent(missedCallTime, callerId); + result = true; + break; } - - if (VDBG) Rlog.v(TAG, "missedCallTime=" + missedCallTime); - } catch (Exception e) { - Rlog.e(TAG, "Can't get the time for missed incoming call"); } - - try { - callerId = matcher.group(SMS_CALLER_ID_TAG); - if (VDBG) Rlog.v(TAG, "caller id=" + callerId); - } catch (IllegalArgumentException e) { - Rlog.d(TAG, "Caller id is not provided or can't be parsed."); - } - createMissedIncomingCallEvent(missedCallTime, callerId); - return true; } } - - Rlog.d(TAG, "SMS did not match any missed incoming call SMS pattern."); - return false; + if (!result) { + Rlog.d(TAG, "SMS did not match any missed incoming call SMS pattern."); + } + return result; } - // Create phone account. The logic is copied from PhoneUtils.makePstnPhoneAccountHandle. - private static PhoneAccountHandle makePstnPhoneAccountHandle(Phone phone) { - return new PhoneAccountHandle(PSTN_CONNECTION_SERVICE_COMPONENT, - String.valueOf(phone.getFullIccSerialNumber())); + private String[] splitCalls(String messageBody) { + String[] messages = null; + if (messageBody != null) { + messages = messageBody.split("(\\n|\\s\\n)" + "(\\n|\\s\\n)"); + Rlog.d(TAG, + "splitTheMultipleCalls no of calls = " + ((messages != null) ? messages.length + : 0)); + } + return messages; } /** @@ -260,4 +274,18 @@ tm.addNewIncomingCall(makePstnPhoneAccountHandle(mPhone), bundle); } } -} + + // Create phone account. The logic is copied from PhoneUtils.makePstnPhoneAccountHandle. + private PhoneAccountHandle makePstnPhoneAccountHandle(Phone phone) { + SubscriptionManager subscriptionManager = + (SubscriptionManager) phone.getContext().getSystemService( + Context.TELEPHONY_SUBSCRIPTION_SERVICE); + UserHandle userHandle = subscriptionManager.getSubscriptionUserHandle(phone.getSubId()); + if (userHandle != null) { + return new PhoneAccountHandle(PSTN_CONNECTION_SERVICE_COMPONENT, + String.valueOf(phone.getSubId()), userHandle); + } + return new PhoneAccountHandle(PSTN_CONNECTION_SERVICE_COMPONENT, + String.valueOf(phone.getSubId())); + } +} \ No newline at end of file
diff --git a/src/java/com/android/internal/telephony/MockModem.java b/src/java/com/android/internal/telephony/MockModem.java index 4266a75..a20e748 100644 --- a/src/java/com/android/internal/telephony/MockModem.java +++ b/src/java/com/android/internal/telephony/MockModem.java
@@ -16,6 +16,14 @@ package com.android.internal.telephony; +import static android.telephony.TelephonyManager.HAL_SERVICE_DATA; +import static android.telephony.TelephonyManager.HAL_SERVICE_IMS; +import static android.telephony.TelephonyManager.HAL_SERVICE_MESSAGING; +import static android.telephony.TelephonyManager.HAL_SERVICE_MODEM; +import static android.telephony.TelephonyManager.HAL_SERVICE_NETWORK; +import static android.telephony.TelephonyManager.HAL_SERVICE_SIM; +import static android.telephony.TelephonyManager.HAL_SERVICE_VOICE; + import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -34,6 +42,7 @@ private static final String BIND_IRADIODATA = "android.telephony.mockmodem.iradiodata"; private static final String BIND_IRADIONETWORK = "android.telephony.mockmodem.iradionetwork"; private static final String BIND_IRADIOVOICE = "android.telephony.mockmodem.iradiovoice"; + private static final String BIND_IRADIOIMS = "android.telephony.mockmodem.iradioims"; private static final String BIND_IRADIOCONFIG = "android.telephony.mockmodem.iradioconfig"; private static final String PHONE_ID = "phone_id"; @@ -42,7 +51,7 @@ static final int RADIOCONFIG_SERVICE = RIL.MAX_SERVICE_IDX + 1; static final int BINDER_RETRY_MILLIS = 3 * 100; - static final int BINDER_MAX_RETRY = 3; + static final int BINDER_MAX_RETRY = 10; private Context mContext; private String mServiceName; @@ -54,6 +63,7 @@ private IBinder mDataBinder; private IBinder mNetworkBinder; private IBinder mVoiceBinder; + private IBinder mImsBinder; private IBinder mConfigBinder; private ServiceConnection mModemServiceConnection; private ServiceConnection mSimServiceConnection; @@ -61,9 +71,11 @@ private ServiceConnection mDataServiceConnection; private ServiceConnection mNetworkServiceConnection; private ServiceConnection mVoiceServiceConnection; + private ServiceConnection mImsServiceConnection; private ServiceConnection mConfigServiceConnection; private byte mPhoneId; + private String mTag; MockModem(Context context, String serviceName) { this(context, serviceName, 0); @@ -71,6 +83,7 @@ MockModem(Context context, String serviceName, int phoneId) { mPhoneId = (byte) phoneId; + mTag = TAG + "-" + mPhoneId; mContext = context; String[] componentInfo = serviceName.split("/", 2); mPackageName = componentInfo[0]; @@ -86,20 +99,22 @@ @Override public void onServiceConnected(ComponentName name, IBinder binder) { - Rlog.d(TAG, "IRadio " + getModuleName(mService) + " - onServiceConnected"); + Rlog.d(mTag, "IRadio " + getModuleName(mService) + " - onServiceConnected"); - if (mService == RIL.MODEM_SERVICE) { + if (mService == HAL_SERVICE_MODEM) { mModemBinder = binder; - } else if (mService == RIL.SIM_SERVICE) { + } else if (mService == HAL_SERVICE_SIM) { mSimBinder = binder; - } else if (mService == RIL.MESSAGING_SERVICE) { + } else if (mService == HAL_SERVICE_MESSAGING) { mMessagingBinder = binder; - } else if (mService == RIL.DATA_SERVICE) { + } else if (mService == HAL_SERVICE_DATA) { mDataBinder = binder; - } else if (mService == RIL.NETWORK_SERVICE) { + } else if (mService == HAL_SERVICE_NETWORK) { mNetworkBinder = binder; - } else if (mService == RIL.VOICE_SERVICE) { + } else if (mService == HAL_SERVICE_VOICE) { mVoiceBinder = binder; + } else if (mService == HAL_SERVICE_IMS) { + mImsBinder = binder; } else if (mService == RADIOCONFIG_SERVICE) { mConfigBinder = binder; } @@ -107,20 +122,22 @@ @Override public void onServiceDisconnected(ComponentName name) { - Rlog.d(TAG, "IRadio " + getModuleName(mService) + " - onServiceDisconnected"); + Rlog.d(mTag, "IRadio " + getModuleName(mService) + " - onServiceDisconnected"); - if (mService == RIL.MODEM_SERVICE) { + if (mService == HAL_SERVICE_MODEM) { mModemBinder = null; - } else if (mService == RIL.SIM_SERVICE) { + } else if (mService == HAL_SERVICE_SIM) { mSimBinder = null; - } else if (mService == RIL.MESSAGING_SERVICE) { + } else if (mService == HAL_SERVICE_MESSAGING) { mMessagingBinder = null; - } else if (mService == RIL.DATA_SERVICE) { + } else if (mService == HAL_SERVICE_DATA) { mDataBinder = null; - } else if (mService == RIL.NETWORK_SERVICE) { + } else if (mService == HAL_SERVICE_NETWORK) { mNetworkBinder = null; - } else if (mService == RIL.VOICE_SERVICE) { + } else if (mService == HAL_SERVICE_VOICE) { mVoiceBinder = null; + } else if (mService == HAL_SERVICE_IMS) { + mImsBinder = null; } else if (mService == RADIOCONFIG_SERVICE) { mConfigBinder = null; } @@ -138,7 +155,7 @@ Intent intent = new Intent(); intent.setComponent(new ComponentName(mPackageName, mServiceName)); - intent.setAction(actionName); + intent.setAction(actionName + phoneId); intent.putExtra(PHONE_ID, phoneId); status = mContext.bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE); @@ -148,18 +165,20 @@ /** waitForBinder */ public IBinder getServiceBinder(int service) { switch (service) { - case RIL.MODEM_SERVICE: + case HAL_SERVICE_MODEM: return mModemBinder; - case RIL.SIM_SERVICE: + case HAL_SERVICE_SIM: return mSimBinder; - case RIL.MESSAGING_SERVICE: + case HAL_SERVICE_MESSAGING: return mMessagingBinder; - case RIL.DATA_SERVICE: + case HAL_SERVICE_DATA: return mDataBinder; - case RIL.NETWORK_SERVICE: + case HAL_SERVICE_NETWORK: return mNetworkBinder; - case RIL.VOICE_SERVICE: + case HAL_SERVICE_VOICE: return mVoiceBinder; + case HAL_SERVICE_IMS: + return mImsBinder; case RADIOCONFIG_SERVICE: return mConfigBinder; default: @@ -183,95 +202,109 @@ boolean status = bindModuleToMockModemService(BIND_IRADIOCONFIG, mConfigServiceConnection); if (!status) { - Rlog.d(TAG, "IRadio Config bind fail"); + Rlog.d(mTag, "IRadio Config bind fail"); mConfigServiceConnection = null; } } else { - Rlog.d(TAG, "IRadio Config is bound"); + Rlog.d(mTag, "IRadio Config is bound"); } - } else if (service == RIL.MODEM_SERVICE) { + } else if (service == HAL_SERVICE_MODEM) { if (mModemBinder == null) { - mModemServiceConnection = new MockModemConnection(RIL.MODEM_SERVICE); + mModemServiceConnection = new MockModemConnection(HAL_SERVICE_MODEM); boolean status = bindModuleToMockModemService( mPhoneId, BIND_IRADIOMODEM, mModemServiceConnection); if (!status) { - Rlog.d(TAG, "IRadio Modem bind fail"); + Rlog.d(mTag, "IRadio Modem bind fail"); mModemServiceConnection = null; } } else { - Rlog.d(TAG, "IRadio Modem is bound"); + Rlog.d(mTag, "IRadio Modem is bound"); } - } else if (service == RIL.SIM_SERVICE) { + } else if (service == HAL_SERVICE_SIM) { if (mSimBinder == null) { - mSimServiceConnection = new MockModemConnection(RIL.SIM_SERVICE); + mSimServiceConnection = new MockModemConnection(HAL_SERVICE_SIM); boolean status = bindModuleToMockModemService( mPhoneId, BIND_IRADIOSIM, mSimServiceConnection); if (!status) { - Rlog.d(TAG, "IRadio Sim bind fail"); + Rlog.d(mTag, "IRadio Sim bind fail"); mSimServiceConnection = null; } } else { - Rlog.d(TAG, "IRadio Sim is bound"); + Rlog.d(mTag, "IRadio Sim is bound"); } - } else if (service == RIL.MESSAGING_SERVICE) { + } else if (service == HAL_SERVICE_MESSAGING) { if (mMessagingBinder == null) { - mMessagingServiceConnection = new MockModemConnection(RIL.MESSAGING_SERVICE); + mMessagingServiceConnection = new MockModemConnection(HAL_SERVICE_MESSAGING); boolean status = bindModuleToMockModemService( mPhoneId, BIND_IRADIOMESSAGING, mMessagingServiceConnection); if (!status) { - Rlog.d(TAG, "IRadio Messaging bind fail"); + Rlog.d(mTag, "IRadio Messaging bind fail"); mMessagingServiceConnection = null; } } else { - Rlog.d(TAG, "IRadio Messaging is bound"); + Rlog.d(mTag, "IRadio Messaging is bound"); } - } else if (service == RIL.DATA_SERVICE) { + } else if (service == HAL_SERVICE_DATA) { if (mDataBinder == null) { - mDataServiceConnection = new MockModemConnection(RIL.DATA_SERVICE); + mDataServiceConnection = new MockModemConnection(HAL_SERVICE_DATA); boolean status = bindModuleToMockModemService( mPhoneId, BIND_IRADIODATA, mDataServiceConnection); if (!status) { - Rlog.d(TAG, "IRadio Data bind fail"); + Rlog.d(mTag, "IRadio Data bind fail"); mDataServiceConnection = null; } } else { - Rlog.d(TAG, "IRadio Data is bound"); + Rlog.d(mTag, "IRadio Data is bound"); } - } else if (service == RIL.NETWORK_SERVICE) { + } else if (service == HAL_SERVICE_NETWORK) { if (mNetworkBinder == null) { - mNetworkServiceConnection = new MockModemConnection(RIL.NETWORK_SERVICE); + mNetworkServiceConnection = new MockModemConnection(HAL_SERVICE_NETWORK); boolean status = bindModuleToMockModemService( mPhoneId, BIND_IRADIONETWORK, mNetworkServiceConnection); if (!status) { - Rlog.d(TAG, "IRadio Network bind fail"); + Rlog.d(mTag, "IRadio Network bind fail"); mNetworkServiceConnection = null; } } else { - Rlog.d(TAG, "IRadio Network is bound"); + Rlog.d(mTag, "IRadio Network is bound"); } - } else if (service == RIL.VOICE_SERVICE) { + } else if (service == HAL_SERVICE_VOICE) { if (mVoiceBinder == null) { - mVoiceServiceConnection = new MockModemConnection(RIL.VOICE_SERVICE); + mVoiceServiceConnection = new MockModemConnection(HAL_SERVICE_VOICE); boolean status = bindModuleToMockModemService( mPhoneId, BIND_IRADIOVOICE, mVoiceServiceConnection); if (!status) { - Rlog.d(TAG, "IRadio Voice bind fail"); + Rlog.d(mTag, "IRadio Voice bind fail"); mVoiceServiceConnection = null; } } else { - Rlog.d(TAG, "IRadio Voice is bound"); + Rlog.d(mTag, "IRadio Voice is bound"); + } + } else if (service == HAL_SERVICE_IMS) { + if (mImsBinder == null) { + mImsServiceConnection = new MockModemConnection(HAL_SERVICE_IMS); + + boolean status = + bindModuleToMockModemService( + mPhoneId, BIND_IRADIOIMS, mImsServiceConnection); + if (!status) { + Rlog.d(TAG, "IRadio Ims bind fail"); + mImsServiceConnection = null; + } + } else { + Rlog.d(TAG, "IRadio Ims is bound"); } } } @@ -284,49 +317,56 @@ mContext.unbindService(mConfigServiceConnection); mConfigServiceConnection = null; mConfigBinder = null; - Rlog.d(TAG, "unbind IRadio Config"); + Rlog.d(mTag, "unbind IRadio Config"); } - } else if (service == RIL.MODEM_SERVICE) { + } else if (service == HAL_SERVICE_MODEM) { if (mModemServiceConnection != null) { mContext.unbindService(mModemServiceConnection); mModemServiceConnection = null; mModemBinder = null; - Rlog.d(TAG, "unbind IRadio Modem"); + Rlog.d(mTag, "unbind IRadio Modem"); } - } else if (service == RIL.SIM_SERVICE) { + } else if (service == HAL_SERVICE_SIM) { if (mSimServiceConnection != null) { mContext.unbindService(mSimServiceConnection); mSimServiceConnection = null; mSimBinder = null; - Rlog.d(TAG, "unbind IRadio Sim"); + Rlog.d(mTag, "unbind IRadio Sim"); } - } else if (service == RIL.MESSAGING_SERVICE) { + } else if (service == HAL_SERVICE_MESSAGING) { if (mMessagingServiceConnection != null) { mContext.unbindService(mMessagingServiceConnection); mMessagingServiceConnection = null; mMessagingBinder = null; - Rlog.d(TAG, "unbind IRadio Messaging"); + Rlog.d(mTag, "unbind IRadio Messaging"); } - } else if (service == RIL.DATA_SERVICE) { + } else if (service == HAL_SERVICE_DATA) { if (mDataServiceConnection != null) { mContext.unbindService(mDataServiceConnection); mDataServiceConnection = null; mDataBinder = null; - Rlog.d(TAG, "unbind IRadio Data"); + Rlog.d(mTag, "unbind IRadio Data"); } - } else if (service == RIL.NETWORK_SERVICE) { + } else if (service == HAL_SERVICE_NETWORK) { if (mNetworkServiceConnection != null) { mContext.unbindService(mNetworkServiceConnection); mNetworkServiceConnection = null; mNetworkBinder = null; - Rlog.d(TAG, "unbind IRadio Network"); + Rlog.d(mTag, "unbind IRadio Network"); } - } else if (service == RIL.VOICE_SERVICE) { + } else if (service == HAL_SERVICE_VOICE) { if (mVoiceServiceConnection != null) { mContext.unbindService(mVoiceServiceConnection); mVoiceServiceConnection = null; mVoiceBinder = null; - Rlog.d(TAG, "unbind IRadio Voice"); + Rlog.d(mTag, "unbind IRadio Voice"); + } + } else if (service == HAL_SERVICE_IMS) { + if (mImsServiceConnection != null) { + mContext.unbindService(mImsServiceConnection); + mImsServiceConnection = null; + mImsBinder = null; + Rlog.d(TAG, "unbind IRadio Ims"); } } } @@ -337,18 +377,20 @@ private String getModuleName(int service) { switch (service) { - case RIL.MODEM_SERVICE: + case HAL_SERVICE_MODEM: return "modem"; - case RIL.SIM_SERVICE: + case HAL_SERVICE_SIM: return "sim"; - case RIL.MESSAGING_SERVICE: + case HAL_SERVICE_MESSAGING: return "messaging"; - case RIL.DATA_SERVICE: + case HAL_SERVICE_DATA: return "data"; - case RIL.NETWORK_SERVICE: + case HAL_SERVICE_NETWORK: return "network"; - case RIL.VOICE_SERVICE: + case HAL_SERVICE_VOICE: return "voice"; + case HAL_SERVICE_IMS: + return "ims"; case RADIOCONFIG_SERVICE: return "config"; default:
diff --git a/src/java/com/android/internal/telephony/ModemIndication.java b/src/java/com/android/internal/telephony/ModemIndication.java index 8baa5fd..0ee40bb 100644 --- a/src/java/com/android/internal/telephony/ModemIndication.java +++ b/src/java/com/android/internal/telephony/ModemIndication.java
@@ -16,6 +16,8 @@ package com.android.internal.telephony; +import static android.telephony.TelephonyManager.HAL_SERVICE_MODEM; + import static com.android.internal.telephony.RILConstants.RIL_UNSOL_HARDWARE_CONFIG_CHANGED; import static com.android.internal.telephony.RILConstants.RIL_UNSOL_MODEM_RESTART; import static com.android.internal.telephony.RILConstants.RIL_UNSOL_RADIO_CAPABILITY; @@ -44,11 +46,11 @@ */ public void hardwareConfigChanged(int indicationType, android.hardware.radio.modem.HardwareConfig[] configs) { - mRil.processIndication(RIL.MODEM_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_MODEM, indicationType); ArrayList<HardwareConfig> response = RILUtils.convertHalHardwareConfigList(configs); - if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_HARDWARE_CONFIG_CHANGED, response); + if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_HARDWARE_CONFIG_CHANGED, response); mRil.mHardwareConfigChangeRegistrants.notifyRegistrants( new AsyncResult(null, response, null)); @@ -62,9 +64,9 @@ * restart" that explains the cause of the modem restart */ public void modemReset(int indicationType, String reason) { - mRil.processIndication(RIL.MODEM_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_MODEM, indicationType); - if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_MODEM_RESTART, reason); + if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_MODEM_RESTART, reason); mRil.writeMetricsModemRestartEvent(reason); mRil.mModemResetRegistrants.notifyRegistrants(new AsyncResult(null, reason, null)); @@ -78,11 +80,11 @@ */ public void radioCapabilityIndication(int indicationType, android.hardware.radio.modem.RadioCapability radioCapability) { - mRil.processIndication(RIL.MODEM_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_MODEM, indicationType); RadioCapability response = RILUtils.convertHalRadioCapability(radioCapability, mRil); - if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_RADIO_CAPABILITY, response); + if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_RADIO_CAPABILITY, response); mRil.mPhoneRadioCapabilityChangedRegistrants.notifyRegistrants( new AsyncResult(null, response, null)); @@ -94,12 +96,12 @@ * @param radioState Current radio state */ public void radioStateChanged(int indicationType, int radioState) { - mRil.processIndication(RIL.MODEM_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_MODEM, indicationType); int state = RILUtils.convertHalRadioState(radioState); - if (RIL.RILJ_LOGD) { - mRil.unsljLogMore(RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED, "radioStateChanged: " - + state); + if (mRil.isLogOrTrace()) { + mRil.unsljLogMore( + RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED, "radioStateChanged: " + state); } mRil.setRadioState(state, false /* forceNotifyRegistrants */); @@ -110,9 +112,9 @@ * @param indicationType Type of radio indication */ public void rilConnected(int indicationType) { - mRil.processIndication(RIL.MODEM_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_MODEM, indicationType); - if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_RIL_CONNECTED); + if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_RIL_CONNECTED); // Initial conditions mRil.setRadioPower(false, null);
diff --git a/src/java/com/android/internal/telephony/ModemResponse.java b/src/java/com/android/internal/telephony/ModemResponse.java index 6e44ddc..bd04d16 100644 --- a/src/java/com/android/internal/telephony/ModemResponse.java +++ b/src/java/com/android/internal/telephony/ModemResponse.java
@@ -16,9 +16,12 @@ package com.android.internal.telephony; +import static android.telephony.TelephonyManager.HAL_SERVICE_MODEM; + import android.hardware.radio.RadioError; import android.hardware.radio.RadioResponseInfo; import android.hardware.radio.modem.IRadioModemResponse; +import android.hardware.radio.modem.ImeiInfo; import android.os.SystemClock; import android.telephony.ActivityStatsTechSpecificInfo; import android.telephony.AnomalyReporter; @@ -51,7 +54,7 @@ * @param responseInfo Response info struct containing response type, serial number and error. */ public void enableModemResponse(RadioResponseInfo responseInfo) { - RadioResponse.responseVoid(RIL.MODEM_SERVICE, mRil, responseInfo); + RadioResponse.responseVoid(HAL_SERVICE_MODEM, mRil, responseInfo); } /** @@ -59,7 +62,7 @@ * @param version String containing version string for log reporting */ public void getBasebandVersionResponse(RadioResponseInfo responseInfo, String version) { - RadioResponse.responseString(RIL.MODEM_SERVICE, mRil, responseInfo, version); + RadioResponse.responseString(HAL_SERVICE_MODEM, mRil, responseInfo, version); } /** @@ -72,7 +75,21 @@ public void getDeviceIdentityResponse(RadioResponseInfo responseInfo, String imei, String imeisv, String esn, String meid) { RadioResponse.responseStrings( - RIL.MODEM_SERVICE, mRil, responseInfo, imei, imeisv, esn, meid); + HAL_SERVICE_MODEM, mRil, responseInfo, imei, imeisv, esn, meid); + } + + /** + * @param responseInfo Response info struct containing response type, serial no. and error + * @param imeiInfo object containing ImeiType, device IMEI and IMEISV + */ + public void getImeiResponse(RadioResponseInfo responseInfo, ImeiInfo imeiInfo) { + RILRequest rr = mRil.processResponse(HAL_SERVICE_MODEM, responseInfo); + if (rr != null) { + if (responseInfo.error == RadioError.NONE) { + RadioResponse.sendMessageResponse(rr.mResult, imeiInfo); + } + mRil.processResponseDone(rr, responseInfo, imeiInfo); + } } /** @@ -81,7 +98,7 @@ */ public void getHardwareConfigResponse(RadioResponseInfo responseInfo, android.hardware.radio.modem.HardwareConfig[] config) { - RILRequest rr = mRil.processResponse(RIL.MODEM_SERVICE, responseInfo); + RILRequest rr = mRil.processResponse(HAL_SERVICE_MODEM, responseInfo); if (rr != null) { ArrayList<HardwareConfig> ret = RILUtils.convertHalHardwareConfigList(config); @@ -98,7 +115,7 @@ */ public void getModemActivityInfoResponse(RadioResponseInfo responseInfo, android.hardware.radio.modem.ActivityStatsInfo activityInfo) { - RILRequest rr = mRil.processResponse(RIL.MODEM_SERVICE, responseInfo); + RILRequest rr = mRil.processResponse(HAL_SERVICE_MODEM, responseInfo); if (rr != null) { ModemActivityInfo ret = null; @@ -141,7 +158,7 @@ * @param isEnabled whether the modem stack is enabled. */ public void getModemStackStatusResponse(RadioResponseInfo responseInfo, boolean isEnabled) { - RILRequest rr = mRil.processResponse(RIL.MODEM_SERVICE, responseInfo); + RILRequest rr = mRil.processResponse(HAL_SERVICE_MODEM, responseInfo); if (rr != null) { if (responseInfo.error == RadioError.NONE) { @@ -157,7 +174,7 @@ */ public void getRadioCapabilityResponse(RadioResponseInfo responseInfo, android.hardware.radio.modem.RadioCapability radioCapability) { - RILRequest rr = mRil.processResponse(RIL.MODEM_SERVICE, responseInfo); + RILRequest rr = mRil.processResponse(HAL_SERVICE_MODEM, responseInfo); if (rr != null) { RadioCapability ret = RILUtils.convertHalRadioCapability(radioCapability, mRil); @@ -179,42 +196,42 @@ * @param result String containing the contents of the NV item */ public void nvReadItemResponse(RadioResponseInfo responseInfo, String result) { - RadioResponse.responseString(RIL.MODEM_SERVICE, mRil, responseInfo, result); + RadioResponse.responseString(HAL_SERVICE_MODEM, mRil, responseInfo, result); } /** * @param responseInfo Response info struct containing response type, serial no. and error */ public void nvResetConfigResponse(RadioResponseInfo responseInfo) { - RadioResponse.responseVoid(RIL.MODEM_SERVICE, mRil, responseInfo); + RadioResponse.responseVoid(HAL_SERVICE_MODEM, mRil, responseInfo); } /** * @param responseInfo Response info struct containing response type, serial no. and error */ public void nvWriteCdmaPrlResponse(RadioResponseInfo responseInfo) { - RadioResponse.responseVoid(RIL.MODEM_SERVICE, mRil, responseInfo); + RadioResponse.responseVoid(HAL_SERVICE_MODEM, mRil, responseInfo); } /** * @param responseInfo Response info struct containing response type, serial no. and error */ public void nvWriteItemResponse(RadioResponseInfo responseInfo) { - RadioResponse.responseVoid(RIL.MODEM_SERVICE, mRil, responseInfo); + RadioResponse.responseVoid(HAL_SERVICE_MODEM, mRil, responseInfo); } /** * @param responseInfo Response info struct containing response type, serial no. and error */ public void requestShutdownResponse(RadioResponseInfo responseInfo) { - RadioResponse.responseVoid(RIL.MODEM_SERVICE, mRil, responseInfo); + RadioResponse.responseVoid(HAL_SERVICE_MODEM, mRil, responseInfo); } /** * @param responseInfo Response info struct containing response type, serial no. and error */ public void sendDeviceStateResponse(RadioResponseInfo responseInfo) { - RadioResponse.responseVoid(RIL.MODEM_SERVICE, mRil, responseInfo); + RadioResponse.responseVoid(HAL_SERVICE_MODEM, mRil, responseInfo); } /** @@ -223,7 +240,7 @@ */ public void setRadioCapabilityResponse(RadioResponseInfo responseInfo, android.hardware.radio.modem.RadioCapability radioCapability) { - RILRequest rr = mRil.processResponse(RIL.MODEM_SERVICE, responseInfo); + RILRequest rr = mRil.processResponse(HAL_SERVICE_MODEM, responseInfo); if (rr != null) { RadioCapability ret = RILUtils.convertHalRadioCapability(radioCapability, mRil); @@ -238,7 +255,7 @@ * @param responseInfo Response info struct containing response type, serial no. and error. */ public void setRadioPowerResponse(RadioResponseInfo responseInfo) { - RadioResponse.responseVoid(RIL.MODEM_SERVICE, mRil, responseInfo); + RadioResponse.responseVoid(HAL_SERVICE_MODEM, mRil, responseInfo); mRil.mLastRadioPowerResult = responseInfo.error; if (responseInfo.error == RadioError.RF_HARDWARE_ISSUE) { AnomalyReporter.reportAnomaly(
diff --git a/src/java/com/android/internal/telephony/MultiSimSettingController.java b/src/java/com/android/internal/telephony/MultiSimSettingController.java index 3f5c23c..0acae4b 100644 --- a/src/java/com/android/internal/telephony/MultiSimSettingController.java +++ b/src/java/com/android/internal/telephony/MultiSimSettingController.java
@@ -33,10 +33,8 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.app.PendingIntent; -import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; -import android.content.IntentFilter; import android.os.AsyncResult; import android.os.Handler; import android.os.Looper; @@ -54,6 +52,8 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.telephony.data.DataSettingsManager.DataSettingsManagerCallback; +import com.android.internal.telephony.subscription.SubscriptionInfoInternal; +import com.android.internal.telephony.subscription.SubscriptionManagerService; import com.android.internal.telephony.util.ArrayUtils; import java.lang.annotation.Retention; @@ -83,7 +83,6 @@ private static final int EVENT_SUBSCRIPTION_INFO_CHANGED = 4; private static final int EVENT_SUBSCRIPTION_GROUP_CHANGED = 5; private static final int EVENT_DEFAULT_DATA_SUBSCRIPTION_CHANGED = 6; - private static final int EVENT_CARRIER_CONFIG_CHANGED = 7; private static final int EVENT_MULTI_SIM_CONFIG_CHANGED = 8; @VisibleForTesting public static final int EVENT_RADIO_STATE_CHANGED = 9; @@ -121,19 +120,15 @@ private static final int PRIMARY_SUB_REMOVED_IN_GROUP = 7; protected final Context mContext; - protected final SubscriptionController mSubController; + private final SubscriptionManagerService mSubscriptionManagerService; + // Keep a record of active primary (non-opportunistic) subscription list. @NonNull private List<Integer> mPrimarySubList = new ArrayList<>(); /** The singleton instance. */ protected static MultiSimSettingController sInstance = null; - // This will be set true when handling EVENT_ALL_SUBSCRIPTIONS_LOADED. The reason of keeping - // a local variable instead of calling SubscriptionInfoUpdater#isSubInfoInitialized is, there - // might be a race condition that we receive EVENT_SUBSCRIPTION_INFO_CHANGED first, then - // EVENT_ALL_SUBSCRIPTIONS_LOADED. And calling SubscriptionInfoUpdater#isSubInfoInitialized - // will make us handle EVENT_SUBSCRIPTION_INFO_CHANGED unexpectedly and causing us to believe - // the SIMs are newly inserted instead of being initialized. + // This will be set true when handling EVENT_ALL_SUBSCRIPTIONS_LOADED. private boolean mSubInfoInitialized = false; // mInitialHandling is to make sure we don't always ask user to re-select data SIM after reboot. @@ -158,19 +153,6 @@ private static final String SETTING_USER_PREF_DATA_SUB = "user_preferred_data_sub"; - private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - if (CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED.equals(intent.getAction())) { - int phoneId = intent.getIntExtra(CarrierConfigManager.EXTRA_SLOT_INDEX, - SubscriptionManager.INVALID_SIM_SLOT_INDEX); - int subId = intent.getIntExtra(CarrierConfigManager.EXTRA_SUBSCRIPTION_INDEX, - SubscriptionManager.INVALID_SUBSCRIPTION_ID); - notifyCarrierConfigChanged(phoneId, subId); - } - } - }; - private static class DataSettingsControllerCallback extends DataSettingsManagerCallback { private final Phone mPhone; @@ -218,10 +200,10 @@ /** * Init instance of MultiSimSettingController. */ - public static MultiSimSettingController init(Context context, SubscriptionController sc) { + public static MultiSimSettingController init(Context context) { synchronized (MultiSimSettingController.class) { if (sInstance == null) { - sInstance = new MultiSimSettingController(context, sc); + sInstance = new MultiSimSettingController(context); } else { Log.wtf(LOG_TAG, "init() called multiple times! sInstance = " + sInstance); } @@ -230,9 +212,9 @@ } @VisibleForTesting - public MultiSimSettingController(Context context, SubscriptionController sc) { + public MultiSimSettingController(Context context) { mContext = context; - mSubController = sc; + mSubscriptionManagerService = SubscriptionManagerService.getInstance(); // Initialize mCarrierConfigLoadedSubIds and register to listen to carrier config change. final int phoneCount = ((TelephonyManager) mContext.getSystemService( @@ -245,8 +227,12 @@ mIsAskEverytimeSupportedForSms = mContext.getResources() .getBoolean(com.android.internal.R.bool.config_sms_ask_every_time_support); - context.registerReceiver(mIntentReceiver, new IntentFilter( - CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)); + + CarrierConfigManager ccm = mContext.getSystemService(CarrierConfigManager.class); + // Listener callback is executed on handler thread to directly handle config change + ccm.registerCarrierConfigChangeListener(this::post, + (slotIndex, subId, carrierId, specificCarrierId) -> + onCarrierConfigChanged(slotIndex, subId)); } /** @@ -303,7 +289,7 @@ case EVENT_USER_DATA_ENABLED: { int subId = msg.arg1; boolean enable = msg.arg2 != 0; - onUserDataEnabled(subId, enable); + onUserDataEnabled(subId, enable, true); break; } case EVENT_ROAMING_DATA_ENABLED: { @@ -325,11 +311,6 @@ case EVENT_DEFAULT_DATA_SUBSCRIPTION_CHANGED: onDefaultDataSettingChanged(); break; - case EVENT_CARRIER_CONFIG_CHANGED: - int phoneId = msg.arg1; - int subId = msg.arg2; - onCarrierConfigChanged(phoneId, subId); - break; case EVENT_MULTI_SIM_CONFIG_CHANGED: int activeModems = (int) ((AsyncResult) msg.obj).result; onMultiSimConfigChanged(activeModems); @@ -352,17 +333,21 @@ * If user is enabling a non-default non-opportunistic subscription, make it default * data subscription. */ - protected void onUserDataEnabled(int subId, boolean enable) { - if (DBG) log("onUserDataEnabled"); + private void onUserDataEnabled(int subId, boolean enable, boolean setDefaultData) { + if (DBG) log("[onUserDataEnabled] subId=" + subId + " enable=" + enable + + " setDefaultData=" + setDefaultData); // Make sure MOBILE_DATA of subscriptions in same group are synced. setUserDataEnabledForGroup(subId, enable); + SubscriptionInfo subInfo = mSubscriptionManagerService.getSubscriptionInfo(subId); + int defaultDataSubId = mSubscriptionManagerService.getDefaultDataSubId(); + // If user is enabling a non-default non-opportunistic subscription, make it default. - if (mSubController.getDefaultDataSubId() != subId && !mSubController.isOpportunistic(subId) - && enable && mSubController.isActiveSubId(subId)) { - android.provider.Settings.Global.putInt(mContext.getContentResolver(), - SETTING_USER_PREF_DATA_SUB, subId); - mSubController.setDefaultDataSubId(subId); + if (defaultDataSubId != subId && subInfo != null && !subInfo.isOpportunistic() && enable + && subInfo.isActive() && setDefaultData) { + android.provider.Settings.Global.putInt(mContext.getContentResolver(), + SETTING_USER_PREF_DATA_SUB, subId); + mSubscriptionManagerService.setDefaultDataSubId(subId); } } @@ -373,8 +358,8 @@ if (DBG) log("onRoamingDataEnabled"); setRoamingDataEnabledForGroup(subId, enable); - // Also inform SubscriptionController as it keeps another copy of user setting. - mSubController.setDataRoaming(enable ? 1 : 0, subId); + // Also inform SubscriptionManagerService as it keeps another copy of user setting. + mSubscriptionManagerService.setDataRoaming(enable ? 1 : 0, subId); } /** @@ -419,14 +404,6 @@ reEvaluateAll(); } - /** - * Called when carrier config changes on any phone. - */ - @VisibleForTesting - public void notifyCarrierConfigChanged(int phoneId, int subId) { - obtainMessage(EVENT_CARRIER_CONFIG_CHANGED, phoneId, subId).sendToTarget(); - } - private void onCarrierConfigChanged(int phoneId, int subId) { log("onCarrierConfigChanged phoneId " + phoneId + " subId " + subId); if (!SubscriptionManager.isValidPhoneId(phoneId)) { @@ -438,14 +415,12 @@ // being specified in it. So here we do additional check to make sur we don't miss the // subId. if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) { - int[] subIds = mSubController.getSubId(phoneId); - if (!ArrayUtils.isEmpty(subIds)) { - CarrierConfigManager cm = (CarrierConfigManager) mContext.getSystemService( - mContext.CARRIER_CONFIG_SERVICE); - if (cm != null && cm.getConfigForSubId(subIds[0]) != null) { - loge("onCarrierConfigChanged with invalid subId while subd " - + subIds[0] + " is active and its config is loaded"); - subId = subIds[0]; + subId = SubscriptionManager.getSubscriptionId(phoneId); + if (SubscriptionManager.isValidSubscriptionId(subId)) { + CarrierConfigManager cm = mContext.getSystemService(CarrierConfigManager.class); + if (cm != null && cm.getConfigForSubId(subId) != null) { + loge("onCarrierConfigChanged with invalid subId while subId " + + subId + " is active and its config is loaded"); } } } @@ -459,7 +434,7 @@ */ @VisibleForTesting public boolean isCarrierConfigLoadedForAllSub() { - int[] activeSubIds = mSubController.getActiveSubIdList(false); + int[] activeSubIds = mSubscriptionManagerService.getActiveSubIdList(false); for (int activeSubId : activeSubIds) { boolean isLoaded = false; for (int configLoadedSub : mCarrierConfigLoadedSubIds) { @@ -526,7 +501,7 @@ private void onSubscriptionGroupChanged(ParcelUuid groupUuid) { if (DBG) log("onSubscriptionGroupChanged"); - List<SubscriptionInfo> infoList = mSubController.getSubscriptionsInGroup( + List<SubscriptionInfo> infoList = mSubscriptionManagerService.getSubscriptionsInGroup( groupUuid, mContext.getOpPackageName(), mContext.getAttributionTag()); if (infoList == null || infoList.isEmpty()) return; @@ -535,24 +510,36 @@ int refSubId = infoList.get(0).getSubscriptionId(); for (SubscriptionInfo info : infoList) { int subId = info.getSubscriptionId(); - if (mSubController.isActiveSubId(subId) && !mSubController.isOpportunistic(subId)) { + if (info.isActive() && !info.isOpportunistic()) { refSubId = subId; break; } } + if (DBG) log("refSubId is " + refSubId); boolean enable = false; try { enable = GlobalSettingsHelper.getBoolean( mContext, Settings.Global.MOBILE_DATA, refSubId); - onUserDataEnabled(refSubId, enable); } catch (SettingNotFoundException exception) { //pass invalid refSubId to fetch the single-sim setting enable = GlobalSettingsHelper.getBoolean( mContext, Settings.Global.MOBILE_DATA, INVALID_SUBSCRIPTION_ID, enable); - onUserDataEnabled(refSubId, enable); } + boolean setDefaultData = true; + List<SubscriptionInfo> activeSubList = mSubscriptionManagerService + .getActiveSubscriptionInfoList(mContext.getOpPackageName(), + mContext.getAttributionTag()); + for (SubscriptionInfo activeInfo : activeSubList) { + if (!(groupUuid.equals(activeInfo.getGroupUuid()))) { + // Do not set refSubId as defaultDataSubId if there are other active + // subscriptions which does not belong to this groupUuid + setDefaultData = false; + break; + } + } + onUserDataEnabled(refSubId, enable, setDefaultData); enable = false; try { @@ -566,8 +553,7 @@ onRoamingDataEnabled(refSubId, enable); } - // Sync settings in subscription database.. - mSubController.syncGroupedSetting(refSubId); + mSubscriptionManagerService.syncGroupedSetting(refSubId); } /** @@ -589,21 +575,24 @@ if (!isReadyToReevaluate()) return; - List<SubscriptionInfo> activeSubInfos = mSubController + List<SubscriptionInfo> activeSubInfos = mSubscriptionManagerService .getActiveSubscriptionInfoList(mContext.getOpPackageName(), mContext.getAttributionTag()); if (ArrayUtils.isEmpty(activeSubInfos)) { mPrimarySubList.clear(); - if (DBG) log("[updateDefaultValues] No active sub. Setting default to INVALID sub."); - mSubController.setDefaultDataSubId(SubscriptionManager.INVALID_SUBSCRIPTION_ID); - mSubController.setDefaultVoiceSubId(SubscriptionManager.INVALID_SUBSCRIPTION_ID); - mSubController.setDefaultSmsSubId(SubscriptionManager.INVALID_SUBSCRIPTION_ID); + if (DBG) log("updateDefaults: No active sub. Setting default to INVALID sub."); + mSubscriptionManagerService.setDefaultDataSubId( + SubscriptionManager.INVALID_SUBSCRIPTION_ID); + mSubscriptionManagerService.setDefaultVoiceSubId( + SubscriptionManager.INVALID_SUBSCRIPTION_ID); + mSubscriptionManagerService.setDefaultSmsSubId( + SubscriptionManager.INVALID_SUBSCRIPTION_ID); return; } int change = updatePrimarySubListAndGetChangeType(activeSubInfos); - if (DBG) log("[updateDefaultValues] change: " + change); + if (DBG) log("updateDefaultValues: change: " + change); if (change == PRIMARY_SUB_NO_CHANGE) return; // If there's only one primary subscription active, we trigger PREFERRED_PICK_DIALOG @@ -615,33 +604,35 @@ || ((TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE)) .getActiveModemCount() == 1)) { int subId = mPrimarySubList.get(0); - if (DBG) log("[updateDefaultValues] to only primary sub " + subId); - mSubController.setDefaultDataSubId(subId); - mSubController.setDefaultVoiceSubId(subId); - mSubController.setDefaultSmsSubId(subId); + if (DBG) log("updateDefaultValues: to only primary sub " + subId); + mSubscriptionManagerService.setDefaultDataSubId(subId); + mSubscriptionManagerService.setDefaultVoiceSubId(subId); + mSubscriptionManagerService.setDefaultSmsSubId(subId); sendDefaultSubConfirmedNotification(subId); return; } - if (DBG) log("[updateDefaultValues] records: " + mPrimarySubList); + if (DBG) log("updateDefaultValues: records: " + mPrimarySubList); + + boolean dataSelected, voiceSelected, smsSelected; // Update default data subscription. - if (DBG) log("[updateDefaultValues] Update default data subscription"); - boolean dataSelected = updateDefaultValue(mPrimarySubList, - mSubController.getDefaultDataSubId(), - (newValue -> mSubController.setDefaultDataSubId(newValue))); + if (DBG) log("updateDefaultValues: Update default data subscription"); + dataSelected = updateDefaultValue(mPrimarySubList, + mSubscriptionManagerService.getDefaultDataSubId(), + mSubscriptionManagerService::setDefaultDataSubId); // Update default voice subscription. - if (DBG) log("[updateDefaultValues] Update default voice subscription"); - boolean voiceSelected = updateDefaultValue(mPrimarySubList, - mSubController.getDefaultVoiceSubId(), - (newValue -> mSubController.setDefaultVoiceSubId(newValue))); + if (DBG) log("updateDefaultValues: Update default voice subscription"); + voiceSelected = updateDefaultValue(mPrimarySubList, + mSubscriptionManagerService.getDefaultVoiceSubId(), + mSubscriptionManagerService::setDefaultVoiceSubId); // Update default sms subscription. - if (DBG) log("[updateDefaultValues] Update default sms subscription"); - boolean smsSelected = updateDefaultValue(mPrimarySubList, - mSubController.getDefaultSmsSubId(), - (newValue -> mSubController.setDefaultSmsSubId(newValue)), + if (DBG) log("updateDefaultValues: Update default sms subscription"); + smsSelected = updateDefaultValue(mPrimarySubList, + mSubscriptionManagerService.getDefaultSmsSubId(), + mSubscriptionManagerService::setDefaultSmsSubId, mIsAskEverytimeSupportedForSms); boolean autoFallbackEnabled = mContext.getResources().getBoolean( @@ -693,7 +684,9 @@ // any previous primary subscription becomes inactive, we consider it for (int subId : prevPrimarySubList) { if (mPrimarySubList.contains(subId)) continue; - if (!mSubController.isActiveSubId(subId)) { + SubscriptionInfo subInfo = mSubscriptionManagerService.getSubscriptionInfo(subId); + + if (subInfo == null || !subInfo.isActive()) { for (int currentSubId : mPrimarySubList) { if (areSubscriptionsInSameGroup(currentSubId, subId)) { return PRIMARY_SUB_REMOVED_IN_GROUP; @@ -701,10 +694,10 @@ } return PRIMARY_SUB_REMOVED; } - if (!mSubController.isOpportunistic(subId)) { + if (!subInfo.isOpportunistic()) { // Should never happen. - loge("[updatePrimarySubListAndGetChangeType]: missing active primary subId " - + subId); + loge("[updatePrimarySubListAndGetChangeType]: missing active primary " + + "subId " + subId); } } return PRIMARY_SUB_MARKED_OPPT; @@ -729,6 +722,7 @@ @TelephonyManager.DefaultSubscriptionSelectType int simSelectDialogType = getSimSelectDialogType( change, dataSelected, voiceSelected, smsSelected); + log("sendSubChangeNotificationIfNeeded: simSelectDialogType=" + simSelectDialogType); SimCombinationWarningParams simCombinationParams = getSimCombinationWarningParams(change); if (simSelectDialogType != EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_NONE @@ -760,6 +754,12 @@ boolean voiceSelected, boolean smsSelected) { int dialogType = EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_NONE; + // Do not show preference selection dialog during SuW as there is fullscreen activity to + // choose preference. + if (Settings.Global.getInt(mContext.getContentResolver(), + Settings.Global.DEVICE_PROVISIONED, 0) == 0) { + return dialogType; + } // If a primary subscription is removed and only one is left active, ask user // for preferred sub selection if any default setting is not set. // If another primary subscription is added or default data is not selected, ask @@ -768,12 +768,8 @@ && (!dataSelected || !smsSelected || !voiceSelected)) { dialogType = EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_ALL; } else if (mPrimarySubList.size() > 1 && (isUserVisibleChange(change) - || (change == PRIMARY_SUB_INITIALIZED && !dataSelected - && Settings.Global.getInt(mContext.getContentResolver(), - Settings.Global.DEVICE_PROVISIONED, 0) != 0))) { + || (change == PRIMARY_SUB_INITIALIZED && !dataSelected))) { // If change is SWAPPED_IN_GROUP or MARKED_OPPT, don't ask user again. - // In default DSDS devices, do not show data selection dialog during SuW as there is - // fullscreen activity to choose data preference. dialogType = EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_DATA; } @@ -801,9 +797,12 @@ // If a dual CDMA SIM combination warning is needed. if (phone != null && phone.isCdmaSubscriptionAppPresent()) { cdmaPhoneCount++; - String simName = mSubController.getActiveSubscriptionInfo( - subId, mContext.getOpPackageName(), mContext.getAttributionTag()) - .getDisplayName().toString(); + String simName = null; + SubscriptionInfoInternal subInfo = mSubscriptionManagerService + .getSubscriptionInfoInternal(subId); + if (subInfo != null) { + simName = subInfo.getDisplayName(); + } if (TextUtils.isEmpty(simName)) { // Fall back to carrier name. simName = phone.getCarrierName(); @@ -828,23 +827,21 @@ protected void disableDataForNonDefaultNonOpportunisticSubscriptions() { if (!isReadyToReevaluate()) return; - int defaultDataSub = mSubController.getDefaultDataSubId(); + int defaultDataSub = mSubscriptionManagerService.getDefaultDataSubId(); for (Phone phone : PhoneFactory.getPhones()) { + SubscriptionInfoInternal subInfo = mSubscriptionManagerService + .getSubscriptionInfoInternal(phone.getSubId()); + boolean isOpportunistic = subInfo != null && subInfo.isOpportunistic(); if (phone.getSubId() != defaultDataSub && SubscriptionManager.isValidSubscriptionId(phone.getSubId()) - && !mSubController.isOpportunistic(phone.getSubId()) + && !isOpportunistic && phone.isUserDataEnabled() && !areSubscriptionsInSameGroup(defaultDataSub, phone.getSubId())) { log("setting data to false on " + phone.getSubId()); - if (phone.isUsingNewDataStack()) { - phone.getDataSettingsManager().setDataEnabled( - TelephonyManager.DATA_ENABLED_REASON_USER, false, - mContext.getOpPackageName()); - } else { - phone.getDataEnabledSettings().setDataEnabled( - TelephonyManager.DATA_ENABLED_REASON_USER, false); - } + phone.getDataSettingsManager().setDataEnabled( + TelephonyManager.DATA_ENABLED_REASON_USER, false, + mContext.getOpPackageName()); } } } @@ -854,9 +851,13 @@ || !SubscriptionManager.isUsableSubscriptionId(subId2)) return false; if (subId1 == subId2) return true; - ParcelUuid groupUuid1 = mSubController.getGroupUuid(subId1); - ParcelUuid groupUuid2 = mSubController.getGroupUuid(subId2); - return groupUuid1 != null && groupUuid1.equals(groupUuid2); + SubscriptionInfoInternal subInfo1 = + mSubscriptionManagerService.getSubscriptionInfoInternal(subId1); + SubscriptionInfoInternal subInfo2 = + mSubscriptionManagerService.getSubscriptionInfoInternal(subId2); + return subInfo1 != null && subInfo2 != null + && !TextUtils.isEmpty(subInfo1.getGroupUuid()) + && subInfo1.getGroupUuid().equals(subInfo2.getGroupUuid()); } /** @@ -865,28 +866,30 @@ */ protected void setUserDataEnabledForGroup(int subId, boolean enable) { log("setUserDataEnabledForGroup subId " + subId + " enable " + enable); - List<SubscriptionInfo> infoList = mSubController.getSubscriptionsInGroup( - mSubController.getGroupUuid(subId), mContext.getOpPackageName(), - mContext.getAttributionTag()); + List<SubscriptionInfo> infoList = null; + SubscriptionInfoInternal subInfo = mSubscriptionManagerService + .getSubscriptionInfoInternal(subId); + if (subInfo != null && !subInfo.getGroupUuid().isEmpty()) { + infoList = mSubscriptionManagerService.getSubscriptionsInGroup( + ParcelUuid.fromString(subInfo.getGroupUuid()), mContext.getOpPackageName(), + mContext.getAttributionTag()); + } if (infoList == null) return; for (SubscriptionInfo info : infoList) { int currentSubId = info.getSubscriptionId(); // TODO: simplify when setUserDataEnabled becomes singleton - if (mSubController.isActiveSubId(currentSubId)) { + if (info.isActive()) { // For active subscription, call setUserDataEnabled through DataSettingsManager. - Phone phone = PhoneFactory.getPhone(mSubController.getPhoneId(currentSubId)); + Phone phone = PhoneFactory.getPhone(mSubscriptionManagerService + .getPhoneId(currentSubId)); // If enable is true and it's not opportunistic subscription, we don't enable it, // as there can't be two if (phone != null) { - if (phone.isUsingNewDataStack()) { - phone.getDataSettingsManager().setDataEnabled( - TelephonyManager.DATA_ENABLED_REASON_USER, enable, - mContext.getOpPackageName()); - } else { - phone.getDataEnabledSettings().setUserDataEnabled(enable, false); - } + phone.getDataSettingsManager().setDataEnabled( + TelephonyManager.DATA_ENABLED_REASON_USER, enable, + mContext.getOpPackageName()); } } else { // For inactive subscription, directly write into global settings. @@ -901,11 +904,12 @@ * are synced. */ private void setRoamingDataEnabledForGroup(int subId, boolean enable) { - SubscriptionController subController = SubscriptionController.getInstance(); - List<SubscriptionInfo> infoList = subController.getSubscriptionsInGroup( - mSubController.getGroupUuid(subId), mContext.getOpPackageName(), - mContext.getAttributionTag()); - + SubscriptionInfoInternal subInfo = mSubscriptionManagerService + .getSubscriptionInfoInternal(subId); + if (subInfo == null || subInfo.getGroupUuid().isEmpty()) return; + List<SubscriptionInfo> infoList = SubscriptionManagerService.getInstance() + .getSubscriptionsInGroup(ParcelUuid.fromString(subInfo.getGroupUuid()), + mContext.getOpPackageName(), mContext.getAttributionTag()); if (infoList == null) return; for (SubscriptionInfo info : infoList) { @@ -958,16 +962,16 @@ // subscription gets deactivated or removed, we need to automatically disable the grouped // opportunistic subscription, which will be marked isGroupDisabled as true by SubController. private void deactivateGroupedOpportunisticSubscriptionIfNeeded() { - if (!SubscriptionInfoUpdater.isSubInfoInitialized()) return; - - List<SubscriptionInfo> opptSubList = mSubController.getOpportunisticSubscriptions( - mContext.getOpPackageName(), mContext.getAttributionTag()); + List<SubscriptionInfo> opptSubList = mSubscriptionManagerService.getAllSubInfoList( + mContext.getOpPackageName(), mContext.getAttributionTag()).stream() + .filter(SubscriptionInfo::isOpportunistic) + .collect(Collectors.toList()); if (ArrayUtils.isEmpty(opptSubList)) return; for (SubscriptionInfo info : opptSubList) { - if (info.isGroupDisabled() && mSubController.isActiveSubId(info.getSubscriptionId())) { - log("[deactivateGroupedOpptSubIfNeeded] " + if (info.isGroupDisabled() && info.isActive()) { + log("deactivateGroupedOpportunisticSubscriptionIfNeeded: " + "Deactivating grouped opportunistic subscription " + info.getSubscriptionId()); deactivateSubscription(info); @@ -997,47 +1001,55 @@ // would be selected as preferred voice/data/sms SIM. private void updateUserPreferences(List<Integer> primarySubList, boolean dataSelected, boolean voiceSelected, boolean smsSelected) { - // In Single SIM case or if there are no activated subs available, no need to update. EXIT. - if ((primarySubList.isEmpty()) || (mSubController.getActiveSubInfoCountMax() == 1)) return; + // In Single SIM case or if there are no activated subs available, no need to update. + // EXIT. + if ((primarySubList.isEmpty()) || (mSubscriptionManagerService + .getActiveSubInfoCountMax() == 1)) { + return; + } if (!isRadioAvailableOnAllSubs()) { log("Radio is in Invalid state, Ignore Updating User Preference!!!"); return; } - final int defaultDataSubId = mSubController.getDefaultDataSubId(); + final int defaultDataSubId = mSubscriptionManagerService.getDefaultDataSubId(); - if (DBG) log("updateUserPreferences: dds = " + defaultDataSubId + " voice = " - + mSubController.getDefaultVoiceSubId() + - " sms = " + mSubController.getDefaultSmsSubId()); + if (DBG) { + log("updateUserPreferences: dds = " + defaultDataSubId + " voice = " + + mSubscriptionManagerService.getDefaultVoiceSubId() + + " sms = " + mSubscriptionManagerService.getDefaultSmsSubId()); + } int autoDefaultSubId = primarySubList.get(0); if ((primarySubList.size() == 1) && !smsSelected) { - mSubController.setDefaultSmsSubId(autoDefaultSubId); + mSubscriptionManagerService.setDefaultSmsSubId(autoDefaultSubId); } if ((primarySubList.size() == 1) && !voiceSelected) { - mSubController.setDefaultVoiceSubId(autoDefaultSubId); + mSubscriptionManagerService.setDefaultVoiceSubId(autoDefaultSubId); } int userPrefDataSubId = getUserPrefDataSubIdFromDB(); - if (DBG) log("User pref subId = " + userPrefDataSubId + " current dds " + defaultDataSubId - + " next active subId " + autoDefaultSubId); + log("User pref subId = " + userPrefDataSubId + " current dds " + defaultDataSubId + + " next active subId " + autoDefaultSubId); // If earlier user selected DDS is now available, set that as DDS subId. - if (primarySubList.contains(userPrefDataSubId) && - SubscriptionManager.isValidSubscriptionId(userPrefDataSubId) && - (defaultDataSubId != userPrefDataSubId)) { - mSubController.setDefaultDataSubId(userPrefDataSubId); + if (primarySubList.contains(userPrefDataSubId) + && SubscriptionManager.isValidSubscriptionId(userPrefDataSubId) + && (defaultDataSubId != userPrefDataSubId)) { + mSubscriptionManagerService.setDefaultDataSubId(userPrefDataSubId); } else if (!dataSelected) { - mSubController.setDefaultDataSubId(autoDefaultSubId); + mSubscriptionManagerService.setDefaultDataSubId(autoDefaultSubId); } - - if (DBG) log("updateUserPreferences: after dds = " + mSubController.getDefaultDataSubId() + - " voice = " + mSubController.getDefaultVoiceSubId() + " sms = " + - mSubController.getDefaultSmsSubId()); + if (DBG) { + log("updateUserPreferences: after dds = " + + mSubscriptionManagerService.getDefaultDataSubId() + " voice = " + + mSubscriptionManagerService.getDefaultVoiceSubId() + " sms = " + + mSubscriptionManagerService.getDefaultSmsSubId()); + } } private int getUserPrefDataSubIdFromDB() { @@ -1061,10 +1073,8 @@ // existing phone instance. Phone[] phones = PhoneFactory.getPhones(); for (int i = mCallbacksCount; i < phones.length; i++) { - if (phones[i].isUsingNewDataStack()) { - phones[i].getDataSettingsManager().registerCallback( - new DataSettingsControllerCallback(phones[i], this::post)); - } + phones[i].getDataSettingsManager().registerCallback( + new DataSettingsControllerCallback(phones[i], this::post)); } mCallbacksCount = phones.length; }
diff --git a/src/java/com/android/internal/telephony/NetworkIndication.java b/src/java/com/android/internal/telephony/NetworkIndication.java index 17d906e..7f9ff79 100644 --- a/src/java/com/android/internal/telephony/NetworkIndication.java +++ b/src/java/com/android/internal/telephony/NetworkIndication.java
@@ -16,14 +16,17 @@ package com.android.internal.telephony; +import static android.telephony.TelephonyManager.HAL_SERVICE_NETWORK; import static android.telephony.TelephonyManager.UNKNOWN_CARRIER_ID; import static com.android.internal.telephony.RILConstants.RIL_UNSOL_CDMA_PRL_CHANGED; import static com.android.internal.telephony.RILConstants.RIL_UNSOL_CELL_INFO_LIST; +import static com.android.internal.telephony.RILConstants.RIL_UNSOL_EMERGENCY_NETWORK_SCAN_RESULT; import static com.android.internal.telephony.RILConstants.RIL_UNSOL_LCEDATA_RECV; import static com.android.internal.telephony.RILConstants.RIL_UNSOL_NETWORK_SCAN_RESULT; import static com.android.internal.telephony.RILConstants.RIL_UNSOL_NITZ_TIME_RECEIVED; import static com.android.internal.telephony.RILConstants.RIL_UNSOL_PHYSICAL_CHANNEL_CONFIG; +import static com.android.internal.telephony.RILConstants.RIL_UNSOL_REGISTRATION_FAILED; import static com.android.internal.telephony.RILConstants.RIL_UNSOL_RESPONSE_IMS_NETWORK_STATE_CHANGED; import static com.android.internal.telephony.RILConstants.RIL_UNSOL_RESPONSE_NETWORK_STATE_CHANGED; import static com.android.internal.telephony.RILConstants.RIL_UNSOL_RESTRICTED_STATE_CHANGED; @@ -39,6 +42,7 @@ import android.telephony.BarringInfo; import android.telephony.CellIdentity; import android.telephony.CellInfo; +import android.telephony.EmergencyRegResult; import android.telephony.LinkCapacityEstimate; import android.telephony.NetworkRegistrationInfo; import android.telephony.PhysicalChannelConfig; @@ -72,7 +76,7 @@ public void barringInfoChanged(int indicationType, android.hardware.radio.network.CellIdentity cellIdentity, android.hardware.radio.network.BarringInfo[] barringInfos) { - mRil.processIndication(RIL.NETWORK_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_NETWORK, indicationType); if (cellIdentity == null || barringInfos == null) { reportAnomaly(UUID.fromString("645b16bb-c930-4c1c-9c5d-568696542e05"), @@ -84,7 +88,7 @@ BarringInfo cbi = new BarringInfo(RILUtils.convertHalCellIdentity(cellIdentity), RILUtils.convertHalBarringInfoList(barringInfos)); - mRil.mBarringInfoChangedRegistrants.notifyRegistrants(new AsyncResult(null, cbi, null)); + mRil.notifyBarringInfoChanged(cbi); } /** @@ -93,11 +97,11 @@ * @param version PRL version after PRL changes */ public void cdmaPrlChanged(int indicationType, int version) { - mRil.processIndication(RIL.NETWORK_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_NETWORK, indicationType); int[] response = new int[]{version}; - if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_CDMA_PRL_CHANGED, response); + if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_CDMA_PRL_CHANGED, response); mRil.mCdmaPrlChangedRegistrants.notifyRegistrants(new AsyncResult(null, response, null)); } @@ -109,9 +113,9 @@ */ public void cellInfoList(int indicationType, android.hardware.radio.network.CellInfo[] records) { - mRil.processIndication(RIL.NETWORK_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_NETWORK, indicationType); ArrayList<CellInfo> response = RILUtils.convertHalCellInfoList(records); - if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_CELL_INFO_LIST, response); + if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_CELL_INFO_LIST, response); mRil.mRilCellInfoListRegistrants.notifyRegistrants(new AsyncResult(null, response, null)); } @@ -122,11 +126,11 @@ */ public void currentLinkCapacityEstimate(int indicationType, android.hardware.radio.network.LinkCapacityEstimate lce) { - mRil.processIndication(RIL.NETWORK_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_NETWORK, indicationType); List<LinkCapacityEstimate> response = RILUtils.convertHalLceData(lce); - if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_LCEDATA_RECV, response); + if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_LCEDATA_RECV, response); if (mRil.mLceInfoRegistrants != null) { mRil.mLceInfoRegistrants.notifyRegistrants(new AsyncResult(null, response, null)); @@ -140,27 +144,34 @@ */ public void currentPhysicalChannelConfigs(int indicationType, android.hardware.radio.network.PhysicalChannelConfig[] configs) { - mRil.processIndication(RIL.NETWORK_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_NETWORK, indicationType); List<PhysicalChannelConfig> response = new ArrayList<>(configs.length); try { for (android.hardware.radio.network.PhysicalChannelConfig config : configs) { PhysicalChannelConfig.Builder builder = new PhysicalChannelConfig.Builder(); + int band = PhysicalChannelConfig.BAND_UNKNOWN; switch (config.band.getTag()) { case android.hardware.radio.network.PhysicalChannelConfigBand.geranBand: - builder.setBand(config.band.getGeranBand()); + band = config.band.getGeranBand(); break; case android.hardware.radio.network.PhysicalChannelConfigBand.utranBand: - builder.setBand(config.band.getUtranBand()); + band = config.band.getUtranBand(); break; case android.hardware.radio.network.PhysicalChannelConfigBand.eutranBand: - builder.setBand(config.band.getEutranBand()); + band = config.band.getEutranBand(); break; case android.hardware.radio.network.PhysicalChannelConfigBand.ngranBand: - builder.setBand(config.band.getNgranBand()); + band = config.band.getNgranBand(); break; default: mRil.riljLoge("Unsupported band type " + config.band.getTag()); } + if (band == PhysicalChannelConfig.BAND_UNKNOWN) { + mRil.riljLoge("Unsupported unknown band."); + return; + } else { + builder.setBand(band); + } response.add(builder.setCellConnectionStatus( RILUtils.convertHalCellConnectionStatus(config.status)) .setDownlinkChannelNumber(config.downlinkChannelNumber) @@ -178,7 +189,7 @@ mRil.riljLoge("Invalid PhysicalChannelConfig " + iae); return; } - if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_PHYSICAL_CHANNEL_CONFIG, response); + if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_PHYSICAL_CHANNEL_CONFIG, response); mRil.mPhysicalChannelConfigurationRegistrants.notifyRegistrants( new AsyncResult(null, response, null)); @@ -191,13 +202,13 @@ */ public void currentSignalStrength(int indicationType, android.hardware.radio.network.SignalStrength signalStrength) { - mRil.processIndication(RIL.NETWORK_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_NETWORK, indicationType); SignalStrength ssInitial = RILUtils.convertHalSignalStrength(signalStrength); SignalStrength ss = mRil.fixupSignalStrength10(ssInitial); // Note this is set to "verbose" because it happens frequently - if (RIL.RILJ_LOGV) mRil.unsljLogvRet(RIL_UNSOL_SIGNAL_STRENGTH, ss); + if (mRil.isLogvOrTrace()) mRil.unsljLogvRet(RIL_UNSOL_SIGNAL_STRENGTH, ss); if (mRil.mSignalStrengthRegistrant != null) { mRil.mSignalStrengthRegistrant.notifyRegistrant(new AsyncResult(null, ss, null)); @@ -209,9 +220,9 @@ * @param indicationType Type of radio indication */ public void imsNetworkStateChanged(int indicationType) { - mRil.processIndication(RIL.NETWORK_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_NETWORK, indicationType); - if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_RESPONSE_IMS_NETWORK_STATE_CHANGED); + if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_RESPONSE_IMS_NETWORK_STATE_CHANGED); mRil.mImsNetworkStateChangedRegistrants.notifyRegistrants(); } @@ -223,11 +234,11 @@ */ public void networkScanResult(int indicationType, android.hardware.radio.network.NetworkScanResult result) { - mRil.processIndication(RIL.NETWORK_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_NETWORK, indicationType); ArrayList<CellInfo> cellInfos = RILUtils.convertHalCellInfoList(result.networkInfos); NetworkScanResult nsr = new NetworkScanResult(result.status, result.error, cellInfos); - if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_NETWORK_SCAN_RESULT, nsr); + if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_NETWORK_SCAN_RESULT, nsr); mRil.mRilNetworkScanResultRegistrants.notifyRegistrants(new AsyncResult(null, nsr, null)); } @@ -236,9 +247,9 @@ * @param indicationType Type of radio indication */ public void networkStateChanged(int indicationType) { - mRil.processIndication(RIL.NETWORK_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_NETWORK, indicationType); - if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_RESPONSE_NETWORK_STATE_CHANGED); + if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_RESPONSE_NETWORK_STATE_CHANGED); mRil.mNetworkStateRegistrants.notifyRegistrants(); } @@ -256,9 +267,9 @@ */ public void nitzTimeReceived(int indicationType, String nitzTime, @ElapsedRealtimeLong long receivedTimeMs, long ageMs) { - mRil.processIndication(RIL.NETWORK_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_NETWORK, indicationType); - if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_NITZ_TIME_RECEIVED, nitzTime); + if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_NITZ_TIME_RECEIVED, nitzTime); // Ignore the NITZ if receivedTimeMs or ageMs is not a valid time. // e.g. receivedTimeMs is non-positive, ageMs is negative or greater than receivedTimeMs. @@ -280,7 +291,7 @@ boolean ignoreNitz = TelephonyProperties.ignore_nitz().orElse(false); if (ignoreNitz) { - if (RIL.RILJ_LOGD) mRil.riljLog("ignoring UNSOL_NITZ_TIME_RECEIVED"); + if (mRil.isLogOrTrace()) mRil.riljLog("ignoring UNSOL_NITZ_TIME_RECEIVED"); } else { if (mRil.mNITZTimeRegistrant != null) { mRil.mNITZTimeRegistrant.notifyRegistrant(new AsyncResult(null, result, null)); @@ -303,23 +314,32 @@ public void registrationFailed(int indicationType, android.hardware.radio.network.CellIdentity cellIdentity, String chosenPlmn, @NetworkRegistrationInfo.Domain int domain, int causeCode, int additionalCauseCode) { - mRil.processIndication(RIL.NETWORK_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_NETWORK, indicationType); CellIdentity ci = RILUtils.convertHalCellIdentity(cellIdentity); if (ci == null || TextUtils.isEmpty(chosenPlmn) || (domain & NetworkRegistrationInfo.DOMAIN_CS_PS) == 0 || (domain & ~NetworkRegistrationInfo.DOMAIN_CS_PS) != 0 || causeCode < 0 || additionalCauseCode < 0 || (causeCode == Integer.MAX_VALUE && additionalCauseCode == Integer.MAX_VALUE)) { - reportAnomaly(UUID.fromString("f16e5703-6105-4341-9eb3-e68189156eb4"), + reportAnomaly(UUID.fromString("f16e5703-6105-4341-9eb3-e68189156eb5"), "Invalid registrationFailed indication"); - mRil.riljLoge("Invalid registrationFailed indication"); + mRil.riljLoge("Invalid registrationFailed indication (ci is null)=" + (ci == null) + + " (chosenPlmn is empty)=" + TextUtils.isEmpty(chosenPlmn) + + " (is CS/PS)=" + ((domain & NetworkRegistrationInfo.DOMAIN_CS_PS) == 0) + + " (only CS/PS)=" + ((domain & ~NetworkRegistrationInfo.DOMAIN_CS_PS) != 0) + + " (causeCode)=" + causeCode + + " (additionalCauseCode)=" + additionalCauseCode); return; } + RegistrationFailedEvent registrationFailedEvent = new RegistrationFailedEvent( + ci, chosenPlmn, domain, causeCode, additionalCauseCode); + if (mRil.isLogOrTrace()) { + mRil.unsljLogMore(RIL_UNSOL_REGISTRATION_FAILED, registrationFailedEvent.toString()); + } mRil.mRegistrationFailedRegistrant.notifyRegistrant( - new AsyncResult(null, new RegistrationFailedEvent( - ci, chosenPlmn, domain, causeCode, additionalCauseCode), null)); + new AsyncResult(null, registrationFailedEvent, null)); } /** @@ -328,9 +348,9 @@ * @param state Bitmask of restricted state as defined by PhoneRestrictedState */ public void restrictedStateChanged(int indicationType, int state) { - mRil.processIndication(RIL.NETWORK_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_NETWORK, indicationType); - if (RIL.RILJ_LOGD) mRil.unsljLogvRet(RIL_UNSOL_RESTRICTED_STATE_CHANGED, state); + if (mRil.isLogOrTrace()) mRil.unsljLogvRet(RIL_UNSOL_RESTRICTED_STATE_CHANGED, state); if (mRil.mRestrictedStateRegistrant != null) { mRil.mRestrictedStateRegistrant.notifyRegistrant(new AsyncResult(null, state, null)); @@ -344,7 +364,7 @@ */ public void suppSvcNotify(int indicationType, android.hardware.radio.network.SuppSvcNotification suppSvcNotification) { - mRil.processIndication(RIL.NETWORK_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_NETWORK, indicationType); SuppServiceNotification notification = new SuppServiceNotification(); notification.notificationType = suppSvcNotification.isMT ? 1 : 0; @@ -353,7 +373,9 @@ notification.type = suppSvcNotification.type; notification.number = suppSvcNotification.number; - if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_SUPP_SVC_NOTIFICATION, notification); + if (mRil.isLogOrTrace()) { + mRil.unsljLogRet(RIL_UNSOL_SUPP_SVC_NOTIFICATION, notification); + } if (mRil.mSsnRegistrant != null) { mRil.mSsnRegistrant.notifyRegistrant(new AsyncResult(null, notification, null)); @@ -366,16 +388,37 @@ * @param rat Current new voice rat */ public void voiceRadioTechChanged(int indicationType, int rat) { - mRil.processIndication(RIL.NETWORK_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_NETWORK, indicationType); int[] response = new int[] {rat}; - if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_VOICE_RADIO_TECH_CHANGED, response); + if (mRil.isLogOrTrace()) { + mRil.unsljLogRet(RIL_UNSOL_VOICE_RADIO_TECH_CHANGED, response); + } mRil.mVoiceRadioTechChangedRegistrants.notifyRegistrants( new AsyncResult(null, response, null)); } + /** + * Emergency Scan Results. + * @param indicationType Type of radio indication + * @param result the result of the Emergency Network Scan + */ + public void emergencyNetworkScanResult(int indicationType, + android.hardware.radio.network.EmergencyRegResult result) { + mRil.processIndication(HAL_SERVICE_NETWORK, indicationType); + + EmergencyRegResult response = RILUtils.convertHalEmergencyRegResult(result); + + if (mRil.isLogOrTrace()) { + mRil.unsljLogRet(RIL_UNSOL_EMERGENCY_NETWORK_SCAN_RESULT, response); + } + + mRil.mEmergencyNetworkScanRegistrants.notifyRegistrants( + new AsyncResult(null, response, null)); + } + @Override public String getInterfaceHash() { return IRadioNetworkIndication.HASH;
diff --git a/src/java/com/android/internal/telephony/NetworkRegistrationManager.java b/src/java/com/android/internal/telephony/NetworkRegistrationManager.java index 3535678..91cd4ec 100644 --- a/src/java/com/android/internal/telephony/NetworkRegistrationManager.java +++ b/src/java/com/android/internal/telephony/NetworkRegistrationManager.java
@@ -16,13 +16,10 @@ package com.android.internal.telephony; -import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.Intent; -import android.content.IntentFilter; import android.content.ServiceConnection; -import android.content.pm.PackageManager; import android.os.AsyncResult; import android.os.Handler; import android.os.IBinder; @@ -30,7 +27,6 @@ import android.os.PersistableBundle; import android.os.RegistrantList; import android.os.RemoteException; -import android.os.UserHandle; import android.telephony.AccessNetworkConstants; import android.telephony.AccessNetworkConstants.TransportType; import android.telephony.CarrierConfigManager; @@ -58,9 +54,6 @@ private final int mTransportType; private final Phone mPhone; - - private final CarrierConfigManager mCarrierConfigManager; - // Registrants who listens registration state change callback from this class. private final RegistrantList mRegStateChangeRegistrants = new RegistrantList(); @@ -72,22 +65,6 @@ private NetworkServiceConnection mServiceConnection; - private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - final String action = intent.getAction(); - if (CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED.equals(action) - && mPhone.getPhoneId() == intent.getIntExtra( - CarrierConfigManager.EXTRA_SLOT_INDEX, 0)) { - // We should wait for carrier config changed event because the target binding - // package name can come from the carrier config. Note that we still get this event - // even when SIM is absent. - logd("Carrier config changed. Try to bind network service."); - sendEmptyMessage(EVENT_BIND_NETWORK_SERVICE); - } - } - }; - public NetworkRegistrationManager(@TransportType int transportType, Phone phone) { mTransportType = transportType; mPhone = phone; @@ -96,19 +73,20 @@ ? "C" : "I") + "-" + mPhone.getPhoneId(); mTag = "NRM" + tagSuffix; - mCarrierConfigManager = (CarrierConfigManager) phone.getContext().getSystemService( - Context.CARRIER_CONFIG_SERVICE); + CarrierConfigManager ccm = phone.getContext().getSystemService(CarrierConfigManager.class); + // Callback directly calls rebindService and should be executed in handler thread + ccm.registerCarrierConfigChangeListener( + this::post, + (slotIndex, subId, carrierId, specificCarrierId) -> { + if (slotIndex == phone.getPhoneId()) { + // We should wait for carrier config changed event because the target + // binding package name can come from the carrier config. Note that + // we still get this event even when SIM is absent. + logd("Carrier config changed. Try to bind network service."); + rebindService(); + } + }); - IntentFilter intentFilter = new IntentFilter(); - intentFilter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED); - try { - Context contextAsUser = phone.getContext().createPackageContextAsUser( - phone.getContext().getPackageName(), 0, UserHandle.ALL); - contextAsUser.registerReceiver(mBroadcastReceiver, intentFilter, - null /* broadcastPermission */, null); - } catch (PackageManager.NameNotFoundException e) { - loge("Package name not found: " + e.getMessage()); - } PhoneConfigurationManager.registerForMultiSimConfigChange( this, EVENT_BIND_NETWORK_SERVICE, null); @@ -333,9 +311,10 @@ // Read package name from resource overlay packageName = mPhone.getContext().getResources().getString(resourceId); - PersistableBundle b = mCarrierConfigManager.getConfigForSubId(mPhone.getSubId()); - - if (b != null && !TextUtils.isEmpty(b.getString(carrierConfig))) { + PersistableBundle b = + CarrierConfigManager.getCarrierConfigSubset( + mPhone.getContext(), mPhone.getSubId(), carrierConfig); + if (!b.isEmpty() && !TextUtils.isEmpty(b.getString(carrierConfig))) { // If carrier config overrides it, use the one from carrier config packageName = b.getString(carrierConfig, packageName); } @@ -367,15 +346,17 @@ // Read class name from resource overlay className = mPhone.getContext().getResources().getString(resourceId); - PersistableBundle b = mCarrierConfigManager.getConfigForSubId(mPhone.getSubId()); - - if (b != null && !TextUtils.isEmpty(b.getString(carrierConfig))) { + PersistableBundle b = + CarrierConfigManager.getCarrierConfigSubset( + mPhone.getContext(), mPhone.getSubId(), carrierConfig); + if (!b.isEmpty() && !TextUtils.isEmpty(b.getString(carrierConfig))) { // If carrier config overrides it, use the one from carrier config className = b.getString(carrierConfig, className); } return className; } + private void logd(String msg) { Rlog.d(mTag, msg); }
diff --git a/src/java/com/android/internal/telephony/NetworkResponse.java b/src/java/com/android/internal/telephony/NetworkResponse.java index d9f70fd..b1eb926 100644 --- a/src/java/com/android/internal/telephony/NetworkResponse.java +++ b/src/java/com/android/internal/telephony/NetworkResponse.java
@@ -16,12 +16,15 @@ package com.android.internal.telephony; +import static android.telephony.TelephonyManager.HAL_SERVICE_NETWORK; + import android.hardware.radio.RadioError; import android.hardware.radio.RadioResponseInfo; import android.hardware.radio.network.IRadioNetworkResponse; import android.os.AsyncResult; import android.telephony.BarringInfo; import android.telephony.CellInfo; +import android.telephony.EmergencyRegResult; import android.telephony.LinkCapacityEstimate; import android.telephony.RadioAccessSpecifier; import android.telephony.SignalStrength; @@ -57,7 +60,7 @@ int halRadioAccessFamilyBitmap) { int networkTypeBitmask = RILUtils.convertHalNetworkTypeBitMask(halRadioAccessFamilyBitmap); mRil.mAllowedNetworkTypesBitmask = networkTypeBitmask; - RadioResponse.responseInts(RIL.NETWORK_SERVICE, mRil, responseInfo, networkTypeBitmask); + RadioResponse.responseInts(HAL_SERVICE_NETWORK, mRil, responseInfo, networkTypeBitmask); } /** @@ -65,7 +68,7 @@ * @param bandModes List of RadioBandMode listing supported modes */ public void getAvailableBandModesResponse(RadioResponseInfo responseInfo, int[] bandModes) { - RadioResponse.responseIntArrayList(RIL.NETWORK_SERVICE, mRil, responseInfo, + RadioResponse.responseIntArrayList(HAL_SERVICE_NETWORK, mRil, responseInfo, RILUtils.primitiveArrayToArrayList(bandModes)); } @@ -75,7 +78,7 @@ */ public void getAvailableNetworksResponse(RadioResponseInfo responseInfo, android.hardware.radio.network.OperatorInfo[] networkInfos) { - RILRequest rr = mRil.processResponse(RIL.NETWORK_SERVICE, responseInfo); + RILRequest rr = mRil.processResponse(HAL_SERVICE_NETWORK, responseInfo); if (rr != null) { ArrayList<OperatorInfo> ret = new ArrayList<>(); @@ -98,7 +101,7 @@ public void getBarringInfoResponse(RadioResponseInfo responseInfo, android.hardware.radio.network.CellIdentity cellIdentity, android.hardware.radio.network.BarringInfo[] barringInfos) { - RILRequest rr = mRil.processResponse(RIL.NETWORK_SERVICE, responseInfo); + RILRequest rr = mRil.processResponse(HAL_SERVICE_NETWORK, responseInfo); if (rr != null) { BarringInfo bi = new BarringInfo(RILUtils.convertHalCellIdentity(cellIdentity), @@ -118,7 +121,7 @@ * @param type CdmaRoamingType defined in types.hal */ public void getCdmaRoamingPreferenceResponse(RadioResponseInfo responseInfo, int type) { - RadioResponse.responseInts(RIL.NETWORK_SERVICE, mRil, responseInfo, type); + RadioResponse.responseInts(HAL_SERVICE_NETWORK, mRil, responseInfo, type); } /** @@ -127,7 +130,7 @@ */ public void getCellInfoListResponse(RadioResponseInfo responseInfo, android.hardware.radio.network.CellInfo[] cellInfo) { - RILRequest rr = mRil.processResponse(RIL.NETWORK_SERVICE, responseInfo); + RILRequest rr = mRil.processResponse(HAL_SERVICE_NETWORK, responseInfo); if (rr != null) { ArrayList<CellInfo> ret = RILUtils.convertHalCellInfoList(cellInfo); @@ -144,7 +147,7 @@ */ public void getDataRegistrationStateResponse(RadioResponseInfo responseInfo, android.hardware.radio.network.RegStateResult dataRegResponse) { - RILRequest rr = mRil.processResponse(RIL.NETWORK_SERVICE, responseInfo); + RILRequest rr = mRil.processResponse(HAL_SERVICE_NETWORK, responseInfo); if (rr != null) { if (responseInfo.error == RadioError.NONE) { @@ -161,7 +164,7 @@ */ public void getImsRegistrationStateResponse(RadioResponseInfo responseInfo, boolean isRegistered, int ratFamily) { - RadioResponse.responseInts(RIL.NETWORK_SERVICE, mRil, responseInfo, isRegistered ? 1 : 0, + RadioResponse.responseInts(HAL_SERVICE_NETWORK, mRil, responseInfo, isRegistered ? 1 : 0, ratFamily == android.hardware.radio.RadioTechnologyFamily.THREE_GPP ? PhoneConstants.PHONE_TYPE_GSM : PhoneConstants.PHONE_TYPE_CDMA); } @@ -171,7 +174,7 @@ * @param selection false for automatic selection, true for manual selection */ public void getNetworkSelectionModeResponse(RadioResponseInfo responseInfo, boolean selection) { - RadioResponse.responseInts(RIL.NETWORK_SERVICE, mRil, responseInfo, selection ? 1 : 0); + RadioResponse.responseInts(HAL_SERVICE_NETWORK, mRil, responseInfo, selection ? 1 : 0); } /** @@ -183,7 +186,7 @@ public void getOperatorResponse(RadioResponseInfo responseInfo, String longName, String shortName, String numeric) { RadioResponse.responseStrings( - RIL.NETWORK_SERVICE, mRil, responseInfo, longName, shortName, numeric); + HAL_SERVICE_NETWORK, mRil, responseInfo, longName, shortName, numeric); } /** @@ -192,7 +195,7 @@ */ public void getSignalStrengthResponse(RadioResponseInfo responseInfo, android.hardware.radio.network.SignalStrength signalStrength) { - RILRequest rr = mRil.processResponse(RIL.NETWORK_SERVICE, responseInfo); + RILRequest rr = mRil.processResponse(HAL_SERVICE_NETWORK, responseInfo); if (rr != null) { SignalStrength ret = RILUtils.convertHalSignalStrength(signalStrength); @@ -209,7 +212,7 @@ */ public void getSystemSelectionChannelsResponse(RadioResponseInfo responseInfo, android.hardware.radio.network.RadioAccessSpecifier[] halSpecifiers) { - RILRequest rr = mRil.processResponse(RIL.NETWORK_SERVICE, responseInfo); + RILRequest rr = mRil.processResponse(HAL_SERVICE_NETWORK, responseInfo); if (rr != null) { ArrayList<RadioAccessSpecifier> specifiers = new ArrayList<>(); @@ -229,7 +232,7 @@ * @param rat Current voice RAT */ public void getVoiceRadioTechnologyResponse(RadioResponseInfo responseInfo, int rat) { - RadioResponse.responseInts(RIL.NETWORK_SERVICE, mRil, responseInfo, rat); + RadioResponse.responseInts(HAL_SERVICE_NETWORK, mRil, responseInfo, rat); } /** @@ -238,7 +241,7 @@ */ public void getVoiceRegistrationStateResponse(RadioResponseInfo responseInfo, android.hardware.radio.network.RegStateResult voiceRegResponse) { - RILRequest rr = mRil.processResponse(RIL.NETWORK_SERVICE, responseInfo); + RILRequest rr = mRil.processResponse(HAL_SERVICE_NETWORK, responseInfo); if (rr != null) { if (responseInfo.error == RadioError.NONE) { RadioResponse.sendMessageResponse(rr.mResult, voiceRegResponse); @@ -254,7 +257,7 @@ */ public void isNrDualConnectivityEnabledResponse(RadioResponseInfo responseInfo, boolean isEnabled) { - RILRequest rr = mRil.processResponse(RIL.NETWORK_SERVICE, responseInfo); + RILRequest rr = mRil.processResponse(HAL_SERVICE_NETWORK, responseInfo); if (rr != null) { if (responseInfo.error == RadioError.NONE) { @@ -270,7 +273,7 @@ */ public void pullLceDataResponse(RadioResponseInfo responseInfo, android.hardware.radio.network.LceDataInfo lceInfo) { - RILRequest rr = mRil.processResponse(RIL.NETWORK_SERVICE, responseInfo); + RILRequest rr = mRil.processResponse(HAL_SERVICE_NETWORK, responseInfo); if (rr != null) { List<LinkCapacityEstimate> ret = RILUtils.convertHalLceData(lceInfo); @@ -285,105 +288,105 @@ * @param responseInfo Response info struct containing response type, serial no. and error */ public void setAllowedNetworkTypesBitmapResponse(RadioResponseInfo responseInfo) { - RadioResponse.responseVoid(RIL.NETWORK_SERVICE, mRil, responseInfo); + RadioResponse.responseVoid(HAL_SERVICE_NETWORK, mRil, responseInfo); } /** * @param responseInfo Response info struct containing response type, serial no. and error */ public void setBandModeResponse(RadioResponseInfo responseInfo) { - RadioResponse.responseVoid(RIL.NETWORK_SERVICE, mRil, responseInfo); + RadioResponse.responseVoid(HAL_SERVICE_NETWORK, mRil, responseInfo); } /** * @param responseInfo Response info struct containing response type, serial no. and error */ public void setBarringPasswordResponse(RadioResponseInfo responseInfo) { - RadioResponse.responseVoid(RIL.NETWORK_SERVICE, mRil, responseInfo); + RadioResponse.responseVoid(HAL_SERVICE_NETWORK, mRil, responseInfo); } /** * @param responseInfo Response info struct containing response type, serial no. and error */ public void setCdmaRoamingPreferenceResponse(RadioResponseInfo responseInfo) { - RadioResponse.responseVoid(RIL.NETWORK_SERVICE, mRil, responseInfo); + RadioResponse.responseVoid(HAL_SERVICE_NETWORK, mRil, responseInfo); } /** * @param responseInfo Response info struct containing response type, serial no. and error */ public void setCellInfoListRateResponse(RadioResponseInfo responseInfo) { - RadioResponse.responseVoid(RIL.NETWORK_SERVICE, mRil, responseInfo); + RadioResponse.responseVoid(HAL_SERVICE_NETWORK, mRil, responseInfo); } /** * @param responseInfo Response info struct containing response type, serial no. and error */ public void setIndicationFilterResponse(RadioResponseInfo responseInfo) { - RadioResponse.responseVoid(RIL.NETWORK_SERVICE, mRil, responseInfo); + RadioResponse.responseVoid(HAL_SERVICE_NETWORK, mRil, responseInfo); } /** * @param responseInfo Response info struct containing response type, serial no. and error */ public void setLinkCapacityReportingCriteriaResponse(RadioResponseInfo responseInfo) { - RadioResponse.responseVoid(RIL.NETWORK_SERVICE, mRil, responseInfo); + RadioResponse.responseVoid(HAL_SERVICE_NETWORK, mRil, responseInfo); } /** * @param responseInfo Response info struct containing response type, serial no. and error */ public void setLocationUpdatesResponse(RadioResponseInfo responseInfo) { - RadioResponse.responseVoid(RIL.NETWORK_SERVICE, mRil, responseInfo); + RadioResponse.responseVoid(HAL_SERVICE_NETWORK, mRil, responseInfo); } /** * @param responseInfo Response info struct containing response type, serial no. and error */ public void setNetworkSelectionModeAutomaticResponse(RadioResponseInfo responseInfo) { - RadioResponse.responseVoid(RIL.NETWORK_SERVICE, mRil, responseInfo); + RadioResponse.responseVoid(HAL_SERVICE_NETWORK, mRil, responseInfo); } /** * @param responseInfo Response info struct containing response type, serial no. and error */ public void setNetworkSelectionModeManualResponse(RadioResponseInfo responseInfo) { - RadioResponse.responseVoid(RIL.NETWORK_SERVICE, mRil, responseInfo); + RadioResponse.responseVoid(HAL_SERVICE_NETWORK, mRil, responseInfo); } /** * @param responseInfo Response info struct containing response type, serial no. and error */ public void setNrDualConnectivityStateResponse(RadioResponseInfo responseInfo) { - RadioResponse.responseVoid(RIL.NETWORK_SERVICE, mRil, responseInfo); + RadioResponse.responseVoid(HAL_SERVICE_NETWORK, mRil, responseInfo); } /** * @param responseInfo Response info struct containing response type, serial no. and error */ public void setSignalStrengthReportingCriteriaResponse(RadioResponseInfo responseInfo) { - RadioResponse.responseVoid(RIL.NETWORK_SERVICE, mRil, responseInfo); + RadioResponse.responseVoid(HAL_SERVICE_NETWORK, mRil, responseInfo); } /** * @param responseInfo Response info struct containing response type, serial no. and error */ public void setSuppServiceNotificationsResponse(RadioResponseInfo responseInfo) { - RadioResponse.responseVoid(RIL.NETWORK_SERVICE, mRil, responseInfo); + RadioResponse.responseVoid(HAL_SERVICE_NETWORK, mRil, responseInfo); } /** * @param responseInfo Response info struct containing response type, serial number and error. */ public void setSystemSelectionChannelsResponse(RadioResponseInfo responseInfo) { - RadioResponse.responseVoid(RIL.NETWORK_SERVICE, mRil, responseInfo); + RadioResponse.responseVoid(HAL_SERVICE_NETWORK, mRil, responseInfo); } /** * @param responseInfo Response info struct containing response type, serial no. and error */ public void startNetworkScanResponse(RadioResponseInfo responseInfo) { - RILRequest rr = mRil.processResponse(RIL.NETWORK_SERVICE, responseInfo); + RILRequest rr = mRil.processResponse(HAL_SERVICE_NETWORK, responseInfo); if (rr != null) { NetworkScanResult nsr = null; if (responseInfo.error == RadioError.NONE) { @@ -399,7 +402,7 @@ * @param responseInfo Response info struct containing response type, serial no. and error */ public void stopNetworkScanResponse(RadioResponseInfo responseInfo) { - RILRequest rr = mRil.processResponse(RIL.NETWORK_SERVICE, responseInfo); + RILRequest rr = mRil.processResponse(HAL_SERVICE_NETWORK, responseInfo); if (rr != null) { NetworkScanResult nsr = null; if (responseInfo.error == RadioError.NONE) { @@ -417,14 +420,14 @@ */ public void supplyNetworkDepersonalizationResponse(RadioResponseInfo responseInfo, int retriesRemaining) { - RadioResponse.responseInts(RIL.NETWORK_SERVICE, mRil, responseInfo, retriesRemaining); + RadioResponse.responseInts(HAL_SERVICE_NETWORK, mRil, responseInfo, retriesRemaining); } /** * @param responseInfo Response info struct containing response type, serial no. and error */ public void setUsageSettingResponse(RadioResponseInfo responseInfo) { - RadioResponse.responseVoid(RIL.NETWORK_SERVICE, mRil, responseInfo); + RadioResponse.responseVoid(HAL_SERVICE_NETWORK, mRil, responseInfo); } /** @@ -433,7 +436,91 @@ */ public void getUsageSettingResponse(RadioResponseInfo responseInfo, /* @TelephonyManager.UsageSetting */ int usageSetting) { - RadioResponse.responseInts(RIL.NETWORK_SERVICE, mRil, responseInfo, usageSetting); + RadioResponse.responseInts(HAL_SERVICE_NETWORK, mRil, responseInfo, usageSetting); + } + + /** + * @param responseInfo Response info struct containing response type, serial no. and error + * @param regState the current registration state of the modem. + */ + public void setEmergencyModeResponse(RadioResponseInfo responseInfo, + android.hardware.radio.network.EmergencyRegResult regState) { + RILRequest rr = mRil.processResponse(HAL_SERVICE_NETWORK, responseInfo); + + if (rr != null) { + EmergencyRegResult response = RILUtils.convertHalEmergencyRegResult(regState); + if (responseInfo.error == RadioError.NONE) { + RadioResponse.sendMessageResponse(rr.mResult, response); + } + mRil.processResponseDone(rr, responseInfo, response); + } + } + + /** + * @param responseInfo Response info struct containing response type, serial no. and error + */ + public void triggerEmergencyNetworkScanResponse(RadioResponseInfo responseInfo) { + RadioResponse.responseVoid(HAL_SERVICE_NETWORK, mRil, responseInfo); + } + + /** + * @param responseInfo Response info struct containing response type, serial no. and error + */ + public void exitEmergencyModeResponse(RadioResponseInfo responseInfo) { + RadioResponse.responseVoid(HAL_SERVICE_NETWORK, mRil, responseInfo); + } + + /** + * @param responseInfo Response info struct containing response type, serial no. and error + */ + public void cancelEmergencyNetworkScanResponse(RadioResponseInfo responseInfo) { + RadioResponse.responseVoid(HAL_SERVICE_NETWORK, mRil, responseInfo); + } + + /** + * @param responseInfo Response info struct containing response type, serial no. and error + */ + public void setNullCipherAndIntegrityEnabledResponse(RadioResponseInfo responseInfo) { + RadioResponse.responseVoid(HAL_SERVICE_NETWORK, mRil, responseInfo); + } + + /** + * @param responseInfo Response info struct containing response type, serial no. and error. + * @param isEnabled Indicates whether null cipher and integrity is enabled, indicating + * potentially unencrypted communication + */ + public void isNullCipherAndIntegrityEnabledResponse(RadioResponseInfo responseInfo, + boolean isEnabled) { + RILRequest rr = mRil.processResponse(HAL_SERVICE_NETWORK, responseInfo); + + if (rr != null) { + if (responseInfo.error == RadioError.NONE) { + RadioResponse.sendMessageResponse(rr.mResult, isEnabled); + } + mRil.processResponseDone(rr, responseInfo, isEnabled); + } + } + + /** + * @param responseInfo Response info struct containing response type, serial no. and error. + * @param isEnabled Indicates whether N1 mode is enabled or not. + */ + public void isN1ModeEnabledResponse(RadioResponseInfo responseInfo, boolean isEnabled) { + RILRequest rr = mRil.processResponse(HAL_SERVICE_NETWORK, responseInfo); + + if (rr != null) { + if (responseInfo.error == RadioError.NONE) { + RadioResponse.sendMessageResponse(rr.mResult, isEnabled); + } + mRil.processResponseDone(rr, responseInfo, isEnabled); + } + } + + /** + * @param responseInfo Response info struct containing response type, serial no. and error. + */ + public void setN1ModeEnabledResponse(RadioResponseInfo responseInfo) { + RadioResponse.responseVoid(HAL_SERVICE_NETWORK, mRil, responseInfo); } @Override @@ -445,4 +532,5 @@ public int getInterfaceVersion() { return IRadioNetworkResponse.VERSION; } + }
diff --git a/src/java/com/android/internal/telephony/NetworkScanRequestTracker.java b/src/java/com/android/internal/telephony/NetworkScanRequestTracker.java index ea68cba..7567566 100644 --- a/src/java/com/android/internal/telephony/NetworkScanRequestTracker.java +++ b/src/java/com/android/internal/telephony/NetworkScanRequestTracker.java
@@ -42,6 +42,8 @@ import android.telephony.TelephonyScanManager; import android.util.Log; +import com.android.internal.telephony.subscription.SubscriptionManagerService; + import java.util.Collection; import java.util.List; import java.util.Set; @@ -195,7 +197,7 @@ public static Set<String> getAllowedMccMncsForLocationRestrictedScan(Context context) { final long token = Binder.clearCallingIdentity(); try { - return SubscriptionController.getInstance() + return SubscriptionManagerService.getInstance() .getAvailableSubscriptionInfoList(context.getOpPackageName(), context.getAttributionTag()).stream() .flatMap(NetworkScanRequestTracker::getAllowableMccMncsFromSubscriptionInfo) @@ -472,21 +474,21 @@ notifyMessenger(nsri, notifyMsg, rilErrorToScanError(nsr.scanError), nsr.networkInfos); if (nsr.scanStatus == NetworkScanResult.SCAN_STATUS_COMPLETE) { - deleteScanAndMayNotify(nsri, NetworkScan.SUCCESS, true); nsri.mPhone.mCi.unregisterForNetworkScanResult(mHandler); + deleteScanAndMayNotify(nsri, NetworkScan.SUCCESS, true); } } else { if (nsr.networkInfos != null) { notifyMessenger(nsri, notifyMsg, rilErrorToScanError(nsr.scanError), nsr.networkInfos); } - deleteScanAndMayNotify(nsri, rilErrorToScanError(nsr.scanError), true); nsri.mPhone.mCi.unregisterForNetworkScanResult(mHandler); + deleteScanAndMayNotify(nsri, rilErrorToScanError(nsr.scanError), true); } } else { logEmptyResultOrException(ar); - deleteScanAndMayNotify(nsri, NetworkScan.ERROR_RADIO_INTERFACE_ERROR, true); nsri.mPhone.mCi.unregisterForNetworkScanResult(mHandler); + deleteScanAndMayNotify(nsri, NetworkScan.ERROR_RADIO_INTERFACE_ERROR, true); } } @@ -514,6 +516,7 @@ Log.e(TAG, "EVENT_STOP_NETWORK_SCAN_DONE: nsri is null"); return; } + nsri.mPhone.mCi.unregisterForNetworkScanResult(mHandler); if (ar.exception == null && ar.result != null) { deleteScanAndMayNotify(nsri, NetworkScan.SUCCESS, true); } else { @@ -526,7 +529,6 @@ Log.wtf(TAG, "EVENT_STOP_NETWORK_SCAN_DONE: ar.exception can not be null!"); } } - nsri.mPhone.mCi.unregisterForNetworkScanResult(mHandler); } // Interrupts the live scan is the scanId matches the mScanId of the mLiveRequestInfo.
diff --git a/src/java/com/android/internal/telephony/NetworkTypeController.java b/src/java/com/android/internal/telephony/NetworkTypeController.java index 2260d34..beebf22 100644 --- a/src/java/com/android/internal/telephony/NetworkTypeController.java +++ b/src/java/com/android/internal/telephony/NetworkTypeController.java
@@ -16,6 +16,8 @@ package com.android.internal.telephony; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -28,20 +30,16 @@ import android.telephony.Annotation; import android.telephony.CarrierConfigManager; import android.telephony.NetworkRegistrationInfo; -import android.telephony.PcoData; import android.telephony.PhysicalChannelConfig; import android.telephony.ServiceState; -import android.telephony.SubscriptionManager; import android.telephony.TelephonyDisplayInfo; import android.telephony.TelephonyManager; -import android.telephony.data.ApnSetting; import android.telephony.data.DataCallResponse; import android.telephony.data.DataCallResponse.LinkStatus; import android.text.TextUtils; import com.android.internal.telephony.data.DataNetworkController.DataNetworkControllerCallback; -import com.android.internal.telephony.dataconnection.DataConnection; -import com.android.internal.telephony.dataconnection.DcTracker; +import com.android.internal.telephony.data.DataUtils; import com.android.internal.telephony.util.ArrayUtils; import com.android.internal.util.IState; import com.android.internal.util.IndentingPrintWriter; @@ -55,6 +53,7 @@ import java.util.Arrays; import java.util.HashMap; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -86,31 +85,35 @@ /** Stop all timers and go to current state. */ public static final int EVENT_UPDATE = 0; /** Quit after processing all existing messages. */ - public static final int EVENT_QUIT = 1; - private static final int EVENT_DATA_RAT_CHANGED = 2; - private static final int EVENT_NR_STATE_CHANGED = 3; - private static final int EVENT_NR_FREQUENCY_CHANGED = 4; - private static final int EVENT_PHYSICAL_LINK_STATUS_CHANGED = 5; - private static final int EVENT_PHYSICAL_CHANNEL_CONFIG_NOTIF_CHANGED = 6; - private static final int EVENT_CARRIER_CONFIG_CHANGED = 7; - private static final int EVENT_PRIMARY_TIMER_EXPIRED = 8; - private static final int EVENT_SECONDARY_TIMER_EXPIRED = 9; - private static final int EVENT_RADIO_OFF_OR_UNAVAILABLE = 10; - private static final int EVENT_PREFERRED_NETWORK_MODE_CHANGED = 11; - private static final int EVENT_INITIALIZE = 12; - private static final int EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED = 13; - private static final int EVENT_PCO_DATA_CHANGED = 14; - private static final int EVENT_BANDWIDTH_CHANGED = 15; - private static final int EVENT_UPDATE_NR_ADVANCED_STATE = 16; - private static final int EVENT_DEVICE_IDLE_MODE_CHANGED = 17; + private static final int EVENT_QUIT = 1; + /** Initialize all events. */ + private static final int EVENT_INITIALIZE = 2; + /** Event for service state changed (data rat, bandwidth, NR state, NR frequency, etc). */ + private static final int EVENT_SERVICE_STATE_CHANGED = 3; + /** Event for physical link status changed. */ + private static final int EVENT_PHYSICAL_LINK_STATUS_CHANGED = 4; + /** Event for physical channel config indications turned on/off. */ + private static final int EVENT_PHYSICAL_CHANNEL_CONFIG_NOTIF_CHANGED = 5; + /** Event for carrier configs changed. */ + private static final int EVENT_CARRIER_CONFIG_CHANGED = 6; + /** Event for primary timer expired. If a secondary timer exists, it will begin afterwards. */ + private static final int EVENT_PRIMARY_TIMER_EXPIRED = 7; + /** Event for secondary timer expired. */ + private static final int EVENT_SECONDARY_TIMER_EXPIRED = 8; + /** Event for radio off or unavailable. */ + private static final int EVENT_RADIO_OFF_OR_UNAVAILABLE = 9; + /** Event for preferred network mode changed. */ + private static final int EVENT_PREFERRED_NETWORK_MODE_CHANGED = 10; + /** Event for physical channel configs changed. */ + private static final int EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED = 11; + /** Event for device idle mode changed, when device goes to deep sleep and pauses all timers. */ + private static final int EVENT_DEVICE_IDLE_MODE_CHANGED = 12; private static final String[] sEvents = new String[EVENT_DEVICE_IDLE_MODE_CHANGED + 1]; static { sEvents[EVENT_UPDATE] = "EVENT_UPDATE"; sEvents[EVENT_QUIT] = "EVENT_QUIT"; - sEvents[EVENT_DATA_RAT_CHANGED] = "EVENT_DATA_RAT_CHANGED"; - sEvents[EVENT_NR_STATE_CHANGED] = "EVENT_NR_STATE_CHANGED"; - sEvents[EVENT_NR_FREQUENCY_CHANGED] = "EVENT_NR_FREQUENCY_CHANGED"; + sEvents[EVENT_SERVICE_STATE_CHANGED] = "EVENT_SERVICE_STATE_CHANGED"; sEvents[EVENT_PHYSICAL_LINK_STATUS_CHANGED] = "EVENT_PHYSICAL_LINK_STATUS_CHANGED"; sEvents[EVENT_PHYSICAL_CHANNEL_CONFIG_NOTIF_CHANGED] = "EVENT_PHYSICAL_CHANNEL_CONFIG_NOTIF_CHANGED"; @@ -121,26 +124,15 @@ sEvents[EVENT_PREFERRED_NETWORK_MODE_CHANGED] = "EVENT_PREFERRED_NETWORK_MODE_CHANGED"; sEvents[EVENT_INITIALIZE] = "EVENT_INITIALIZE"; sEvents[EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED] = "EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED"; - sEvents[EVENT_PCO_DATA_CHANGED] = "EVENT_PCO_DATA_CHANGED"; - sEvents[EVENT_BANDWIDTH_CHANGED] = "EVENT_BANDWIDTH_CHANGED"; - sEvents[EVENT_UPDATE_NR_ADVANCED_STATE] = "EVENT_UPDATE_NR_ADVANCED_STATE"; sEvents[EVENT_DEVICE_IDLE_MODE_CHANGED] = "EVENT_DEVICE_IDLE_MODE_CHANGED"; } - private final Phone mPhone; - private final DisplayInfoController mDisplayInfoController; - private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { + private final @NonNull Phone mPhone; + private final @NonNull DisplayInfoController mDisplayInfoController; + private final @NonNull BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { switch (intent.getAction()) { - case CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED: - if (intent.getIntExtra(SubscriptionManager.EXTRA_SLOT_INDEX, - SubscriptionManager.INVALID_PHONE_INDEX) == mPhone.getPhoneId() - && !intent.getBooleanExtra( - CarrierConfigManager.EXTRA_REBROADCAST_ON_UNLOCK, false)) { - sendMessage(EVENT_CARRIER_CONFIG_CHANGED); - } - break; case PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED: sendMessage(EVENT_DEVICE_IDLE_MODE_CHANGED); break; @@ -148,19 +140,33 @@ } }; - private Map<String, OverrideTimerRule> mOverrideTimerRules = new HashMap<>(); - private String mLteEnhancedPattern = ""; - private int mOverrideNetworkType; + private final @NonNull CarrierConfigManager.CarrierConfigChangeListener + mCarrierConfigChangeListener = + new CarrierConfigManager.CarrierConfigChangeListener() { + @Override + public void onCarrierConfigChanged(int slotIndex, int subId, int carrierId, + int specificCarrierId) { + // CarrierConfigChangeListener wouldn't send notification on device unlock + if (slotIndex == mPhone.getPhoneId()) { + sendMessage(EVENT_CARRIER_CONFIG_CHANGED); + } + } + }; + + private @NonNull Map<String, OverrideTimerRule> mOverrideTimerRules = new HashMap<>(); + private @NonNull String mLteEnhancedPattern = ""; + private @Annotation.OverrideNetworkType int mOverrideNetworkType; private boolean mIsPhysicalChannelConfigOn; private boolean mIsPrimaryTimerActive; private boolean mIsSecondaryTimerActive; - private boolean mIsTimerResetEnabledForLegacyStateRRCIdle; + private boolean mIsTimerResetEnabledForLegacyStateRrcIdle; private int mLtePlusThresholdBandwidth; private int mNrAdvancedThresholdBandwidth; - private int[] mAdditionalNrAdvancedBandsList; - private String mPrimaryTimerState; - private String mSecondaryTimerState; - private String mPreviousState; + private boolean mIncludeLteForNrAdvancedThresholdBandwidth; + private @NonNull int[] mAdditionalNrAdvancedBandsList; + private @NonNull String mPrimaryTimerState; + private @NonNull String mSecondaryTimerState; + private @NonNull String mPreviousState; private @LinkStatus int mPhysicalLinkStatus; private boolean mIsPhysicalChannelConfig16Supported; private boolean mIsNrAdvancedAllowedByPco = false; @@ -169,6 +175,13 @@ private boolean mEnableNrAdvancedWhileRoaming = true; private boolean mIsDeviceIdleMode = false; + private @Nullable DataNetworkControllerCallback mNrAdvancedCapableByPcoChangedCallback = null; + private @Nullable DataNetworkControllerCallback mNrPhysicalLinkStatusChangedCallback = null; + + // Cached copies below to prevent race conditions + private @NonNull ServiceState mServiceState; + private @Nullable List<PhysicalChannelConfig> mPhysicalChannelConfigs; + /** * NetworkTypeController constructor. * @@ -181,13 +194,22 @@ mDisplayInfoController = displayInfoController; mOverrideNetworkType = TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE; mIsPhysicalChannelConfigOn = true; - addState(mDefaultState); - addState(mLegacyState, mDefaultState); - addState(mIdleState, mDefaultState); - addState(mLteConnectedState, mDefaultState); - addState(mNrConnectedState, mDefaultState); - setInitialState(mDefaultState); + mPrimaryTimerState = ""; + mSecondaryTimerState = ""; + mPreviousState = ""; + DefaultState defaultState = new DefaultState(); + addState(defaultState); + addState(mLegacyState, defaultState); + addState(mIdleState, defaultState); + addState(mLteConnectedState, defaultState); + addState(mNrConnectedState, defaultState); + addState(mNrConnectedAdvancedState, defaultState); + setInitialState(defaultState); start(); + + mServiceState = mPhone.getServiceStateTracker().getServiceState(); + mPhysicalChannelConfigs = mPhone.getServiceStateTracker().getPhysicalChannelConfigList(); + sendMessage(EVENT_INITIALIZE); } @@ -200,10 +222,21 @@ } /** - * @return True if either the primary or secondary 5G hysteresis timer is active, - * and false if neither are. + * @return The current data network type, used to create TelephonyDisplayInfo in + * DisplayInfoController. */ - public boolean is5GHysteresisActive() { + public @Annotation.NetworkType int getDataNetworkType() { + NetworkRegistrationInfo nri = mServiceState.getNetworkRegistrationInfo( + NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN); + return nri == null ? TelephonyManager.NETWORK_TYPE_UNKNOWN + : nri.getAccessNetworkTechnology(); + } + + /** + * @return {@code true} if either the primary or secondary 5G icon timers are active, + * and {@code false} if neither are. + */ + public boolean areAnyTimersActive() { return mIsPrimaryTimerActive || mIsSecondaryTimerActive; } @@ -214,133 +247,101 @@ EVENT_PREFERRED_NETWORK_MODE_CHANGED, null); mPhone.registerForPhysicalChannelConfig(getHandler(), EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED, null); - mPhone.getServiceStateTracker().registerForDataRegStateOrRatChanged( - AccessNetworkConstants.TRANSPORT_TYPE_WWAN, getHandler(), - EVENT_DATA_RAT_CHANGED, null); - mPhone.getServiceStateTracker().registerForBandwidthChanged( - getHandler(), EVENT_BANDWIDTH_CHANGED, null); + mPhone.getServiceStateTracker().registerForServiceStateChanged(getHandler(), + EVENT_SERVICE_STATE_CHANGED, null); mIsPhysicalChannelConfig16Supported = mPhone.getContext().getSystemService( TelephonyManager.class).isRadioInterfaceCapabilitySupported( TelephonyManager.CAPABILITY_PHYSICAL_CHANNEL_CONFIG_1_6_SUPPORTED); - mPhone.getServiceStateTracker().registerForNrStateChanged(getHandler(), - EVENT_NR_STATE_CHANGED, null); - mPhone.getServiceStateTracker().registerForNrFrequencyChanged(getHandler(), - EVENT_NR_FREQUENCY_CHANGED, null); mPhone.getDeviceStateMonitor().registerForPhysicalChannelConfigNotifChanged(getHandler(), EVENT_PHYSICAL_CHANNEL_CONFIG_NOTIF_CHANGED, null); IntentFilter filter = new IntentFilter(); - filter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED); filter.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED); mPhone.getContext().registerReceiver(mIntentReceiver, filter, null, mPhone); - if (!mPhone.isUsingNewDataStack()) { - mPhone.mCi.registerForPcoData(getHandler(), EVENT_PCO_DATA_CHANGED, null); - } + CarrierConfigManager ccm = mPhone.getContext().getSystemService(CarrierConfigManager.class); + ccm.registerCarrierConfigChangeListener(Runnable::run, mCarrierConfigChangeListener); } private void unRegisterForAllEvents() { mPhone.unregisterForRadioOffOrNotAvailable(getHandler()); mPhone.unregisterForPreferredNetworkTypeChanged(getHandler()); - mPhone.getServiceStateTracker().unregisterForDataRegStateOrRatChanged( - AccessNetworkConstants.TRANSPORT_TYPE_WWAN, getHandler()); - mPhone.getServiceStateTracker().unregisterForNrStateChanged(getHandler()); - mPhone.getServiceStateTracker().unregisterForNrFrequencyChanged(getHandler()); + mPhone.getServiceStateTracker().unregisterForServiceStateChanged(getHandler()); mPhone.getDeviceStateMonitor().unregisterForPhysicalChannelConfigNotifChanged(getHandler()); mPhone.getContext().unregisterReceiver(mIntentReceiver); - if (!mPhone.isUsingNewDataStack()) { - mPhone.mCi.unregisterForPcoData(getHandler()); + CarrierConfigManager ccm = mPhone.getContext().getSystemService(CarrierConfigManager.class); + if (mCarrierConfigChangeListener != null) { + ccm.unregisterCarrierConfigChangeListener(mCarrierConfigChangeListener); } } private void parseCarrierConfigs() { - String nrIconConfiguration = CarrierConfigManager.getDefaultConfig().getString( - CarrierConfigManager.KEY_5G_ICON_CONFIGURATION_STRING); - String overrideTimerRule = CarrierConfigManager.getDefaultConfig().getString( - CarrierConfigManager.KEY_5G_ICON_DISPLAY_GRACE_PERIOD_STRING); - String overrideSecondaryTimerRule = CarrierConfigManager.getDefaultConfig().getString( - CarrierConfigManager.KEY_5G_ICON_DISPLAY_SECONDARY_GRACE_PERIOD_STRING); - mLteEnhancedPattern = CarrierConfigManager.getDefaultConfig().getString( - CarrierConfigManager.KEY_SHOW_CARRIER_DATA_ICON_PATTERN_STRING); - mIsTimerResetEnabledForLegacyStateRRCIdle = - CarrierConfigManager.getDefaultConfig().getBoolean( - CarrierConfigManager.KEY_NR_TIMERS_RESET_IF_NON_ENDC_AND_RRC_IDLE_BOOL); - mLtePlusThresholdBandwidth = CarrierConfigManager.getDefaultConfig().getInt( - CarrierConfigManager.KEY_LTE_PLUS_THRESHOLD_BANDWIDTH_KHZ_INT); - mNrAdvancedThresholdBandwidth = CarrierConfigManager.getDefaultConfig().getInt( - CarrierConfigManager.KEY_NR_ADVANCED_THRESHOLD_BANDWIDTH_KHZ_INT); - mEnableNrAdvancedWhileRoaming = CarrierConfigManager.getDefaultConfig().getBoolean( - CarrierConfigManager.KEY_ENABLE_NR_ADVANCED_WHILE_ROAMING_BOOL); - - CarrierConfigManager configManager = (CarrierConfigManager) mPhone.getContext() - .getSystemService(Context.CARRIER_CONFIG_SERVICE); + PersistableBundle config = CarrierConfigManager.getDefaultConfig(); + CarrierConfigManager configManager = + mPhone.getContext().getSystemService(CarrierConfigManager.class); if (configManager != null) { PersistableBundle b = configManager.getConfigForSubId(mPhone.getSubId()); if (b != null) { - if (b.getString(CarrierConfigManager.KEY_5G_ICON_CONFIGURATION_STRING) != null) { - nrIconConfiguration = b.getString( - CarrierConfigManager.KEY_5G_ICON_CONFIGURATION_STRING); - } - if (b.getString(CarrierConfigManager - .KEY_5G_ICON_DISPLAY_GRACE_PERIOD_STRING) != null) { - overrideTimerRule = b.getString( - CarrierConfigManager.KEY_5G_ICON_DISPLAY_GRACE_PERIOD_STRING); - } - if (b.getString(CarrierConfigManager - .KEY_5G_ICON_DISPLAY_SECONDARY_GRACE_PERIOD_STRING) != null) { - overrideSecondaryTimerRule = b.getString( - CarrierConfigManager.KEY_5G_ICON_DISPLAY_SECONDARY_GRACE_PERIOD_STRING); - } - if (b.getString(CarrierConfigManager - .KEY_SHOW_CARRIER_DATA_ICON_PATTERN_STRING) != null) { - mLteEnhancedPattern = b.getString( - CarrierConfigManager.KEY_SHOW_CARRIER_DATA_ICON_PATTERN_STRING); - } - mIsTimerResetEnabledForLegacyStateRRCIdle = b.getBoolean( - CarrierConfigManager.KEY_NR_TIMERS_RESET_IF_NON_ENDC_AND_RRC_IDLE_BOOL); - mLtePlusThresholdBandwidth = b.getInt( - CarrierConfigManager.KEY_LTE_PLUS_THRESHOLD_BANDWIDTH_KHZ_INT, - mLtePlusThresholdBandwidth); - mNrAdvancedThresholdBandwidth = b.getInt( - CarrierConfigManager.KEY_NR_ADVANCED_THRESHOLD_BANDWIDTH_KHZ_INT, - mNrAdvancedThresholdBandwidth); - mAdditionalNrAdvancedBandsList = b.getIntArray( - CarrierConfigManager.KEY_ADDITIONAL_NR_ADVANCED_BANDS_INT_ARRAY); - mNrAdvancedCapablePcoId = b.getInt( - CarrierConfigManager.KEY_NR_ADVANCED_CAPABLE_PCO_ID_INT); - if (mNrAdvancedCapablePcoId > 0 && mPhone.isUsingNewDataStack()) { - mPhone.getDataNetworkController().registerDataNetworkControllerCallback( - new DataNetworkControllerCallback(getHandler()::post) { - @Override - public void onNrAdvancedCapableByPcoChanged( - boolean nrAdvancedCapable) { - log("mIsNrAdvancedAllowedByPco=" + nrAdvancedCapable); - mIsNrAdvancedAllowedByPco = nrAdvancedCapable; - sendMessage(EVENT_UPDATE_NR_ADVANCED_STATE); - } - }); - } - mEnableNrAdvancedWhileRoaming = b.getBoolean( - CarrierConfigManager.KEY_ENABLE_NR_ADVANCED_WHILE_ROAMING_BOOL); - mIsUsingUserDataForRrcDetection = b.getBoolean( - CarrierConfigManager.KEY_LTE_ENDC_USING_USER_DATA_FOR_RRC_DETECTION_BOOL); - if (!mIsPhysicalChannelConfig16Supported || mIsUsingUserDataForRrcDetection) { - if (mPhone.isUsingNewDataStack()) { - mPhone.getDataNetworkController().registerDataNetworkControllerCallback( - new DataNetworkControllerCallback(getHandler()::post) { - @Override - public void onPhysicalLinkStatusChanged( - @LinkStatus int status) { - sendMessage(obtainMessage( - EVENT_PHYSICAL_LINK_STATUS_CHANGED, - new AsyncResult(null, status, null))); - }}); - } else { - mPhone.getDcTracker(AccessNetworkConstants.TRANSPORT_TYPE_WWAN) - .registerForPhysicalLinkStatusChanged(getHandler(), - EVENT_PHYSICAL_LINK_STATUS_CHANGED); - } - } + config = b; } } + mLteEnhancedPattern = config.getString( + CarrierConfigManager.KEY_SHOW_CARRIER_DATA_ICON_PATTERN_STRING); + mIsTimerResetEnabledForLegacyStateRrcIdle = config.getBoolean( + CarrierConfigManager.KEY_NR_TIMERS_RESET_IF_NON_ENDC_AND_RRC_IDLE_BOOL); + mLtePlusThresholdBandwidth = config.getInt( + CarrierConfigManager.KEY_LTE_PLUS_THRESHOLD_BANDWIDTH_KHZ_INT); + mNrAdvancedThresholdBandwidth = config.getInt( + CarrierConfigManager.KEY_NR_ADVANCED_THRESHOLD_BANDWIDTH_KHZ_INT); + mIncludeLteForNrAdvancedThresholdBandwidth = config.getBoolean( + CarrierConfigManager.KEY_INCLUDE_LTE_FOR_NR_ADVANCED_THRESHOLD_BANDWIDTH_BOOL); + mEnableNrAdvancedWhileRoaming = config.getBoolean( + CarrierConfigManager.KEY_ENABLE_NR_ADVANCED_WHILE_ROAMING_BOOL); + mAdditionalNrAdvancedBandsList = config.getIntArray( + CarrierConfigManager.KEY_ADDITIONAL_NR_ADVANCED_BANDS_INT_ARRAY); + mNrAdvancedCapablePcoId = config.getInt( + CarrierConfigManager.KEY_NR_ADVANCED_CAPABLE_PCO_ID_INT); + if (mNrAdvancedCapablePcoId > 0 && mNrAdvancedCapableByPcoChangedCallback == null) { + mNrAdvancedCapableByPcoChangedCallback = + new DataNetworkControllerCallback(getHandler()::post) { + @Override + public void onNrAdvancedCapableByPcoChanged(boolean nrAdvancedCapable) { + log("mIsNrAdvancedAllowedByPco=" + nrAdvancedCapable); + mIsNrAdvancedAllowedByPco = nrAdvancedCapable; + sendMessage(EVENT_UPDATE); + } + }; + mPhone.getDataNetworkController().registerDataNetworkControllerCallback( + mNrAdvancedCapableByPcoChangedCallback); + } else if (mNrAdvancedCapablePcoId == 0 && mNrAdvancedCapableByPcoChangedCallback != null) { + mPhone.getDataNetworkController().unregisterDataNetworkControllerCallback( + mNrAdvancedCapableByPcoChangedCallback); + mNrAdvancedCapableByPcoChangedCallback = null; + } + mIsUsingUserDataForRrcDetection = config.getBoolean( + CarrierConfigManager.KEY_LTE_ENDC_USING_USER_DATA_FOR_RRC_DETECTION_BOOL); + if (!isUsingPhysicalChannelConfigForRrcDetection()) { + if (mNrPhysicalLinkStatusChangedCallback == null) { + mNrPhysicalLinkStatusChangedCallback = + new DataNetworkControllerCallback(getHandler()::post) { + @Override + public void onPhysicalLinkStatusChanged(@LinkStatus int status) { + sendMessage(obtainMessage(EVENT_PHYSICAL_LINK_STATUS_CHANGED, + new AsyncResult(null, status, null))); + } + }; + mPhone.getDataNetworkController().registerDataNetworkControllerCallback( + mNrPhysicalLinkStatusChangedCallback); + } + } else if (mNrPhysicalLinkStatusChangedCallback != null) { + mPhone.getDataNetworkController().unregisterDataNetworkControllerCallback( + mNrPhysicalLinkStatusChangedCallback); + mNrPhysicalLinkStatusChangedCallback = null; + } + String nrIconConfiguration = config.getString( + CarrierConfigManager.KEY_5G_ICON_CONFIGURATION_STRING); + String overrideTimerRule = config.getString( + CarrierConfigManager.KEY_5G_ICON_DISPLAY_GRACE_PERIOD_STRING); + String overrideSecondaryTimerRule = config.getString( + CarrierConfigManager.KEY_5G_ICON_DISPLAY_SECONDARY_GRACE_PERIOD_STRING); createTimerRules(nrIconConfiguration, overrideTimerRule, overrideSecondaryTimerRule); } @@ -349,7 +350,7 @@ if (!TextUtils.isEmpty(icons)) { // Format: "STATE:ICON,STATE2:ICON2" for (String pair : icons.trim().split(",")) { - String[] kv = (pair.trim().toLowerCase()).split(":"); + String[] kv = (pair.trim().toLowerCase(Locale.ROOT)).split(":"); if (kv.length != 2) { if (DBG) loge("Invalid 5G icon configuration, config = " + pair); continue; @@ -376,7 +377,7 @@ if (!TextUtils.isEmpty(timers)) { // Format: "FROM_STATE,TO_STATE,DURATION;FROM_STATE_2,TO_STATE_2,DURATION_2" for (String triple : timers.trim().split(";")) { - String[] kv = (triple.trim().toLowerCase()).split(","); + String[] kv = (triple.trim().toLowerCase(Locale.ROOT)).split(","); if (kv.length != 3) { if (DBG) loge("Invalid 5G icon timer configuration, config = " + triple); continue; @@ -402,7 +403,7 @@ if (!TextUtils.isEmpty(secondaryTimers)) { // Format: "PRIMARY_STATE,TO_STATE,DURATION;PRIMARY_STATE_2,TO_STATE_2,DURATION_2" for (String triple : secondaryTimers.trim().split(";")) { - String[] kv = (triple.trim().toLowerCase()).split(","); + String[] kv = (triple.trim().toLowerCase(Locale.ROOT)).split(","); if (kv.length != 3) { if (DBG) { loge("Invalid 5G icon secondary timer configuration, config = " + triple); @@ -444,7 +445,7 @@ int displayNetworkType = TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE; int dataNetworkType = getDataNetworkType(); boolean nrNsa = isLte(dataNetworkType) - && mPhone.getServiceState().getNrState() != NetworkRegistrationInfo.NR_STATE_NONE; + && mServiceState.getNrState() != NetworkRegistrationInfo.NR_STATE_NONE; boolean nrSa = dataNetworkType == TelephonyManager.NETWORK_TYPE_NR; // NR display is not accurate when physical channel config notifications are off @@ -475,7 +476,7 @@ keys.add(STATE_CONNECTED_NR_ADVANCED); } } else { - switch (mPhone.getServiceState().getNrState()) { + switch (mServiceState.getNrState()) { case NetworkRegistrationInfo.NR_STATE_CONNECTED: if (isNrAdvanced()) { keys.add(STATE_CONNECTED_NR_ADVANCED); @@ -505,9 +506,9 @@ private @Annotation.OverrideNetworkType int getLteDisplayType() { int value = TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE; if ((getDataNetworkType() == TelephonyManager.NETWORK_TYPE_LTE_CA - || mPhone.getServiceState().isUsingCarrierAggregation()) - && (IntStream.of(mPhone.getServiceState().getCellBandwidths()).sum() - > mLtePlusThresholdBandwidth)) { + || mServiceState.isUsingCarrierAggregation()) + && IntStream.of(mServiceState.getCellBandwidths()).sum() + > mLtePlusThresholdBandwidth) { value = TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_CA; } if (isLteEnhancedAvailable()) { @@ -521,8 +522,8 @@ return false; } Pattern stringPattern = Pattern.compile(mLteEnhancedPattern); - for (String opName : new String[] {mPhone.getServiceState().getOperatorAlphaLongRaw(), - mPhone.getServiceState().getOperatorAlphaShortRaw()}) { + for (String opName : new String[] {mServiceState.getOperatorAlphaLongRaw(), + mServiceState.getOperatorAlphaShortRaw()}) { if (!TextUtils.isEmpty(opName)) { Matcher matcher = stringPattern.matcher(opName); if (matcher.find()) { @@ -542,8 +543,6 @@ if (DBG) log("DefaultState: process " + getEventName(msg.what)); switch (msg.what) { case EVENT_UPDATE: - case EVENT_PREFERRED_NETWORK_MODE_CHANGED: - if (DBG) log("Reset timers since preferred network mode changed."); resetAllTimers(); transitionToCurrentState(); break; @@ -555,26 +554,15 @@ break; case EVENT_INITIALIZE: // The reason that we do it here is because some of the works below requires - // other modules (e.g. DcTracker, ServiceStateTracker), which is not created - // yet when NetworkTypeController is created. + // other modules (e.g. DataNetworkController, ServiceStateTracker), which is not + // created yet when NetworkTypeController is created. registerForAllEvents(); parseCarrierConfigs(); break; - case EVENT_DATA_RAT_CHANGED: - case EVENT_NR_STATE_CHANGED: - case EVENT_NR_FREQUENCY_CHANGED: - case EVENT_PCO_DATA_CHANGED: - case EVENT_UPDATE_NR_ADVANCED_STATE: - // ignored - break; - case EVENT_BANDWIDTH_CHANGED: - // Update in case of LTE/LTE+ switch - updateOverrideNetworkType(); - break; - case EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED: - if (isUsingPhysicalChannelConfigForRrcDetection()) { - mPhysicalLinkStatus = getPhysicalLinkStatusFromPhysicalChannelConfig(); - } + case EVENT_SERVICE_STATE_CHANGED: + mServiceState = mPhone.getServiceStateTracker().getServiceState(); + if (DBG) log("ServiceState updated: " + mServiceState); + transitionToCurrentState(); break; case EVENT_PHYSICAL_LINK_STATUS_CHANGED: AsyncResult ar = (AsyncResult) msg.obj; @@ -616,6 +604,20 @@ resetAllTimers(); transitionTo(mLegacyState); break; + case EVENT_PREFERRED_NETWORK_MODE_CHANGED: + if (DBG) log("Reset timers since preferred network mode changed."); + resetAllTimers(); + transitionToCurrentState(); + break; + case EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED: + mPhysicalChannelConfigs = + mPhone.getServiceStateTracker().getPhysicalChannelConfigList(); + if (DBG) log("Physical channel configs updated: " + mPhysicalChannelConfigs); + if (isUsingPhysicalChannelConfigForRrcDetection()) { + mPhysicalLinkStatus = getPhysicalLinkStatusFromPhysicalChannelConfig(); + } + transitionToCurrentState(); + break; case EVENT_DEVICE_IDLE_MODE_CHANGED: PowerManager pm = mPhone.getContext().getSystemService(PowerManager.class); mIsDeviceIdleMode = pm.isDeviceIdleMode(); @@ -635,8 +637,6 @@ } } - private final DefaultState mDefaultState = new DefaultState(); - /** * Device does not have NR available, due to any of the below reasons: * <ul> @@ -664,11 +664,19 @@ public boolean processMessage(Message msg) { if (DBG) log("LegacyState: process " + getEventName(msg.what)); updateTimers(); - int rat = getDataNetworkType(); switch (msg.what) { - case EVENT_DATA_RAT_CHANGED: + case EVENT_SERVICE_STATE_CHANGED: + mServiceState = mPhone.getServiceStateTracker().getServiceState(); + if (DBG) log("ServiceState updated: " + mServiceState); + // fallthrough + case EVENT_UPDATE: + int rat = getDataNetworkType(); if (rat == TelephonyManager.NETWORK_TYPE_NR || isLte(rat) && isNrConnected()) { - transitionTo(mNrConnectedState); + if (isNrAdvanced()) { + transitionTo(mNrConnectedAdvancedState); + } else { + transitionTo(mNrConnectedState); + } } else if (isLte(rat) && isNrNotRestricted()) { transitionWithTimerTo(isPhysicalLinkActive() ? mLteConnectedState : mIdleState); @@ -681,35 +689,22 @@ } mIsNrRestricted = isNrRestricted(); break; - case EVENT_NR_STATE_CHANGED: - if (isNrConnected()) { - transitionTo(mNrConnectedState); - } else if (isLte(rat) && isNrNotRestricted()) { - transitionWithTimerTo(isPhysicalLinkActive() - ? mLteConnectedState : mIdleState); - } else if (isLte(rat) && isNrRestricted()) { - updateOverrideNetworkType(); - } - mIsNrRestricted = isNrRestricted(); - break; - case EVENT_NR_FREQUENCY_CHANGED: - // ignored - break; case EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED: + mPhysicalChannelConfigs = + mPhone.getServiceStateTracker().getPhysicalChannelConfigList(); + if (DBG) log("Physical channel configs updated: " + mPhysicalChannelConfigs); if (isUsingPhysicalChannelConfigForRrcDetection()) { mPhysicalLinkStatus = getPhysicalLinkStatusFromPhysicalChannelConfig(); - if (mIsTimerResetEnabledForLegacyStateRRCIdle && !isPhysicalLinkActive()) { + if (mIsTimerResetEnabledForLegacyStateRrcIdle && !isPhysicalLinkActive()) { if (DBG) log("Reset timers since timer reset is enabled for RRC idle."); resetAllTimers(); } } - // Update in case of LTE/LTE+ switch - updateOverrideNetworkType(); break; case EVENT_PHYSICAL_LINK_STATUS_CHANGED: AsyncResult ar = (AsyncResult) msg.obj; mPhysicalLinkStatus = (int) ar.result; - if (mIsTimerResetEnabledForLegacyStateRRCIdle && !isPhysicalLinkActive()) { + if (mIsTimerResetEnabledForLegacyStateRrcIdle && !isPhysicalLinkActive()) { if (DBG) log("Reset timers since timer reset is enabled for RRC idle."); resetAllTimers(); updateOverrideNetworkType(); @@ -751,52 +746,52 @@ if (DBG) log("IdleState: process " + getEventName(msg.what)); updateTimers(); switch (msg.what) { - case EVENT_DATA_RAT_CHANGED: + case EVENT_SERVICE_STATE_CHANGED: + mServiceState = mPhone.getServiceStateTracker().getServiceState(); + if (DBG) log("ServiceState updated: " + mServiceState); + // fallthrough + case EVENT_UPDATE: int rat = getDataNetworkType(); - if (rat == TelephonyManager.NETWORK_TYPE_NR) { - transitionTo(mNrConnectedState); + if (rat == TelephonyManager.NETWORK_TYPE_NR + || (isLte(rat) && isNrConnected())) { + if (isNrAdvanced()) { + transitionTo(mNrConnectedAdvancedState); + } else { + transitionTo(mNrConnectedState); + } } else if (!isLte(rat) || !isNrNotRestricted()) { transitionWithTimerTo(mLegacyState); - } - break; - case EVENT_NR_STATE_CHANGED: - if (isNrConnected()) { - transitionTo(mNrConnectedState); - } else if (!isNrNotRestricted()) { - transitionWithTimerTo(mLegacyState); - } - break; - case EVENT_NR_FREQUENCY_CHANGED: - // ignore - break; - case EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED: - if (isUsingPhysicalChannelConfigForRrcDetection()) { - mPhysicalLinkStatus = getPhysicalLinkStatusFromPhysicalChannelConfig(); - if (isNrNotRestricted()) { - // NOT_RESTRICTED_RRC_IDLE -> NOT_RESTRICTED_RRC_CON - if (isPhysicalLinkActive()) { - transitionWithTimerTo(mLteConnectedState); - break; - } + } else { + if (isPhysicalLinkActive()) { + transitionWithTimerTo(mLteConnectedState); } else { - log("NR state changed. Sending EVENT_NR_STATE_CHANGED"); - sendMessage(EVENT_NR_STATE_CHANGED); + // Update in case the override network type changed + updateOverrideNetworkType(); } } - // Update in case of LTE/LTE+ switch - updateOverrideNetworkType(); + break; + case EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED: + mPhysicalChannelConfigs = + mPhone.getServiceStateTracker().getPhysicalChannelConfigList(); + if (DBG) log("Physical channel configs updated: " + mPhysicalChannelConfigs); + if (isUsingPhysicalChannelConfigForRrcDetection()) { + mPhysicalLinkStatus = getPhysicalLinkStatusFromPhysicalChannelConfig(); + if (isPhysicalLinkActive()) { + transitionWithTimerTo(mLteConnectedState); + } else { + log("Reevaluating state due to link status changed."); + sendMessage(EVENT_UPDATE); + } + } break; case EVENT_PHYSICAL_LINK_STATUS_CHANGED: AsyncResult ar = (AsyncResult) msg.obj; mPhysicalLinkStatus = (int) ar.result; - if (isNrNotRestricted()) { - // NOT_RESTRICTED_RRC_IDLE -> NOT_RESTRICTED_RRC_CON - if (isPhysicalLinkActive()) { - transitionWithTimerTo(mLteConnectedState); - } + if (isPhysicalLinkActive()) { + transitionWithTimerTo(mLteConnectedState); } else { - log("NR state changed. Sending EVENT_NR_STATE_CHANGED"); - sendMessage(EVENT_NR_STATE_CHANGED); + log("Reevaluating state due to link status changed."); + sendMessage(EVENT_UPDATE); } break; default: @@ -835,52 +830,52 @@ if (DBG) log("LteConnectedState: process " + getEventName(msg.what)); updateTimers(); switch (msg.what) { - case EVENT_DATA_RAT_CHANGED: + case EVENT_SERVICE_STATE_CHANGED: + mServiceState = mPhone.getServiceStateTracker().getServiceState(); + if (DBG) log("ServiceState updated: " + mServiceState); + // fallthrough + case EVENT_UPDATE: int rat = getDataNetworkType(); - if (rat == TelephonyManager.NETWORK_TYPE_NR) { - transitionTo(mNrConnectedState); + if (rat == TelephonyManager.NETWORK_TYPE_NR + || (isLte(rat) && isNrConnected())) { + if (isNrAdvanced()) { + transitionTo(mNrConnectedAdvancedState); + } else { + transitionTo(mNrConnectedState); + } } else if (!isLte(rat) || !isNrNotRestricted()) { transitionWithTimerTo(mLegacyState); - } - break; - case EVENT_NR_STATE_CHANGED: - if (isNrConnected()) { - transitionTo(mNrConnectedState); - } else if (!isNrNotRestricted()) { - transitionWithTimerTo(mLegacyState); - } - break; - case EVENT_NR_FREQUENCY_CHANGED: - // ignore - break; - case EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED: - if (isUsingPhysicalChannelConfigForRrcDetection()) { - mPhysicalLinkStatus = getPhysicalLinkStatusFromPhysicalChannelConfig(); - if (isNrNotRestricted()) { - // NOT_RESTRICTED_RRC_CON -> NOT_RESTRICTED_RRC_IDLE - if (!isPhysicalLinkActive()) { - transitionWithTimerTo(mIdleState); - break; - } + } else { + if (!isPhysicalLinkActive()) { + transitionWithTimerTo(mIdleState); } else { - log("NR state changed. Sending EVENT_NR_STATE_CHANGED"); - sendMessage(EVENT_NR_STATE_CHANGED); + // Update in case the override network type changed + updateOverrideNetworkType(); } } - // Update in case of LTE/LTE+ switch - updateOverrideNetworkType(); + break; + case EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED: + mPhysicalChannelConfigs = + mPhone.getServiceStateTracker().getPhysicalChannelConfigList(); + if (DBG) log("Physical channel configs updated: " + mPhysicalChannelConfigs); + if (isUsingPhysicalChannelConfigForRrcDetection()) { + mPhysicalLinkStatus = getPhysicalLinkStatusFromPhysicalChannelConfig(); + if (!isPhysicalLinkActive()) { + transitionWithTimerTo(mIdleState); + } else { + log("Reevaluating state due to link status changed."); + sendMessage(EVENT_UPDATE); + } + } break; case EVENT_PHYSICAL_LINK_STATUS_CHANGED: AsyncResult ar = (AsyncResult) msg.obj; mPhysicalLinkStatus = (int) ar.result; - if (isNrNotRestricted()) { - // NOT_RESTRICTED_RRC_CON -> NOT_RESTRICTED_RRC_IDLE - if (!isPhysicalLinkActive()) { - transitionWithTimerTo(mIdleState); - } + if (!isPhysicalLinkActive()) { + transitionWithTimerTo(mIdleState); } else { - log("NR state changed. Sending EVENT_NR_STATE_CHANGED"); - sendMessage(EVENT_NR_STATE_CHANGED); + log("Reevaluating state due to link status changed."); + sendMessage(EVENT_UPDATE); } break; default: @@ -904,28 +899,35 @@ * Device is connected to 5G NR as the primary or secondary cell. */ private final class NrConnectedState extends State { - private boolean mIsNrAdvanced = false; - @Override public void enter() { - if (DBG) log("Entering NrConnectedState(" + getName() + ")"); + if (DBG) log("Entering NrConnectedState"); updateTimers(); updateOverrideNetworkType(); if (!mIsPrimaryTimerActive && !mIsSecondaryTimerActive) { - mIsNrAdvanced = isNrAdvanced(); mPreviousState = getName(); } } @Override public boolean processMessage(Message msg) { - if (DBG) log("NrConnectedState(" + getName() + "): process " + getEventName(msg.what)); + if (DBG) log("NrConnectedState: process " + getEventName(msg.what)); updateTimers(); - int rat = getDataNetworkType(); switch (msg.what) { - case EVENT_DATA_RAT_CHANGED: - if (rat == TelephonyManager.NETWORK_TYPE_NR || isLte(rat) && isNrConnected()) { - updateOverrideNetworkType(); + case EVENT_SERVICE_STATE_CHANGED: + mServiceState = mPhone.getServiceStateTracker().getServiceState(); + if (DBG) log("ServiceState updated: " + mServiceState); + // fallthrough + case EVENT_UPDATE: + int rat = getDataNetworkType(); + if (rat == TelephonyManager.NETWORK_TYPE_NR + || (isLte(rat) && isNrConnected())) { + if (isNrAdvanced()) { + transitionTo(mNrConnectedAdvancedState); + } else { + // Update in case the override network type changed + updateOverrideNetworkType(); + } } else if (isLte(rat) && isNrNotRestricted()) { transitionWithTimerTo(isPhysicalLinkActive() ? mLteConnectedState : mIdleState); @@ -933,37 +935,21 @@ transitionWithTimerTo(mLegacyState); } break; - case EVENT_NR_STATE_CHANGED: - if (isLte(rat) && isNrNotRestricted()) { - transitionWithTimerTo(isPhysicalLinkActive() - ? mLteConnectedState : mIdleState); - } else if (rat != TelephonyManager.NETWORK_TYPE_NR && !isNrConnected()) { - transitionWithTimerTo(mLegacyState); - } - break; - case EVENT_PCO_DATA_CHANGED: - handlePcoData((AsyncResult) msg.obj); - break; - case EVENT_UPDATE_NR_ADVANCED_STATE: - updateNrAdvancedState(); - break; - case EVENT_NR_FREQUENCY_CHANGED: case EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED: + mPhysicalChannelConfigs = + mPhone.getServiceStateTracker().getPhysicalChannelConfigList(); + if (DBG) log("Physical channel configs updated: " + mPhysicalChannelConfigs); if (isUsingPhysicalChannelConfigForRrcDetection()) { mPhysicalLinkStatus = getPhysicalLinkStatusFromPhysicalChannelConfig(); } - updateNrAdvancedState(); + // Check NR advanced in case NR advanced bands were added + if (isNrAdvanced()) { + transitionTo(mNrConnectedAdvancedState); + } break; case EVENT_PHYSICAL_LINK_STATUS_CHANGED: AsyncResult ar = (AsyncResult) msg.obj; mPhysicalLinkStatus = (int) ar.result; - if (!isNrConnected()) { - log("NR state changed. Sending EVENT_NR_STATE_CHANGED"); - sendMessage(EVENT_NR_STATE_CHANGED); - } - break; - case EVENT_BANDWIDTH_CHANGED: - updateNrAdvancedState(); break; default: return NOT_HANDLED; @@ -976,55 +962,94 @@ @Override public String getName() { - return mIsNrAdvanced ? STATE_CONNECTED_NR_ADVANCED : STATE_CONNECTED; - } - - private void updateNrAdvancedState() { - if (!isNrConnected()) { - log("NR state changed. Sending EVENT_NR_STATE_CHANGED"); - sendMessage(EVENT_NR_STATE_CHANGED); - return; - } - if (!isNrAdvanced()) { - if (DBG) log("updateNrAdvancedState: CONNECTED_NR_ADVANCED -> CONNECTED"); - transitionWithTimerTo(mNrConnectedState); - } else { - if (DBG) log("updateNrAdvancedState: CONNECTED -> CONNECTED_NR_ADVANCED"); - transitionTo(mNrConnectedState); - } - mIsNrAdvanced = isNrAdvanced(); - } - - private void handlePcoData(AsyncResult ar) { - if (ar.exception != null) { - loge("PCO_DATA exception: " + ar.exception); - return; - } - PcoData pcodata = (PcoData) ar.result; - if (pcodata == null) { - return; - } - log("EVENT_PCO_DATA_CHANGED: pco data: " + pcodata); - DcTracker dcTracker = mPhone.getDcTracker( - AccessNetworkConstants.TRANSPORT_TYPE_WWAN); - DataConnection dc = - dcTracker != null ? dcTracker.getDataConnectionByContextId(pcodata.cid) : null; - ApnSetting apnSettings = dc != null ? dc.getApnSetting() : null; - if (apnSettings != null && apnSettings.canHandleType(ApnSetting.TYPE_DEFAULT) - && mNrAdvancedCapablePcoId > 0 - && pcodata.pcoId == mNrAdvancedCapablePcoId - ) { - log("EVENT_PCO_DATA_CHANGED: NR_ADVANCED is allowed by PCO. length:" - + pcodata.contents.length + ",value: " + Arrays.toString(pcodata.contents)); - mIsNrAdvancedAllowedByPco = pcodata.contents.length > 0 - && pcodata.contents[pcodata.contents.length - 1] == 1; - updateNrAdvancedState(); - } + return STATE_CONNECTED; } } private final NrConnectedState mNrConnectedState = new NrConnectedState(); + /** + * Device is connected to 5G NR as the primary cell and the data rate is higher than + * the generic 5G data rate. + */ + private final class NrConnectedAdvancedState extends State { + @Override + public void enter() { + if (DBG) log("Entering NrConnectedAdvancedState"); + updateTimers(); + updateOverrideNetworkType(); + if (!mIsPrimaryTimerActive && !mIsSecondaryTimerActive) { + mPreviousState = getName(); + } + } + + @Override + public boolean processMessage(Message msg) { + if (DBG) log("NrConnectedAdvancedState: process " + getEventName(msg.what)); + updateTimers(); + switch (msg.what) { + case EVENT_SERVICE_STATE_CHANGED: + mServiceState = mPhone.getServiceStateTracker().getServiceState(); + if (DBG) log("ServiceState updated: " + mServiceState); + // fallthrough + case EVENT_UPDATE: + int rat = getDataNetworkType(); + if (rat == TelephonyManager.NETWORK_TYPE_NR + || (isLte(rat) && isNrConnected())) { + if (isNrAdvanced()) { + // Update in case the override network type changed + updateOverrideNetworkType(); + } else { + if (rat == TelephonyManager.NETWORK_TYPE_NR && mOverrideNetworkType + != TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED) { + // manually override network type after data rat changes since + // timer will prevent it from being updated + mOverrideNetworkType = + TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE; + } + transitionWithTimerTo(mNrConnectedState); + } + } else if (isLte(rat) && isNrNotRestricted()) { + transitionWithTimerTo(isPhysicalLinkActive() + ? mLteConnectedState : mIdleState); + } else { + transitionWithTimerTo(mLegacyState); + } + break; + case EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED: + mPhysicalChannelConfigs = + mPhone.getServiceStateTracker().getPhysicalChannelConfigList(); + if (DBG) log("Physical channel configs updated: " + mPhysicalChannelConfigs); + if (isUsingPhysicalChannelConfigForRrcDetection()) { + mPhysicalLinkStatus = getPhysicalLinkStatusFromPhysicalChannelConfig(); + } + // Check NR advanced in case NR advanced bands were removed + if (!isNrAdvanced()) { + transitionWithTimerTo(mNrConnectedState); + } + break; + case EVENT_PHYSICAL_LINK_STATUS_CHANGED: + AsyncResult ar = (AsyncResult) msg.obj; + mPhysicalLinkStatus = (int) ar.result; + break; + default: + return NOT_HANDLED; + } + if (!mIsPrimaryTimerActive && !mIsSecondaryTimerActive) { + mPreviousState = getName(); + } + return HANDLED; + } + + @Override + public String getName() { + return STATE_CONNECTED_NR_ADVANCED; + } + } + + private final NrConnectedAdvancedState mNrConnectedAdvancedState = + new NrConnectedAdvancedState(); + private void transitionWithTimerTo(IState destState) { String destName = destState.getName(); if (DBG) log("Transition with primary timer from " + mPreviousState + " to " + destName); @@ -1062,22 +1087,23 @@ private void transitionToCurrentState() { int dataRat = getDataNetworkType(); IState transitionState; - if (dataRat == TelephonyManager.NETWORK_TYPE_NR || isNrConnected()) { - transitionState = mNrConnectedState; - mPreviousState = isNrAdvanced() ? STATE_CONNECTED_NR_ADVANCED : STATE_CONNECTED; + if (dataRat == TelephonyManager.NETWORK_TYPE_NR || (isLte(dataRat) && isNrConnected())) { + if (isNrAdvanced()) { + transitionState = mNrConnectedAdvancedState; + } else { + transitionState = mNrConnectedState; + } } else if (isLte(dataRat) && isNrNotRestricted()) { if (isPhysicalLinkActive()) { transitionState = mLteConnectedState; - mPreviousState = STATE_NOT_RESTRICTED_RRC_CON; } else { transitionState = mIdleState; - mPreviousState = STATE_NOT_RESTRICTED_RRC_IDLE; } } else { transitionState = mLegacyState; - mPreviousState = isNrRestricted() ? STATE_RESTRICTED : STATE_LEGACY; } if (!transitionState.equals(getCurrentState())) { + mPreviousState = getCurrentState().getName(); transitionTo(transitionState); } else { updateOverrideNetworkType(); @@ -1103,6 +1129,8 @@ removeMessages(EVENT_PRIMARY_TIMER_EXPIRED); mIsPrimaryTimerActive = false; mPrimaryTimerState = ""; + transitionToCurrentState(); + return; } if (mIsSecondaryTimerActive && !mSecondaryTimerState.equals(currentState)) { @@ -1114,18 +1142,25 @@ removeMessages(EVENT_SECONDARY_TIMER_EXPIRED); mIsSecondaryTimerActive = false; mSecondaryTimerState = ""; + transitionToCurrentState(); + return; } if (mIsPrimaryTimerActive || mIsSecondaryTimerActive) { if (currentState.equals(STATE_CONNECTED_NR_ADVANCED)) { if (DBG) log("Reset timers since state is NR_ADVANCED."); resetAllTimers(); - } - - int rat = getDataNetworkType(); - if (!isLte(rat) && rat != TelephonyManager.NETWORK_TYPE_NR) { - if (DBG) log("Reset timers since 2G and 3G don't need NR timers."); + } else if (currentState.equals(STATE_CONNECTED) + && !mPrimaryTimerState.equals(STATE_CONNECTED_NR_ADVANCED) + && !mSecondaryTimerState.equals(STATE_CONNECTED_NR_ADVANCED)) { + if (DBG) log("Reset non-NR_ADVANCED timers since state is NR_CONNECTED"); resetAllTimers(); + } else { + int rat = getDataNetworkType(); + if (!isLte(rat) && rat != TelephonyManager.NETWORK_TYPE_NR) { + if (DBG) log("Reset timers since 2G and 3G don't need NR timers."); + resetAllTimers(); + } } } } @@ -1222,17 +1257,15 @@ } private boolean isNrConnected() { - return mPhone.getServiceState().getNrState() == NetworkRegistrationInfo.NR_STATE_CONNECTED; + return mServiceState.getNrState() == NetworkRegistrationInfo.NR_STATE_CONNECTED; } private boolean isNrNotRestricted() { - return mPhone.getServiceState().getNrState() - == NetworkRegistrationInfo.NR_STATE_NOT_RESTRICTED; + return mServiceState.getNrState() == NetworkRegistrationInfo.NR_STATE_NOT_RESTRICTED; } private boolean isNrRestricted() { - return mPhone.getServiceState().getNrState() - == NetworkRegistrationInfo.NR_STATE_RESTRICTED; + return mServiceState.getNrState() == NetworkRegistrationInfo.NR_STATE_RESTRICTED; } /** @@ -1247,15 +1280,24 @@ // Check if NR advanced is enabled when the device is roaming. Some carriers disable it // while the device is roaming. - if (mPhone.getServiceState().getDataRoaming() && !mEnableNrAdvancedWhileRoaming) { + if (mServiceState.getDataRoaming() && !mEnableNrAdvancedWhileRoaming) { return false; } + int bandwidths = 0; + if (mPhone.getServiceStateTracker().getPhysicalChannelConfigList() != null) { + bandwidths = mPhone.getServiceStateTracker().getPhysicalChannelConfigList() + .stream() + .filter(config -> mIncludeLteForNrAdvancedThresholdBandwidth + || config.getNetworkType() == TelephonyManager.NETWORK_TYPE_NR) + .map(PhysicalChannelConfig::getCellBandwidthDownlinkKhz) + .mapToInt(Integer::intValue) + .sum(); + } + // Check if meeting minimum bandwidth requirement. For most carriers, there is no minimum // bandwidth requirement and mNrAdvancedThresholdBandwidth is 0. - if (mNrAdvancedThresholdBandwidth > 0 - && IntStream.of(mPhone.getServiceState().getCellBandwidths()).sum() - < mNrAdvancedThresholdBandwidth) { + if (mNrAdvancedThresholdBandwidth > 0 && bandwidths < mNrAdvancedThresholdBandwidth) { return false; } @@ -1265,18 +1307,15 @@ } private boolean isNrMmwave() { - return mPhone.getServiceState().getNrFrequencyRange() - == ServiceState.FREQUENCY_RANGE_MMWAVE; + return mServiceState.getNrFrequencyRange() == ServiceState.FREQUENCY_RANGE_MMWAVE; } private boolean isAdditionalNrAdvancedBand() { - List<PhysicalChannelConfig> physicalChannelConfigList = - mPhone.getServiceStateTracker().getPhysicalChannelConfigList(); if (ArrayUtils.isEmpty(mAdditionalNrAdvancedBandsList) - || physicalChannelConfigList == null) { + || mPhysicalChannelConfigs == null) { return false; } - for (PhysicalChannelConfig item : physicalChannelConfigList) { + for (PhysicalChannelConfig item : mPhysicalChannelConfigs) { if (item.getNetworkType() == TelephonyManager.NETWORK_TYPE_NR && ArrayUtils.contains(mAdditionalNrAdvancedBandsList, item.getBand())) { return true; @@ -1295,19 +1334,10 @@ } private int getPhysicalLinkStatusFromPhysicalChannelConfig() { - List<PhysicalChannelConfig> physicalChannelConfigList = - mPhone.getServiceStateTracker().getPhysicalChannelConfigList(); - return (physicalChannelConfigList == null || physicalChannelConfigList.isEmpty()) + return (mPhysicalChannelConfigs == null || mPhysicalChannelConfigs.isEmpty()) ? DataCallResponse.LINK_STATUS_DORMANT : DataCallResponse.LINK_STATUS_ACTIVE; } - private int getDataNetworkType() { - NetworkRegistrationInfo nri = mPhone.getServiceState().getNetworkRegistrationInfo( - NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN); - return nri == null ? TelephonyManager.NETWORK_TYPE_UNKNOWN - : nri.getAccessNetworkTechnology(); - } - private String getEventName(int event) { try { return sEvents[event]; @@ -1354,16 +1384,16 @@ pw.println("mIsPhysicalChannelConfigOn=" + mIsPhysicalChannelConfigOn); pw.println("mIsPrimaryTimerActive=" + mIsPrimaryTimerActive); pw.println("mIsSecondaryTimerActive=" + mIsSecondaryTimerActive); - pw.println("mIsTimerRestEnabledForLegacyStateRRCIdle=" - + mIsTimerResetEnabledForLegacyStateRRCIdle); + pw.println("mIsTimerResetEnabledForLegacyStateRrcIdle=" + + mIsTimerResetEnabledForLegacyStateRrcIdle); pw.println("mLtePlusThresholdBandwidth=" + mLtePlusThresholdBandwidth); pw.println("mNrAdvancedThresholdBandwidth=" + mNrAdvancedThresholdBandwidth); + pw.println("mAdditionalNrAdvancedBandsList=" + + Arrays.toString(mAdditionalNrAdvancedBandsList)); pw.println("mPrimaryTimerState=" + mPrimaryTimerState); pw.println("mSecondaryTimerState=" + mSecondaryTimerState); pw.println("mPreviousState=" + mPreviousState); - pw.println("mPhysicalLinkStatus=" + mPhysicalLinkStatus); - pw.println("mAdditionalNrAdvancedBandsList=" - + Arrays.toString(mAdditionalNrAdvancedBandsList)); + pw.println("mPhysicalLinkStatus=" + DataUtils.linkStatusToString(mPhysicalLinkStatus)); pw.println("mIsPhysicalChannelConfig16Supported=" + mIsPhysicalChannelConfig16Supported); pw.println("mIsNrAdvancedAllowedByPco=" + mIsNrAdvancedAllowedByPco); pw.println("mNrAdvancedCapablePcoId=" + mNrAdvancedCapablePcoId);
diff --git a/src/java/com/android/internal/telephony/NitzData.java b/src/java/com/android/internal/telephony/NitzData.java index 6b19e08..8430585 100644 --- a/src/java/com/android/internal/telephony/NitzData.java +++ b/src/java/com/android/internal/telephony/NitzData.java
@@ -21,6 +21,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.telephony.Rlog; +import java.time.DateTimeException; import java.time.LocalDateTime; import java.time.ZoneOffset; import java.util.Objects; @@ -39,9 +40,6 @@ private static final int MS_PER_QUARTER_HOUR = 15 * 60 * 1000; private static final int MS_PER_HOUR = 60 * 60 * 1000; - /* Time stamp after 19 January 2038 is not supported under 32 bit */ - private static final int MAX_NITZ_YEAR = 2037; - private static final Pattern NITZ_SPLIT_PATTERN = Pattern.compile("[/:,+-]"); // Stored For logging / debugging only. @@ -77,22 +75,37 @@ try { String[] nitzSubs = NITZ_SPLIT_PATTERN.split(nitz); - int year = 2000 + Integer.parseInt(nitzSubs[0]); - if (year > MAX_NITZ_YEAR) { - if (ServiceStateTracker.DBG) { - Rlog.e(LOG_TAG, "NITZ year: " + year + " exceeds limit, skip NITZ time update"); - } - return null; + int year = Integer.parseInt(nitzSubs[0]); + if (year < 1 || year > 99) { + // 0 > year > 99 imply an invalid string. + // + // At the time of this comment (year 2023), a zero year is considered invalid and + // assumed to be the result of invalid data being converted to zero in the code that + // turns the binary NITZ into a string. For the next few decades at least, Android + // devices should not need to interpret zero. Hopefully, NITZ will be replaced by + // the time that's not true, or folks dealing the Y2K1 issue can handle it. + // + // DateTimeException is also thrown by LocalDateTime below if the values are out of + // range and will be handled in the catch block. + throw new DateTimeException("Invalid NITZ year == 0"); } + // Values < {current year} could be considered invalid but are used in test code, so no + // window is applied to adjust low values < {current year} with "+ 2100" (and would also + // need to consider zero as valid). Code that processes the NitzData is in a better + // position to log and discard obviously invalid NITZ signals from past years. + year += 2000; + int month = Integer.parseInt(nitzSubs[1]); int date = Integer.parseInt(nitzSubs[2]); int hour = Integer.parseInt(nitzSubs[3]); int minute = Integer.parseInt(nitzSubs[4]); int second = Integer.parseInt(nitzSubs[5]); - /* NITZ time (hour:min:sec) will be in UTC but it supplies the timezone - * offset as well (which we won't worry about until later) */ + // NITZ time (hour:min:sec) will be in UTC but it supplies the timezone + // offset as well (which we won't worry about until later). + // The LocalDateTime.of() will throw DateTimeException for values outside the allowed + // range for the Gregorian calendar. long epochMillis = LocalDateTime.of(year, month, date, hour, minute, second) .toInstant(ZoneOffset.UTC) .toEpochMilli();
diff --git a/src/java/com/android/internal/telephony/NitzSignal.java b/src/java/com/android/internal/telephony/NitzSignal.java index 889fe95..2619f3d 100644 --- a/src/java/com/android/internal/telephony/NitzSignal.java +++ b/src/java/com/android/internal/telephony/NitzSignal.java
@@ -19,7 +19,7 @@ import android.annotation.DurationMillisLong; import android.annotation.ElapsedRealtimeLong; import android.annotation.NonNull; -import android.os.TimestampedValue; +import android.app.time.UnixEpochTime; import java.time.Duration; import java.util.Objects; @@ -88,13 +88,12 @@ } /** - * Creates a {@link android.os.TimestampedValue} containing the UTC time as the number of - * milliseconds since the start of the Unix epoch. The reference time is the time according to - * the elapsed realtime clock when that would have been the time, accounting for receipt time - * and age. + * Creates a {@link UnixEpochTime} containing the UTC time as the number of milliseconds since + * the start of the Unix epoch. The reference time is the time according to the elapsed realtime + * clock when that would have been the time, accounting for receipt time and age. */ - public TimestampedValue<Long> createTimeSignal() { - return new TimestampedValue<>( + public UnixEpochTime createTimeSignal() { + return new UnixEpochTime( getAgeAdjustedElapsedRealtimeMillis(), getNitzData().getCurrentTimeInMillis()); }
diff --git a/src/java/com/android/internal/telephony/Phone.java b/src/java/com/android/internal/telephony/Phone.java index 4afccd1..4e62d20 100644 --- a/src/java/com/android/internal/telephony/Phone.java +++ b/src/java/com/android/internal/telephony/Phone.java
@@ -16,6 +16,8 @@ package com.android.internal.telephony; +import static android.telephony.TelephonyManager.HAL_SERVICE_RADIO; + import android.annotation.NonNull; import android.annotation.Nullable; import android.app.BroadcastOptions; @@ -24,6 +26,7 @@ import android.content.Intent; import android.content.SharedPreferences; import android.content.res.Configuration; +import android.hardware.radio.modem.ImeiInfo; import android.net.Uri; import android.os.AsyncResult; import android.os.Build; @@ -35,17 +38,21 @@ import android.os.RegistrantList; import android.os.SystemClock; import android.os.SystemProperties; +import android.os.UserHandle; +import android.os.UserManager; import android.os.WorkSource; import android.preference.PreferenceManager; import android.sysprop.TelephonyProperties; import android.telecom.VideoProfile; import android.telephony.AccessNetworkConstants; -import android.telephony.Annotation.ApnType; +import android.telephony.Annotation.SrvccState; import android.telephony.CarrierConfigManager; import android.telephony.CarrierRestrictionRules; +import android.telephony.CellBroadcastIdRange; import android.telephony.CellIdentity; import android.telephony.CellInfo; import android.telephony.ClientRequestStats; +import android.telephony.DomainSelectionService; import android.telephony.ImsiEncryptionInfo; import android.telephony.LinkCapacityEstimate; import android.telephony.NetworkRegistrationInfo; @@ -60,14 +67,15 @@ import android.telephony.SubscriptionManager; import android.telephony.TelephonyDisplayInfo; import android.telephony.TelephonyManager; -import android.telephony.data.ApnSetting; +import android.telephony.TelephonyManager.HalService; import android.telephony.emergency.EmergencyNumber; import android.telephony.ims.RegistrationManager; +import android.telephony.ims.feature.MmTelFeature; import android.telephony.ims.stub.ImsRegistrationImplBase; +import android.telephony.satellite.SatelliteDatagram; import android.text.TextUtils; import android.util.LocalLog; import android.util.Log; -import android.util.SparseArray; import android.util.Xml; import com.android.ims.ImsCall; @@ -80,15 +88,17 @@ import com.android.internal.telephony.data.DataNetworkController; import com.android.internal.telephony.data.DataSettingsManager; import com.android.internal.telephony.data.LinkBandwidthEstimator; -import com.android.internal.telephony.dataconnection.DataConnectionReasons; -import com.android.internal.telephony.dataconnection.DataEnabledSettings; -import com.android.internal.telephony.dataconnection.DcTracker; -import com.android.internal.telephony.dataconnection.TransportManager; +import com.android.internal.telephony.domainselection.DomainSelectionResolver; +import com.android.internal.telephony.emergency.EmergencyConstants; import com.android.internal.telephony.emergency.EmergencyNumberTracker; +import com.android.internal.telephony.emergency.EmergencyStateTracker; +import com.android.internal.telephony.imsphone.ImsCallInfo; import com.android.internal.telephony.imsphone.ImsPhone; import com.android.internal.telephony.imsphone.ImsPhoneCall; import com.android.internal.telephony.metrics.SmsStats; import com.android.internal.telephony.metrics.VoiceCallSessionStats; +import com.android.internal.telephony.subscription.SubscriptionInfoInternal; +import com.android.internal.telephony.subscription.SubscriptionManagerService; import com.android.internal.telephony.test.SimulatedRadioControl; import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppType; import com.android.internal.telephony.uicc.IccFileHandler; @@ -172,7 +182,8 @@ protected static final int EVENT_RADIO_ON = 5; protected static final int EVENT_GET_BASEBAND_VERSION_DONE = 6; protected static final int EVENT_USSD = 7; - protected static final int EVENT_RADIO_OFF_OR_NOT_AVAILABLE = 8; + @VisibleForTesting + public static final int EVENT_RADIO_OFF_OR_NOT_AVAILABLE = 8; private static final int EVENT_GET_SIM_STATUS_DONE = 11; protected static final int EVENT_SET_CALL_FORWARD_DONE = 12; protected static final int EVENT_GET_CALL_FORWARD_DONE = 13; @@ -222,11 +233,8 @@ // Radio state change protected static final int EVENT_RADIO_STATE_CHANGED = 47; protected static final int EVENT_SET_CARRIER_DATA_ENABLED = 48; - protected static final int EVENT_DEVICE_PROVISIONED_CHANGE = 49; - protected static final int EVENT_DEVICE_PROVISIONING_DATA_SETTING_CHANGE = 50; protected static final int EVENT_GET_AVAILABLE_NETWORKS_DONE = 51; - private static final int EVENT_ALL_DATA_DISCONNECTED = 52; protected static final int EVENT_UICC_APPS_ENABLEMENT_STATUS_CHANGED = 53; protected static final int EVENT_UICC_APPS_ENABLEMENT_SETTING_CHANGED = 54; protected static final int EVENT_GET_UICC_APPS_ENABLEMENT_DONE = 55; @@ -239,8 +247,12 @@ protected static final int EVENT_SUBSCRIPTIONS_CHANGED = 62; protected static final int EVENT_GET_USAGE_SETTING_DONE = 63; protected static final int EVENT_SET_USAGE_SETTING_DONE = 64; + protected static final int EVENT_IMS_DEREGISTRATION_TRIGGERED = 65; + protected static final int EVENT_SET_NULL_CIPHER_AND_INTEGRITY_DONE = 66; + protected static final int EVENT_GET_DEVICE_IMEI_DONE = 67; + protected static final int EVENT_TRIGGER_NOTIFY_ANBR = 68; - protected static final int EVENT_LAST = EVENT_SET_USAGE_SETTING_DONE; + protected static final int EVENT_LAST = EVENT_TRIGGER_NOTIFY_ANBR; // For shared prefs. private static final String GSM_ROAMING_LIST_OVERRIDE_PREFIX = "gsm_roaming_list_"; @@ -267,6 +279,10 @@ // Integer used to let the calling application know that the we are ignoring auto mode switch. private static final int ALREADY_IN_AUTO_SELECTION = 1; + public static final String PREF_NULL_CIPHER_AND_INTEGRITY_ENABLED = + "pref_null_cipher_and_integrity_enabled"; + private final TelephonyAdminReceiver m2gAdminUpdater; + /** * This method is invoked when the Phone exits Emergency Callback Mode. */ @@ -303,11 +319,6 @@ public CommandsInterface mCi; protected int mVmCount = 0; private boolean mDnsCheckDisabled; - // Data connection trackers. For each transport type (e.g. WWAN, WLAN), there will be a - // corresponding DcTracker. The WWAN DcTracker is for cellular data connections while - // WLAN DcTracker is for IWLAN data connection. For IWLAN legacy mode, only one (WWAN) DcTracker - // will be created. - protected final SparseArray<DcTracker> mDcTrackers = new SparseArray<>(); protected DataNetworkController mDataNetworkController; /* Used for dispatching signals to configured carrier apps */ protected CarrierSignalAgent mCarrierSignalAgent; @@ -340,15 +351,13 @@ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) protected AtomicReference<UiccCardApplication> mUiccApplication = new AtomicReference<UiccCardApplication>(); - TelephonyTester mTelephonyTester; + private TelephonyTester mTelephonyTester; private String mName; private final String mActionDetached; private final String mActionAttached; protected DeviceStateMonitor mDeviceStateMonitor; protected DisplayInfoController mDisplayInfoController; - protected TransportManager mTransportManager; protected AccessNetworksManager mAccessNetworksManager; - protected DataEnabledSettings mDataEnabledSettings; // Used for identify the carrier of current subscription protected CarrierResolver mCarrierResolver; protected SignalStrengthController mSignalStrengthController; @@ -399,7 +408,7 @@ public static final String EXTRA_KEY_ALERT_SHOW = "alertShow"; public static final String EXTRA_KEY_NOTIFICATION_MESSAGE = "notificationMessage"; - private final RegistrantList mPreciseCallStateRegistrants = new RegistrantList(); + protected final RegistrantList mPreciseCallStateRegistrants = new RegistrantList(); private final RegistrantList mHandoverRegistrants = new RegistrantList(); @@ -428,8 +437,6 @@ protected final RegistrantList mEmergencyCallToggledRegistrants = new RegistrantList(); - private final RegistrantList mAllDataDisconnectedRegistrants = new RegistrantList(); - private final RegistrantList mCellInfoRegistrants = new RegistrantList(); private final RegistrantList mRedialRegistrants = new RegistrantList(); @@ -449,6 +456,8 @@ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) protected final Context mContext; + protected SubscriptionManagerService mSubscriptionManagerService; + /** * PhoneNotifier is an abstraction for all system-wide * state change notification. DefaultPhoneNotifier is @@ -474,9 +483,9 @@ protected LinkBandwidthEstimator mLinkBandwidthEstimator; - /** The flag indicating using the new data stack or not. */ - // This flag and the old data stack code will be deleted in Android 14. - private final boolean mNewDataStackEnabled; + public static final int IMEI_TYPE_UNKNOWN = -1; + public static final int IMEI_TYPE_PRIMARY = ImeiInfo.ImeiType.PRIMARY; + public static final int IMEI_TYPE_SECONDARY = ImeiInfo.ImeiType.SECONDARY; public IccRecords getIccRecords() { return mIccRecords.get(); @@ -580,10 +589,6 @@ .makeAppSmsManager(context); mLocalLog = new LocalLog(64); - if (TelephonyUtils.IS_DEBUGGABLE) { - mTelephonyTester = new TelephonyTester(this); - } - setUnitTestMode(unitTestMode); SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context); @@ -619,13 +624,17 @@ // Initialize SMS stats mSmsStats = new SmsStats(this); - mNewDataStackEnabled = !mContext.getResources().getBoolean( - com.android.internal.R.bool.config_force_disable_telephony_new_data_stack); + mSubscriptionManagerService = SubscriptionManagerService.getInstance(); + m2gAdminUpdater = new TelephonyAdminReceiver(context, this); if (getPhoneType() == PhoneConstants.PHONE_TYPE_IMS) { return; } + if (TelephonyUtils.IS_DEBUGGABLE) { + mTelephonyTester = new TelephonyTester(this); + } + // Initialize device storage and outgoing SMS usage monitors for SMSDispatchers. mTelephonyComponentFactory = telephonyComponentFactory; mSmsStorageMonitor = mTelephonyComponentFactory.inject(SmsStorageMonitor.class.getName()) @@ -814,11 +823,6 @@ break; } - case EVENT_ALL_DATA_DISCONNECTED: - if (areAllDataDisconnected()) { - mAllDataDisconnectedRegistrants.notifyRegistrants(); - } - break; case EVENT_GET_USAGE_SETTING_DONE: ar = (AsyncResult) msg.obj; if (ar.exception == null) { @@ -871,7 +875,11 @@ return null; } - public void notifySrvccState(Call.SrvccState state) { + /** + * Notifies the change of the SRVCC state. + * @param state the new SRVCC state. + */ + public void notifySrvccState(@SrvccState int state) { } public void registerForSilentRedial(Handler h, int what, Object obj) { @@ -894,6 +902,9 @@ Call.SrvccState srvccState = Call.SrvccState.NONE; if (ret != null && ret.length != 0) { int state = ret[0]; + if (imsPhone != null) { + imsPhone.notifySrvccState(state); + } switch(state) { case TelephonyManager.SRVCC_STATE_HANDOVER_STARTED: srvccState = Call.SrvccState.STARTED; @@ -906,11 +917,6 @@ break; case TelephonyManager.SRVCC_STATE_HANDOVER_COMPLETED: srvccState = Call.SrvccState.COMPLETED; - if (imsPhone != null) { - imsPhone.notifySrvccState(srvccState); - } else { - Rlog.d(LOG_TAG, "HANDOVER_COMPLETED: mImsPhone null"); - } break; case TelephonyManager.SRVCC_STATE_HANDOVER_FAILED: case TelephonyManager.SRVCC_STATE_HANDOVER_CANCELED: @@ -985,17 +991,6 @@ } /** - * Subclasses of Phone probably want to replace this with a - * version scoped to their packages - */ - protected void notifyPreciseCallStateChangedP() { - AsyncResult ar = new AsyncResult(null, this, null); - mPreciseCallStateRegistrants.notifyRegistrants(ar); - - mNotifier.notifyPreciseCallState(this); - } - - /** * Notifies when a Handover happens due to SRVCC or Silent Redial */ public void registerForHandoverStateChanged(Handler h, int what, Object obj) { @@ -1442,8 +1437,8 @@ @UnsupportedAppUsage public void setNetworkSelectionModeAutomatic(Message response) { Rlog.d(LOG_TAG, "setNetworkSelectionModeAutomatic, querying current mode"); - // we don't want to do this unecesarily - it acutally causes - // the radio to repeate network selection and is costly + // we don't want to do this unnecessarily - it actually causes + // the radio to repeat network selection and is costly // first check if we're already in automatic mode Message msg = obtainMessage(EVENT_CHECK_FOR_NETWORK_AUTOMATIC); msg.obj = response; @@ -1476,6 +1471,7 @@ nsm.operatorAlphaShort = ""; if (doAutomatic) { + Rlog.d(LOG_TAG, "setNetworkSelectionModeAutomatic - set network selection auto"); Message msg = obtainMessage(EVENT_SET_NETWORK_AUTOMATIC_COMPLETE, nsm); mCi.setNetworkSelectionModeAutomatic(msg); } else { @@ -1932,7 +1928,7 @@ public boolean isRadioOffForThermalMitigation() { ServiceStateTracker sst = getServiceStateTracker(); return sst != null && sst.getRadioPowerOffReasons() - .contains(Phone.RADIO_POWER_REASON_THERMAL); + .contains(TelephonyManager.RADIO_POWER_REASON_THERMAL); } /** @@ -1951,13 +1947,6 @@ } /** - * @return The instance of transport manager. - */ - public TransportManager getTransportManager() { - return null; - } - - /** * @return The instance of access networks manager. */ public AccessNetworksManager getAccessNetworksManager() { @@ -2326,6 +2315,12 @@ if (!mIsCarrierNrSupported) { allowedNetworkTypes &= ~TelephonyManager.NETWORK_TYPE_BITMASK_NR; } + if (m2gAdminUpdater.isCellular2gDisabled()) { + logd("SubId " + getSubId() + + " disabling 2g in getEffectiveAllowedNetworkTypes according to admin user " + + "restriction"); + allowedNetworkTypes &= ~TelephonyManager.NETWORK_CLASS_BITMASK_2G; + } logd("SubId" + getSubId() + ",getEffectiveAllowedNetworkTypes: " + TelephonyManager.convertNetworkTypeBitmaskToString(allowedNetworkTypes)); return allowedNetworkTypes; @@ -2401,17 +2396,18 @@ * Loads the allowed network type from subscription database. */ public void loadAllowedNetworksFromSubscriptionDatabase() { - // Try to load ALLOWED_NETWORK_TYPES from SIMINFO. - if (SubscriptionController.getInstance() == null) { - return; + String result = null; + SubscriptionInfoInternal subInfo = mSubscriptionManagerService + .getSubscriptionInfoInternal(getSubId()); + if (subInfo != null) { + result = subInfo.getAllowedNetworkTypesForReasons(); } - 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) { + mIsAllowedNetworkTypesLoadedFromDb = SubscriptionManager.isValidSubscriptionId( + getSubId()); + + if (TextUtils.isEmpty(result)) { return; } @@ -2421,14 +2417,14 @@ try { // Format: "REASON=VALUE,REASON2=VALUE2" for (String pair : result.trim().split(",")) { - String[] networkTypesValues = (pair.trim().toLowerCase()).split("="); + String[] networkTypesValues = (pair.trim().toLowerCase(Locale.ROOT)).split("="); if (networkTypesValues.length != 2) { Rlog.e(LOG_TAG, "Invalid ALLOWED_NETWORK_TYPES from DB, value = " + pair); continue; } int key = convertAllowedNetworkTypeDbNameToMapIndex(networkTypesValues[0]); long value = Long.parseLong(networkTypesValues[1]); - if (key != INVALID_ALLOWED_NETWORK_TYPES + if (TelephonyManager.isValidAllowedNetworkTypesReason(key) && value != INVALID_ALLOWED_NETWORK_TYPES) { synchronized (mAllowedNetworkTypesForReasons) { mAllowedNetworkTypesForReasons.put(key, value); @@ -2466,7 +2462,15 @@ } } - private String convertAllowedNetworkTypeMapIndexToDbName(int reason) { + /** + * Convert the allowed network types reason to string. + * + * @param reason The allowed network types reason. + * + * @return The converted string. + */ + public static String convertAllowedNetworkTypeMapIndexToDbName( + @TelephonyManager.AllowedNetworkTypesReason int reason) { switch (reason) { case TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER: return ALLOWED_NETWORK_TYPES_TEXT_USER; @@ -2477,7 +2481,10 @@ case TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G: return ALLOWED_NETWORK_TYPES_TEXT_ENABLE_2G; default: - return Integer.toString(INVALID_ALLOWED_NETWORK_TYPES); + throw new IllegalArgumentException( + "No DB name conversion available for allowed network type reason: " + reason + + ". Did you forget to add an ALLOWED_NETWORK_TYPE_TEXT entry for" + + " a new reason?"); } } @@ -2977,6 +2984,9 @@ // This property is used to handle phone process crashes, and is the same for CDMA and IMS // phones protected static boolean getInEcmMode() { + if (DomainSelectionResolver.getInstance().isDomainSelectionSupported()) { + return EmergencyStateTracker.getInstance().isInEcm(); + } return TelephonyProperties.in_ecm_mode().orElse(false); } @@ -2986,6 +2996,9 @@ * emergency operator. */ public boolean isInEcm() { + if (DomainSelectionResolver.getInstance().isDomainSelectionSupported()) { + return EmergencyStateTracker.getInstance().isInEcm(); + } return mIsPhoneInEcmState; } @@ -2994,6 +3007,9 @@ } public boolean isInCdmaEcm() { + if (DomainSelectionResolver.getInstance().isDomainSelectionSupported()) { + return EmergencyStateTracker.getInstance().isInCdmaEcm(); + } return getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA && isInEcm() && (mImsPhone == null || !mImsPhone.isInImsEcm()); } @@ -3730,18 +3746,11 @@ * @return true if there is a matching DUN APN. */ public boolean hasMatchedTetherApnSetting() { - if (isUsingNewDataStack()) { - NetworkRegistrationInfo nrs = getServiceState().getNetworkRegistrationInfo( - NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN); - if (nrs != null) { - return getDataNetworkController().getDataProfileManager() - .isTetheringDataProfileExisting(nrs.getAccessNetworkTechnology()); - } - return false; - } - if (getDcTracker(AccessNetworkConstants.TRANSPORT_TYPE_WWAN) != null) { - return getDcTracker(AccessNetworkConstants.TRANSPORT_TYPE_WWAN) - .hasMatchedTetherApnSetting(); + NetworkRegistrationInfo nrs = getServiceState().getNetworkRegistrationInfo( + NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN); + if (nrs != null) { + return getDataNetworkController().getDataProfileManager() + .isTetheringDataProfileExisting(nrs.getAccessNetworkTechnology()); } return false; } @@ -3752,31 +3761,10 @@ * @return {@code true} if internet data is allowed to be established. */ public boolean isDataAllowed() { - if (isUsingNewDataStack()) { - return getDataNetworkController().isInternetDataAllowed(); - } - return isDataAllowed(ApnSetting.TYPE_DEFAULT, null); + return getDataNetworkController().isInternetDataAllowed(); } /** - * Report on whether data connectivity is allowed. - * - * @param apnType APN type - * @param reasons The reasons that data can/can't be established. This is an output param. - * @return True if data is allowed to be established - */ - public boolean isDataAllowed(@ApnType int apnType, DataConnectionReasons reasons) { - if (mAccessNetworksManager != null) { - int transport = mAccessNetworksManager.getCurrentTransport(apnType); - if (getDcTracker(transport) != null) { - return getDcTracker(transport).isDataAllowed(reasons); - } - } - return false; - } - - - /** * Action set from carrier signalling broadcast receivers to enable/disable metered apns. */ public void carrierActionSetMeteredApnsEnabled(boolean enabled) { @@ -3932,16 +3920,6 @@ return null; } - /** - * Get the current for the default apn DataState. No change notification - * exists at this interface -- use - * {@link android.telephony.PhoneStateListener} instead. - */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - public PhoneConstants.DataState getDataConnectionState() { - return getDataConnectionState(ApnSetting.TYPE_DEFAULT_STRING); - } - public void notifyCallForwardingIndicator() { } @@ -4117,14 +4095,7 @@ */ @UnsupportedAppUsage public int getSubId() { - if (SubscriptionController.getInstance() == null) { - // TODO b/78359408 getInstance sometimes returns null in Treehugger tests, which causes - // flakiness. Even though we haven't seen this crash in the wild we should keep this - // check in until we've figured out the root cause. - Rlog.e(LOG_TAG, "SubscriptionController.getInstance = null! Returning default subId"); - return SubscriptionManager.DEFAULT_SUBSCRIPTION_ID; - } - return SubscriptionController.getInstance().getSubIdUsingPhoneId(mPhoneId); + return mSubscriptionManagerService.getSubId(mPhoneId); } /** @@ -4420,6 +4391,7 @@ // When radio capability switch is done, query IMEI value and update it in Phone objects // to make it in sync with the IMEI value currently used by Logical-Modem. if (capabilitySwitched) { + mCi.getImei(obtainMessage(EVENT_GET_DEVICE_IMEI_DONE)); mCi.getDeviceIdentity(obtainMessage(EVENT_GET_DEVICE_IDENTITY_DONE)); } } @@ -4438,7 +4410,12 @@ } private int getResolvedUsageSetting(int subId) { - SubscriptionInfo subInfo = SubscriptionController.getInstance().getSubscriptionInfo(subId); + SubscriptionInfo subInfo = null; + SubscriptionInfoInternal subInfoInternal = mSubscriptionManagerService + .getSubscriptionInfoInternal(subId); + if (subInfoInternal != null) { + subInfo = subInfoInternal.toSubscriptionInfo(); + } if (subInfo == null || subInfo.getUsageSetting() == SubscriptionManager.USAGE_SETTING_UNKNOWN) { @@ -4621,42 +4598,6 @@ return false; } - /** - * @return True if all data connections are disconnected. - */ - public boolean areAllDataDisconnected() { - if (mAccessNetworksManager != null) { - for (int transport : mAccessNetworksManager.getAvailableTransports()) { - if (getDcTracker(transport) != null - && !getDcTracker(transport).areAllDataDisconnected()) { - return false; - } - } - } - return true; - } - - public void registerForAllDataDisconnected(Handler h, int what) { - mAllDataDisconnectedRegistrants.addUnique(h, what, null); - if (mAccessNetworksManager != null) { - for (int transport : mAccessNetworksManager.getAvailableTransports()) { - if (getDcTracker(transport) != null - && !getDcTracker(transport).areAllDataDisconnected()) { - getDcTracker(transport).registerForAllDataDisconnected( - this, EVENT_ALL_DATA_DISCONNECTED); - } - } - } - } - - public void unregisterForAllDataDisconnected(Handler h) { - mAllDataDisconnectedRegistrants.remove(h); - } - - public DataEnabledSettings getDataEnabledSettings() { - return mDataEnabledSettings; - } - @UnsupportedAppUsage public IccSmsInterfaceManager getIccSmsInterfaceManager(){ return null; @@ -4787,16 +4728,6 @@ return isEmergencyCallOnly; } - /** - * Get data connection tracker based on the transport type - * - * @param transportType Transport type defined in AccessNetworkConstants.TransportType - * @return The data connection tracker. Null if not found. - */ - public @Nullable DcTracker getDcTracker(int transportType) { - return mDcTrackers.get(transportType); - } - // Return true if either CSIM or RUIM app is present. By default it returns false. public boolean isCdmaSubscriptionAppPresent() { return false; @@ -4820,10 +4751,24 @@ * Get the HAL version. * * @return the current HalVersion + * + * @deprecated Use {@link #getHalVersion(int service)} instead. */ + @Deprecated public HalVersion getHalVersion() { + return getHalVersion(HAL_SERVICE_RADIO); + } + + /** + * Get the HAL version with a specific service. + * + * @param service the service id to query + * @return the current HalVersion for a specific service + * + */ + public HalVersion getHalVersion(@HalService int service) { if (mCi != null && mCi instanceof RIL) { - return ((RIL) mCi).getHalVersion(); + return ((RIL) mCi).getHalVersion(service); } return RIL.RADIO_HAL_VERSION_UNKNOWN; } @@ -4967,11 +4912,600 @@ } /** - * @return {@code true} if using the new telephony data stack. + * Returns the user's last setting for terminal-based call waiting + * @param forCsOnly indicates the caller expects the result for CS calls only */ - // This flag and the old data stack code will be deleted in Android 14. - public boolean isUsingNewDataStack() { - return mNewDataStackEnabled; + public int getTerminalBasedCallWaitingState(boolean forCsOnly) { + return CallWaitingController.TERMINAL_BASED_NOT_SUPPORTED; + } + + /** + * Notifies the change of the user setting of the terminal-based call waiting service + * to IMS service. + */ + public void setTerminalBasedCallWaitingStatus(int state) { + } + + /** + * Notifies that the IMS service connected supports the terminal-based call waiting service + */ + public void setTerminalBasedCallWaitingSupported(boolean supported) { + } + + /** + * Notifies the NAS and RRC layers of the radio the type of upcoming IMS traffic. + * + * @param token A nonce to identify the request. + * @param trafficType IMS traffic type like registration, voice, video, SMS, emergency, and etc. + * @param accessNetworkType The type of the radio access network used. + * @param trafficDirection Indicates whether traffic is originated by mobile originated or + * mobile terminated use case eg. MO/MT call/SMS etc. + * @param response is callback message. + */ + public void startImsTraffic(int token, + @MmTelFeature.ImsTrafficType int trafficType, + @AccessNetworkConstants.RadioAccessNetworkType int accessNetworkType, + @MmTelFeature.ImsTrafficDirection int trafficDirection, Message response) { + mCi.startImsTraffic(token, trafficType, accessNetworkType, trafficDirection, response); + } + + /** + * Notifies IMS traffic has been stopped. + * + * @param token The token assigned by startImsTraffic. + * @param response is callback message. + */ + public void stopImsTraffic(int token, Message response) { + mCi.stopImsTraffic(token, response); + } + + /** + * Register for notifications of connection setup failure + * + * @param h Handler for notification message. + * @param what User-defined message code. + * @param obj User object. + */ + public void registerForConnectionSetupFailure(Handler h, int what, Object obj) { + mCi.registerForConnectionSetupFailure(h, what, obj); + } + + /** + * Unregister for notifications of connection setup failure + * + * @param h Handler to be removed from the registrant list. + */ + public void unregisterForConnectionSetupFailure(Handler h) { + mCi.unregisterForConnectionSetupFailure(h); + } + + /** + * Triggers the UE initiated EPS fallback procedure. + * + * @param reason specifies the reason for EPS fallback. + * @param response is callback message. + */ + public void triggerEpsFallback(@MmTelFeature.EpsFallbackReason int reason, Message response) { + mCi.triggerEpsFallback(reason, response); + } + + /** + * Notifies the recommended bit rate for the indicated logical channel and direction. + * + * @param mediaType MediaType is used to identify media stream such as audio or video. + * @param direction Direction of this packet stream (e.g. uplink or downlink). + * @param bitsPerSecond The recommended bit rate for the UE for a specific logical channel and + * a specific direction by NW. + */ + public void triggerNotifyAnbr(int mediaType, int direction, int bitsPerSecond) { + } + + /** + * Sets the emergency mode + * + * @param emcMode The radio emergency mode type. + * @param result Callback message. + */ + public void setEmergencyMode(@EmergencyConstants.EmergencyMode int emcMode, + @Nullable Message result) { + mCi.setEmergencyMode(emcMode, result); + } + + /** + * Triggers an emergency network scan. + * + * @param accessNetwork Contains the list of access network types to be prioritized + * during emergency scan. The 1st entry has the highest priority. + * @param scanType Indicates the type of scans to be performed i.e. limited scan, + * full service scan or any scan. + * @param result Callback message. + */ + public void triggerEmergencyNetworkScan( + @NonNull @AccessNetworkConstants.RadioAccessNetworkType int[] accessNetwork, + @DomainSelectionService.EmergencyScanType int scanType, @Nullable Message result) { + mCi.triggerEmergencyNetworkScan(accessNetwork, scanType, result); + } + + /** + * Cancels ongoing emergency network scan + * @param resetScan Indicates how the next {@link #triggerEmergencyNetworkScan} should work. + * If {@code true}, then the modem shall start the new scan from the beginning, + * otherwise the modem shall resume from the last search. + * @param result Callback message. + */ + public void cancelEmergencyNetworkScan(boolean resetScan, @Nullable Message result) { + mCi.cancelEmergencyNetworkScan(resetScan, result); + } + + /** + * Exits ongoing emergency mode + * @param result Callback message. + */ + public void exitEmergencyMode(@Nullable Message result) { + mCi.exitEmergencyMode(result); + } + + /** + * Registers for emergency network scan result. + * + * @param h Handler for notification message. + * @param what User-defined message code. + * @param obj User object. + */ + public void registerForEmergencyNetworkScan(@NonNull Handler h, + int what, @Nullable Object obj) { + mCi.registerForEmergencyNetworkScan(h, what, obj); + } + + /** + * Unregisters for emergency network scan result. + * + * @param h Handler to be removed from the registrant list. + */ + public void unregisterForEmergencyNetworkScan(@NonNull Handler h) { + mCi.unregisterForEmergencyNetworkScan(h); + } + + /** + * Notifies that IMS deregistration is triggered. + * + * @param reason the reason why the deregistration is triggered. + */ + public void triggerImsDeregistration( + @ImsRegistrationImplBase.ImsDeregistrationReason int reason) { + if (mImsPhone != null) { + mImsPhone.triggerImsDeregistration(reason); + } + } + + /** + * Registers for the domain selected for emergency calls. + * + * @param h Handler for notification message. + * @param what User-defined message code. + * @param obj User object. + */ + public void registerForEmergencyDomainSelected( + @NonNull Handler h, int what, @Nullable Object obj) { + } + + /** + * Unregisters for the domain selected for emergency calls. + * + * @param h Handler to be removed from the registrant list. + */ + public void unregisterForEmergencyDomainSelected(@NonNull Handler h) { + } + + /** + * Notifies the domain selected. + * + * @param transportType The preferred transport type. + */ + public void notifyEmergencyDomainSelected( + @AccessNetworkConstants.TransportType int transportType) { + } + + /** + * @return Telephony tester instance. + */ + public @Nullable TelephonyTester getTelephonyTester() { + return mTelephonyTester; + } + + /** + * @return User handle associated with the phone's subscription id. {@code null} if subscription + * is invalid or not found. + */ + @Nullable + public UserHandle getUserHandle() { + int subId = getSubId(); + + UserHandle userHandle = null; + try { + SubscriptionManager subManager = mContext.getSystemService(SubscriptionManager.class); + if (subManager != null) { + userHandle = subManager.getSubscriptionUserHandle(subId); + } + } catch (IllegalArgumentException ex) { + loge("getUserHandle: ex=" + ex); + } + + return userHandle; + } + + /** + * Checks if the context user is a managed profile. + * + * Note that this applies specifically to <em>managed</em> profiles. + * + * @return whether the context user is a managed profile. + */ + public boolean isManagedProfile() { + UserHandle userHandle = getUserHandle(); + UserManager userManager = mContext.getSystemService(UserManager.class); + if (userHandle == null || userManager == null) return false; + return userManager.isManagedProfile(userHandle.getIdentifier()); + } + + /** + * @return global null cipher and integrity enabled preference + */ + public boolean getNullCipherAndIntegrityEnabledPreference() { + SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext()); + return sp.getBoolean(PREF_NULL_CIPHER_AND_INTEGRITY_ENABLED, true); + } + + /** + * @return whether or not this Phone interacts with a modem that supports the null cipher + * and integrity feature. + */ + public boolean isNullCipherAndIntegritySupported() { + return false; + } + + /** + * Override to implement handling of an update to the enablement of the null cipher and + * integrity preference. + * {@see #PREF_NULL_CIPHER_AND_INTEGRITY_ENABLED} + */ + public void handleNullCipherEnabledChange() { + } + + /** + * Notifies the IMS call status to the modem. + * + * @param imsCallInfo The list of {@link ImsCallInfo}. + * @param response A callback to receive the response. + */ + public void updateImsCallStatus(@NonNull List<ImsCallInfo> imsCallInfo, Message response) { + mCi.updateImsCallStatus(imsCallInfo, response); + } + + /** + * Enables or disables N1 mode (access to 5G core network) in accordance with + * 3GPP TS 24.501 4.9. + * @param enable {@code true} to enable N1 mode, {@code false} to disable N1 mode. + * @param result Callback message to receive the result. + */ + public void setN1ModeEnabled(boolean enable, Message result) { + mCi.setN1ModeEnabled(enable, result); + } + + /** + * Check whether N1 mode (access to 5G core network) is enabled or not. + * @param result Callback message to receive the result. + */ + public void isN1ModeEnabled(Message result) { + mCi.isN1ModeEnabled(result); + } + + /** + * Return current cell broadcast ranges. + */ + public List<CellBroadcastIdRange> getCellBroadcastIdRanges() { + return new ArrayList<>(); + } + + /** + * Set reception of cell broadcast messages with the list of the given ranges. + */ + public void setCellBroadcastIdRanges( + @NonNull List<CellBroadcastIdRange> ranges, Consumer<Integer> callback) { + callback.accept(TelephonyManager.CELL_BROADCAST_RESULT_UNSUPPORTED); + } + + /** + * Start receiving satellite position updates. + * This can be called by the pointing UI when the user starts pointing to the satellite. + * Modem should continue to report the pointing input as the device or satellite moves. + * + * @param result The Message to send to result of the operation to. + **/ + public void startSatellitePositionUpdates(Message result) { + mCi.startSendingSatellitePointingInfo(result); + } + + /** + * Stop receiving satellite position updates. + * This can be called by the pointing UI when the user stops pointing to the satellite. + * + * @param result The Message to send to result of the operation to. + **/ + public void stopSatellitePositionUpdates(Message result) { + mCi.stopSendingSatellitePointingInfo(result); + } + + /** + * Get maximum number of characters per text message on satellite. + * @param result The Message to send the result of the operation to. + */ + public void getMaxCharactersPerSatelliteTextMessage(Message result) { + mCi.getMaxCharactersPerSatelliteTextMessage(result); + } + + /** + * Power on or off the satellite modem. + * @param result The Message to send the result of the operation to. + * @param powerOn {@code true} to power on the satellite modem and {@code false} to power off. + */ + public void setSatellitePower(Message result, boolean powerOn) { + mCi.setSatellitePower(result, powerOn); + } + + /** + * Check whether the satellite modem is powered on. + * @param result The Message to send the result of the operation to. + */ + public void isSatellitePowerOn(Message result) { + mCi.getSatellitePowerState(result); + } + + /** + * Check whether the satellite service is supported on the device. + * @param result The Message to send the result of the operation to. + */ + public void isSatelliteSupported(Message result) { + mCi.isSatelliteSupported(result); + } + + /** + * Check whether the satellite modem is provisioned. + * @param result The Message to send the result of the operation to. + */ + public void isSatelliteProvisioned(Message result) { + mCi.getSatelliteProvisionState(result); + } + + /** + * Get the satellite capabilities. + * @param result The Message to send the result of the operation to. + */ + public void getSatelliteCapabilities(Message result) { + mCi.getSatelliteCapabilities(result); + } + + /** + * Registers for pointing info changed from satellite modem. + * + * @param h Handler for notification message. + * @param what User-defined message code. + * @param obj User object. + */ + public void registerForSatellitePositionInfoChanged(@NonNull Handler h, + int what, @Nullable Object obj) { + //TODO: Rename CommandsInterface and other modules when updating HAL APIs. + mCi.registerForSatellitePointingInfoChanged(h, what, obj); + } + + /** + * Unregisters for pointing info changed from satellite modem. + * + * @param h Handler to be removed from the registrant list. + */ + public void unregisterForSatellitePositionInfoChanged(@NonNull Handler h) { + //TODO: Rename CommandsInterface and other modules when updating HAL APIs. + mCi.unregisterForSatellitePointingInfoChanged(h); + } + + /** + * Registers for datagrams delivered events from satellite modem. + * + * @param h Handler for notification message. + * @param what User-defined message code. + * @param obj User object. + */ + public void registerForSatelliteDatagramsDelivered(@NonNull Handler h, + int what, @Nullable Object obj) { + //TODO: Remove. + mCi.registerForSatelliteMessagesTransferComplete(h, what, obj); + } + + /** + * Unregisters for datagrams delivered events from satellite modem. + * + * @param h Handler to be removed from the registrant list. + */ + public void unregisterForSatelliteDatagramsDelivered(@NonNull Handler h) { + //TODO: Remove. + mCi.unregisterForSatelliteMessagesTransferComplete(h); + } + + /** + * Provision the subscription with a satellite provider. + * This is needed to register the device/subscription if the provider allows dynamic + * registration. + * + * @param result Callback message to receive the result. + * @param token The token of the device/subscription to be provisioned. + */ + public void provisionSatelliteService(Message result, String token) { + // TODO: update parameters in HAL + // mCi.provisionSatelliteService(result, token); + } + + /** + * Deprovision the device/subscription with a satellite provider. + * This is needed to unregister the device/subscription if the provider allows dynamic + * registration. + * If provisioning is in progress for the given SIM, cancel the request. + * If there is no request in progress, deprovision the given SIM. + * + * @param result Callback message to receive the result. + * @param token The token of the device/subscription to be deprovisioned. + */ + public void deprovisionSatelliteService(Message result, String token) { + //TODO (b/266126070): add implementation. + } + + /** + * Register for a satellite provision state changed event. + * + * @param h Handler for notification message. + * @param what User-defined message code. + * @param obj User object. + */ + public void registerForSatelliteProvisionStateChanged(Handler h, int what, Object obj) { + mCi.registerForSatelliteProvisionStateChanged(h, what, obj); + } + + /** + * Unregister for a satellite provision state changed event. + * + * @param h Handler to be removed from the registrant list. + */ + public void unregisterForSatelliteProvisionStateChanged(Handler h) { + mCi.unregisterForSatelliteProvisionStateChanged(h); + } + + /** + * Get the list of provisioned satellite features. + * + * @param result Callback message to receive the result. + */ + public void getProvisionedSatelliteFeatures(Message result) { + //TODO (b/266126070): add implementation. + } + + /** + * Registers for satellite state changed from satellite modem. + * + * @param h Handler for notification message. + * @param what User-defined message code. + * @param obj User object. + */ + public void registerForSatelliteModemStateChanged(@NonNull Handler h, int what, + @Nullable Object obj) { + mCi.registerForSatelliteModeChanged(h, what, obj); + } + + /** + * Unregisters for satellite state changed from satellite modem. + * + * @param h Handler to be removed from registrant list. + */ + public void unregisterForSatelliteModemStateChanged(@NonNull Handler h) { + mCi.unregisterForSatelliteModeChanged(h); + } + + /** + * Registers for pending datagram count info from satellite modem. + * + * @param h Handler for notification message. + * @param what User-defined message code. + * @param obj User object. + */ + public void registerForPendingDatagramCount(@NonNull Handler h, int what, + @Nullable Object obj) { + mCi.registerForPendingSatelliteMessageCount(h, what, obj); + } + + /** + * Unregisters for pending datagram count info from satellite modem. + * + * @param h Handler to be removed from registrant list. + */ + public void unregisterForPendingDatagramCount(@NonNull Handler h) { + mCi.unregisterForPendingSatelliteMessageCount(h); + } + + /** + * Register to receive incoming datagrams over satellite. + * + * @param h Handler for notification message. + * @param what User-defined message code. + * @param obj User object. + */ + public void registerForSatelliteDatagramsReceived(@NonNull Handler h, int what, + @Nullable Object obj) { + // TODO: rename + mCi.registerForNewSatelliteMessages(h, what, obj); + } + + /** + * Unregister to stop receiving incoming datagrams over satellite. + * + * @param h Handler to be removed from registrant list. + */ + public void unregisterForSatelliteDatagramsReceived(@NonNull Handler h) { + // TODO: rename + mCi.unregisterForNewSatelliteMessages(h); + } + + /** + * Poll pending datagrams over satellite. + * @param result The Message to send the result of the operation to. + */ + public void pollPendingSatelliteDatagrams(Message result) { + //mCi.pollPendingSatelliteDatagrams(result); + } + + /** + * Send datagram over satellite. + * @param result The Message to send the result of the operation to. + * @param datagram Datagram to send over satellite. + * @param needFullScreenPointingUI this is used to indicate pointingUI app to open in + * full screen mode. + */ + public void sendSatelliteDatagram(Message result, SatelliteDatagram datagram, + boolean needFullScreenPointingUI) { + //mCi.sendSatelliteDatagram(result, datagram); + } + + /** + * Check whether satellite communication is allowed for the current location. + * @param result The Message to send the result of the operation to. + */ + public void isSatelliteCommunicationAllowedForCurrentLocation(Message result) { + mCi.isSatelliteCommunicationAllowedForCurrentLocation(result); + } + + /** + * Get the time after which the satellite will be visible. + * @param result The Message to send the result of the operation to. + */ + public void requestTimeForNextSatelliteVisibility(Message result) { + mCi.getTimeForNextSatelliteVisibility(result); + } + + /** + * Start callback mode + * @param type for callback mode entry. + */ + public void startCallbackMode(@TelephonyManager.EmergencyCallbackModeType int type) { + Rlog.d(LOG_TAG, "startCallbackMode:type=" + type); + mNotifier.notifyCallbackModeStarted(this, type); + } + + /** + * Stop callback mode + * @param type for callback mode exit. + * @param reason for stopping callback mode. + */ + public void stopCallbackMode(@TelephonyManager.EmergencyCallbackModeType int type, + @TelephonyManager.EmergencyCallbackModeStopReason int reason) { + Rlog.d(LOG_TAG, "stopCallbackMode:type=" + type + ", reason=" + reason); + mNotifier.notifyCallbackModeStopped(this, type, reason); } public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { @@ -4996,7 +5530,7 @@ pw.println(" isDnsCheckDisabled()=" + isDnsCheckDisabled()); pw.println(" getUnitTestMode()=" + getUnitTestMode()); pw.println(" getState()=" + getState()); - pw.println(" getIccSerialNumber()=" + getIccSerialNumber()); + pw.println(" getIccSerialNumber()=" + Rlog.pii(LOG_TAG, getIccSerialNumber())); pw.println(" getIccRecordsLoaded()=" + getIccRecordsLoaded()); pw.println(" getMessageWaitingIndicator()=" + getMessageWaitingIndicator()); pw.println(" getCallForwardingIndicator()=" + getCallForwardingIndicator()); @@ -5009,7 +5543,6 @@ pw.println(" needsOtaServiceProvisioning=" + needsOtaServiceProvisioning()); pw.println(" isInEmergencySmsMode=" + isInEmergencySmsMode()); pw.println(" isEcmCanceledForEmergency=" + isEcmCanceledForEmergency()); - pw.println(" isUsingNewDataStack=" + isUsingNewDataStack()); pw.println(" service state=" + getServiceState()); pw.flush(); pw.println("++++++++++++++++++++++++++++++++"); @@ -5025,16 +5558,6 @@ pw.println("++++++++++++++++++++++++++++++++"); } - if (mAccessNetworksManager != null) { - for (int transport : mAccessNetworksManager.getAvailableTransports()) { - if (getDcTracker(transport) != null) { - getDcTracker(transport).dump(fd, pw, args); - pw.flush(); - pw.println("++++++++++++++++++++++++++++++++"); - } - } - } - if (mDataNetworkController != null) { try { mDataNetworkController.dump(fd, pw, args);
diff --git a/src/java/com/android/internal/telephony/PhoneConfigurationManager.java b/src/java/com/android/internal/telephony/PhoneConfigurationManager.java index 05c325e..8b95824 100644 --- a/src/java/com/android/internal/telephony/PhoneConfigurationManager.java +++ b/src/java/com/android/internal/telephony/PhoneConfigurationManager.java
@@ -35,6 +35,7 @@ import android.util.Log; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.telephony.subscription.SubscriptionManagerService; import com.android.telephony.Rlog; import java.util.HashMap; @@ -71,6 +72,7 @@ private TelephonyManager mTelephonyManager; private static final RegistrantList sMultiSimConfigChangeRegistrants = new RegistrantList(); private static final String ALLOW_MOCK_MODEM_PROPERTY = "persist.radio.allow_mock_modem"; + private static final String BOOT_ALLOW_MOCK_MODEM_PROPERTY = "ro.boot.radio.allow_mock_modem"; private static final boolean DEBUG = !"user".equals(Build.TYPE); /** * Init method to instantiate the object @@ -122,6 +124,21 @@ } } + // If virtual DSDA is enabled for this UE, then updates maxActiveVoiceSubscriptions to 2. + private PhoneCapability maybeUpdateMaxActiveVoiceSubscriptions( + final PhoneCapability staticCapability) { + boolean enableVirtualDsda = mContext.getResources().getBoolean( + com.android.internal.R.bool.config_enable_virtual_dsda); + + if (staticCapability.getLogicalModemList().size() > 1 && enableVirtualDsda) { + return new PhoneCapability.Builder(staticCapability) + .setMaxActiveVoiceSubscriptions(2) + .build(); + } else { + return staticCapability; + } + } + /** * Static method to get instance. */ @@ -180,6 +197,8 @@ ar = (AsyncResult) msg.obj; if (ar != null && ar.exception == null) { mStaticCapability = (PhoneCapability) ar.result; + mStaticCapability = + maybeUpdateMaxActiveVoiceSubscriptions(mStaticCapability); notifyCapabilityChanged(); } else { log(msg.what + " failure. Not getting phone capability." + ar.exception); @@ -366,7 +385,7 @@ // eg if we are going from 2 phones to 1 phone, we need to deregister RIL for the // second phone. This loop does nothing if numOfActiveModems is increasing. for (int phoneId = numOfActiveModems; phoneId < oldNumOfActiveModems; phoneId++) { - SubscriptionController.getInstance().clearSubInfoRecord(phoneId); + SubscriptionManagerService.getInstance().markSubscriptionsInactive(phoneId); subInfoCleared = true; mPhones[phoneId].mCi.onSlotActiveStatusChange( SubscriptionManager.isValidPhoneId(phoneId)); @@ -400,7 +419,7 @@ + "setting VOICE & SMS subId to -1 (No Preference)"); //Set the default VOICE subId to -1 ("No Preference") - SubscriptionController.getInstance().setDefaultVoiceSubId( + SubscriptionManagerService.getInstance().setDefaultVoiceSubId( SubscriptionManager.INVALID_SUBSCRIPTION_ID); //TODO:: Set the default SMS sub to "No Preference". Tracking this bug (b/227386042) @@ -465,34 +484,46 @@ * @return true if the modem service is set successfully, false otherwise. */ public boolean setModemService(String serviceName) { - if (mRadioConfig == null || mPhones[0] == null) { - return false; - } - log("setModemService: " + serviceName); boolean statusRadioConfig = false; boolean statusRil = false; final boolean isAllowed = SystemProperties.getBoolean(ALLOW_MOCK_MODEM_PROPERTY, false); + final boolean isAllowedForBoot = + SystemProperties.getBoolean(BOOT_ALLOW_MOCK_MODEM_PROPERTY, false); - // Check for ALLOW_MOCK_MODEM_PROPERTY on user builds - if (isAllowed || DEBUG) { - if (serviceName != null) { + // Check for ALLOW_MOCK_MODEM_PROPERTY and BOOT_ALLOW_MOCK_MODEM_PROPERTY on user builds + if (isAllowed || isAllowedForBoot || DEBUG) { + if (mRadioConfig != null) { statusRadioConfig = mRadioConfig.setModemService(serviceName); - - //TODO: consider multi-sim case (b/210073692) - statusRil = mPhones[0].mCi.setModemService(serviceName); - } else { - statusRadioConfig = mRadioConfig.setModemService(null); - - //TODO: consider multi-sim case - statusRil = mPhones[0].mCi.setModemService(null); } - return statusRadioConfig && statusRil; + if (!statusRadioConfig) { + loge("setModemService: switching modem service for radioconfig fail"); + return false; + } + + for (int i = 0; i < getPhoneCount(); i++) { + if (mPhones[i] != null) { + statusRil = mPhones[i].mCi.setModemService(serviceName); + } + + if (!statusRil) { + loge("setModemService: switch modem for radio " + i + " fail"); + + // Disconnect the switched service + mRadioConfig.setModemService(null); + for (int t = 0; t < i; t++) { + mPhones[t].mCi.setModemService(null); + } + return false; + } + } } else { loge("setModemService is not allowed"); return false; } + + return true; } /** @@ -500,7 +531,6 @@ * @return the service name of the connected service. */ public String getModemService() { - //TODO: consider multi-sim case if (mPhones[0] == null) { return ""; }
diff --git a/src/java/com/android/internal/telephony/PhoneFactory.java b/src/java/com/android/internal/telephony/PhoneFactory.java index 3361b74..57a375b 100644 --- a/src/java/com/android/internal/telephony/PhoneFactory.java +++ b/src/java/com/android/internal/telephony/PhoneFactory.java
@@ -16,6 +16,8 @@ package com.android.internal.telephony; +import static android.telephony.TelephonyManager.HAL_SERVICE_RADIO; + import static com.android.internal.telephony.PhoneConstants.PHONE_TYPE_CDMA; import static com.android.internal.telephony.PhoneConstants.PHONE_TYPE_CDMA_LTE; @@ -29,7 +31,6 @@ import android.content.pm.PackageManager; import android.net.LocalServerSocket; import android.os.Build; -import android.os.HandlerThread; import android.os.Looper; import android.preference.PreferenceManager; import android.provider.Settings; @@ -50,6 +51,7 @@ import com.android.internal.telephony.imsphone.ImsPhoneFactory; import com.android.internal.telephony.metrics.MetricsCollector; import com.android.internal.telephony.metrics.TelephonyMetrics; +import com.android.internal.telephony.subscription.SubscriptionManagerService; import com.android.internal.telephony.uicc.UiccController; import com.android.internal.telephony.util.NotificationChannelController; import com.android.internal.util.IndentingPrintWriter; @@ -83,8 +85,7 @@ private static IntentBroadcaster sIntentBroadcaster; private static @Nullable EuiccController sEuiccController; private static @Nullable EuiccCardController sEuiccCardController; - - static private SubscriptionInfoUpdater sSubInfoRecordUpdater = null; + private static SubscriptionManagerService sSubscriptionManagerService; @UnsupportedAppUsage static private boolean sMadeDefaults = false; @@ -117,6 +118,7 @@ synchronized (sLockProxyPhones) { if (!sMadeDefaults) { sContext = context; + // create the telephony device controller. TelephonyDevController.create(); @@ -179,7 +181,7 @@ if (numPhones > 0) { final RadioConfig radioConfig = RadioConfig.make(context, - sCommandsInterfaces[0].getHalVersion()); + sCommandsInterfaces[0].getHalVersion(HAL_SERVICE_RADIO)); sRadioHalCapabilities = RadioInterfaceCapabilityController.init(radioConfig, sCommandsInterfaces[0]); } else { @@ -194,12 +196,12 @@ // call getInstance() sUiccController = UiccController.make(context); - Rlog.i(LOG_TAG, "Creating SubscriptionController"); - TelephonyComponentFactory.getInstance().inject(SubscriptionController.class. - getName()).initSubscriptionController(context); + Rlog.i(LOG_TAG, "Creating SubscriptionManagerService"); + sSubscriptionManagerService = new SubscriptionManagerService(context, + Looper.myLooper()); + TelephonyComponentFactory.getInstance().inject(MultiSimSettingController.class. - getName()).initMultiSimSettingController(context, - SubscriptionController.getInstance()); + getName()).initMultiSimSettingController(context); if (context.getPackageManager().hasSystemFeature( PackageManager.FEATURE_TELEPHONY_EUICC)) { @@ -231,14 +233,6 @@ sMadeDefaults = true; - Rlog.i(LOG_TAG, "Creating SubInfoRecordUpdater "); - HandlerThread pfhandlerThread = new HandlerThread("PhoneFactoryHandlerThread"); - pfhandlerThread.start(); - sSubInfoRecordUpdater = TelephonyComponentFactory.getInstance().inject( - SubscriptionInfoUpdater.class.getName()). - makeSubscriptionInfoUpdater(pfhandlerThread. - getLooper(), context, SubscriptionController.getInstance()); - // Only bring up IMS if the device supports having an IMS stack. if (context.getPackageManager().hasSystemFeature( PackageManager.FEATURE_TELEPHONY_IMS)) { @@ -376,10 +370,6 @@ } } - public static SubscriptionInfoUpdater getSubscriptionInfoUpdater() { - return sSubInfoRecordUpdater; - } - /** * Get the network factory associated with a given phone ID. * @param phoneId the phone id @@ -416,7 +406,6 @@ * @param phoneId The phone's id. * @return the preferred network mode bitmask that should be set. */ - // TODO: Fix when we "properly" have TelephonyDevController/SubscriptionController .. @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public static int calculatePreferredNetworkType(int phoneId) { if (getPhone(phoneId) == null) { @@ -433,7 +422,7 @@ /* Gets the default subscription */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public static int getDefaultSubscription() { - return SubscriptionController.getInstance().getDefaultSubId(); + return SubscriptionManagerService.getInstance().getDefaultSubId(); } /* Returns User SMS Prompt property, enabled or not */ @@ -461,19 +450,7 @@ } /** - * Request a refresh of the embedded subscription list. - * - * @param cardId the card ID of the eUICC. - * @param callback Optional callback to execute after the refresh completes. Must terminate - * quickly as it will be called from SubscriptionInfoUpdater's handler thread. - */ - public static void requestEmbeddedSubscriptionInfoListRefresh( - int cardId, @Nullable Runnable callback) { - sSubInfoRecordUpdater.requestEmbeddedSubscriptionInfoListRefresh(cardId, callback); - } - - /** - * Get a the SmsController. + * Get the instance of {@link SmsController}. */ public static SmsController getSmsController() { synchronized (sLockProxyPhones) { @@ -576,21 +553,14 @@ pw.decreaseIndent(); pw.println("++++++++++++++++++++++++++++++++"); - pw.println("SubscriptionController:"); - pw.increaseIndent(); - try { - SubscriptionController.getInstance().dump(fd, pw, args); - } catch (Exception e) { - e.printStackTrace(); - } pw.flush(); pw.decreaseIndent(); pw.println("++++++++++++++++++++++++++++++++"); - pw.println("SubInfoRecordUpdater:"); + pw.println("sRadioHalCapabilities:"); pw.increaseIndent(); try { - sSubInfoRecordUpdater.dump(fd, pw, args); + sRadioHalCapabilities.dump(fd, pw, args); } catch (Exception e) { e.printStackTrace(); }
diff --git a/src/java/com/android/internal/telephony/PhoneInternalInterface.java b/src/java/com/android/internal/telephony/PhoneInternalInterface.java index 5b4d5e5..32c0c73 100644 --- a/src/java/com/android/internal/telephony/PhoneInternalInterface.java +++ b/src/java/com/android/internal/telephony/PhoneInternalInterface.java
@@ -16,7 +16,6 @@ package com.android.internal.telephony; -import android.annotation.IntDef; import android.annotation.NonNull; import android.compat.annotation.UnsupportedAppUsage; import android.os.Build; @@ -36,9 +35,9 @@ import com.android.internal.telephony.PhoneConstants.DataState; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; +import java.util.HashSet; import java.util.List; +import java.util.Set; import java.util.function.Consumer; /** @@ -211,16 +210,6 @@ static final String REASON_DATA_UNTHROTTLED = "dataUnthrottled"; static final String REASON_TRAFFIC_DESCRIPTORS_UPDATED = "trafficDescriptorsUpdated"; - // Reasons for Radio being powered off - int RADIO_POWER_REASON_USER = 0; - int RADIO_POWER_REASON_THERMAL = 1; - @Retention(RetentionPolicy.SOURCE) - @IntDef(prefix = {"RADIO_POWER_REASON_"}, - value = { - RADIO_POWER_REASON_USER, - RADIO_POWER_REASON_THERMAL}) - public @interface RadioPowerReason {} - // Used for band mode selection methods static final int BM_UNSPECIFIED = RILConstants.BAND_MODE_UNSPECIFIED; // automatic static final int BM_EURO_BAND = RILConstants.BAND_MODE_EURO; @@ -593,8 +582,9 @@ * getServiceState().getState() will not change immediately after this call. * registerForServiceStateChanged() to find out when the * request is complete. This will set the reason for radio power state as {@link - * #RADIO_POWER_REASON_USER}. This will not guarantee that the requested radio power state will - * actually be set. See {@link #setRadioPowerForReason(boolean, boolean, boolean, boolean, int)} + * android.telephony.TelephonyManager#RADIO_POWER_REASON_USER}. This will not guarantee that the + * requested radio power state will actually be set. + * See {@link #setRadioPowerForReason(boolean, boolean, boolean, boolean, int)} * for details. * * @param power true means "on", false means "off". @@ -620,8 +610,9 @@ * getServiceState().getState() will not change immediately after this call. * registerForServiceStateChanged() to find out when the * request is complete. This will set the reason for radio power state as {@link - * #RADIO_POWER_REASON_USER}. This will not guarantee that the requested radio power state will - * actually be set. See {@link #setRadioPowerForReason(boolean, boolean, boolean, boolean, int)} + * android.telephony.TelephonyManager#RADIO_POWER_REASON_USER}. This will not guarantee that the + * requested radio power state will actually be set. + * See {@link #setRadioPowerForReason(boolean, boolean, boolean, boolean, int)} * for details. * * @param power true means "on", false means "off". @@ -638,7 +629,7 @@ default void setRadioPower(boolean power, boolean forEmergencyCall, boolean isSelectedPhoneForEmergencyCall, boolean forceApply) { setRadioPowerForReason(power, forEmergencyCall, isSelectedPhoneForEmergencyCall, forceApply, - RADIO_POWER_REASON_USER); + TelephonyManager.RADIO_POWER_REASON_USER); } /** @@ -656,11 +647,19 @@ * @param power true means "on", false means "off". * @param reason RadioPowerReason constant defining the reason why the radio power was set. */ - default void setRadioPowerForReason(boolean power, @RadioPowerReason int reason) { + default void setRadioPowerForReason(boolean power, + @TelephonyManager.RadioPowerReason int reason) { setRadioPowerForReason(power, false, false, false, reason); } /** + * @return reasons for powering off radio. + */ + default Set<Integer> getRadioPowerOffReasons() { + return new HashSet<>(); + } + + /** * Sets the radio power on/off state with option to specify whether it's for emergency call * (off is sometimes called "airplane mode") and option to set the reason for setting the power * state. Current state can be gotten via {@link #getServiceState()}. @@ -686,7 +685,7 @@ */ default void setRadioPowerForReason(boolean power, boolean forEmergencyCall, boolean isSelectedPhoneForEmergencyCall, boolean forceApply, - @RadioPowerReason int reason) {} + @TelephonyManager.RadioPowerReason int reason) {} /** * Get the line 1 phone number (MSISDN). For CDMA phones, the MDN is returned @@ -1036,6 +1035,10 @@ String getImei(); /** + * Retrieves IMEI type for phones. + */ + int getImeiType(); + /** * Retrieves the IccPhoneBookInterfaceManager of the Phone */ public IccPhoneBookInterfaceManager getIccPhoneBookInterfaceManager();
diff --git a/src/java/com/android/internal/telephony/PhoneNotifier.java b/src/java/com/android/internal/telephony/PhoneNotifier.java index 701a157..20d6702 100644 --- a/src/java/com/android/internal/telephony/PhoneNotifier.java +++ b/src/java/com/android/internal/telephony/PhoneNotifier.java
@@ -18,6 +18,7 @@ import android.annotation.NonNull; import android.compat.annotation.UnsupportedAppUsage; +import android.telephony.Annotation; import android.telephony.Annotation.RadioPowerState; import android.telephony.Annotation.SrvccState; import android.telephony.BarringInfo; @@ -31,8 +32,11 @@ import android.telephony.ServiceState; import android.telephony.TelephonyDisplayInfo; import android.telephony.TelephonyManager.DataEnabledReason; +import android.telephony.TelephonyManager.EmergencyCallbackModeStopReason; +import android.telephony.TelephonyManager.EmergencyCallbackModeType; import android.telephony.emergency.EmergencyNumber; import android.telephony.ims.ImsReasonInfo; +import android.telephony.ims.MediaQualityStatus; import java.util.List; @@ -78,7 +82,10 @@ void notifyCellInfo(Phone sender, List<CellInfo> cellInfo); - void notifyPreciseCallState(Phone sender); + /** Send a notification that precise call state changed. */ + void notifyPreciseCallState(Phone sender, String[] imsCallIds, + @Annotation.ImsCallServiceType int[] imsCallServiceTypes, + @Annotation.ImsCallType int[] imsCallTypes); void notifyDisconnectCause(Phone sender, int cause, int preciseCause); @@ -113,6 +120,9 @@ /** Notify of a change to the call quality of an active foreground call. */ void notifyCallQualityChanged(Phone sender, CallQuality callQuality, int callNetworkType); + /** Notify of a change to the media quality status of an active foreground call. */ + void notifyMediaQualityStatusChanged(Phone sender, MediaQualityStatus status); + /** Notify registration failed */ void notifyRegistrationFailed(Phone sender, @NonNull CellIdentity cellIdentity, @NonNull String chosenPlmn, int domain, int causeCode, int additionalCauseCode); @@ -132,4 +142,11 @@ /** Notify link capacity estimate has changed. */ void notifyLinkCapacityEstimateChanged(Phone sender, List<LinkCapacityEstimate> linkCapacityEstimateList); + + /** Notify callback mode started. */ + void notifyCallbackModeStarted(Phone sender, @EmergencyCallbackModeType int type); + + /** Notify callback mode stopped. */ + void notifyCallbackModeStopped(Phone sender, @EmergencyCallbackModeType int type, + @EmergencyCallbackModeStopReason int reason); }
diff --git a/src/java/com/android/internal/telephony/PhoneSubInfoController.java b/src/java/com/android/internal/telephony/PhoneSubInfoController.java index b1dfa5f..d30a73c 100644 --- a/src/java/com/android/internal/telephony/PhoneSubInfoController.java +++ b/src/java/com/android/internal/telephony/PhoneSubInfoController.java
@@ -27,22 +27,29 @@ import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.pm.PackageManager; +import android.net.Uri; import android.os.Binder; import android.os.Build; import android.os.RemoteException; import android.os.TelephonyServiceManager.ServiceRegisterer; -import android.os.UserHandle; import android.telephony.ImsiEncryptionInfo; import android.telephony.PhoneNumberUtils; import android.telephony.SubscriptionManager; import android.telephony.TelephonyFrameworkInitializer; +import android.text.TextUtils; import android.util.EventLog; +import com.android.internal.telephony.subscription.SubscriptionInfoInternal; +import com.android.internal.telephony.subscription.SubscriptionManagerService; import com.android.internal.telephony.uicc.IsimRecords; +import com.android.internal.telephony.uicc.SIMRecords; import com.android.internal.telephony.uicc.UiccCardApplication; import com.android.internal.telephony.uicc.UiccPort; import com.android.telephony.Rlog; +import java.util.ArrayList; +import java.util.List; + public class PhoneSubInfoController extends IPhoneSubInfo.Stub { private static final String TAG = "PhoneSubInfoController"; private static final boolean DBG = true; @@ -147,14 +154,13 @@ public String getSubscriberIdForSubscriber(int subId, String callingPackage, String callingFeatureId) { String message = "getSubscriberIdForSubscriber"; - - enforceCallingPackage(callingPackage, Binder.getCallingUid(), message); + mAppOps.checkPackage(Binder.getCallingUid(), callingPackage); long identity = Binder.clearCallingIdentity(); boolean isActive; try { - isActive = SubscriptionController.getInstance().isActiveSubId(subId, callingPackage, - callingFeatureId); + isActive = SubscriptionManagerService.getInstance().isActiveSubId(subId, + callingPackage, callingFeatureId); } finally { Binder.restoreCallingIdentity(identity); } @@ -168,7 +174,12 @@ } identity = Binder.clearCallingIdentity(); try { - return SubscriptionController.getInstance().getImsiPrivileged(subId); + SubscriptionInfoInternal subInfo = SubscriptionManagerService.getInstance() + .getSubscriptionInfoInternal(subId); + if (subInfo != null && !TextUtils.isEmpty(subInfo.getImsi())) { + return subInfo.getImsi(); + } + return null; } finally { Binder.restoreCallingIdentity(identity); } @@ -316,28 +327,6 @@ "Requires MODIFY_PHONE_STATE"); } - /** - * Make sure the caller is the calling package itself - * - * @throws SecurityException if the caller is not the calling package - */ - private void enforceCallingPackage(String callingPackage, int callingUid, String message) { - int packageUid = -1; - PackageManager pm = mContext.createContextAsUser( - UserHandle.getUserHandleForUid(callingUid), 0).getPackageManager(); - if (pm != null) { - try { - packageUid = pm.getPackageUid(callingPackage, 0); - } catch (PackageManager.NameNotFoundException e) { - // packageUid is -1 - } - } - if (packageUid != callingUid) { - throw new SecurityException(message + ": Package " + callingPackage - + " does not belong to " + callingUid); - } - } - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) private int getDefaultSubscription() { return PhoneFactory.getDefaultSubscription(); @@ -359,6 +348,34 @@ } /** + * Fetches the IMS private user identity (EF_IMPI) based on subscriptionId. + * + * @param subId subscriptionId + * @return IMPI (IMS private user identity) of type string. + * @throws IllegalArgumentException if the subscriptionId is not valid + * @throws IllegalStateException in case the ISIM hasn’t been loaded. + * @throws SecurityException if the caller does not have the required permission + */ + public String getImsPrivateUserIdentity(int subId, String callingPackage, + String callingFeatureId) { + if (!SubscriptionManager.isValidSubscriptionId(subId)) { + throw new IllegalArgumentException("Invalid SubscriptionID = " + subId); + } + if (!TelephonyPermissions.checkCallingOrSelfUseIccAuthWithDeviceIdentifier(mContext, + callingPackage, callingFeatureId, "getImsPrivateUserIdentity")) { + throw (new SecurityException("No permissions to the caller")); + } + Phone phone = getPhone(subId); + assert phone != null; + IsimRecords isim = phone.getIsimRecords(); + if (isim != null) { + return isim.getIsimImpi(); + } else { + throw new IllegalStateException("ISIM is not loaded"); + } + } + + /** * get the Isim Domain based on subId */ public String getIsimDomain(int subId) { @@ -389,6 +406,42 @@ } /** + * Fetches the ISIM public user identities (EF_IMPU) from UICC based on subId + * + * @param subId subscriptionId + * @param callingPackage package name of the caller + * @param callingFeatureId feature Id of the caller + * @return List of public user identities of type android.net.Uri or empty list if + * EF_IMPU is not available. + * @throws IllegalArgumentException if the subscriptionId is not valid + * @throws IllegalStateException in case the ISIM hasn’t been loaded. + * @throws SecurityException if the caller does not have the required permission + */ + public List<Uri> getImsPublicUserIdentities(int subId, String callingPackage, + String callingFeatureId) { + if (TelephonyPermissions. + checkCallingOrSelfReadPrivilegedPhoneStatePermissionOrReadPhoneNumber( + mContext, subId, callingPackage, callingFeatureId, "getImsPublicUserIdentities")) { + Phone phone = getPhone(subId); + assert phone != null; + IsimRecords isimRecords = phone.getIsimRecords(); + if (isimRecords != null) { + String[] impus = isimRecords.getIsimImpu(); + List<Uri> impuList = new ArrayList<>(); + for (String impu : impus) { + if (impu != null && impu.trim().length() > 0) { + impuList.add(Uri.parse(impu)); + } + } + return impuList; + } + throw new IllegalStateException("ISIM is not loaded"); + } else { + throw new IllegalArgumentException("Invalid SubscriptionID = " + subId); + } + } + + /** * get the Isim Ist based on subId */ public String getIsimIst(int subId) throws RemoteException { @@ -418,6 +471,29 @@ }); } + /** + * Returns the USIM service table that fetched from EFUST elementary field that are loaded + * based on the appType. + */ + public String getSimServiceTable(int subId, int appType) throws RemoteException { + return callPhoneMethodForSubIdWithPrivilegedCheck(subId, "getSimServiceTable", + (phone) -> { + UiccPort uiccPort = phone.getUiccPort(); + if (uiccPort == null || uiccPort.getUiccProfile() == null) { + loge("getSimServiceTable(): uiccPort or uiccProfile is null"); + return null; + } + UiccCardApplication uiccApp = uiccPort.getUiccProfile().getApplicationByType( + appType); + if (uiccApp == null) { + loge("getSimServiceTable(): no app with specified apptype=" + + appType); + return null; + } + return ((SIMRecords)uiccApp.getIccRecords()).getSimServiceTable(); + }); + } + @Override public String getIccSimChallengeResponse(int subId, int appType, int authType, String data, String callingPackage, String callingFeatureId) throws RemoteException { @@ -438,7 +514,9 @@ } if (authType != UiccCardApplication.AUTH_CONTEXT_EAP_SIM - && authType != UiccCardApplication.AUTH_CONTEXT_EAP_AKA) { + && authType != UiccCardApplication.AUTH_CONTEXT_EAP_AKA + && authType != UiccCardApplication.AUTH_CONTEXT_GBA_BOOTSTRAP + && authType != UiccCardApplication.AUTHTYPE_GBA_NAF_KEY_EXTERNAL) { loge("getIccSimChallengeResponse() unsupported authType: " + authType); return null; } @@ -491,7 +569,7 @@ if (phone != null) { return callMethodHelper.callMethod(phone); } else { - loge(message + " phone is null for Subscription:" + subId); + if (VDBG) loge(message + " phone is null for Subscription:" + subId); return null; } } finally { @@ -581,6 +659,37 @@ } } + /** + * Returns SIP URI or tel URI of the Public Service Identity of the SM-SC fetched from + * EF_PSISMSC elementary field as defined in Section 4.5.9 (3GPP TS 31.102). + * @throws IllegalStateException in case if phone or UiccApplication is not available. + */ + public Uri getSmscIdentity(int subId, int appType) throws RemoteException { + Uri smscIdentityUri = callPhoneMethodForSubIdWithPrivilegedCheck(subId, "getSmscIdentity", + (phone) -> { + try { + String smscIdentity = null; + UiccPort uiccPort = phone.getUiccPort(); + UiccCardApplication uiccApp = + uiccPort.getUiccProfile().getApplicationByType( + appType); + smscIdentity = (uiccApp != null) ? uiccApp.getIccRecords().getSmscIdentity() + : null; + if (TextUtils.isEmpty(smscIdentity)) { + return Uri.EMPTY; + } + return Uri.parse(smscIdentity); + } catch (NullPointerException ex) { + Rlog.e(TAG, "getSmscIdentity(): Exception = " + ex); + return null; + } + }); + if (smscIdentityUri == null) { + throw new IllegalStateException("Telephony service error"); + } + return smscIdentityUri; + } + private void log(String s) { Rlog.d(TAG, s); }
diff --git a/src/java/com/android/internal/telephony/ProxyController.java b/src/java/com/android/internal/telephony/ProxyController.java index 76f0041..ed9982e 100644 --- a/src/java/com/android/internal/telephony/ProxyController.java +++ b/src/java/com/android/internal/telephony/ProxyController.java
@@ -28,7 +28,6 @@ import android.os.PowerManager; import android.os.PowerManager.WakeLock; import android.telephony.RadioAccessFamily; -import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import android.util.Log; @@ -48,8 +47,10 @@ @VisibleForTesting static final int EVENT_START_RC_RESPONSE = 2; private static final int EVENT_APPLY_RC_RESPONSE = 3; - private static final int EVENT_FINISH_RC_RESPONSE = 4; - private static final int EVENT_TIMEOUT = 5; + @VisibleForTesting + public static final int EVENT_FINISH_RC_RESPONSE = 4; + @VisibleForTesting + public static final int EVENT_TIMEOUT = 5; @VisibleForTesting public static final int EVENT_MULTI_SIM_CONFIG_CHANGED = 6; @@ -157,34 +158,6 @@ logd("Constructor - Exit"); } - public void registerForAllDataDisconnected(int subId, Handler h, int what) { - int phoneId = SubscriptionController.getInstance().getPhoneId(subId); - - if (SubscriptionManager.isValidPhoneId(phoneId)) { - mPhones[phoneId].registerForAllDataDisconnected(h, what); - } - } - - public void unregisterForAllDataDisconnected(int subId, Handler h) { - int phoneId = SubscriptionController.getInstance().getPhoneId(subId); - - if (SubscriptionManager.isValidPhoneId(phoneId)) { - mPhones[phoneId].unregisterForAllDataDisconnected(h); - } - } - - - public boolean areAllDataDisconnected(int subId) { - int phoneId = SubscriptionController.getInstance().getPhoneId(subId); - - if (SubscriptionManager.isValidPhoneId(phoneId)) { - return mPhones[phoneId].areAllDataDisconnected(); - } else { - // if we can't find a phone for the given subId, it is disconnected. - return true; - } - } - /** * Get phone radio type and access technology. * @@ -241,6 +214,7 @@ clearTransaction(); // Keep a wake lock until we finish radio capability changed + logd("Acquiring wake lock for setting radio capability"); mWakeLock.acquire(); return doSetRadioCapabilities(rafs); @@ -386,19 +360,25 @@ } } RadioCapability rc = (RadioCapability) ((AsyncResult) msg.obj).result; - if ((rc == null) || (rc.getSession() != mRadioCapabilitySessionId)) { + // Added exception condition to continue to mark as transaction fail case. + // Checking session validity during exception is not valid + if (ar.exception == null + && ((rc == null) || (rc.getSession() != mRadioCapabilitySessionId))) { logd("onStartRadioCapabilityResponse: Ignore session=" + mRadioCapabilitySessionId + " rc=" + rc); return; } mRadioAccessFamilyStatusCounter--; - int id = rc.getPhoneId(); + //rc.getPhoneId() moved to avoid Null Pointer Exception, since when exception occurs + //its expected rc is null. if (ar.exception != null) { - logd("onStartRadioCapabilityResponse: Error response session=" + rc.getSession()); - logd("onStartRadioCapabilityResponse: phoneId=" + id + " status=FAIL"); - mSetRadioAccessFamilyStatus[id] = SET_RC_STATUS_FAIL; + logd("onStartRadioCapabilityResponse got exception=" + ar.exception); + //mSetRadioAccessFamilyStatus will be set anyway to SET_RC_STATUS_FAIL + // if either of them fail at issueFinish() method below,i.e. both phone id count + // is set to SET_RC_STATUS_FAIL. mTransactionFailed = true; } else { + int id = rc.getPhoneId(); logd("onStartRadioCapabilityResponse: phoneId=" + id + " status=STARTED"); mSetRadioAccessFamilyStatus[id] = SET_RC_STATUS_STARTED; } @@ -510,13 +490,20 @@ * @param msg obj field isa RadioCapability */ void onFinishRadioCapabilityResponse(Message msg) { - RadioCapability rc = (RadioCapability) ((AsyncResult) msg.obj).result; - if ((rc == null) || (rc.getSession() != mRadioCapabilitySessionId)) { - logd("onFinishRadioCapabilityResponse: Ignore session=" + mRadioCapabilitySessionId - + " rc=" + rc); - return; - } synchronized (mSetRadioAccessFamilyStatus) { + AsyncResult ar = (AsyncResult) msg.obj; + RadioCapability rc = (RadioCapability) ((AsyncResult) msg.obj).result; + // Added exception condition on finish to continue to revert if exception occurred. + // Checking session validity during exception is not valid + if (ar.exception == null + && ((rc == null) || (rc.getSession() != mRadioCapabilitySessionId))) { + logd("onFinishRadioCapabilityResponse: Ignore session=" + + mRadioCapabilitySessionId + " rc=" + rc); + return; + } + if (ar.exception != null) { + logd("onFinishRadioCapabilityResponse got exception=" + ar.exception); + } logd(" onFinishRadioCapabilityResponse mRadioAccessFamilyStatusCounter=" + mRadioAccessFamilyStatusCounter); mRadioAccessFamilyStatusCounter--; @@ -604,7 +591,6 @@ clearTransaction(); } else { intent = new Intent(TelephonyIntents.ACTION_SET_RADIO_CAPABILITY_FAILED); - // now revert. mTransactionFailed = false; RadioAccessFamily[] rafs = new RadioAccessFamily[mPhones.length]; @@ -631,6 +617,7 @@ } if (isWakeLockHeld()) { + logd("clearTransaction:checking wakelock held and releasing"); mWakeLock.release(); } }
diff --git a/src/java/com/android/internal/telephony/RIL.java b/src/java/com/android/internal/telephony/RIL.java index 49e8297..5ecdfcb 100644 --- a/src/java/com/android/internal/telephony/RIL.java +++ b/src/java/com/android/internal/telephony/RIL.java
@@ -16,6 +16,15 @@ package com.android.internal.telephony; +import static android.telephony.TelephonyManager.HAL_SERVICE_DATA; +import static android.telephony.TelephonyManager.HAL_SERVICE_IMS; +import static android.telephony.TelephonyManager.HAL_SERVICE_MESSAGING; +import static android.telephony.TelephonyManager.HAL_SERVICE_MODEM; +import static android.telephony.TelephonyManager.HAL_SERVICE_NETWORK; +import static android.telephony.TelephonyManager.HAL_SERVICE_RADIO; +import static android.telephony.TelephonyManager.HAL_SERVICE_SIM; +import static android.telephony.TelephonyManager.HAL_SERVICE_VOICE; + import static com.android.internal.telephony.RILConstants.*; import android.annotation.NonNull; @@ -41,10 +50,13 @@ import android.os.ServiceManager; import android.os.SystemClock; import android.os.SystemProperties; +import android.os.Trace; import android.os.WorkSource; import android.provider.Settings; import android.sysprop.TelephonyProperties; +import android.telephony.AccessNetworkConstants; import android.telephony.AccessNetworkConstants.AccessNetworkType; +import android.telephony.BarringInfo; import android.telephony.CarrierRestrictionRules; import android.telephony.CellInfo; import android.telephony.CellSignalStrengthCdma; @@ -54,6 +66,7 @@ import android.telephony.CellSignalStrengthTdscdma; import android.telephony.CellSignalStrengthWcdma; import android.telephony.ClientRequestStats; +import android.telephony.DomainSelectionService; import android.telephony.ImsiEncryptionInfo; import android.telephony.ModemActivityInfo; import android.telephony.NeighboringCellInfo; @@ -66,18 +79,24 @@ import android.telephony.SubscriptionManager; import android.telephony.TelephonyHistogram; import android.telephony.TelephonyManager; +import android.telephony.TelephonyManager.HalService; import android.telephony.TelephonyManager.PrefNetworkMode; import android.telephony.data.DataProfile; import android.telephony.data.NetworkSliceInfo; import android.telephony.data.TrafficDescriptor; import android.telephony.emergency.EmergencyNumber; +import android.telephony.ims.RegistrationManager; +import android.telephony.ims.feature.ConnectionFailureInfo; +import android.telephony.ims.stub.ImsRegistrationImplBase; import android.text.TextUtils; import android.util.SparseArray; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.telephony.cdma.CdmaInformationRecords; import com.android.internal.telephony.cdma.CdmaSmsBroadcastConfigInfo; +import com.android.internal.telephony.emergency.EmergencyConstants; import com.android.internal.telephony.gsm.SmsBroadcastConfigInfo; +import com.android.internal.telephony.imsphone.ImsCallInfo; import com.android.internal.telephony.metrics.ModemRestartStats; import com.android.internal.telephony.metrics.TelephonyMetrics; import com.android.internal.telephony.nano.TelephonyProto.SmsSession; @@ -91,8 +110,10 @@ import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.NoSuchElementException; import java.util.Objects; import java.util.Set; @@ -132,6 +153,9 @@ private final ClientWakelockTracker mClientWakelockTracker = new ClientWakelockTracker(); /** @hide */ + public static final HalVersion RADIO_HAL_VERSION_UNSUPPORTED = HalVersion.UNSUPPORTED; + + /** @hide */ public static final HalVersion RADIO_HAL_VERSION_UNKNOWN = HalVersion.UNKNOWN; /** @hide */ @@ -158,8 +182,11 @@ /** @hide */ public static final HalVersion RADIO_HAL_VERSION_2_0 = new HalVersion(2, 0); - // IRadio version - private HalVersion mRadioVersion = RADIO_HAL_VERSION_UNKNOWN; + /** @hide */ + public static final HalVersion RADIO_HAL_VERSION_2_1 = new HalVersion(2, 1); + + // Hal version + private Map<Integer, HalVersion> mHalVersion = new HashMap<>(); //***** Instance Variables @@ -197,15 +224,9 @@ private static final String PROPERTY_IS_VONR_ENABLED = "persist.radio.is_vonr_enabled_"; - static final int RADIO_SERVICE = 0; - static final int DATA_SERVICE = 1; - static final int MESSAGING_SERVICE = 2; - static final int MODEM_SERVICE = 3; - static final int NETWORK_SERVICE = 4; - static final int SIM_SERVICE = 5; - static final int VOICE_SERVICE = 6; - static final int MIN_SERVICE_IDX = RADIO_SERVICE; - static final int MAX_SERVICE_IDX = VOICE_SERVICE; + public static final int MIN_SERVICE_IDX = HAL_SERVICE_RADIO; + + public static final int MAX_SERVICE_IDX = HAL_SERVICE_IMS; /** * An array of sets that records if services are disabled in the HAL for a specific phone ID @@ -232,6 +253,8 @@ private volatile IRadio mRadioProxy = null; private DataResponse mDataResponse; private DataIndication mDataIndication; + private ImsResponse mImsResponse; + private ImsIndication mImsIndication; private MessagingResponse mMessagingResponse; private MessagingIndication mMessagingIndication; private ModemResponse mModemResponse; @@ -361,11 +384,11 @@ case EVENT_AIDL_PROXY_DEAD: int aidlService = msg.arg1; - AtomicLong obj = (AtomicLong) msg.obj; - riljLog("handleMessage: EVENT_AIDL_PROXY_DEAD cookie = " + msg.obj + long msgCookie = (long) msg.obj; + riljLog("handleMessage: EVENT_AIDL_PROXY_DEAD cookie = " + msgCookie + ", service = " + serviceToString(aidlService) + ", cookie = " + mServiceCookies.get(aidlService)); - if (obj.get() == mServiceCookies.get(aidlService).get()) { + if (msgCookie == mServiceCookies.get(aidlService).get()) { mIsRadioProxyInitialized = false; resetProxyAndRequestList(aidlService); } @@ -412,8 +435,8 @@ public void serviceDied(long cookie) { // Deal with service going away riljLog("serviceDied"); - mRilHandler.sendMessage(mRilHandler.obtainMessage(EVENT_RADIO_PROXY_DEAD, RADIO_SERVICE, - 0 /* ignored arg2 */, cookie)); + mRilHandler.sendMessage(mRilHandler.obtainMessage(EVENT_RADIO_PROXY_DEAD, + HAL_SERVICE_RADIO, 0 /* ignored arg2 */, cookie)); } } @@ -427,8 +450,11 @@ public void linkToDeath(IBinder service) throws RemoteException { if (service != null) { + riljLog("Linked to death for service " + serviceToString(mService)); mBinder = service; mBinder.linkToDeath(this, (int) mServiceCookies.get(mService).incrementAndGet()); + } else { + riljLoge("Unable to link to death for service " + serviceToString(mService)); } } @@ -443,13 +469,13 @@ public void binderDied() { riljLog("Service " + serviceToString(mService) + " has died."); mRilHandler.sendMessage(mRilHandler.obtainMessage(EVENT_AIDL_PROXY_DEAD, mService, - 0 /* ignored arg2 */, mServiceCookies.get(mService))); + 0 /* ignored arg2 */, mServiceCookies.get(mService).get())); unlinkToDeath(); } } private synchronized void resetProxyAndRequestList(int service) { - if (service == RADIO_SERVICE) { + if (service == HAL_SERVICE_RADIO) { mRadioProxy = null; } else { mServiceProxies.get(service).clear(); @@ -466,7 +492,7 @@ // Clear request list on close clearRequestList(RADIO_NOT_AVAILABLE, false); - if (service == RADIO_SERVICE) { + if (service == HAL_SERVICE_RADIO) { getRadioProxy(null); } else { getRadioServiceProxy(service, null); @@ -495,13 +521,13 @@ // Disable HIDL service if (mRadioProxy != null) { riljLog("Disable HIDL service"); - mDisabledRadioServices.get(RADIO_SERVICE).add(mPhoneId); + mDisabledRadioServices.get(HAL_SERVICE_RADIO).add(mPhoneId); } mMockModem.bindAllMockModemService(); for (int service = MIN_SERVICE_IDX; service <= MAX_SERVICE_IDX; service++) { - if (service == RADIO_SERVICE) continue; + if (service == HAL_SERVICE_RADIO) continue; int retryCount = 0; IBinder binder; @@ -536,14 +562,26 @@ if ((serviceName == null) || (!serviceBound)) { if (serviceBound) riljLog("Unbinding to MockModemService"); - if (mDisabledRadioServices.get(RADIO_SERVICE).contains(mPhoneId)) { - mDisabledRadioServices.get(RADIO_SERVICE).clear(); + if (mDisabledRadioServices.get(HAL_SERVICE_RADIO).contains(mPhoneId)) { + mDisabledRadioServices.get(HAL_SERVICE_RADIO).clear(); } if (mMockModem != null) { - mRadioVersion = RADIO_HAL_VERSION_UNKNOWN; mMockModem = null; for (int service = MIN_SERVICE_IDX; service <= MAX_SERVICE_IDX; service++) { + if (service == HAL_SERVICE_RADIO) { + if (isRadioVersion2_0()) { + mHalVersion.put(service, RADIO_HAL_VERSION_2_0); + } else { + mHalVersion.put(service, RADIO_HAL_VERSION_UNKNOWN); + } + } else { + if (isRadioServiceSupported(service)) { + mHalVersion.put(service, RADIO_HAL_VERSION_UNKNOWN); + } else { + mHalVersion.put(service, RADIO_HAL_VERSION_UNSUPPORTED); + } + } resetProxyAndRequestList(service); } } @@ -585,7 +623,10 @@ /** Returns a {@link IRadio} instance or null if the service is not available. */ @VisibleForTesting public synchronized IRadio getRadioProxy(Message result) { - if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_2_0)) return null; + if (mHalVersion.containsKey(HAL_SERVICE_RADIO) + && mHalVersion.get(HAL_SERVICE_RADIO).greaterOrEqual(RADIO_HAL_VERSION_2_0)) { + return null; + } if (!SubscriptionManager.isValidPhoneId(mPhoneId)) return null; if (!mIsCellularSupported) { if (RILJ_LOGV) riljLog("getRadioProxy: Not calling getService(): wifi-only"); @@ -602,14 +643,14 @@ } try { - if (mDisabledRadioServices.get(RADIO_SERVICE).contains(mPhoneId)) { + if (mDisabledRadioServices.get(HAL_SERVICE_RADIO).contains(mPhoneId)) { riljLoge("getRadioProxy: mRadioProxy for " + HIDL_SERVICE_NAME[mPhoneId] + " is disabled"); } else { try { mRadioProxy = android.hardware.radio.V1_6.IRadio.getService( HIDL_SERVICE_NAME[mPhoneId], true); - mRadioVersion = RADIO_HAL_VERSION_1_6; + mHalVersion.put(HAL_SERVICE_RADIO, RADIO_HAL_VERSION_1_6); } catch (NoSuchElementException e) { } @@ -617,7 +658,7 @@ try { mRadioProxy = android.hardware.radio.V1_5.IRadio.getService( HIDL_SERVICE_NAME[mPhoneId], true); - mRadioVersion = RADIO_HAL_VERSION_1_5; + mHalVersion.put(HAL_SERVICE_RADIO, RADIO_HAL_VERSION_1_5); } catch (NoSuchElementException e) { } } @@ -626,7 +667,7 @@ try { mRadioProxy = android.hardware.radio.V1_4.IRadio.getService( HIDL_SERVICE_NAME[mPhoneId], true); - mRadioVersion = RADIO_HAL_VERSION_1_4; + mHalVersion.put(HAL_SERVICE_RADIO, RADIO_HAL_VERSION_1_4); } catch (NoSuchElementException e) { } } @@ -635,7 +676,7 @@ try { mRadioProxy = android.hardware.radio.V1_3.IRadio.getService( HIDL_SERVICE_NAME[mPhoneId], true); - mRadioVersion = RADIO_HAL_VERSION_1_3; + mHalVersion.put(HAL_SERVICE_RADIO, RADIO_HAL_VERSION_1_3); } catch (NoSuchElementException e) { } } @@ -644,7 +685,7 @@ try { mRadioProxy = android.hardware.radio.V1_2.IRadio.getService( HIDL_SERVICE_NAME[mPhoneId], true); - mRadioVersion = RADIO_HAL_VERSION_1_2; + mHalVersion.put(HAL_SERVICE_RADIO, RADIO_HAL_VERSION_1_2); } catch (NoSuchElementException e) { } } @@ -653,7 +694,7 @@ try { mRadioProxy = android.hardware.radio.V1_1.IRadio.getService( HIDL_SERVICE_NAME[mPhoneId], true); - mRadioVersion = RADIO_HAL_VERSION_1_1; + mHalVersion.put(HAL_SERVICE_RADIO, RADIO_HAL_VERSION_1_1); } catch (NoSuchElementException e) { } } @@ -662,7 +703,7 @@ try { mRadioProxy = android.hardware.radio.V1_0.IRadio.getService( HIDL_SERVICE_NAME[mPhoneId], true); - mRadioVersion = RADIO_HAL_VERSION_1_0; + mHalVersion.put(HAL_SERVICE_RADIO, RADIO_HAL_VERSION_1_0); } catch (NoSuchElementException e) { } } @@ -671,11 +712,11 @@ if (!mIsRadioProxyInitialized) { mIsRadioProxyInitialized = true; mRadioProxy.linkToDeath(mRadioProxyDeathRecipient, - mServiceCookies.get(RADIO_SERVICE).incrementAndGet()); + mServiceCookies.get(HAL_SERVICE_RADIO).incrementAndGet()); mRadioProxy.setResponseFunctions(mRadioResponse, mRadioIndication); } } else { - mDisabledRadioServices.get(RADIO_SERVICE).add(mPhoneId); + mDisabledRadioServices.get(HAL_SERVICE_RADIO).add(mPhoneId); riljLoge("getRadioProxy: set mRadioProxy for " + HIDL_SERVICE_NAME[mPhoneId] + " as disabled"); } @@ -700,29 +741,32 @@ /** * Returns a {@link RadioDataProxy}, {@link RadioMessagingProxy}, {@link RadioModemProxy}, - * {@link RadioNetworkProxy}, {@link RadioSimProxy}, {@link RadioVoiceProxy}, or an empty {@link RadioServiceProxy} - * if the service is not available. + * {@link RadioNetworkProxy}, {@link RadioSimProxy}, {@link RadioVoiceProxy}, + * {@link RadioImsProxy}, or null if the service is not available. */ @NonNull public <T extends RadioServiceProxy> T getRadioServiceProxy(Class<T> serviceClass, Message result) { if (serviceClass == RadioDataProxy.class) { - return (T) getRadioServiceProxy(DATA_SERVICE, result); + return (T) getRadioServiceProxy(HAL_SERVICE_DATA, result); } if (serviceClass == RadioMessagingProxy.class) { - return (T) getRadioServiceProxy(MESSAGING_SERVICE, result); + return (T) getRadioServiceProxy(HAL_SERVICE_MESSAGING, result); } if (serviceClass == RadioModemProxy.class) { - return (T) getRadioServiceProxy(MODEM_SERVICE, result); + return (T) getRadioServiceProxy(HAL_SERVICE_MODEM, result); } if (serviceClass == RadioNetworkProxy.class) { - return (T) getRadioServiceProxy(NETWORK_SERVICE, result); + return (T) getRadioServiceProxy(HAL_SERVICE_NETWORK, result); } if (serviceClass == RadioSimProxy.class) { - return (T) getRadioServiceProxy(SIM_SERVICE, result); + return (T) getRadioServiceProxy(HAL_SERVICE_SIM, result); } if (serviceClass == RadioVoiceProxy.class) { - return (T) getRadioServiceProxy(VOICE_SERVICE, result); + return (T) getRadioServiceProxy(HAL_SERVICE_VOICE, result); + } + if (serviceClass == RadioImsProxy.class) { + return (T) getRadioServiceProxy(HAL_SERVICE_IMS, result); } riljLoge("getRadioServiceProxy: unrecognized " + serviceClass); return null; @@ -730,12 +774,15 @@ /** * Returns a {@link RadioServiceProxy}, which is empty if the service is not available. - * For RADIO_SERVICE, use {@link #getRadioProxy} instead, as this will always return null. + * For HAL_SERVICE_RADIO, use {@link #getRadioProxy} instead, as this will always return null. */ @VisibleForTesting @NonNull public synchronized RadioServiceProxy getRadioServiceProxy(int service, Message result) { if (!SubscriptionManager.isValidPhoneId(mPhoneId)) return mServiceProxies.get(service); + if ((service >= HAL_SERVICE_IMS) && !isRadioServiceSupported(service)) { + return mServiceProxies.get(service); + } if (!mIsCellularSupported) { if (RILJ_LOGV) riljLog("getRadioServiceProxy: Not calling getService(): wifi-only"); if (result != null) { @@ -752,168 +799,190 @@ } try { - if (mDisabledRadioServices.get(service).contains(mPhoneId)) { + if (mMockModem == null && mDisabledRadioServices.get(service).contains(mPhoneId)) { riljLoge("getRadioServiceProxy: " + serviceToString(service) + " for " + HIDL_SERVICE_NAME[mPhoneId] + " is disabled"); } else { IBinder binder; switch (service) { - case DATA_SERVICE: + case HAL_SERVICE_DATA: if (mMockModem == null) { binder = ServiceManager.waitForDeclaredService( android.hardware.radio.data.IRadioData.DESCRIPTOR + "/" + HIDL_SERVICE_NAME[mPhoneId]); } else { - binder = mMockModem.getServiceBinder(DATA_SERVICE); + binder = mMockModem.getServiceBinder(HAL_SERVICE_DATA); } if (binder != null) { - mRadioVersion = RADIO_HAL_VERSION_2_0; - ((RadioDataProxy) serviceProxy).setAidl(mRadioVersion, + mHalVersion.put(service, ((RadioDataProxy) serviceProxy).setAidl( + mHalVersion.get(service), android.hardware.radio.data.IRadioData.Stub.asInterface( - binder)); + binder))); } break; - case MESSAGING_SERVICE: + case HAL_SERVICE_MESSAGING: if (mMockModem == null) { binder = ServiceManager.waitForDeclaredService( android.hardware.radio.messaging.IRadioMessaging.DESCRIPTOR + "/" + HIDL_SERVICE_NAME[mPhoneId]); } else { - binder = mMockModem.getServiceBinder(MESSAGING_SERVICE); + binder = mMockModem.getServiceBinder(HAL_SERVICE_MESSAGING); } if (binder != null) { - mRadioVersion = RADIO_HAL_VERSION_2_0; - ((RadioMessagingProxy) serviceProxy).setAidl(mRadioVersion, + mHalVersion.put(service, ((RadioMessagingProxy) serviceProxy).setAidl( + mHalVersion.get(service), android.hardware.radio.messaging.IRadioMessaging.Stub - .asInterface(binder)); + .asInterface(binder))); } break; - case MODEM_SERVICE: + case HAL_SERVICE_MODEM: if (mMockModem == null) { binder = ServiceManager.waitForDeclaredService( android.hardware.radio.modem.IRadioModem.DESCRIPTOR + "/" + HIDL_SERVICE_NAME[mPhoneId]); } else { - binder = mMockModem.getServiceBinder(MODEM_SERVICE); + binder = mMockModem.getServiceBinder(HAL_SERVICE_MODEM); } if (binder != null) { - mRadioVersion = RADIO_HAL_VERSION_2_0; - ((RadioModemProxy) serviceProxy).setAidl(mRadioVersion, + mHalVersion.put(service, ((RadioModemProxy) serviceProxy).setAidl( + mHalVersion.get(service), android.hardware.radio.modem.IRadioModem.Stub - .asInterface(binder)); + .asInterface(binder))); } break; - case NETWORK_SERVICE: + case HAL_SERVICE_NETWORK: if (mMockModem == null) { binder = ServiceManager.waitForDeclaredService( android.hardware.radio.network.IRadioNetwork.DESCRIPTOR + "/" + HIDL_SERVICE_NAME[mPhoneId]); } else { - binder = mMockModem.getServiceBinder(NETWORK_SERVICE); + binder = mMockModem.getServiceBinder(HAL_SERVICE_NETWORK); } if (binder != null) { - mRadioVersion = RADIO_HAL_VERSION_2_0; - ((RadioNetworkProxy) serviceProxy).setAidl(mRadioVersion, + mHalVersion.put(service, ((RadioNetworkProxy) serviceProxy).setAidl( + mHalVersion.get(service), android.hardware.radio.network.IRadioNetwork.Stub - .asInterface(binder)); + .asInterface(binder))); } break; - case SIM_SERVICE: + case HAL_SERVICE_SIM: if (mMockModem == null) { binder = ServiceManager.waitForDeclaredService( android.hardware.radio.sim.IRadioSim.DESCRIPTOR + "/" + HIDL_SERVICE_NAME[mPhoneId]); } else { - binder = mMockModem.getServiceBinder(SIM_SERVICE); + binder = mMockModem.getServiceBinder(HAL_SERVICE_SIM); } if (binder != null) { - mRadioVersion = RADIO_HAL_VERSION_2_0; - ((RadioSimProxy) serviceProxy).setAidl(mRadioVersion, + mHalVersion.put(service, ((RadioSimProxy) serviceProxy).setAidl( + mHalVersion.get(service), android.hardware.radio.sim.IRadioSim.Stub - .asInterface(binder)); + .asInterface(binder))); } break; - case VOICE_SERVICE: + case HAL_SERVICE_VOICE: if (mMockModem == null) { binder = ServiceManager.waitForDeclaredService( android.hardware.radio.voice.IRadioVoice.DESCRIPTOR + "/" + HIDL_SERVICE_NAME[mPhoneId]); } else { - binder = mMockModem.getServiceBinder(VOICE_SERVICE); + binder = mMockModem.getServiceBinder(HAL_SERVICE_VOICE); } if (binder != null) { - mRadioVersion = RADIO_HAL_VERSION_2_0; - ((RadioVoiceProxy) serviceProxy).setAidl(mRadioVersion, + mHalVersion.put(service, ((RadioVoiceProxy) serviceProxy).setAidl( + mHalVersion.get(service), android.hardware.radio.voice.IRadioVoice.Stub - .asInterface(binder)); + .asInterface(binder))); + } + break; + case HAL_SERVICE_IMS: + if (mMockModem == null) { + binder = ServiceManager.waitForDeclaredService( + android.hardware.radio.ims.IRadioIms.DESCRIPTOR + "/" + + HIDL_SERVICE_NAME[mPhoneId]); + } else { + binder = mMockModem.getServiceBinder(HAL_SERVICE_IMS); + } + if (binder != null) { + mHalVersion.put(service, ((RadioImsProxy) serviceProxy).setAidl( + mHalVersion.get(service), + android.hardware.radio.ims.IRadioIms.Stub + .asInterface(binder))); } break; } - if (serviceProxy.isEmpty() && mRadioVersion.less(RADIO_HAL_VERSION_2_0)) { + if (serviceProxy.isEmpty() + && mHalVersion.get(service).less(RADIO_HAL_VERSION_2_0)) { try { - mRadioVersion = RADIO_HAL_VERSION_1_6; - serviceProxy.setHidl(mRadioVersion, + mHalVersion.put(service, RADIO_HAL_VERSION_1_6); + serviceProxy.setHidl(mHalVersion.get(service), android.hardware.radio.V1_6.IRadio.getService( HIDL_SERVICE_NAME[mPhoneId], true)); } catch (NoSuchElementException e) { } } - if (serviceProxy.isEmpty() && mRadioVersion.less(RADIO_HAL_VERSION_2_0)) { + if (serviceProxy.isEmpty() + && mHalVersion.get(service).less(RADIO_HAL_VERSION_2_0)) { try { - mRadioVersion = RADIO_HAL_VERSION_1_5; - serviceProxy.setHidl(mRadioVersion, + mHalVersion.put(service, RADIO_HAL_VERSION_1_5); + serviceProxy.setHidl(mHalVersion.get(service), android.hardware.radio.V1_5.IRadio.getService( HIDL_SERVICE_NAME[mPhoneId], true)); } catch (NoSuchElementException e) { } } - if (serviceProxy.isEmpty() && mRadioVersion.less(RADIO_HAL_VERSION_2_0)) { + if (serviceProxy.isEmpty() + && mHalVersion.get(service).less(RADIO_HAL_VERSION_2_0)) { try { - mRadioVersion = RADIO_HAL_VERSION_1_4; - serviceProxy.setHidl(mRadioVersion, + mHalVersion.put(service, RADIO_HAL_VERSION_1_4); + serviceProxy.setHidl(mHalVersion.get(service), android.hardware.radio.V1_4.IRadio.getService( HIDL_SERVICE_NAME[mPhoneId], true)); } catch (NoSuchElementException e) { } } - if (serviceProxy.isEmpty() && mRadioVersion.less(RADIO_HAL_VERSION_2_0)) { + if (serviceProxy.isEmpty() + && mHalVersion.get(service).less(RADIO_HAL_VERSION_2_0)) { try { - mRadioVersion = RADIO_HAL_VERSION_1_3; - serviceProxy.setHidl(mRadioVersion, + mHalVersion.put(service, RADIO_HAL_VERSION_1_3); + serviceProxy.setHidl(mHalVersion.get(service), android.hardware.radio.V1_3.IRadio.getService( HIDL_SERVICE_NAME[mPhoneId], true)); } catch (NoSuchElementException e) { } } - if (serviceProxy.isEmpty() && mRadioVersion.less(RADIO_HAL_VERSION_2_0)) { + if (serviceProxy.isEmpty() + && mHalVersion.get(service).less(RADIO_HAL_VERSION_2_0)) { try { - mRadioVersion = RADIO_HAL_VERSION_1_2; - serviceProxy.setHidl(mRadioVersion, + mHalVersion.put(service, RADIO_HAL_VERSION_1_2); + serviceProxy.setHidl(mHalVersion.get(service), android.hardware.radio.V1_2.IRadio.getService( HIDL_SERVICE_NAME[mPhoneId], true)); } catch (NoSuchElementException e) { } } - if (serviceProxy.isEmpty() && mRadioVersion.less(RADIO_HAL_VERSION_2_0)) { + if (serviceProxy.isEmpty() + && mHalVersion.get(service).less(RADIO_HAL_VERSION_2_0)) { try { - mRadioVersion = RADIO_HAL_VERSION_1_1; - serviceProxy.setHidl(mRadioVersion, + mHalVersion.put(service, RADIO_HAL_VERSION_1_1); + serviceProxy.setHidl(mHalVersion.get(service), android.hardware.radio.V1_1.IRadio.getService( HIDL_SERVICE_NAME[mPhoneId], true)); } catch (NoSuchElementException e) { } } - if (serviceProxy.isEmpty() && mRadioVersion.less(RADIO_HAL_VERSION_2_0)) { + if (serviceProxy.isEmpty() + && mHalVersion.get(service).less(RADIO_HAL_VERSION_2_0)) { try { - mRadioVersion = RADIO_HAL_VERSION_1_0; - serviceProxy.setHidl(mRadioVersion, + mHalVersion.put(service, RADIO_HAL_VERSION_1_0); + serviceProxy.setHidl(mHalVersion.get(service), android.hardware.radio.V1_0.IRadio.getService( HIDL_SERVICE_NAME[mPhoneId], true)); } catch (NoSuchElementException e) { @@ -923,57 +992,65 @@ if (!serviceProxy.isEmpty()) { if (serviceProxy.isAidl()) { switch (service) { - case DATA_SERVICE: + case HAL_SERVICE_DATA: mDeathRecipients.get(service).linkToDeath( ((RadioDataProxy) serviceProxy).getAidl().asBinder()); ((RadioDataProxy) serviceProxy).getAidl().setResponseFunctions( mDataResponse, mDataIndication); break; - case MESSAGING_SERVICE: + case HAL_SERVICE_MESSAGING: mDeathRecipients.get(service).linkToDeath( ((RadioMessagingProxy) serviceProxy).getAidl().asBinder()); ((RadioMessagingProxy) serviceProxy).getAidl().setResponseFunctions( mMessagingResponse, mMessagingIndication); break; - case MODEM_SERVICE: + case HAL_SERVICE_MODEM: mDeathRecipients.get(service).linkToDeath( ((RadioModemProxy) serviceProxy).getAidl().asBinder()); ((RadioModemProxy) serviceProxy).getAidl().setResponseFunctions( mModemResponse, mModemIndication); break; - case NETWORK_SERVICE: + case HAL_SERVICE_NETWORK: mDeathRecipients.get(service).linkToDeath( ((RadioNetworkProxy) serviceProxy).getAidl().asBinder()); ((RadioNetworkProxy) serviceProxy).getAidl().setResponseFunctions( mNetworkResponse, mNetworkIndication); break; - case SIM_SERVICE: + case HAL_SERVICE_SIM: mDeathRecipients.get(service).linkToDeath( ((RadioSimProxy) serviceProxy).getAidl().asBinder()); ((RadioSimProxy) serviceProxy).getAidl().setResponseFunctions( mSimResponse, mSimIndication); break; - case VOICE_SERVICE: + case HAL_SERVICE_VOICE: mDeathRecipients.get(service).linkToDeath( ((RadioVoiceProxy) serviceProxy).getAidl().asBinder()); ((RadioVoiceProxy) serviceProxy).getAidl().setResponseFunctions( mVoiceResponse, mVoiceIndication); break; + case HAL_SERVICE_IMS: + mDeathRecipients.get(service).linkToDeath( + ((RadioImsProxy) serviceProxy).getAidl().asBinder()); + ((RadioImsProxy) serviceProxy).getAidl().setResponseFunctions( + mImsResponse, mImsIndication); + break; } } else { - if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_2_0)) { + if (mHalVersion.get(service) + .greaterOrEqual(RADIO_HAL_VERSION_2_0)) { throw new AssertionError("serviceProxy shouldn't be HIDL with HAL 2.0"); } if (!mIsRadioProxyInitialized) { mIsRadioProxyInitialized = true; serviceProxy.getHidl().linkToDeath(mRadioProxyDeathRecipient, - mServiceCookies.get(service).incrementAndGet()); + mServiceCookies.get(HAL_SERVICE_RADIO).incrementAndGet()); serviceProxy.getHidl().setResponseFunctions( mRadioResponse, mRadioIndication); } } } else { mDisabledRadioServices.get(service).add(mPhoneId); + mHalVersion.put(service, RADIO_HAL_VERSION_UNKNOWN); riljLoge("getRadioServiceProxy: set " + serviceToString(service) + " for " + HIDL_SERVICE_NAME[mPhoneId] + " as disabled"); } @@ -1002,7 +1079,7 @@ for (int service = MIN_SERVICE_IDX; service <= MAX_SERVICE_IDX; service++) { if (active) { // Try to connect to RIL services and set response functions. - if (service == RADIO_SERVICE) { + if (service == HAL_SERVICE_RADIO) { getRadioProxy(null); } else { getRadioServiceProxy(service, null); @@ -1043,7 +1120,11 @@ mRadioBugDetector = new RadioBugDetector(context, mPhoneId); } try { - if (isRadioVersion2_0()) mRadioVersion = RADIO_HAL_VERSION_2_0; + if (isRadioVersion2_0()) { + mHalVersion.put(HAL_SERVICE_RADIO, RADIO_HAL_VERSION_2_0); + } else { + mHalVersion.put(HAL_SERVICE_RADIO, RADIO_HAL_VERSION_UNKNOWN); + } } catch (SecurityException ex) { /* TODO(b/211920208): instead of the following workaround (guessing if we're in a test * based on proxies being populated), mock ServiceManager to not throw @@ -1059,6 +1140,8 @@ mRadioIndication = new RadioIndication(this); mDataResponse = new DataResponse(this); mDataIndication = new DataIndication(this); + mImsResponse = new ImsResponse(this); + mImsIndication = new ImsIndication(this); mMessagingResponse = new MessagingResponse(this); mMessagingIndication = new MessagingIndication(this); mModemResponse = new ModemResponse(this); @@ -1072,19 +1155,33 @@ mRilHandler = new RilHandler(); mRadioProxyDeathRecipient = new RadioProxyDeathRecipient(); for (int service = MIN_SERVICE_IDX; service <= MAX_SERVICE_IDX; service++) { - if (service != RADIO_SERVICE) { + if (service != HAL_SERVICE_RADIO) { + try { + if (isRadioServiceSupported(service)) { + mHalVersion.put(service, RADIO_HAL_VERSION_UNKNOWN); + } else { + mHalVersion.put(service, RADIO_HAL_VERSION_UNSUPPORTED); + } + } catch (SecurityException ex) { + /* TODO(b/211920208): instead of the following workaround (guessing if + * we're in a test based on proxies being populated), mock ServiceManager + * to not throw SecurityException and return correct value based on what + * HAL we're testing. */ + if (proxies == null) throw ex; + } mDeathRecipients.put(service, new BinderServiceDeathRecipient(service)); } mDisabledRadioServices.put(service, new HashSet<>()); mServiceCookies.put(service, new AtomicLong(0)); } if (proxies == null) { - mServiceProxies.put(DATA_SERVICE, new RadioDataProxy()); - mServiceProxies.put(MESSAGING_SERVICE, new RadioMessagingProxy()); - mServiceProxies.put(MODEM_SERVICE, new RadioModemProxy()); - mServiceProxies.put(NETWORK_SERVICE, new RadioNetworkProxy()); - mServiceProxies.put(SIM_SERVICE, new RadioSimProxy()); - mServiceProxies.put(VOICE_SERVICE, new RadioVoiceProxy()); + mServiceProxies.put(HAL_SERVICE_DATA, new RadioDataProxy()); + mServiceProxies.put(HAL_SERVICE_MESSAGING, new RadioMessagingProxy()); + mServiceProxies.put(HAL_SERVICE_MODEM, new RadioModemProxy()); + mServiceProxies.put(HAL_SERVICE_NETWORK, new RadioNetworkProxy()); + mServiceProxies.put(HAL_SERVICE_SIM, new RadioSimProxy()); + mServiceProxies.put(HAL_SERVICE_VOICE, new RadioVoiceProxy()); + mServiceProxies.put(HAL_SERVICE_IMS, new RadioImsProxy()); } else { mServiceProxies = proxies; } @@ -1113,7 +1210,7 @@ // Set radio callback; needed to set RadioIndication callback (should be done after // wakelock stuff is initialized above as callbacks are received on separate binder threads) for (int service = MIN_SERVICE_IDX; service <= MAX_SERVICE_IDX; service++) { - if (service == RADIO_SERVICE) { + if (service == HAL_SERVICE_RADIO) { getRadioProxy(null); } else { if (proxies == null) { @@ -1121,30 +1218,62 @@ getRadioServiceProxy(service, null); } } - } - if (RILJ_LOGD) { - riljLog("Radio HAL version: " + mRadioVersion); + if (RILJ_LOGD) { + riljLog("HAL version of " + serviceToString(service) + + ": " + mHalVersion.get(service)); + } } } private boolean isRadioVersion2_0() { - final String[] serviceNames = new String[] { - android.hardware.radio.data.IRadioData.DESCRIPTOR, - android.hardware.radio.messaging.IRadioMessaging.DESCRIPTOR, - android.hardware.radio.modem.IRadioModem.DESCRIPTOR, - android.hardware.radio.network.IRadioNetwork.DESCRIPTOR, - android.hardware.radio.sim.IRadioSim.DESCRIPTOR, - android.hardware.radio.voice.IRadioVoice.DESCRIPTOR, - }; - for (String serviceName : serviceNames) { - if (ServiceManager.isDeclared(serviceName + '/' + HIDL_SERVICE_NAME[mPhoneId])) { + for (int service = HAL_SERVICE_DATA; service <= MAX_SERVICE_IDX; service++) { + if (isRadioServiceSupported(service)) { return true; } } return false; } + private boolean isRadioServiceSupported(int service) { + String serviceName = ""; + + if (service == HAL_SERVICE_RADIO) { + return true; + } + + switch (service) { + case HAL_SERVICE_DATA: + serviceName = android.hardware.radio.data.IRadioData.DESCRIPTOR; + break; + case HAL_SERVICE_MESSAGING: + serviceName = android.hardware.radio.messaging.IRadioMessaging.DESCRIPTOR; + break; + case HAL_SERVICE_MODEM: + serviceName = android.hardware.radio.modem.IRadioModem.DESCRIPTOR; + break; + case HAL_SERVICE_NETWORK: + serviceName = android.hardware.radio.network.IRadioNetwork.DESCRIPTOR; + break; + case HAL_SERVICE_SIM: + serviceName = android.hardware.radio.sim.IRadioSim.DESCRIPTOR; + break; + case HAL_SERVICE_VOICE: + serviceName = android.hardware.radio.voice.IRadioVoice.DESCRIPTOR; + break; + case HAL_SERVICE_IMS: + serviceName = android.hardware.radio.ims.IRadioIms.DESCRIPTOR; + break; + } + + if (!serviceName.equals("") + && ServiceManager.isDeclared(serviceName + '/' + HIDL_SERVICE_NAME[mPhoneId])) { + return true; + } + + return false; + } + private boolean isRadioBugDetectionEnabled() { return Settings.Global.getInt(mContext.getContentResolver(), Settings.Global.ENABLE_RADIO_BUG_DETECTION, 1) != 0; @@ -1162,6 +1291,8 @@ private void addRequest(RILRequest rr) { acquireWakeLock(rr, FOR_WAKELOCK); + Trace.asyncTraceForTrackBegin( + Trace.TRACE_TAG_NETWORK, "RIL", RILUtils.requestToString(rr.mRequest), rr.mSerial); synchronized (mRequestList) { rr.mStartTimeMs = SystemClock.elapsedRealtime(); mRequestList.append(rr.mSerial, rr); @@ -1202,7 +1333,7 @@ try { simProxy.getIccCardStatus(rr.mSerial); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(SIM_SERVICE, "getIccCardStatus", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "getIccCardStatus", e); } } } @@ -1249,7 +1380,7 @@ simProxy.supplyIccPinForApp(rr.mSerial, RILUtils.convertNullToEmptyString(pin), RILUtils.convertNullToEmptyString(aid)); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(SIM_SERVICE, "supplyIccPinForApp", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "supplyIccPinForApp", e); } } } @@ -1276,7 +1407,7 @@ RILUtils.convertNullToEmptyString(newPin), RILUtils.convertNullToEmptyString(aid)); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(SIM_SERVICE, "supplyIccPukForApp", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "supplyIccPukForApp", e); } } } @@ -1302,7 +1433,7 @@ simProxy.supplyIccPin2ForApp(rr.mSerial, RILUtils.convertNullToEmptyString(pin), RILUtils.convertNullToEmptyString(aid)); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(SIM_SERVICE, "supplyIccPin2ForApp", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "supplyIccPin2ForApp", e); } } } @@ -1329,7 +1460,7 @@ RILUtils.convertNullToEmptyString(newPin2), RILUtils.convertNullToEmptyString(aid)); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(SIM_SERVICE, "supplyIccPuk2ForApp", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "supplyIccPuk2ForApp", e); } } } @@ -1357,7 +1488,7 @@ RILUtils.convertNullToEmptyString(newPin), RILUtils.convertNullToEmptyString(aid)); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(SIM_SERVICE, "changeIccPinForApp", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "changeIccPinForApp", e); } } } @@ -1385,7 +1516,7 @@ RILUtils.convertNullToEmptyString(newPin2), RILUtils.convertNullToEmptyString(aid)); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(SIM_SERVICE, "changeIccPin2ForApp", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "changeIccPin2ForApp", e); } } } @@ -1407,7 +1538,7 @@ RILUtils.convertNullToEmptyString(netpin)); } catch (RemoteException | RuntimeException e) { handleRadioProxyExceptionForRR( - NETWORK_SERVICE, "supplyNetworkDepersonalization", e); + HAL_SERVICE_NETWORK, "supplyNetworkDepersonalization", e); } } } @@ -1417,7 +1548,7 @@ Message result) { RadioSimProxy simProxy = getRadioServiceProxy(RadioSimProxy.class, result); if (simProxy.isEmpty()) return; - if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_5)) { + if (mHalVersion.get(HAL_SERVICE_SIM).greaterOrEqual(RADIO_HAL_VERSION_1_5)) { RILRequest rr = obtainRequest(RIL_REQUEST_ENTER_SIM_DEPERSONALIZATION, result, mRILDefaultWorkSource); @@ -1430,7 +1561,7 @@ simProxy.supplySimDepersonalization(rr.mSerial, persoType, RILUtils.convertNullToEmptyString(controlKey)); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(SIM_SERVICE, "supplySimDepersonalization", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "supplySimDepersonalization", e); } } else { if (PersoSubState.PERSOSUBSTATE_SIM_NETWORK == persoType) { @@ -1462,7 +1593,7 @@ try { voiceProxy.getCurrentCalls(rr.mSerial); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(VOICE_SERVICE, "getCurrentCalls", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "getCurrentCalls", e); } } } @@ -1478,7 +1609,7 @@ public void enableModem(boolean enable, Message result) { RadioModemProxy modemProxy = getRadioServiceProxy(RadioModemProxy.class, result); if (modemProxy.isEmpty()) return; - if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_3)) { + if (mHalVersion.get(HAL_SERVICE_MODEM).greaterOrEqual(RADIO_HAL_VERSION_1_3)) { RILRequest rr = obtainRequest(RIL_REQUEST_ENABLE_MODEM, result, mRILDefaultWorkSource); if (RILJ_LOGD) { @@ -1489,7 +1620,8 @@ try { modemProxy.enableModem(rr.mSerial, enable); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(MODEM_SERVICE, "enableModem", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_MODEM, + "enableModem", e); } } else { if (RILJ_LOGV) riljLog("enableModem: not supported."); @@ -1506,7 +1638,7 @@ Message result) { RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result); if (networkProxy.isEmpty()) return; - if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_3)) { + if (mHalVersion.get(HAL_SERVICE_NETWORK).greaterOrEqual(RADIO_HAL_VERSION_1_3)) { RILRequest rr = obtainRequest(RIL_REQUEST_SET_SYSTEM_SELECTION_CHANNELS, result, mRILDefaultWorkSource); @@ -1518,7 +1650,8 @@ try { networkProxy.setSystemSelectionChannels(rr.mSerial, specifiers); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(NETWORK_SERVICE, "setSystemSelectionChannels", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, + "setSystemSelectionChannels", e); } } else { if (RILJ_LOGV) riljLog("setSystemSelectionChannels: not supported."); @@ -1534,7 +1667,7 @@ public void getSystemSelectionChannels(Message result) { RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result); if (networkProxy.isEmpty()) return; - if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_6)) { + if (mHalVersion.get(HAL_SERVICE_NETWORK).greaterOrEqual(RADIO_HAL_VERSION_1_6)) { RILRequest rr = obtainRequest(RIL_REQUEST_GET_SYSTEM_SELECTION_CHANNELS, result, mRILDefaultWorkSource); @@ -1546,7 +1679,8 @@ try { networkProxy.getSystemSelectionChannels(rr.mSerial); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(NETWORK_SERVICE, "getSystemSelectionChannels", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, + "getSystemSelectionChannels", e); } } else { if (RILJ_LOGV) riljLog("getSystemSelectionChannels: not supported."); @@ -1562,7 +1696,7 @@ public void getModemStatus(Message result) { RadioModemProxy modemProxy = getRadioServiceProxy(RadioModemProxy.class, result); if (modemProxy.isEmpty()) return; - if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_3)) { + if (mHalVersion.get(HAL_SERVICE_MODEM).greaterOrEqual(RADIO_HAL_VERSION_1_3)) { RILRequest rr = obtainRequest(RIL_REQUEST_GET_MODEM_STATUS, result, mRILDefaultWorkSource); @@ -1573,7 +1707,7 @@ try { modemProxy.getModemStackStatus(rr.mSerial); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(MODEM_SERVICE, "getModemStatus", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_MODEM, "getModemStatus", e); } } else { if (RILJ_LOGV) riljLog("getModemStatus: not supported."); @@ -1588,7 +1722,8 @@ @Override public void dial(String address, boolean isEmergencyCall, EmergencyNumber emergencyNumberInfo, boolean hasKnownUserIntentEmergency, int clirMode, UUSInfo uusInfo, Message result) { - if (isEmergencyCall && mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_4) + if (isEmergencyCall + && mHalVersion.get(HAL_SERVICE_VOICE).greaterOrEqual(RADIO_HAL_VERSION_1_4) && emergencyNumberInfo != null) { emergencyDial(address, emergencyNumberInfo, hasKnownUserIntentEmergency, clirMode, uusInfo, result); @@ -1606,7 +1741,7 @@ try { voiceProxy.dial(rr.mSerial, address, clirMode, uusInfo); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(VOICE_SERVICE, "dial", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "dial", e); } } } @@ -1615,7 +1750,7 @@ boolean hasKnownUserIntentEmergency, int clirMode, UUSInfo uusInfo, Message result) { RadioVoiceProxy voiceProxy = getRadioServiceProxy(RadioVoiceProxy.class, result); if (voiceProxy.isEmpty()) return; - if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_4)) { + if (mHalVersion.get(HAL_SERVICE_VOICE).greaterOrEqual(RADIO_HAL_VERSION_1_4)) { RILRequest rr = obtainRequest(RIL_REQUEST_EMERGENCY_DIAL, result, mRILDefaultWorkSource); @@ -1628,7 +1763,7 @@ voiceProxy.emergencyDial(rr.mSerial, RILUtils.convertNullToEmptyString(address), emergencyNumberInfo, hasKnownUserIntentEmergency, clirMode, uusInfo); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(VOICE_SERVICE, "emergencyDial", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "emergencyDial", e); } } else { riljLoge("emergencyDial is not supported with 1.4 below IRadio"); @@ -1647,13 +1782,13 @@ RILRequest rr = obtainRequest(RIL_REQUEST_GET_IMSI, result, mRILDefaultWorkSource); if (RILJ_LOGD) { - riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest) + riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest) + " aid = " + aid); } try { simProxy.getImsiForApp(rr.mSerial, RILUtils.convertNullToEmptyString(aid)); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(SIM_SERVICE, "getImsiForApp", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "getImsiForApp", e); } } } @@ -1672,7 +1807,7 @@ try { voiceProxy.hangup(rr.mSerial, gsmIndex); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(VOICE_SERVICE, "hangup", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "hangup", e); } } } @@ -1692,7 +1827,7 @@ try { voiceProxy.hangupWaitingOrBackground(rr.mSerial); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(VOICE_SERVICE, "hangupWaitingOrBackground", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "hangupWaitingOrBackground", e); } } } @@ -1713,7 +1848,7 @@ voiceProxy.hangupForegroundResumeBackground(rr.mSerial); } catch (RemoteException | RuntimeException e) { handleRadioProxyExceptionForRR( - VOICE_SERVICE, "hangupForegroundResumeBackground", e); + HAL_SERVICE_VOICE, "hangupForegroundResumeBackground", e); } } } @@ -1732,7 +1867,8 @@ try { voiceProxy.switchWaitingOrHoldingAndActive(rr.mSerial); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(VOICE_SERVICE, "switchWaitingOrHoldingAndActive", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, + "switchWaitingOrHoldingAndActive", e); } } } @@ -1750,7 +1886,7 @@ try { voiceProxy.conference(rr.mSerial); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(VOICE_SERVICE, "conference", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "conference", e); } } } @@ -1768,7 +1904,7 @@ try { voiceProxy.rejectCall(rr.mSerial); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(VOICE_SERVICE, "rejectCall", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "rejectCall", e); } } } @@ -1787,7 +1923,7 @@ try { voiceProxy.getLastCallFailCause(rr.mSerial); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(VOICE_SERVICE, "getLastCallFailCause", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "getLastCallFailCause", e); } } } @@ -1806,7 +1942,7 @@ try { networkProxy.getSignalStrength(rr.mSerial); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(NETWORK_SERVICE, "getSignalStrength", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "getSignalStrength", e); } } } @@ -1830,7 +1966,7 @@ try { networkProxy.getVoiceRegistrationState(rr.mSerial, overrideHalVersion); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(NETWORK_SERVICE, "getVoiceRegistrationState", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "getVoiceRegistrationState", e); } } } @@ -1854,7 +1990,7 @@ try { networkProxy.getDataRegistrationState(rr.mSerial, overrideHalVersion); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(NETWORK_SERVICE, "getDataRegistrationState", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "getDataRegistrationState", e); } } } @@ -1872,7 +2008,7 @@ try { networkProxy.getOperator(rr.mSerial); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(NETWORK_SERVICE, "getOperator", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "getOperator", e); } } } @@ -1895,7 +2031,7 @@ modemProxy.setRadioPower(rr.mSerial, on, forEmergencyCall, preferredForEmergencyCall); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(MODEM_SERVICE, "setRadioPower", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_MODEM, "setRadioPower", e); } } } @@ -1914,7 +2050,7 @@ try { voiceProxy.sendDtmf(rr.mSerial, c + ""); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(VOICE_SERVICE, "sendDtmf", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "sendDtmf", e); } } } @@ -1936,7 +2072,7 @@ mMetrics.writeRilSendSms(mPhoneId, rr.mSerial, SmsSession.Event.Tech.SMS_GSM, SmsSession.Event.Format.SMS_FORMAT_3GPP, getOutgoingSmsMessageId(result)); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(MESSAGING_SERVICE, "sendSMS", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_MESSAGING, "sendSMS", e); } } } @@ -1977,7 +2113,7 @@ mMetrics.writeRilSendSms(mPhoneId, rr.mSerial, SmsSession.Event.Tech.SMS_GSM, SmsSession.Event.Format.SMS_FORMAT_3GPP, getOutgoingSmsMessageId(result)); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(MESSAGING_SERVICE, "sendSMSExpectMore", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_MESSAGING, "sendSMSExpectMore", e); } } } @@ -2007,8 +2143,21 @@ dataProxy.setupDataCall(rr.mSerial, mPhoneId, accessNetworkType, dataProfile, isRoaming, allowRoaming, reason, linkProperties, pduSessionId, sliceInfo, trafficDescriptor, matchAllRuleAllowed); - } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(DATA_SERVICE, "setupDataCall", e); + } catch (RemoteException e) { + handleRadioProxyExceptionForRR(HAL_SERVICE_DATA, "setupDataCall", e); + } catch (RuntimeException e) { + riljLoge("setupDataCall RuntimeException: " + e); + int error = RadioError.SYSTEM_ERR; + int responseType = RadioResponseType.SOLICITED; + processResponseInternal(HAL_SERVICE_DATA, rr.mSerial, error, responseType); + processResponseDoneInternal(rr, error, responseType, null); + } + } else { + riljLoge("setupDataCall: DataProxy is empty"); + if (result != null) { + AsyncResult.forMessage(result, null, + CommandException.fromRilErrno(RADIO_NOT_AVAILABLE)); + result.sendToTarget(); } } } @@ -2045,7 +2194,7 @@ RILUtils.convertNullToEmptyString(pin2), RILUtils.convertNullToEmptyString(aid)); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(SIM_SERVICE, "iccIoForApp", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "iccIoForApp", e); } } } @@ -2067,7 +2216,7 @@ try { voiceProxy.sendUssd(rr.mSerial, RILUtils.convertNullToEmptyString(ussd)); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(VOICE_SERVICE, "sendUssd", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "sendUssd", e); } } } @@ -2086,7 +2235,7 @@ try { voiceProxy.cancelPendingUssd(rr.mSerial); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(VOICE_SERVICE, "cancelPendingUssd", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "cancelPendingUssd", e); } } } @@ -2104,7 +2253,7 @@ try { voiceProxy.getClir(rr.mSerial); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(VOICE_SERVICE, "getClir", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "getClir", e); } } } @@ -2123,7 +2272,7 @@ try { voiceProxy.setClir(rr.mSerial, clirMode); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(VOICE_SERVICE, "setClir", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "setClir", e); } } } @@ -2144,7 +2293,7 @@ try { voiceProxy.getCallForwardStatus(rr.mSerial, cfReason, serviceClass, number); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(VOICE_SERVICE, "getCallForwardStatus", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "getCallForwardStatus", e); } } } @@ -2167,7 +2316,7 @@ voiceProxy.setCallForward( rr.mSerial, action, cfReason, serviceClass, number, timeSeconds); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(VOICE_SERVICE, "setCallForward", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "setCallForward", e); } } } @@ -2187,7 +2336,7 @@ try { voiceProxy.getCallWaiting(rr.mSerial, serviceClass); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(VOICE_SERVICE, "getCallWaiting", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "getCallWaiting", e); } } } @@ -2207,7 +2356,7 @@ try { voiceProxy.setCallWaiting(rr.mSerial, enable, serviceClass); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(VOICE_SERVICE, "setCallWaiting", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "setCallWaiting", e); } } } @@ -2228,7 +2377,7 @@ try { messagingProxy.acknowledgeLastIncomingGsmSms(rr.mSerial, success, cause); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(MESSAGING_SERVICE, + handleRadioProxyExceptionForRR(HAL_SERVICE_MESSAGING, "acknowledgeLastIncomingGsmSms", e); } } @@ -2248,7 +2397,7 @@ voiceProxy.acceptCall(rr.mSerial); mMetrics.writeRilAnswer(mPhoneId, rr.mSerial); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(VOICE_SERVICE, "acceptCall", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "acceptCall", e); } } } @@ -2270,7 +2419,7 @@ dataProxy.deactivateDataCall(rr.mSerial, cid, reason); mMetrics.writeRilDeactivateDataCall(mPhoneId, rr.mSerial, cid, reason); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(DATA_SERVICE, "deactivateDataCall", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_DATA, "deactivateDataCall", e); } } } @@ -2301,7 +2450,7 @@ RILUtils.convertNullToEmptyString(password), serviceClass, RILUtils.convertNullToEmptyString(appId)); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(SIM_SERVICE, "getFacilityLockForApp", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "getFacilityLockForApp", e); } } } @@ -2332,7 +2481,7 @@ RILUtils.convertNullToEmptyString(password), serviceClass, RILUtils.convertNullToEmptyString(appId)); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(SIM_SERVICE, "setFacilityLockForApp", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "setFacilityLockForApp", e); } } } @@ -2357,7 +2506,7 @@ RILUtils.convertNullToEmptyString(oldPwd), RILUtils.convertNullToEmptyString(newPwd)); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(NETWORK_SERVICE, "changeBarringPassword", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "changeBarringPassword", e); } } } @@ -2376,7 +2525,7 @@ try { networkProxy.getNetworkSelectionMode(rr.mSerial); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(NETWORK_SERVICE, "getNetworkSelectionMode", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "getNetworkSelectionMode", e); } } } @@ -2396,7 +2545,7 @@ networkProxy.setNetworkSelectionModeAutomatic(rr.mSerial); } catch (RemoteException | RuntimeException e) { handleRadioProxyExceptionForRR( - NETWORK_SERVICE, "setNetworkSelectionModeAutomatic", e); + HAL_SERVICE_NETWORK, "setNetworkSelectionModeAutomatic", e); } } } @@ -2417,7 +2566,8 @@ networkProxy.setNetworkSelectionModeManual(rr.mSerial, RILUtils.convertNullToEmptyString(operatorNumeric), ran); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(NETWORK_SERVICE, "setNetworkSelectionModeManual", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, + "setNetworkSelectionModeManual", e); } } } @@ -2436,7 +2586,7 @@ try { networkProxy.getAvailableNetworks(rr.mSerial); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(NETWORK_SERVICE, "getAvailableNetworks", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "getAvailableNetworks", e); } } } @@ -2451,7 +2601,7 @@ public void startNetworkScan(NetworkScanRequest networkScanRequest, Message result) { RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result); if (networkProxy.isEmpty()) return; - if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_1)) { + if (mHalVersion.get(HAL_SERVICE_NETWORK).greaterOrEqual(RADIO_HAL_VERSION_1_1)) { HalVersion overrideHalVersion = getCompatVersion(RIL_REQUEST_START_NETWORK_SCAN); if (RILJ_LOGD) { riljLog("startNetworkScan: overrideHalVersion=" + overrideHalVersion); @@ -2468,7 +2618,7 @@ networkProxy.startNetworkScan(rr.mSerial, networkScanRequest, overrideHalVersion, result); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(NETWORK_SERVICE, "startNetworkScan", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "startNetworkScan", e); } } else { if (RILJ_LOGD) Rlog.d(RILJ_LOG_TAG, "startNetworkScan: REQUEST_NOT_SUPPORTED"); @@ -2484,7 +2634,7 @@ public void stopNetworkScan(Message result) { RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result); if (networkProxy.isEmpty()) return; - if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_1)) { + if (mHalVersion.get(HAL_SERVICE_NETWORK).greaterOrEqual(RADIO_HAL_VERSION_1_1)) { RILRequest rr = obtainRequest(RIL_REQUEST_STOP_NETWORK_SCAN, result, mRILDefaultWorkSource); @@ -2495,7 +2645,7 @@ try { networkProxy.stopNetworkScan(rr.mSerial); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(NETWORK_SERVICE, "stopNetworkScan", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "stopNetworkScan", e); } } else { if (RILJ_LOGD) Rlog.d(RILJ_LOG_TAG, "stopNetworkScan: REQUEST_NOT_SUPPORTED"); @@ -2521,7 +2671,7 @@ try { voiceProxy.startDtmf(rr.mSerial, c + ""); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(VOICE_SERVICE, "startDtmf", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "startDtmf", e); } } } @@ -2539,7 +2689,7 @@ try { voiceProxy.stopDtmf(rr.mSerial); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(VOICE_SERVICE, "stopDtmf", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "stopDtmf", e); } } } @@ -2559,7 +2709,7 @@ try { voiceProxy.separateConnection(rr.mSerial, gsmIndex); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(VOICE_SERVICE, "separateConnection", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "separateConnection", e); } } } @@ -2578,7 +2728,7 @@ try { modemProxy.getBasebandVersion(rr.mSerial); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(MODEM_SERVICE, "getBasebandVersion", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_MODEM, "getBasebandVersion", e); } } } @@ -2597,7 +2747,7 @@ try { voiceProxy.setMute(rr.mSerial, enableMute); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(VOICE_SERVICE, "setMute", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "setMute", e); } } } @@ -2615,7 +2765,7 @@ try { voiceProxy.getMute(rr.mSerial); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(VOICE_SERVICE, "getMute", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "getMute", e); } } } @@ -2633,7 +2783,7 @@ try { voiceProxy.getClip(rr.mSerial); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(VOICE_SERVICE, "getClip", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "getClip", e); } } } @@ -2661,7 +2811,7 @@ try { dataProxy.getDataCallList(rr.mSerial); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(DATA_SERVICE, "getDataCallList", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_DATA, "getDataCallList", e); } } } @@ -2692,7 +2842,8 @@ try { networkProxy.setSuppServiceNotifications(rr.mSerial, enable); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(NETWORK_SERVICE, "setSuppServiceNotifications", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, + "setSuppServiceNotifications", e); } } } @@ -2715,7 +2866,7 @@ RILUtils.convertNullToEmptyString(smsc), RILUtils.convertNullToEmptyString(pdu)); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(MESSAGING_SERVICE, "writeSmsToSim", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_MESSAGING, "writeSmsToSim", e); } } } @@ -2736,7 +2887,7 @@ try { messagingProxy.deleteSmsOnSim(rr.mSerial, index); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(MESSAGING_SERVICE, "deleteSmsOnSim", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_MESSAGING, "deleteSmsOnSim", e); } } } @@ -2755,7 +2906,7 @@ try { networkProxy.setBandMode(rr.mSerial, bandMode); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(NETWORK_SERVICE, "setBandMode", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "setBandMode", e); } } } @@ -2774,7 +2925,7 @@ try { networkProxy.getAvailableBandModes(rr.mSerial); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(NETWORK_SERVICE, "queryAvailableBandMode", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "queryAvailableBandMode", e); } } } @@ -2794,7 +2945,7 @@ try { simProxy.sendEnvelope(rr.mSerial, RILUtils.convertNullToEmptyString(contents)); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(SIM_SERVICE, "sendEnvelope", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "sendEnvelope", e); } } } @@ -2816,7 +2967,7 @@ simProxy.sendTerminalResponseToSim(rr.mSerial, RILUtils.convertNullToEmptyString(contents)); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(SIM_SERVICE, "sendTerminalResponse", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "sendTerminalResponse", e); } } } @@ -2837,7 +2988,7 @@ simProxy.sendEnvelopeWithStatus(rr.mSerial, RILUtils.convertNullToEmptyString(contents)); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(SIM_SERVICE, "sendEnvelopeWithStatus", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "sendEnvelopeWithStatus", e); } } } @@ -2856,7 +3007,7 @@ try { voiceProxy.explicitCallTransfer(rr.mSerial); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(VOICE_SERVICE, "explicitCallTransfer", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "explicitCallTransfer", e); } } } @@ -2878,7 +3029,7 @@ try { networkProxy.setPreferredNetworkTypeBitmap(rr.mSerial, mAllowedNetworkTypesBitmask); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(NETWORK_SERVICE, "setPreferredNetworkType", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "setPreferredNetworkType", e); } } } @@ -2897,7 +3048,7 @@ try { networkProxy.getAllowedNetworkTypesBitmap(rr.mSerial); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(NETWORK_SERVICE, "getPreferredNetworkType", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "getPreferredNetworkType", e); } } } @@ -2907,7 +3058,7 @@ @TelephonyManager.NetworkTypeBitMask int networkTypeBitmask, Message result) { RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result); if (!networkProxy.isEmpty()) { - if (mRadioVersion.less(RADIO_HAL_VERSION_1_6)) { + if (mHalVersion.get(HAL_SERVICE_NETWORK).less(RADIO_HAL_VERSION_1_6)) { // For older HAL, redirects the call to setPreferredNetworkType. setPreferredNetworkType( RadioAccessFamily.getNetworkTypeFromRaf(networkTypeBitmask), result); @@ -2924,7 +3075,8 @@ try { networkProxy.setAllowedNetworkTypesBitmap(rr.mSerial, mAllowedNetworkTypesBitmask); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(NETWORK_SERVICE, "setAllowedNetworkTypeBitmask", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, + "setAllowedNetworkTypeBitmask", e); } } } @@ -2943,7 +3095,8 @@ try { networkProxy.getAllowedNetworkTypesBitmap(rr.mSerial); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(NETWORK_SERVICE, "getAllowedNetworkTypeBitmask", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, + "getAllowedNetworkTypeBitmask", e); } } } @@ -2963,7 +3116,7 @@ try { networkProxy.setLocationUpdates(rr.mSerial, enable); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(NETWORK_SERVICE, "setLocationUpdates", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "setLocationUpdates", e); } } } @@ -2975,7 +3128,7 @@ public void isNrDualConnectivityEnabled(Message result, WorkSource workSource) { RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result); if (networkProxy.isEmpty()) return; - if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_6)) { + if (mHalVersion.get(HAL_SERVICE_NETWORK).greaterOrEqual(RADIO_HAL_VERSION_1_6)) { RILRequest rr = obtainRequest(RIL_REQUEST_IS_NR_DUAL_CONNECTIVITY_ENABLED, result, getDefaultWorkSourceIfInvalid(workSource)); @@ -2986,7 +3139,8 @@ try { networkProxy.isNrDualConnectivityEnabled(rr.mSerial); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(NETWORK_SERVICE, "isNrDualConnectivityEnabled", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, + "isNrDualConnectivityEnabled", e); } } else { if (RILJ_LOGD) { @@ -3016,7 +3170,7 @@ WorkSource workSource) { RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result); if (networkProxy.isEmpty()) return; - if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_6)) { + if (mHalVersion.get(HAL_SERVICE_NETWORK).greaterOrEqual(RADIO_HAL_VERSION_1_6)) { RILRequest rr = obtainRequest(RIL_REQUEST_ENABLE_NR_DUAL_CONNECTIVITY, result, getDefaultWorkSourceIfInvalid(workSource)); @@ -3028,7 +3182,7 @@ try { networkProxy.setNrDualConnectivityState(rr.mSerial, (byte) nrDualConnectivityState); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(NETWORK_SERVICE, "enableNrDualConnectivity", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "enableNrDualConnectivity", e); } } else { if (RILJ_LOGD) Rlog.d(RILJ_LOG_TAG, "enableNrDualConnectivity: REQUEST_NOT_SUPPORTED"); @@ -3054,7 +3208,7 @@ @Override public void isVoNrEnabled(Message result, WorkSource workSource) { - if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_2_0)) { + if (mHalVersion.get(HAL_SERVICE_VOICE).greaterOrEqual(RADIO_HAL_VERSION_2_0)) { RadioVoiceProxy voiceProxy = getRadioServiceProxy(RadioVoiceProxy.class, result); if (!voiceProxy.isEmpty()) { RILRequest rr = obtainRequest(RIL_REQUEST_IS_VONR_ENABLED , result, @@ -3067,7 +3221,7 @@ try { voiceProxy.isVoNrEnabled(rr.mSerial); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(VOICE_SERVICE, "isVoNrEnabled", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "isVoNrEnabled", e); } } } else { @@ -3087,7 +3241,7 @@ public void setVoNrEnabled(boolean enabled, Message result, WorkSource workSource) { setVoNrEnabled(enabled); - if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_2_0)) { + if (mHalVersion.get(HAL_SERVICE_VOICE).greaterOrEqual(RADIO_HAL_VERSION_2_0)) { RadioVoiceProxy voiceProxy = getRadioServiceProxy(RadioVoiceProxy.class, result); if (!voiceProxy.isEmpty()) { RILRequest rr = obtainRequest(RIL_REQUEST_ENABLE_VONR, result, @@ -3100,7 +3254,7 @@ try { voiceProxy.setVoNrEnabled(rr.mSerial, enabled); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(VOICE_SERVICE, "setVoNrEnabled", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "setVoNrEnabled", e); } } } else { @@ -3132,7 +3286,7 @@ try { simProxy.setCdmaSubscriptionSource(rr.mSerial, cdmaSubscription); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(SIM_SERVICE, "setCdmaSubscriptionSource", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "setCdmaSubscriptionSource", e); } } } @@ -3151,7 +3305,8 @@ try { networkProxy.getCdmaRoamingPreference(rr.mSerial); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(NETWORK_SERVICE, "queryCdmaRoamingPreference", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, + "queryCdmaRoamingPreference", e); } } } @@ -3171,7 +3326,7 @@ try { networkProxy.setCdmaRoamingPreference(rr.mSerial, cdmaRoamingType); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(NETWORK_SERVICE, "setCdmaRoamingPreference", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "setCdmaRoamingPreference", e); } } } @@ -3190,7 +3345,7 @@ try { voiceProxy.getTtyMode(rr.mSerial); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(VOICE_SERVICE, "getTtyMode", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "getTtyMode", e); } } } @@ -3209,7 +3364,7 @@ try { voiceProxy.setTtyMode(rr.mSerial, ttyMode); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(VOICE_SERVICE, "setTtyMode", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "setTtyMode", e); } } } @@ -3229,7 +3384,7 @@ try { voiceProxy.setPreferredVoicePrivacy(rr.mSerial, enable); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(VOICE_SERVICE, "setPreferredVoicePrivacy", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "setPreferredVoicePrivacy", e); } } } @@ -3248,7 +3403,7 @@ try { voiceProxy.getPreferredVoicePrivacy(rr.mSerial); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(VOICE_SERVICE, "getPreferredVoicePrivacy", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "getPreferredVoicePrivacy", e); } } } @@ -3268,7 +3423,7 @@ voiceProxy.sendCdmaFeatureCode(rr.mSerial, RILUtils.convertNullToEmptyString(featureCode)); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(VOICE_SERVICE, "sendCdmaFeatureCode", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "sendCdmaFeatureCode", e); } } } @@ -3289,7 +3444,7 @@ voiceProxy.sendBurstDtmf(rr.mSerial, RILUtils.convertNullToEmptyString(dtmfString), on, off); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(VOICE_SERVICE, "sendBurstDtmf", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "sendBurstDtmf", e); } } } @@ -3309,13 +3464,13 @@ try { messagingProxy.sendCdmaSmsExpectMore(rr.mSerial, pdu); - if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_5)) { + if (mHalVersion.get(HAL_SERVICE_MESSAGING).greaterOrEqual(RADIO_HAL_VERSION_1_5)) { mMetrics.writeRilSendSms(mPhoneId, rr.mSerial, SmsSession.Event.Tech.SMS_CDMA, SmsSession.Event.Format.SMS_FORMAT_3GPP2, getOutgoingSmsMessageId(result)); } } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(MESSAGING_SERVICE, "sendCdmaSMSExpectMore", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_MESSAGING, "sendCdmaSMSExpectMore", e); } } } @@ -3337,7 +3492,7 @@ mMetrics.writeRilSendSms(mPhoneId, rr.mSerial, SmsSession.Event.Tech.SMS_CDMA, SmsSession.Event.Format.SMS_FORMAT_3GPP2, getOutgoingSmsMessageId(result)); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(MESSAGING_SERVICE, "sendCdmaSms", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_MESSAGING, "sendCdmaSms", e); } } } @@ -3358,7 +3513,7 @@ try { messagingProxy.acknowledgeLastIncomingCdmaSms(rr.mSerial, success, cause); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(MESSAGING_SERVICE, + handleRadioProxyExceptionForRR(HAL_SERVICE_MESSAGING, "acknowledgeLastIncomingCdmaSms", e); } } @@ -3379,7 +3534,7 @@ try { messagingProxy.getGsmBroadcastConfig(rr.mSerial); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(MESSAGING_SERVICE, "getGsmBroadcastConfig", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_MESSAGING, "getGsmBroadcastConfig", e); } } } @@ -3403,7 +3558,7 @@ try { messagingProxy.setGsmBroadcastConfig(rr.mSerial, config); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(MESSAGING_SERVICE, "setGsmBroadcastConfig", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_MESSAGING, "setGsmBroadcastConfig", e); } } } @@ -3424,7 +3579,8 @@ try { messagingProxy.setGsmBroadcastActivation(rr.mSerial, activate); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(MESSAGING_SERVICE, "setGsmBroadcastActivation", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_MESSAGING, + "setGsmBroadcastActivation", e); } } } @@ -3444,7 +3600,7 @@ try { messagingProxy.getCdmaBroadcastConfig(rr.mSerial); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(MESSAGING_SERVICE, "getCdmaBroadcastConfig", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_MESSAGING, "getCdmaBroadcastConfig", e); } } } @@ -3468,7 +3624,7 @@ try { messagingProxy.setCdmaBroadcastConfig(rr.mSerial, configs); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(MESSAGING_SERVICE, "setCdmaBroadcastConfig", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_MESSAGING, "setCdmaBroadcastConfig", e); } } } @@ -3489,7 +3645,8 @@ try { messagingProxy.setCdmaBroadcastActivation(rr.mSerial, activate); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(MESSAGING_SERVICE, "setCdmaBroadcastActivation", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_MESSAGING, + "setCdmaBroadcastActivation", e); } } } @@ -3508,7 +3665,7 @@ try { simProxy.getCdmaSubscription(rr.mSerial); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(SIM_SERVICE, "getCdmaSubscription", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "getCdmaSubscription", e); } } } @@ -3529,7 +3686,7 @@ try { messagingProxy.writeSmsToRuim(rr.mSerial, status, pdu); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(MESSAGING_SERVICE, "writeSmsToRuim", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_MESSAGING, "writeSmsToRuim", e); } } } @@ -3550,7 +3707,7 @@ try { messagingProxy.deleteSmsOnRuim(rr.mSerial, index); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(MESSAGING_SERVICE, "deleteSmsOnRuim", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_MESSAGING, "deleteSmsOnRuim", e); } } } @@ -3569,7 +3726,41 @@ try { modemProxy.getDeviceIdentity(rr.mSerial); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(MODEM_SERVICE, "getDeviceIdentity", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_MODEM, "getDeviceIdentity", e); + } + } + } + + @Override + public void getImei(Message result) { + RadioModemProxy modemProxy = getRadioServiceProxy(RadioModemProxy.class, result); + if (modemProxy.isEmpty()) { + if (RILJ_LOGD) { + Rlog.e(RILJ_LOG_TAG, "getImei: modemProxy is Empty"); + } + return; + } + if (mHalVersion.get(HAL_SERVICE_MODEM).greaterOrEqual(RADIO_HAL_VERSION_2_1)) { + RILRequest rr = obtainRequest(RIL_REQUEST_DEVICE_IMEI, result, + mRILDefaultWorkSource); + + if (RILJ_LOGD) { + riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)); + } + + try { + modemProxy.getImei(rr.mSerial); + } catch (RemoteException | RuntimeException e) { + handleRadioProxyExceptionForRR(HAL_SERVICE_MODEM, "getImei", e); + } + } else { + if (RILJ_LOGD) { + Rlog.e(RILJ_LOG_TAG, "getImei: REQUEST_NOT_SUPPORTED"); + } + if (result != null) { + AsyncResult.forMessage(result, null, + CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED)); + result.sendToTarget(); } } } @@ -3588,7 +3779,7 @@ try { voiceProxy.exitEmergencyCallbackMode(rr.mSerial); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(VOICE_SERVICE, "exitEmergencyCallbackMode", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "exitEmergencyCallbackMode", e); } } } @@ -3608,7 +3799,7 @@ try { messagingProxy.getSmscAddress(rr.mSerial); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(MESSAGING_SERVICE, "getSmscAddress", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_MESSAGING, "getSmscAddress", e); } } } @@ -3630,7 +3821,7 @@ messagingProxy.setSmscAddress(rr.mSerial, RILUtils.convertNullToEmptyString(address)); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(MESSAGING_SERVICE, "setSmscAddress", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_MESSAGING, "setSmscAddress", e); } } } @@ -3651,7 +3842,7 @@ try { messagingProxy.reportSmsMemoryStatus(rr.mSerial, available); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(MESSAGING_SERVICE, "reportSmsMemoryStatus", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_MESSAGING, "reportSmsMemoryStatus", e); } } } @@ -3670,7 +3861,7 @@ try { simProxy.reportStkServiceIsRunning(rr.mSerial); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(SIM_SERVICE, "reportStkServiceIsRunning", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "reportStkServiceIsRunning", e); } } } @@ -3689,7 +3880,7 @@ try { simProxy.getCdmaSubscriptionSource(rr.mSerial); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(SIM_SERVICE, "getCdmaSubscriptionSource", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "getCdmaSubscriptionSource", e); } } } @@ -3711,7 +3902,7 @@ messagingProxy.acknowledgeIncomingGsmSmsWithPdu(rr.mSerial, success, RILUtils.convertNullToEmptyString(ackPdu)); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(MESSAGING_SERVICE, + handleRadioProxyExceptionForRR(HAL_SERVICE_MESSAGING, "acknowledgeIncomingGsmSmsWithPdu", e); } } @@ -3731,7 +3922,7 @@ try { networkProxy.getVoiceRadioTechnology(rr.mSerial); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(NETWORK_SERVICE, "getVoiceRadioTechnology", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "getVoiceRadioTechnology", e); } } } @@ -3750,7 +3941,7 @@ try { networkProxy.getCellInfoList(rr.mSerial); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(NETWORK_SERVICE, "getCellInfoList", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "getCellInfoList", e); } } } @@ -3770,7 +3961,7 @@ try { networkProxy.setCellInfoListRate(rr.mSerial, rateInMillis); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(NETWORK_SERVICE, "setCellInfoListRate", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "setCellInfoListRate", e); } } } @@ -3790,7 +3981,7 @@ try { dataProxy.setInitialAttachApn(rr.mSerial, dataProfile, isRoaming); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(DATA_SERVICE, "setInitialAttachApn", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_DATA, "setInitialAttachApn", e); } } } @@ -3809,7 +4000,7 @@ try { networkProxy.getImsRegistrationState(rr.mSerial); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(NETWORK_SERVICE, "getImsRegistrationState", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "getImsRegistrationState", e); } } } @@ -3832,7 +4023,7 @@ mMetrics.writeRilSendSms(mPhoneId, rr.mSerial, SmsSession.Event.Tech.SMS_IMS, SmsSession.Event.Format.SMS_FORMAT_3GPP, getOutgoingSmsMessageId(result)); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(MESSAGING_SERVICE, "sendImsGsmSms", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_MESSAGING, "sendImsGsmSms", e); } } } @@ -3854,7 +4045,7 @@ mMetrics.writeRilSendSms(mPhoneId, rr.mSerial, SmsSession.Event.Tech.SMS_IMS, SmsSession.Event.Format.SMS_FORMAT_3GPP2, getOutgoingSmsMessageId(result)); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(MESSAGING_SERVICE, "sendImsCdmaSms", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_MESSAGING, "sendImsCdmaSms", e); } } } @@ -3882,7 +4073,7 @@ simProxy.iccTransmitApduBasicChannel( rr.mSerial, cla, instruction, p1, p2, p3, data); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(SIM_SERVICE, "iccTransmitApduBasicChannel", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "iccTransmitApduBasicChannel", e); } } } @@ -3907,13 +4098,13 @@ simProxy.iccOpenLogicalChannel(rr.mSerial, RILUtils.convertNullToEmptyString(aid), p2); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(SIM_SERVICE, "iccOpenLogicalChannel", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "iccOpenLogicalChannel", e); } } } @Override - public void iccCloseLogicalChannel(int channel, Message result) { + public void iccCloseLogicalChannel(int channel, boolean isEs10, Message result) { RadioSimProxy simProxy = getRadioServiceProxy(RadioSimProxy.class, result); if (!simProxy.isEmpty()) { RILRequest rr = obtainRequest(RIL_REQUEST_SIM_CLOSE_CHANNEL, result, @@ -3921,20 +4112,19 @@ if (RILJ_LOGD) { riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest) - + " channel = " + channel); + + " channel = " + channel + " isEs10 = " + isEs10); } - try { - simProxy.iccCloseLogicalChannel(rr.mSerial, channel); + simProxy.iccCloseLogicalChannel(rr.mSerial, channel, isEs10); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(SIM_SERVICE, "iccCloseLogicalChannel", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "iccCloseLogicalChannel", e); } } } @Override public void iccTransmitApduLogicalChannel(int channel, int cla, int instruction, int p1, int p2, - int p3, String data, Message result) { + int p3, String data, boolean isEs10Command, Message result) { if (channel <= 0) { throw new RuntimeException( "Invalid channel in iccTransmitApduLogicalChannel: " + channel); @@ -3951,6 +4141,7 @@ + String.format(" channel = %d", channel) + String.format(" cla = 0x%02X ins = 0x%02X", cla, instruction) + String.format(" p1 = 0x%02X p2 = 0x%02X p3 = 0x%02X", p1, p2, p3) + + " isEs10Command = " + isEs10Command + " data = " + data); } else { riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)); @@ -3959,9 +4150,9 @@ try { simProxy.iccTransmitApduLogicalChannel( - rr.mSerial, channel, cla, instruction, p1, p2, p3, data); + rr.mSerial, channel, cla, instruction, p1, p2, p3, data, isEs10Command); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(SIM_SERVICE, "iccTransmitApduLogicalChannel", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "iccTransmitApduLogicalChannel", e); } } } @@ -3981,7 +4172,7 @@ try { modemProxy.nvReadItem(rr.mSerial, itemID); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(MODEM_SERVICE, "nvReadItem", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_MODEM, "nvReadItem", e); } } } @@ -4002,7 +4193,7 @@ modemProxy.nvWriteItem(rr.mSerial, itemId, RILUtils.convertNullToEmptyString(itemValue)); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(MODEM_SERVICE, "nvWriteItem", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_MODEM, "nvWriteItem", e); } } } @@ -4023,7 +4214,7 @@ try { modemProxy.nvWriteCdmaPrl(rr.mSerial, preferredRoamingList); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(MODEM_SERVICE, "nvWriteCdmaPrl", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_MODEM, "nvWriteCdmaPrl", e); } } } @@ -4043,7 +4234,7 @@ try { modemProxy.nvResetConfig(rr.mSerial, resetType); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(MODEM_SERVICE, "nvResetConfig", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_MODEM, "nvResetConfig", e); } } } @@ -4065,7 +4256,7 @@ try { simProxy.setUiccSubscription(rr.mSerial, slotId, appIndex, subId, subStatus); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(SIM_SERVICE, "setUiccSubscription", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "setUiccSubscription", e); } } } @@ -4080,7 +4271,7 @@ // EID should be supported as long as HAL >= 1.2. // - in HAL 1.2 we have EID through ATR // - in later HAL versions we also have EID through slot / card status. - return mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_2); + return mHalVersion.get(HAL_SERVICE_RADIO).greaterOrEqual(RADIO_HAL_VERSION_1_2); } @Override @@ -4097,7 +4288,7 @@ try { dataProxy.setDataAllowed(rr.mSerial, allowed); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(DATA_SERVICE, "setDataAllowed", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_DATA, "setDataAllowed", e); } } } @@ -4117,7 +4308,7 @@ try { modemProxy.getHardwareConfig(rr.mSerial); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(MODEM_SERVICE, "getHardwareConfig", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_MODEM, "getHardwareConfig", e); } } } @@ -4140,7 +4331,7 @@ RILUtils.convertNullToEmptyString(data), RILUtils.convertNullToEmptyString(aid)); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(SIM_SERVICE, "requestIccSimAuthentication", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "requestIccSimAuthentication", e); } } } @@ -4163,7 +4354,7 @@ try { dataProxy.setDataProfile(rr.mSerial, dps, isRoaming); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(DATA_SERVICE, "setDataProfile", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_DATA, "setDataProfile", e); } } } @@ -4181,7 +4372,7 @@ try { modemProxy.requestShutdown(rr.mSerial); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(MODEM_SERVICE, "requestShutdown", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_MODEM, "requestShutdown", e); } } } @@ -4200,7 +4391,7 @@ try { modemProxy.getRadioCapability(rr.mSerial); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(MODEM_SERVICE, "getRadioCapability", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_MODEM, "getRadioCapability", e); } } } @@ -4220,14 +4411,14 @@ try { modemProxy.setRadioCapability(rr.mSerial, rc); } catch (Exception e) { - handleRadioProxyExceptionForRR(MODEM_SERVICE, "setRadioCapability", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_MODEM, "setRadioCapability", e); } } } @Override public void startLceService(int reportIntervalMs, boolean pullMode, Message result) { - if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_2)) { + if (mHalVersion.get(HAL_SERVICE_RADIO).greaterOrEqual(RADIO_HAL_VERSION_1_2)) { // We have a 1.2 or later radio, so the LCE 1.0 LCE service control path is unused. // Instead the LCE functionality is always-on and provides unsolicited indications. if (RILJ_LOGD) Rlog.d(RILJ_LOG_TAG, "startLceService: REQUEST_NOT_SUPPORTED"); @@ -4251,14 +4442,14 @@ try { radioProxy.startLceService(rr.mSerial, reportIntervalMs, pullMode); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(RADIO_SERVICE, "startLceService", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_RADIO, "startLceService", e); } } } @Override public void stopLceService(Message result) { - if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_2)) { + if (mHalVersion.get(HAL_SERVICE_RADIO).greaterOrEqual(RADIO_HAL_VERSION_1_2)) { // We have a 1.2 or later radio, so the LCE 1.0 LCE service control is unused. // Instead the LCE functionality is always-on and provides unsolicited indications. if (RILJ_LOGD) Rlog.d(RILJ_LOG_TAG, "stopLceService: REQUEST_NOT_SUPPORTED"); @@ -4281,7 +4472,7 @@ try { radioProxy.stopLceService(rr.mSerial); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(RADIO_SERVICE, "stopLceService", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_RADIO, "stopLceService", e); } } } @@ -4300,7 +4491,7 @@ long completionWindowMillis) { RadioDataProxy dataProxy = getRadioServiceProxy(RadioDataProxy.class, result); if (dataProxy.isEmpty()) return; - if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_6)) { + if (mHalVersion.get(HAL_SERVICE_DATA).greaterOrEqual(RADIO_HAL_VERSION_1_6)) { RILRequest rr = obtainRequest(RIL_REQUEST_SET_DATA_THROTTLING, result, getDefaultWorkSourceIfInvalid(workSource)); @@ -4315,7 +4506,7 @@ dataProxy.setDataThrottling(rr.mSerial, (byte) dataThrottlingAction, completionWindowMillis); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(DATA_SERVICE, "setDataThrottling", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_DATA, "setDataThrottling", e); } } else { if (RILJ_LOGD) Rlog.d(RILJ_LOG_TAG, "setDataThrottling: REQUEST_NOT_SUPPORTED"); @@ -4340,7 +4531,7 @@ @Deprecated @Override public void pullLceData(Message result) { - if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_2)) { + if (mHalVersion.get(HAL_SERVICE_RADIO).greaterOrEqual(RADIO_HAL_VERSION_1_2)) { // We have a 1.2 or later radio, so the LCE 1.0 LCE service control path is unused. // Instead the LCE functionality is always-on and provides unsolicited indications. if (RILJ_LOGD) Rlog.d(RILJ_LOG_TAG, "pullLceData: REQUEST_NOT_SUPPORTED"); @@ -4363,7 +4554,7 @@ try { radioProxy.pullLceData(rr.mSerial); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(RADIO_SERVICE, "pullLceData", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_RADIO, "pullLceData", e); } } } @@ -4385,7 +4576,7 @@ mRilHandler.obtainMessage(EVENT_BLOCKING_RESPONSE_TIMEOUT, rr.mSerial); mRilHandler.sendMessageDelayed(msg, DEFAULT_BLOCKING_MESSAGE_RESPONSE_TIMEOUT_MS); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(MODEM_SERVICE, "getModemActivityInfo", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_MODEM, "getModemActivityInfo", e); } } } @@ -4408,7 +4599,7 @@ try { simProxy.setAllowedCarriers(rr.mSerial, carrierRestrictionRules, result); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(SIM_SERVICE, "setAllowedCarriers", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "setAllowedCarriers", e); } } } @@ -4427,7 +4618,7 @@ try { simProxy.getAllowedCarriers(rr.mSerial); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(SIM_SERVICE, "getAllowedCarriers", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "getAllowedCarriers", e); } } } @@ -4447,7 +4638,7 @@ try { modemProxy.sendDeviceState(rr.mSerial, stateType, state); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(MODEM_SERVICE, "sendDeviceState", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_MODEM, "sendDeviceState", e); } } } @@ -4467,7 +4658,7 @@ try { networkProxy.setIndicationFilter(rr.mSerial, filter); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(NETWORK_SERVICE, "setIndicationFilter", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "setIndicationFilter", e); } } } @@ -4477,7 +4668,7 @@ @NonNull List<SignalThresholdInfo> signalThresholdInfos, @Nullable Message result) { RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result); if (networkProxy.isEmpty()) return; - if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_2)) { + if (mHalVersion.get(HAL_SERVICE_NETWORK).greaterOrEqual(RADIO_HAL_VERSION_1_2)) { RILRequest rr = obtainRequest(RIL_REQUEST_SET_SIGNAL_STRENGTH_REPORTING_CRITERIA, result, mRILDefaultWorkSource); @@ -4488,7 +4679,7 @@ try { networkProxy.setSignalStrengthReportingCriteria(rr.mSerial, signalThresholdInfos); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(NETWORK_SERVICE, + handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "setSignalStrengthReportingCriteria", e); } } else { @@ -4502,7 +4693,7 @@ Message result) { RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result); if (networkProxy.isEmpty()) return; - if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_2)) { + if (mHalVersion.get(HAL_SERVICE_NETWORK).greaterOrEqual(RADIO_HAL_VERSION_1_2)) { RILRequest rr = obtainRequest(RIL_REQUEST_SET_LINK_CAPACITY_REPORTING_CRITERIA, result, mRILDefaultWorkSource); @@ -4516,7 +4707,7 @@ ran); } catch (RemoteException | RuntimeException e) { handleRadioProxyExceptionForRR( - NETWORK_SERVICE, "setLinkCapacityReportingCriteria", e); + HAL_SERVICE_NETWORK, "setLinkCapacityReportingCriteria", e); } } else { riljLoge("setLinkCapacityReportingCriteria ignored on IRadio version less than 1.2"); @@ -4538,7 +4729,7 @@ try { simProxy.setSimCardPower(rr.mSerial, state, result); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(SIM_SERVICE, "setSimCardPower", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "setSimCardPower", e); } } } @@ -4549,7 +4740,7 @@ Objects.requireNonNull(imsiEncryptionInfo, "ImsiEncryptionInfo cannot be null."); RadioSimProxy simProxy = getRadioServiceProxy(RadioSimProxy.class, result); if (simProxy.isEmpty()) return; - if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_1)) { + if (mHalVersion.get(HAL_SERVICE_SIM).greaterOrEqual(RADIO_HAL_VERSION_1_1)) { RILRequest rr = obtainRequest(RIL_REQUEST_SET_CARRIER_INFO_IMSI_ENCRYPTION, result, mRILDefaultWorkSource); if (RILJ_LOGD) { @@ -4559,7 +4750,8 @@ try { simProxy.setCarrierInfoForImsiEncryption(rr.mSerial, imsiEncryptionInfo); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(SIM_SERVICE, "setCarrierInfoForImsiEncryption", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, + "setCarrierInfoForImsiEncryption", e); } } else { if (RILJ_LOGD) { @@ -4579,7 +4771,7 @@ Objects.requireNonNull(packetData, "KeepaliveRequest cannot be null."); RadioDataProxy dataProxy = getRadioServiceProxy(RadioDataProxy.class, result); if (dataProxy.isEmpty()) return; - if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_1)) { + if (mHalVersion.get(HAL_SERVICE_DATA).greaterOrEqual(RADIO_HAL_VERSION_1_1)) { RILRequest rr = obtainRequest(RIL_REQUEST_START_KEEPALIVE, result, mRILDefaultWorkSource); @@ -4590,7 +4782,7 @@ try { dataProxy.startKeepalive(rr.mSerial, contextId, packetData, intervalMillis, result); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(DATA_SERVICE, "startNattKeepalive", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_DATA, "startNattKeepalive", e); } } else { if (RILJ_LOGD) Rlog.d(RILJ_LOG_TAG, "startNattKeepalive: REQUEST_NOT_SUPPORTED"); @@ -4606,7 +4798,7 @@ public void stopNattKeepalive(int sessionHandle, Message result) { RadioDataProxy dataProxy = getRadioServiceProxy(RadioDataProxy.class, result); if (dataProxy.isEmpty()) return; - if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_1)) { + if (mHalVersion.get(HAL_SERVICE_DATA).greaterOrEqual(RADIO_HAL_VERSION_1_1)) { RILRequest rr = obtainRequest(RIL_REQUEST_STOP_KEEPALIVE, result, mRILDefaultWorkSource); @@ -4617,7 +4809,7 @@ try { dataProxy.stopKeepalive(rr.mSerial, sessionHandle); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(DATA_SERVICE, "stopNattKeepalive", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_DATA, "stopNattKeepalive", e); } } else { if (RILJ_LOGD) Rlog.d(RILJ_LOG_TAG, "stopNattKeepalive: REQUEST_NOT_SUPPORTED"); @@ -4666,18 +4858,19 @@ public void enableUiccApplications(boolean enable, Message result) { RadioSimProxy simProxy = getRadioServiceProxy(RadioSimProxy.class, result); if (simProxy.isEmpty()) return; - if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_5)) { + if (mHalVersion.get(HAL_SERVICE_SIM).greaterOrEqual(RADIO_HAL_VERSION_1_5)) { RILRequest rr = obtainRequest(RIL_REQUEST_ENABLE_UICC_APPLICATIONS, result, mRILDefaultWorkSource); if (RILJ_LOGD) { - riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)); + riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest) + + " " + enable); } try { simProxy.enableUiccApplications(rr.mSerial, enable); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(SIM_SERVICE, "enableUiccApplications", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "enableUiccApplications", e); } } else { if (RILJ_LOGD) Rlog.d(RILJ_LOG_TAG, "enableUiccApplications: REQUEST_NOT_SUPPORTED"); @@ -4698,7 +4891,7 @@ public void areUiccApplicationsEnabled(Message result) { RadioSimProxy simProxy = getRadioServiceProxy(RadioSimProxy.class, result); if (simProxy.isEmpty()) return; - if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_5)) { + if (mHalVersion.get(HAL_SERVICE_SIM).greaterOrEqual(RADIO_HAL_VERSION_1_5)) { RILRequest rr = obtainRequest(RIL_REQUEST_GET_UICC_APPLICATIONS_ENABLEMENT, result, mRILDefaultWorkSource); @@ -4709,7 +4902,7 @@ try { simProxy.areUiccApplicationsEnabled(rr.mSerial); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(SIM_SERVICE, "areUiccApplicationsEnabled", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "areUiccApplicationsEnabled", e); } } else { if (RILJ_LOGD) { @@ -4729,7 +4922,7 @@ @Override public boolean canToggleUiccApplicationsEnablement() { return !getRadioServiceProxy(RadioSimProxy.class, null).isEmpty() - && mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_5); + && mHalVersion.get(HAL_SERVICE_SIM).greaterOrEqual(RADIO_HAL_VERSION_1_5); } @Override @@ -4755,7 +4948,7 @@ voiceProxy.handleStkCallSetupRequestFromSim(rr.mSerial, accept); } catch (RemoteException | RuntimeException e) { handleRadioProxyExceptionForRR( - VOICE_SERVICE, "handleStkCallSetupRequestFromSim", e); + HAL_SERVICE_VOICE, "handleStkCallSetupRequestFromSim", e); } } } @@ -4767,7 +4960,7 @@ public void getBarringInfo(Message result) { RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result); if (networkProxy.isEmpty()) return; - if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_5)) { + if (mHalVersion.get(HAL_SERVICE_NETWORK).greaterOrEqual(RADIO_HAL_VERSION_1_5)) { RILRequest rr = obtainRequest(RIL_REQUEST_GET_BARRING_INFO, result, mRILDefaultWorkSource); @@ -4778,7 +4971,7 @@ try { networkProxy.getBarringInfo(rr.mSerial); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(NETWORK_SERVICE, "getBarringInfo", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "getBarringInfo", e); } } else { if (RILJ_LOGD) Rlog.d(RILJ_LOG_TAG, "getBarringInfo: REQUEST_NOT_SUPPORTED"); @@ -4797,7 +4990,7 @@ public void allocatePduSessionId(Message result) { RadioDataProxy dataProxy = getRadioServiceProxy(RadioDataProxy.class, result); if (dataProxy.isEmpty()) return; - if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_6)) { + if (mHalVersion.get(HAL_SERVICE_DATA).greaterOrEqual(RADIO_HAL_VERSION_1_6)) { RILRequest rr = obtainRequest(RIL_REQUEST_ALLOCATE_PDU_SESSION_ID, result, mRILDefaultWorkSource); if (RILJ_LOGD) { @@ -4807,7 +5000,7 @@ try { dataProxy.allocatePduSessionId(rr.mSerial); } catch (RemoteException e) { - handleRadioProxyExceptionForRR(DATA_SERVICE, "allocatePduSessionId", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_DATA, "allocatePduSessionId", e); } } else { AsyncResult.forMessage(result, null, @@ -4823,7 +5016,7 @@ public void releasePduSessionId(Message result, int pduSessionId) { RadioDataProxy dataProxy = getRadioServiceProxy(RadioDataProxy.class, result); if (dataProxy.isEmpty()) return; - if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_6)) { + if (mHalVersion.get(HAL_SERVICE_DATA).greaterOrEqual(RADIO_HAL_VERSION_1_6)) { RILRequest rr = obtainRequest(RIL_REQUEST_RELEASE_PDU_SESSION_ID, result, mRILDefaultWorkSource); if (RILJ_LOGD) { @@ -4833,7 +5026,7 @@ try { dataProxy.releasePduSessionId(rr.mSerial, pduSessionId); } catch (RemoteException e) { - handleRadioProxyExceptionForRR(DATA_SERVICE, "releasePduSessionId", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_DATA, "releasePduSessionId", e); } } else { AsyncResult.forMessage(result, null, @@ -4849,7 +5042,7 @@ public void startHandover(Message result, int callId) { RadioDataProxy dataProxy = getRadioServiceProxy(RadioDataProxy.class, result); if (dataProxy.isEmpty()) return; - if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_6)) { + if (mHalVersion.get(HAL_SERVICE_DATA).greaterOrEqual(RADIO_HAL_VERSION_1_6)) { RILRequest rr = obtainRequest(RIL_REQUEST_START_HANDOVER, result, mRILDefaultWorkSource); if (RILJ_LOGD) { @@ -4859,7 +5052,7 @@ try { dataProxy.startHandover(rr.mSerial, callId); } catch (RemoteException e) { - handleRadioProxyExceptionForRR(DATA_SERVICE, "startHandover", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_DATA, "startHandover", e); } } else { if (RILJ_LOGD) Rlog.d(RILJ_LOG_TAG, "startHandover: REQUEST_NOT_SUPPORTED"); @@ -4878,7 +5071,7 @@ public void cancelHandover(Message result, int callId) { RadioDataProxy dataProxy = getRadioServiceProxy(RadioDataProxy.class, result); if (dataProxy.isEmpty()) return; - if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_6)) { + if (mHalVersion.get(HAL_SERVICE_DATA).greaterOrEqual(RADIO_HAL_VERSION_1_6)) { RILRequest rr = obtainRequest(RIL_REQUEST_CANCEL_HANDOVER, result, mRILDefaultWorkSource); if (RILJ_LOGD) { @@ -4888,7 +5081,7 @@ try { dataProxy.cancelHandover(rr.mSerial, callId); } catch (RemoteException e) { - handleRadioProxyExceptionForRR(DATA_SERVICE, "cancelHandover", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_DATA, "cancelHandover", e); } } else { if (RILJ_LOGD) Rlog.d(RILJ_LOG_TAG, "cancelHandover: REQUEST_NOT_SUPPORTED"); @@ -4905,7 +5098,7 @@ public void getSlicingConfig(Message result) { RadioDataProxy dataProxy = getRadioServiceProxy(RadioDataProxy.class, result); if (dataProxy.isEmpty()) return; - if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_6)) { + if (mHalVersion.get(HAL_SERVICE_DATA).greaterOrEqual(RADIO_HAL_VERSION_1_6)) { RILRequest rr = obtainRequest(RIL_REQUEST_GET_SLICING_CONFIG, result, mRILDefaultWorkSource); @@ -4916,7 +5109,7 @@ try { dataProxy.getSlicingConfig(rr.mSerial); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(DATA_SERVICE, "getSlicingConfig", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_DATA, "getSlicingConfig", e); } } else { if (RILJ_LOGD) Rlog.d(RILJ_LOG_TAG, "getSlicingConfig: REQUEST_NOT_SUPPORTED"); @@ -4930,7 +5123,7 @@ public void getSimPhonebookRecords(Message result) { RadioSimProxy simProxy = getRadioServiceProxy(RadioSimProxy.class, result); if (simProxy.isEmpty()) return; - if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_6)) { + if (mHalVersion.get(HAL_SERVICE_SIM).greaterOrEqual(RADIO_HAL_VERSION_1_6)) { RILRequest rr = obtainRequest(RIL_REQUEST_GET_SIM_PHONEBOOK_RECORDS, result, mRILDefaultWorkSource); @@ -4941,7 +5134,7 @@ try { simProxy.getSimPhonebookRecords(rr.mSerial); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(SIM_SERVICE, "getSimPhonebookRecords", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "getSimPhonebookRecords", e); } } else { if (RILJ_LOGD) { @@ -4959,7 +5152,7 @@ public void getSimPhonebookCapacity(Message result) { RadioSimProxy simProxy = getRadioServiceProxy(RadioSimProxy.class, result); if (simProxy.isEmpty()) return; - if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_6)) { + if (mHalVersion.get(HAL_SERVICE_SIM).greaterOrEqual(RADIO_HAL_VERSION_1_6)) { RILRequest rr = obtainRequest(RIL_REQUEST_GET_SIM_PHONEBOOK_CAPACITY, result, mRILDefaultWorkSource); @@ -4970,7 +5163,7 @@ try { simProxy.getSimPhonebookCapacity(rr.mSerial); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(SIM_SERVICE, "getSimPhonebookCapacity", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "getSimPhonebookCapacity", e); } } else { if (RILJ_LOGD) { @@ -4988,7 +5181,7 @@ public void updateSimPhonebookRecord(SimPhonebookRecord phonebookRecord, Message result) { RadioSimProxy simProxy = getRadioServiceProxy(RadioSimProxy.class, result); if (simProxy.isEmpty()) return; - if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_6)) { + if (mHalVersion.get(HAL_SERVICE_SIM).greaterOrEqual(RADIO_HAL_VERSION_1_6)) { RILRequest rr = obtainRequest(RIL_REQUEST_UPDATE_SIM_PHONEBOOK_RECORD, result, mRILDefaultWorkSource); @@ -5000,7 +5193,7 @@ try { simProxy.updateSimPhonebookRecords(rr.mSerial, phonebookRecord); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(SIM_SERVICE, "updateSimPhonebookRecords", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "updateSimPhonebookRecords", e); } } else { if (RILJ_LOGD) { @@ -5025,7 +5218,7 @@ /* @TelephonyManager.UsageSetting */ int usageSetting) { RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result); if (networkProxy.isEmpty()) return; - if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_2_0)) { + if (mHalVersion.get(HAL_SERVICE_NETWORK).greaterOrEqual(RADIO_HAL_VERSION_2_0)) { RILRequest rr = obtainRequest(RIL_REQUEST_SET_USAGE_SETTING, result, mRILDefaultWorkSource); @@ -5036,7 +5229,7 @@ try { networkProxy.setUsageSetting(rr.mSerial, usageSetting); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(NETWORK_SERVICE, "setUsageSetting", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "setUsageSetting", e); } } else { if (RILJ_LOGD) { @@ -5059,7 +5252,7 @@ public void getUsageSetting(Message result) { RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result); if (networkProxy.isEmpty()) return; - if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_2_0)) { + if (mHalVersion.get(HAL_SERVICE_NETWORK).greaterOrEqual(RADIO_HAL_VERSION_2_0)) { RILRequest rr = obtainRequest(RIL_REQUEST_GET_USAGE_SETTING, result, mRILDefaultWorkSource); @@ -5070,7 +5263,7 @@ try { networkProxy.getUsageSetting(rr.mSerial); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(NETWORK_SERVICE, "getUsageSetting", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "getUsageSetting", e); } } else { if (RILJ_LOGD) { @@ -5084,6 +5277,783 @@ } } + @Override + public void setSrvccCallInfo(SrvccConnection[] srvccConnections, Message result) { + RadioImsProxy imsProxy = getRadioServiceProxy(RadioImsProxy.class, result); + if (imsProxy.isEmpty()) return; + if (mHalVersion.get(HAL_SERVICE_IMS).greaterOrEqual(RADIO_HAL_VERSION_2_0)) { + RILRequest rr = obtainRequest(RIL_REQUEST_SET_SRVCC_CALL_INFO, result, + mRILDefaultWorkSource); + + if (RILJ_LOGD) { + // Do not log function arg for privacy + riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)); + } + + try { + imsProxy.setSrvccCallInfo(rr.mSerial, + RILUtils.convertToHalSrvccCall(srvccConnections)); + } catch (RemoteException | RuntimeException e) { + handleRadioProxyExceptionForRR(HAL_SERVICE_IMS, "setSrvccCallInfo", e); + } + } else { + if (RILJ_LOGD) { + Rlog.d(RILJ_LOG_TAG, "setSrvccCallInfo: REQUEST_NOT_SUPPORTED"); + } + if (result != null) { + AsyncResult.forMessage(result, null, + CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED)); + result.sendToTarget(); + } + } + } + + @Override + public void updateImsRegistrationInfo( + @RegistrationManager.ImsRegistrationState int state, + @ImsRegistrationImplBase.ImsRegistrationTech int imsRadioTech, + @RegistrationManager.SuggestedAction int suggestedAction, + int capabilities, Message result) { + RadioImsProxy imsProxy = getRadioServiceProxy(RadioImsProxy.class, result); + if (imsProxy.isEmpty()) return; + if (mHalVersion.get(HAL_SERVICE_IMS).greaterOrEqual(RADIO_HAL_VERSION_2_0)) { + RILRequest rr = obtainRequest(RIL_REQUEST_UPDATE_IMS_REGISTRATION_INFO, result, + mRILDefaultWorkSource); + + if (RILJ_LOGD) { + riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest) + + " state=" + state + ", radioTech=" + imsRadioTech + + ", suggested=" + suggestedAction + ", cap=" + capabilities); + } + + android.hardware.radio.ims.ImsRegistration registrationInfo = + new android.hardware.radio.ims.ImsRegistration(); + registrationInfo.regState = RILUtils.convertImsRegistrationState(state); + registrationInfo.accessNetworkType = RILUtils.convertImsRegistrationTech(imsRadioTech); + registrationInfo.suggestedAction = suggestedAction; + registrationInfo.capabilities = RILUtils.convertImsCapability(capabilities); + + try { + imsProxy.updateImsRegistrationInfo(rr.mSerial, registrationInfo); + } catch (RemoteException | RuntimeException e) { + handleRadioProxyExceptionForRR(HAL_SERVICE_IMS, "updateImsRegistrationInfo", e); + } + } else { + if (RILJ_LOGD) { + Rlog.d(RILJ_LOG_TAG, "updateImsRegistrationInfo: REQUEST_NOT_SUPPORTED"); + } + if (result != null) { + AsyncResult.forMessage(result, null, + CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED)); + result.sendToTarget(); + } + } + } + + @Override + public void startImsTraffic(int token, + int trafficType, int accessNetworkType, int trafficDirection, Message result) { + RadioImsProxy imsProxy = getRadioServiceProxy(RadioImsProxy.class, result); + if (imsProxy.isEmpty()) return; + if (mHalVersion.get(HAL_SERVICE_IMS).greaterOrEqual(RADIO_HAL_VERSION_2_0)) { + RILRequest rr = obtainRequest(RIL_REQUEST_START_IMS_TRAFFIC, result, + mRILDefaultWorkSource); + + if (RILJ_LOGD) { + riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest) + + "{" + token + ", " + trafficType + ", " + + accessNetworkType + ", " + trafficDirection + "}"); + } + + try { + imsProxy.startImsTraffic(rr.mSerial, token, + RILUtils.convertImsTrafficType(trafficType), accessNetworkType, + RILUtils.convertImsTrafficDirection(trafficDirection)); + } catch (RemoteException | RuntimeException e) { + handleRadioProxyExceptionForRR(HAL_SERVICE_IMS, "startImsTraffic", e); + } + } else { + if (RILJ_LOGD) { + Rlog.d(RILJ_LOG_TAG, "startImsTraffic: REQUEST_NOT_SUPPORTED"); + } + if (result != null) { + AsyncResult.forMessage(result, null, + CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED)); + result.sendToTarget(); + } + } + } + + @Override + public void stopImsTraffic(int token, Message result) { + RadioImsProxy imsProxy = getRadioServiceProxy(RadioImsProxy.class, result); + if (imsProxy.isEmpty()) return; + if (mHalVersion.get(HAL_SERVICE_IMS).greaterOrEqual(RADIO_HAL_VERSION_2_0)) { + RILRequest rr = obtainRequest(RIL_REQUEST_STOP_IMS_TRAFFIC, result, + mRILDefaultWorkSource); + + if (RILJ_LOGD) { + riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest) + + "{" + token + "}"); + } + + try { + imsProxy.stopImsTraffic(rr.mSerial, token); + } catch (RemoteException | RuntimeException e) { + handleRadioProxyExceptionForRR(HAL_SERVICE_IMS, "stopImsTraffic", e); + } + } else { + if (RILJ_LOGD) { + Rlog.d(RILJ_LOG_TAG, "stopImsTraffic: REQUEST_NOT_SUPPORTED"); + } + if (result != null) { + AsyncResult.forMessage(result, null, + CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED)); + result.sendToTarget(); + } + } + } + + @Override + public void triggerEpsFallback(int reason, Message result) { + RadioImsProxy imsProxy = getRadioServiceProxy(RadioImsProxy.class, result); + if (imsProxy.isEmpty()) return; + if (mHalVersion.get(HAL_SERVICE_IMS).greaterOrEqual(RADIO_HAL_VERSION_2_0)) { + RILRequest rr = obtainRequest(RIL_REQUEST_TRIGGER_EPS_FALLBACK, result, + mRILDefaultWorkSource); + + if (RILJ_LOGD) { + riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest) + + " reason=" + reason); + } + + try { + imsProxy.triggerEpsFallback(rr.mSerial, reason); + } catch (RemoteException | RuntimeException e) { + handleRadioProxyExceptionForRR(HAL_SERVICE_IMS, "triggerEpsFallback", e); + } + } else { + if (RILJ_LOGD) { + Rlog.d(RILJ_LOG_TAG, "triggerEpsFallback: REQUEST_NOT_SUPPORTED"); + } + if (result != null) { + AsyncResult.forMessage(result, null, + CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED)); + result.sendToTarget(); + } + } + } + + @Override + public void sendAnbrQuery(int mediaType, int direction, int bitsPerSecond, + Message result) { + RadioImsProxy imsProxy = getRadioServiceProxy(RadioImsProxy.class, result); + if (imsProxy.isEmpty()) return; + if (mHalVersion.get(HAL_SERVICE_IMS).greaterOrEqual(RADIO_HAL_VERSION_2_0)) { + RILRequest rr = obtainRequest(RIL_REQUEST_SEND_ANBR_QUERY, result, + mRILDefaultWorkSource); + + if (RILJ_LOGD) { + riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)); + } + + try { + imsProxy.sendAnbrQuery(rr.mSerial, mediaType, direction, bitsPerSecond); + } catch (RemoteException | RuntimeException e) { + handleRadioProxyExceptionForRR(HAL_SERVICE_IMS, "sendAnbrQuery", e); + } + } else { + if (RILJ_LOGD) { + Rlog.d(RILJ_LOG_TAG, "sendAnbrQuery: REQUEST_NOT_SUPPORTED"); + } + if (result != null) { + AsyncResult.forMessage(result, null, + CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED)); + result.sendToTarget(); + } + } + } + + /** + * {@inheritDoc} + */ + @Override + public void setEmergencyMode(int emcMode, Message result) { + RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result); + if (networkProxy.isEmpty()) return; + if (mHalVersion.get(HAL_SERVICE_NETWORK).greaterOrEqual(RADIO_HAL_VERSION_2_1)) { + RILRequest rr = obtainRequest(RIL_REQUEST_SET_EMERGENCY_MODE, result, + mRILDefaultWorkSource); + + if (RILJ_LOGD) { + riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest) + + " mode=" + EmergencyConstants.emergencyModeToString(emcMode)); + } + + try { + networkProxy.setEmergencyMode(rr.mSerial, emcMode); + } catch (RemoteException | RuntimeException e) { + handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "setEmergencyMode", e); + } + } else { + if (RILJ_LOGD) { + Rlog.d(RILJ_LOG_TAG, "setEmergencyMode: REQUEST_NOT_SUPPORTED"); + } + if (result != null) { + AsyncResult.forMessage(result, null, + CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED)); + result.sendToTarget(); + } + } + } + + /** + * {@inheritDoc} + */ + @Override + public void triggerEmergencyNetworkScan( + @NonNull @AccessNetworkConstants.RadioAccessNetworkType int[] accessNetwork, + @DomainSelectionService.EmergencyScanType int scanType, Message result) { + RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result); + if (networkProxy.isEmpty()) return; + if (mHalVersion.get(HAL_SERVICE_NETWORK).greaterOrEqual(RADIO_HAL_VERSION_2_1)) { + RILRequest rr = obtainRequest(RIL_REQUEST_TRIGGER_EMERGENCY_NETWORK_SCAN, result, + mRILDefaultWorkSource); + + if (RILJ_LOGD) { + riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest) + + " networkType=" + RILUtils.accessNetworkTypesToString(accessNetwork) + + ", scanType=" + RILUtils.scanTypeToString(scanType)); + } + + try { + networkProxy.triggerEmergencyNetworkScan(rr.mSerial, + RILUtils.convertEmergencyNetworkScanTrigger(accessNetwork, scanType)); + } catch (RemoteException | RuntimeException e) { + handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, + "triggerEmergencyNetworkScan", e); + } + } else { + if (RILJ_LOGD) { + Rlog.d(RILJ_LOG_TAG, "triggerEmergencyNetworkScan: REQUEST_NOT_SUPPORTED"); + } + if (result != null) { + AsyncResult.forMessage(result, null, + CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED)); + result.sendToTarget(); + } + } + } + + /** + * {@inheritDoc} + */ + @Override + public void cancelEmergencyNetworkScan(boolean resetScan, Message result) { + RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result); + if (networkProxy.isEmpty()) return; + if (mHalVersion.get(HAL_SERVICE_NETWORK).greaterOrEqual(RADIO_HAL_VERSION_2_1)) { + RILRequest rr = obtainRequest(RIL_REQUEST_CANCEL_EMERGENCY_NETWORK_SCAN, result, + mRILDefaultWorkSource); + + if (RILJ_LOGD) { + riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest) + + " resetScan=" + resetScan); + } + + try { + networkProxy.cancelEmergencyNetworkScan(rr.mSerial, resetScan); + } catch (RemoteException | RuntimeException e) { + handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, + "cancelEmergencyNetworkScan", e); + } + } else { + if (RILJ_LOGD) { + Rlog.d(RILJ_LOG_TAG, "cancelEmergencyNetworkScan: REQUEST_NOT_SUPPORTED"); + } + if (result != null) { + AsyncResult.forMessage(result, null, + CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED)); + result.sendToTarget(); + } + } + } + + /** + * {@inheritDoc} + */ + @Override + public void exitEmergencyMode(Message result) { + RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result); + if (networkProxy.isEmpty()) return; + if (mHalVersion.get(HAL_SERVICE_NETWORK).greaterOrEqual(RADIO_HAL_VERSION_2_1)) { + RILRequest rr = obtainRequest(RIL_REQUEST_EXIT_EMERGENCY_MODE, result, + mRILDefaultWorkSource); + + if (RILJ_LOGD) { + riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)); + } + + try { + networkProxy.exitEmergencyMode(rr.mSerial); + } catch (RemoteException | RuntimeException e) { + handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "exitEmergencyMode", e); + } + } else { + if (RILJ_LOGD) { + Rlog.d(RILJ_LOG_TAG, "exitEmergencyMode: REQUEST_NOT_SUPPORTED"); + } + if (result != null) { + AsyncResult.forMessage(result, null, + CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED)); + result.sendToTarget(); + } + } + } + + /** + * Set if null ciphering / null integrity modes are permitted. + * + * @param result Callback message containing the success or failure status. + * @param enabled true if null ciphering / null integrity modes are permitted, false otherwise + */ + @Override + public void setNullCipherAndIntegrityEnabled(boolean enabled, Message result) { + RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result); + if (networkProxy.isEmpty()) return; + if (mHalVersion.get(HAL_SERVICE_NETWORK).greaterOrEqual(RADIO_HAL_VERSION_2_1)) { + RILRequest rr = obtainRequest(RIL_REQUEST_SET_NULL_CIPHER_AND_INTEGRITY_ENABLED, result, + mRILDefaultWorkSource); + + if (RILJ_LOGD) { + riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)); + } + + try { + networkProxy.setNullCipherAndIntegrityEnabled(rr.mSerial, enabled); + } catch (RemoteException | RuntimeException e) { + handleRadioProxyExceptionForRR( + HAL_SERVICE_NETWORK, "setNullCipherAndIntegrityEnabled", e); + } + } else { + if (RILJ_LOGD) { + Rlog.d(RILJ_LOG_TAG, "setNullCipherAndIntegrityEnabled: REQUEST_NOT_SUPPORTED"); + } + if (result != null) { + AsyncResult.forMessage(result, null, + CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED)); + result.sendToTarget(); + } + } + } + + /** + * Get if null ciphering / null integrity are enabled / disabled. + * + * @param result Callback message containing the success or failure status. + */ + @Override + public void isNullCipherAndIntegrityEnabled(Message result) { + RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result); + if (networkProxy.isEmpty()) return; + if (mHalVersion.get(HAL_SERVICE_NETWORK).greaterOrEqual(RADIO_HAL_VERSION_2_1)) { + RILRequest rr = obtainRequest(RIL_REQUEST_IS_NULL_CIPHER_AND_INTEGRITY_ENABLED, result, + mRILDefaultWorkSource); + + if (RILJ_LOGD) { + riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)); + } + + try { + networkProxy.isNullCipherAndIntegrityEnabled(rr.mSerial); + } catch (RemoteException | RuntimeException e) { + handleRadioProxyExceptionForRR( + HAL_SERVICE_NETWORK, "isNullCipherAndIntegrityEnabled", e); + } + } else { + if (RILJ_LOGD) { + Rlog.d(RILJ_LOG_TAG, "isNullCipherAndIntegrityEnabled: REQUEST_NOT_SUPPORTED"); + } + if (result != null) { + AsyncResult.forMessage(result, null, + CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED)); + result.sendToTarget(); + } + } + } + + /** + * {@inheritDoc} + */ + @Override + public void updateImsCallStatus(@NonNull List<ImsCallInfo> imsCallInfo, Message result) { + RadioImsProxy imsProxy = getRadioServiceProxy(RadioImsProxy.class, result); + if (imsProxy.isEmpty()) return; + if (mHalVersion.get(HAL_SERVICE_IMS).greaterOrEqual(RADIO_HAL_VERSION_2_0)) { + RILRequest rr = obtainRequest(RIL_REQUEST_UPDATE_IMS_CALL_STATUS, result, + mRILDefaultWorkSource); + + if (RILJ_LOGD) { + riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest) + + " " + imsCallInfo); + } + try { + imsProxy.updateImsCallStatus(rr.mSerial, RILUtils.convertImsCallInfo(imsCallInfo)); + } catch (RemoteException | RuntimeException e) { + handleRadioProxyExceptionForRR(HAL_SERVICE_IMS, "updateImsCallStatus", e); + } + } else { + if (RILJ_LOGD) { + Rlog.d(RILJ_LOG_TAG, "updateImsCallStatus: REQUEST_NOT_SUPPORTED"); + } + if (result != null) { + AsyncResult.forMessage(result, null, + CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED)); + result.sendToTarget(); + } + } + } + + /** + * {@inheritDoc} + */ + @Override + public void setN1ModeEnabled(boolean enable, Message result) { + RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result); + if (networkProxy.isEmpty()) return; + if (mHalVersion.get(HAL_SERVICE_NETWORK).greaterOrEqual(RADIO_HAL_VERSION_2_1)) { + RILRequest rr = obtainRequest(RIL_REQUEST_SET_N1_MODE_ENABLED, result, + mRILDefaultWorkSource); + + if (RILJ_LOGD) { + riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest) + + " enable=" + enable); + } + + try { + networkProxy.setN1ModeEnabled(rr.mSerial, enable); + } catch (RemoteException | RuntimeException e) { + handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "setN1ModeEnabled", e); + } + } else { + if (RILJ_LOGD) { + Rlog.d(RILJ_LOG_TAG, "setN1ModeEnabled: REQUEST_NOT_SUPPORTED"); + } + if (result != null) { + AsyncResult.forMessage(result, null, + CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED)); + result.sendToTarget(); + } + } + } + + /** + * {@inheritDoc} + */ + @Override + public void isN1ModeEnabled(Message result) { + RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result); + if (networkProxy.isEmpty()) return; + if (mHalVersion.get(HAL_SERVICE_NETWORK).greaterOrEqual(RADIO_HAL_VERSION_2_1)) { + RILRequest rr = obtainRequest(RIL_REQUEST_IS_N1_MODE_ENABLED, result, + mRILDefaultWorkSource); + + if (RILJ_LOGD) { + riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)); + } + + try { + networkProxy.isN1ModeEnabled(rr.mSerial); + } catch (RemoteException | RuntimeException e) { + handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "isN1ModeEnabled", e); + } + } else { + if (RILJ_LOGD) { + Rlog.d(RILJ_LOG_TAG, "isN1ModeEnabled: REQUEST_NOT_SUPPORTED"); + } + if (result != null) { + AsyncResult.forMessage(result, null, + CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED)); + result.sendToTarget(); + } + } + } + + /** + * Get feature capabilities supported by satellite. + * + * @param result Message that will be sent back to the requester + */ + @Override + public void getSatelliteCapabilities(Message result) { + // Satellite HAL APIs are not supported before Android V. + if (result != null) { + AsyncResult.forMessage(result, null, + CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED)); + result.sendToTarget(); + } + } + + /** + * Turn satellite modem on/off. + * + * @param result Message that will be sent back to the requester + * @param on True for turning on. + * False for turning off. + */ + @Override + public void setSatellitePower(Message result, boolean on) { + // Satellite HAL APIs are not supported before Android V. + if (result != null) { + AsyncResult.forMessage(result, null, + CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED)); + result.sendToTarget(); + } + } + + /** + * Get satellite modem state. + * + * @param result Message that will be sent back to the requester + */ + @Override + public void getSatellitePowerState(Message result) { + // Satellite HAL APIs are not supported before Android V. + if (result != null) { + AsyncResult.forMessage(result, null, + CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED)); + result.sendToTarget(); + } + } + + /** + * Get satellite provision state. + * + * @param result Message that will be sent back to the requester + */ + @Override + public void getSatelliteProvisionState(Message result) { + // Satellite HAL APIs are not supported before Android V. + if (result != null) { + AsyncResult.forMessage(result, null, + CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED)); + result.sendToTarget(); + } + } + + /** + * Provision the subscription with a satellite provider. This is needed to register the + * subscription if the provider allows dynamic registration. + * + * @param result Message that will be sent back to the requester. + * @param imei IMEI of the SIM associated with the satellite modem. + * @param msisdn MSISDN of the SIM associated with the satellite modem. + * @param imsi IMSI of the SIM associated with the satellite modem. + * @param features List of features to be provisioned. + */ + @Override + public void provisionSatelliteService( + Message result, String imei, String msisdn, String imsi, int[] features) { + // Satellite HAL APIs are not supported before Android V. + if (result != null) { + AsyncResult.forMessage(result, null, + CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED)); + result.sendToTarget(); + } + } + + /** + * Add contacts that are allowed to be used for satellite communication. This is applicable for + * incoming messages as well. + * + * @param result Message that will be sent back to the requester. + * @param contacts List of allowed contacts to be added. + */ + @Override + public void addAllowedSatelliteContacts(Message result, String[] contacts) { + // Satellite HAL APIs are not supported before Android V. + if (result != null) { + AsyncResult.forMessage(result, null, + CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED)); + result.sendToTarget(); + } + } + + /** + * Remove contacts that are allowed to be used for satellite communication. This is applicable + * for incoming messages as well. + * + * @param result Message that will be sent back to the requester. + * @param contacts List of allowed contacts to be removed. + */ + @Override + public void removeAllowedSatelliteContacts(Message result, String[] contacts) { + // Satellite HAL APIs are not supported before Android V. + if (result != null) { + AsyncResult.forMessage(result, null, + CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED)); + result.sendToTarget(); + } + } + + /** + * Send text messages. + * + * @param result Message that will be sent back to the requester. + * @param messages List of messages in text format to be sent. + * @param destination The recipient of the message. + * @param latitude The current latitude of the device. + * @param longitude The current longitude of the device. + */ + @Override + public void sendSatelliteMessages(Message result, String[] messages, String destination, + double latitude, double longitude) { + // Satellite HAL APIs are not supported before Android V. + if (result != null) { + AsyncResult.forMessage(result, null, + CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED)); + result.sendToTarget(); + } + } + + /** + * Get pending messages. + * + * @param result Message that will be sent back to the requester. + */ + @Override + public void getPendingSatelliteMessages(Message result) { + // Satellite HAL APIs are not supported before Android V. + if (result != null) { + AsyncResult.forMessage(result, null, + CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED)); + result.sendToTarget(); + } + } + + /** + * Get current satellite registration mode. + * + * @param result Message that will be sent back to the requester. + */ + @Override + public void getSatelliteMode(Message result) { + // Satellite HAL APIs are not supported before Android V. + if (result != null) { + AsyncResult.forMessage(result, null, + CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED)); + result.sendToTarget(); + } + } + + /** + * Set the filter for what type of indication framework want to receive from modem. + * + * @param result Message that will be sent back to the requester. + * @param filterBitmask The filter bitmask identifying what type of indication framework want to + * receive from modem. + */ + @Override + public void setSatelliteIndicationFilter(Message result, int filterBitmask) { + // Satellite HAL APIs are not supported before Android V. + if (result != null) { + AsyncResult.forMessage(result, null, + CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED)); + result.sendToTarget(); + } + } + + /** + * Check whether satellite modem is supported by the device. + * + * @param result Message that will be sent back to the requester. + */ + @Override + public void isSatelliteSupported(Message result) { + // Satellite HAL APIs are not supported before Android V. + if (result != null) { + AsyncResult.forMessage(result, null, + CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED)); + result.sendToTarget(); + } + } + + /** + * User started pointing to the satellite. Modem should continue to update the ponting input + * as user moves device. + * + * @param result Message that will be sent back to the requester. + */ + @Override + public void startSendingSatellitePointingInfo(Message result) { + // Satellite HAL APIs are not supported before Android V. + if (result != null) { + AsyncResult.forMessage(result, null, + CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED)); + result.sendToTarget(); + } + } + + /** + * Stop pointing to satellite indications. + * + * @param result Message that will be sent back to the requester. + */ + @Override + public void stopSendingSatellitePointingInfo(Message result) { + // Satellite HAL APIs are not supported before Android V. + if (result != null) { + AsyncResult.forMessage(result, null, + CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED)); + result.sendToTarget(); + } + } + + /** + * Get max text limit for messaging per message. + * + * @param result Message that will be sent back to the requester. + */ + @Override + public void getMaxCharactersPerSatelliteTextMessage(Message result) { + // Satellite HAL APIs are not supported before Android V. + if (result != null) { + AsyncResult.forMessage(result, null, + CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED)); + result.sendToTarget(); + } + } + + /** + * Get whether satellite communication is allowed for the current location + * + * @param result Message that will be sent back to the requester. + */ + @Override + public void isSatelliteCommunicationAllowedForCurrentLocation(Message result) { + // Satellite HAL APIs are not supported before Android V. + if (result != null) { + AsyncResult.forMessage(result, null, + CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED)); + result.sendToTarget(); + } + } + + /** + * Get time for next visibility of satellite. + * + * @param result Message that will be sent back to the requester. + */ + @Override + public void getTimeForNextSatelliteVisibility(Message result) { + // Satellite HAL APIs are not supported before Android V. + if (result != null) { + AsyncResult.forMessage(result, null, + CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED)); + result.sendToTarget(); + } + } + //***** Private Methods /** * This is a helper function to be called when an indication callback is called for any radio @@ -5125,7 +6095,7 @@ */ @VisibleForTesting public RILRequest processResponse(RadioResponseInfo responseInfo) { - return processResponseInternal(RADIO_SERVICE, responseInfo.serial, responseInfo.error, + return processResponseInternal(HAL_SERVICE_RADIO, responseInfo.serial, responseInfo.error, responseInfo.type); } @@ -5139,7 +6109,7 @@ @VisibleForTesting public RILRequest processResponse_1_6( android.hardware.radio.V1_6.RadioResponseInfo responseInfo) { - return processResponseInternal(RADIO_SERVICE, responseInfo.serial, responseInfo.error, + return processResponseInternal(HAL_SERVICE_RADIO, responseInfo.serial, responseInfo.error, responseInfo.type); } @@ -5151,7 +6121,6 @@ * @param responseInfo RadioResponseInfo received in response callback * @return RILRequest corresponding to the response */ - @VisibleForTesting public RILRequest processResponse(int service, android.hardware.radio.RadioResponseInfo responseInfo) { return processResponseInternal(service, responseInfo.serial, responseInfo.error, @@ -5186,6 +6155,7 @@ + " ,error: " + error); return null; } + Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_NETWORK, "RIL", rr.mSerial); // Time logging for RIL command and storing it in TelephonyHistogram. addToRilHistogram(rr); @@ -5350,13 +6320,13 @@ RILRequest rr = RILRequest.obtain(RIL_RESPONSE_ACKNOWLEDGEMENT, null, mRILDefaultWorkSource); acquireWakeLock(rr, FOR_ACK_WAKELOCK); - if (service == RADIO_SERVICE) { + if (service == HAL_SERVICE_RADIO) { IRadio radioProxy = getRadioProxy(null); if (radioProxy != null) { try { radioProxy.responseAcknowledgement(); } catch (RemoteException | RuntimeException e) { - handleRadioProxyExceptionForRR(RADIO_SERVICE, "sendAck", e); + handleRadioProxyExceptionForRR(HAL_SERVICE_RADIO, "sendAck", e); riljLoge("sendAck: " + e); } } else { @@ -5501,7 +6471,7 @@ synchronized (mWakeLock) { if (mWakeLockCount == 0 && !mWakeLock.isHeld()) return false; Rlog.d(RILJ_LOG_TAG, "NOTE: mWakeLockCount is " + mWakeLockCount - + "at time of clearing"); + + " at time of clearing"); mWakeLockCount = 0; mWakeLock.release(); mClientWakelockTracker.stopTrackingAll(); @@ -5674,6 +6644,22 @@ sb.append("[").append(hwcfg).append("] "); } s = sb.toString(); + } else if (req == RIL_REQUEST_START_IMS_TRAFFIC + || req == RIL_UNSOL_CONNECTION_SETUP_FAILURE) { + sb = new StringBuilder("{"); + Object[] info = (Object[]) ret; + int token = (Integer) info[0]; + sb.append(token).append(", "); + if (info[1] != null) { + ConnectionFailureInfo failureInfo = (ConnectionFailureInfo) info[1]; + sb.append(failureInfo.getReason()).append(", "); + sb.append(failureInfo.getCauseCode()).append(", "); + sb.append(failureInfo.getWaitTimeMillis()); + } else { + sb.append("null"); + } + sb.append("}"); + s = sb.toString(); } else { // Check if toString() was overridden. Java classes created from HIDL have a built-in // toString() method, but AIDL classes only have it if the parcelable contains a @@ -5732,43 +6718,43 @@ int response = RIL_UNSOL_CDMA_INFO_REC; if (infoRec.record instanceof CdmaInformationRecords.CdmaDisplayInfoRec) { if (mDisplayInfoRegistrants != null) { - if (RILJ_LOGD) unsljLogRet(response, infoRec.record); + if (isLogOrTrace()) unsljLogRet(response, infoRec.record); mDisplayInfoRegistrants.notifyRegistrants( new AsyncResult(null, infoRec.record, null)); } } else if (infoRec.record instanceof CdmaInformationRecords.CdmaSignalInfoRec) { if (mSignalInfoRegistrants != null) { - if (RILJ_LOGD) unsljLogRet(response, infoRec.record); + if (isLogOrTrace()) unsljLogRet(response, infoRec.record); mSignalInfoRegistrants.notifyRegistrants( new AsyncResult(null, infoRec.record, null)); } } else if (infoRec.record instanceof CdmaInformationRecords.CdmaNumberInfoRec) { if (mNumberInfoRegistrants != null) { - if (RILJ_LOGD) unsljLogRet(response, infoRec.record); + if (isLogOrTrace()) unsljLogRet(response, infoRec.record); mNumberInfoRegistrants.notifyRegistrants( new AsyncResult(null, infoRec.record, null)); } } else if (infoRec.record instanceof CdmaInformationRecords.CdmaRedirectingNumberInfoRec) { if (mRedirNumInfoRegistrants != null) { - if (RILJ_LOGD) unsljLogRet(response, infoRec.record); + if (isLogOrTrace()) unsljLogRet(response, infoRec.record); mRedirNumInfoRegistrants.notifyRegistrants( new AsyncResult(null, infoRec.record, null)); } } else if (infoRec.record instanceof CdmaInformationRecords.CdmaLineControlInfoRec) { if (mLineControlInfoRegistrants != null) { - if (RILJ_LOGD) unsljLogRet(response, infoRec.record); + if (isLogOrTrace()) unsljLogRet(response, infoRec.record); mLineControlInfoRegistrants.notifyRegistrants( new AsyncResult(null, infoRec.record, null)); } } else if (infoRec.record instanceof CdmaInformationRecords.CdmaT53ClirInfoRec) { if (mT53ClirInfoRegistrants != null) { - if (RILJ_LOGD) unsljLogRet(response, infoRec.record); + if (isLogOrTrace()) unsljLogRet(response, infoRec.record); mT53ClirInfoRegistrants.notifyRegistrants( new AsyncResult(null, infoRec.record, null)); } } else if (infoRec.record instanceof CdmaInformationRecords.CdmaT53AudioControlInfoRec) { if (mT53AudCntrlInfoRegistrants != null) { - if (RILJ_LOGD) { + if (isLogOrTrace()) { unsljLogRet(response, infoRec.record); } mT53AudCntrlInfoRegistrants.notifyRegistrants( @@ -5790,26 +6776,48 @@ Rlog.v(RILJ_LOG_TAG, msg + (" [PHONE" + mPhoneId + "]")); } + boolean isLogOrTrace() { + return RIL.RILJ_LOGD || Trace.isTagEnabled(Trace.TRACE_TAG_NETWORK); + } + + boolean isLogvOrTrace() { + return RIL.RILJ_LOGV || Trace.isTagEnabled(Trace.TRACE_TAG_NETWORK); + } + @UnsupportedAppUsage void unsljLog(int response) { - riljLog("[UNSL]< " + RILUtils.responseToString(response)); + String logStr = RILUtils.responseToString(response); + if (RIL.RILJ_LOGD) { + riljLog("[UNSL]< " + logStr); + } + Trace.instantForTrack(Trace.TRACE_TAG_NETWORK, "RIL", logStr); } @UnsupportedAppUsage void unsljLogMore(int response, String more) { - riljLog("[UNSL]< " + RILUtils.responseToString(response) + " " + more); + String logStr = RILUtils.responseToString(response) + " " + more; + if (RIL.RILJ_LOGD) { + riljLog("[UNSL]< " + logStr); + } + Trace.instantForTrack(Trace.TRACE_TAG_NETWORK, "RIL", logStr); } @UnsupportedAppUsage void unsljLogRet(int response, Object ret) { - riljLog("[UNSL]< " + RILUtils.responseToString(response) + " " - + retToString(response, ret)); + String logStr = RILUtils.responseToString(response) + " " + retToString(response, ret); + if (RIL.RILJ_LOGD) { + riljLog("[UNSL]< " + logStr); + } + Trace.instantForTrack(Trace.TRACE_TAG_NETWORK, "RIL", logStr); } @UnsupportedAppUsage void unsljLogvRet(int response, Object ret) { - riljLogv("[UNSL]< " + RILUtils.responseToString(response) + " " - + retToString(response, ret)); + String logStr = RILUtils.responseToString(response) + " " + retToString(response, ret); + if (RIL.RILJ_LOGV) { + riljLogv("[UNSL]< " + logStr); + } + Trace.instantForTrack(Trace.TRACE_TAG_NETWORK, "RIL", logStr); } @Override @@ -5829,6 +6837,13 @@ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { pw.println("RIL: " + this); + pw.println(" " + mServiceProxies.get(HAL_SERVICE_DATA)); + pw.println(" " + mServiceProxies.get(HAL_SERVICE_MESSAGING)); + pw.println(" " + mServiceProxies.get(HAL_SERVICE_MODEM)); + pw.println(" " + mServiceProxies.get(HAL_SERVICE_NETWORK)); + pw.println(" " + mServiceProxies.get(HAL_SERVICE_SIM)); + pw.println(" " + mServiceProxies.get(HAL_SERVICE_VOICE)); + pw.println(" " + mServiceProxies.get(HAL_SERVICE_IMS)); pw.println(" mWakeLock=" + mWakeLock); pw.println(" mWakeLockTimeout=" + mWakeLockTimeout); synchronized (mRequestList) { @@ -5904,31 +6919,60 @@ new CellSignalStrengthNr()); } - /** - * Get the HAL version. - * - * @return the current HalVersion - */ - public HalVersion getHalVersion() { - return mRadioVersion; + void notifyBarringInfoChanged(@NonNull BarringInfo barringInfo) { + mLastBarringInfo = barringInfo; + mBarringInfoChangedRegistrants.notifyRegistrants(new AsyncResult(null, barringInfo, null)); } - private static String serviceToString(int service) { + /** + * Get the HAL version with a specific service. + * + * @param service the hal service id + * @return the current HalVersion + */ + public HalVersion getHalVersion(int service) { + HalVersion halVersion = mHalVersion.get(service); + if (halVersion == null) { + if (isRadioServiceSupported(service)) { + halVersion = RADIO_HAL_VERSION_UNKNOWN; + } else { + halVersion = RADIO_HAL_VERSION_UNSUPPORTED; + } + } + return halVersion; + } + + /** + * Get the HAL version corresponding to the interface version of a IRadioService module. + * @param interfaceVersion The interface version, from IRadioService#getInterfaceVersion(). + * @return The corresponding HalVersion. + */ + public static HalVersion getServiceHalVersion(int interfaceVersion) { + switch (interfaceVersion) { + case 1: return RADIO_HAL_VERSION_2_0; + case 2: return RADIO_HAL_VERSION_2_1; + default: return RADIO_HAL_VERSION_UNKNOWN; + } + } + + private static String serviceToString(@HalService int service) { switch (service) { - case RADIO_SERVICE: + case HAL_SERVICE_RADIO: return "RADIO"; - case DATA_SERVICE: + case HAL_SERVICE_DATA: return "DATA"; - case MESSAGING_SERVICE: + case HAL_SERVICE_MESSAGING: return "MESSAGING"; - case MODEM_SERVICE: + case HAL_SERVICE_MODEM: return "MODEM"; - case NETWORK_SERVICE: + case HAL_SERVICE_NETWORK: return "NETWORK"; - case SIM_SERVICE: + case HAL_SERVICE_SIM: return "SIM"; - case VOICE_SERVICE: + case HAL_SERVICE_VOICE: return "VOICE"; + case HAL_SERVICE_IMS: + return "IMS"; default: return "UNKNOWN:" + service; }
diff --git a/src/java/com/android/internal/telephony/RILUtils.java b/src/java/com/android/internal/telephony/RILUtils.java index 61f1e82..9db186f 100644 --- a/src/java/com/android/internal/telephony/RILUtils.java +++ b/src/java/com/android/internal/telephony/RILUtils.java
@@ -29,6 +29,7 @@ import static com.android.internal.telephony.RILConstants.RIL_REQUEST_ALLOW_DATA; import static com.android.internal.telephony.RILConstants.RIL_REQUEST_ANSWER; import static com.android.internal.telephony.RILConstants.RIL_REQUEST_BASEBAND_VERSION; +import static com.android.internal.telephony.RILConstants.RIL_REQUEST_CANCEL_EMERGENCY_NETWORK_SCAN; import static com.android.internal.telephony.RILConstants.RIL_REQUEST_CANCEL_HANDOVER; import static com.android.internal.telephony.RILConstants.RIL_REQUEST_CANCEL_USSD; import static com.android.internal.telephony.RILConstants.RIL_REQUEST_CDMA_BROADCAST_ACTIVATION; @@ -58,6 +59,7 @@ import static com.android.internal.telephony.RILConstants.RIL_REQUEST_DEACTIVATE_DATA_CALL; import static com.android.internal.telephony.RILConstants.RIL_REQUEST_DELETE_SMS_ON_SIM; import static com.android.internal.telephony.RILConstants.RIL_REQUEST_DEVICE_IDENTITY; +import static com.android.internal.telephony.RILConstants.RIL_REQUEST_DEVICE_IMEI; import static com.android.internal.telephony.RILConstants.RIL_REQUEST_DIAL; import static com.android.internal.telephony.RILConstants.RIL_REQUEST_DTMF; import static com.android.internal.telephony.RILConstants.RIL_REQUEST_DTMF_START; @@ -74,6 +76,7 @@ import static com.android.internal.telephony.RILConstants.RIL_REQUEST_ENTER_SIM_PUK; import static com.android.internal.telephony.RILConstants.RIL_REQUEST_ENTER_SIM_PUK2; import static com.android.internal.telephony.RILConstants.RIL_REQUEST_EXIT_EMERGENCY_CALLBACK_MODE; +import static com.android.internal.telephony.RILConstants.RIL_REQUEST_EXIT_EMERGENCY_MODE; import static com.android.internal.telephony.RILConstants.RIL_REQUEST_EXPLICIT_CALL_TRANSFER; import static com.android.internal.telephony.RILConstants.RIL_REQUEST_GET_ACTIVITY_INFO; import static com.android.internal.telephony.RILConstants.RIL_REQUEST_GET_ALLOWED_CARRIERS; @@ -112,7 +115,9 @@ import static com.android.internal.telephony.RILConstants.RIL_REQUEST_IMS_REGISTRATION_STATE; import static com.android.internal.telephony.RILConstants.RIL_REQUEST_IMS_SEND_SMS; import static com.android.internal.telephony.RILConstants.RIL_REQUEST_ISIM_AUTHENTICATION; +import static com.android.internal.telephony.RILConstants.RIL_REQUEST_IS_N1_MODE_ENABLED; import static com.android.internal.telephony.RILConstants.RIL_REQUEST_IS_NR_DUAL_CONNECTIVITY_ENABLED; +import static com.android.internal.telephony.RILConstants.RIL_REQUEST_IS_NULL_CIPHER_AND_INTEGRITY_ENABLED; import static com.android.internal.telephony.RILConstants.RIL_REQUEST_IS_VONR_ENABLED; import static com.android.internal.telephony.RILConstants.RIL_REQUEST_LAST_CALL_FAIL_CAUSE; import static com.android.internal.telephony.RILConstants.RIL_REQUEST_LAST_DATA_CALL_FAIL_CAUSE; @@ -138,6 +143,7 @@ import static com.android.internal.telephony.RILConstants.RIL_REQUEST_REPORT_STK_SERVICE_IS_RUNNING; import static com.android.internal.telephony.RILConstants.RIL_REQUEST_RESET_RADIO; import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SCREEN_STATE; +import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SEND_ANBR_QUERY; import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SEND_DEVICE_STATE; import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SEND_SMS; import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SEND_SMS_EXPECT_MORE; @@ -154,20 +160,24 @@ import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_DATA_PROFILE; import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_DATA_THROTTLING; import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_DC_RT_INFO_RATE; +import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_EMERGENCY_MODE; import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_FACILITY_LOCK; import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_INITIAL_ATTACH_APN; import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_LINK_CAPACITY_REPORTING_CRITERIA; import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_LOCATION_UPDATES; import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_LOGICAL_TO_PHYSICAL_SLOT_MAPPING; import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_MUTE; +import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_N1_MODE_ENABLED; import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_NETWORK_SELECTION_AUTOMATIC; import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_NETWORK_SELECTION_MANUAL; +import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_NULL_CIPHER_AND_INTEGRITY_ENABLED; import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_PREFERRED_DATA_MODEM; import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_PREFERRED_NETWORK_TYPE; import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_RADIO_CAPABILITY; import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_SIGNAL_STRENGTH_REPORTING_CRITERIA; import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_SIM_CARD_POWER; import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_SMSC_ADDRESS; +import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_SRVCC_CALL_INFO; import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_SUPP_SVC_NOTIFICATION; import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_SYSTEM_SELECTION_CHANNELS; import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_TTY_MODE; @@ -185,6 +195,7 @@ import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SIM_TRANSMIT_APDU_CHANNEL; import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SMS_ACKNOWLEDGE; import static com.android.internal.telephony.RILConstants.RIL_REQUEST_START_HANDOVER; +import static com.android.internal.telephony.RILConstants.RIL_REQUEST_START_IMS_TRAFFIC; import static com.android.internal.telephony.RILConstants.RIL_REQUEST_START_KEEPALIVE; import static com.android.internal.telephony.RILConstants.RIL_REQUEST_START_LCE; import static com.android.internal.telephony.RILConstants.RIL_REQUEST_START_NETWORK_SCAN; @@ -194,12 +205,17 @@ import static com.android.internal.telephony.RILConstants.RIL_REQUEST_STK_SEND_ENVELOPE_WITH_STATUS; import static com.android.internal.telephony.RILConstants.RIL_REQUEST_STK_SEND_TERMINAL_RESPONSE; import static com.android.internal.telephony.RILConstants.RIL_REQUEST_STK_SET_PROFILE; +import static com.android.internal.telephony.RILConstants.RIL_REQUEST_STOP_IMS_TRAFFIC; import static com.android.internal.telephony.RILConstants.RIL_REQUEST_STOP_KEEPALIVE; import static com.android.internal.telephony.RILConstants.RIL_REQUEST_STOP_LCE; import static com.android.internal.telephony.RILConstants.RIL_REQUEST_STOP_NETWORK_SCAN; import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SWITCH_DUAL_SIM_CONFIG; import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SWITCH_WAITING_OR_HOLDING_AND_ACTIVE; +import static com.android.internal.telephony.RILConstants.RIL_REQUEST_TRIGGER_EMERGENCY_NETWORK_SCAN; +import static com.android.internal.telephony.RILConstants.RIL_REQUEST_TRIGGER_EPS_FALLBACK; import static com.android.internal.telephony.RILConstants.RIL_REQUEST_UDUB; +import static com.android.internal.telephony.RILConstants.RIL_REQUEST_UPDATE_IMS_CALL_STATUS; +import static com.android.internal.telephony.RILConstants.RIL_REQUEST_UPDATE_IMS_REGISTRATION_INFO; import static com.android.internal.telephony.RILConstants.RIL_REQUEST_UPDATE_SIM_PHONEBOOK_RECORD; import static com.android.internal.telephony.RILConstants.RIL_REQUEST_VOICE_RADIO_TECH; import static com.android.internal.telephony.RILConstants.RIL_REQUEST_VOICE_REGISTRATION_STATE; @@ -214,8 +230,10 @@ import static com.android.internal.telephony.RILConstants.RIL_UNSOL_CDMA_RUIM_SMS_STORAGE_FULL; import static com.android.internal.telephony.RILConstants.RIL_UNSOL_CDMA_SUBSCRIPTION_SOURCE_CHANGED; import static com.android.internal.telephony.RILConstants.RIL_UNSOL_CELL_INFO_LIST; +import static com.android.internal.telephony.RILConstants.RIL_UNSOL_CONNECTION_SETUP_FAILURE; import static com.android.internal.telephony.RILConstants.RIL_UNSOL_DATA_CALL_LIST_CHANGED; import static com.android.internal.telephony.RILConstants.RIL_UNSOL_DC_RT_INFO_CHANGED; +import static com.android.internal.telephony.RILConstants.RIL_UNSOL_EMERGENCY_NETWORK_SCAN_RESULT; import static com.android.internal.telephony.RILConstants.RIL_UNSOL_EMERGENCY_NUMBER_LIST; import static com.android.internal.telephony.RILConstants.RIL_UNSOL_ENTER_EMERGENCY_CALLBACK_MODE; import static com.android.internal.telephony.RILConstants.RIL_UNSOL_EXIT_EMERGENCY_CALLBACK_MODE; @@ -226,6 +244,7 @@ import static com.android.internal.telephony.RILConstants.RIL_UNSOL_MODEM_RESTART; import static com.android.internal.telephony.RILConstants.RIL_UNSOL_NETWORK_SCAN_RESULT; import static com.android.internal.telephony.RILConstants.RIL_UNSOL_NITZ_TIME_RECEIVED; +import static com.android.internal.telephony.RILConstants.RIL_UNSOL_NOTIFY_ANBR; import static com.android.internal.telephony.RILConstants.RIL_UNSOL_OEM_HOOK_RAW; import static com.android.internal.telephony.RILConstants.RIL_UNSOL_ON_SS; import static com.android.internal.telephony.RILConstants.RIL_UNSOL_ON_USSD; @@ -253,6 +272,7 @@ import static com.android.internal.telephony.RILConstants.RIL_UNSOL_SIGNAL_STRENGTH; import static com.android.internal.telephony.RILConstants.RIL_UNSOL_SIM_REFRESH; import static com.android.internal.telephony.RILConstants.RIL_UNSOL_SIM_SMS_STORAGE_FULL; +import static com.android.internal.telephony.RILConstants.RIL_UNSOL_SLICING_CONFIG_CHANGED; import static com.android.internal.telephony.RILConstants.RIL_UNSOL_SRVCC_STATE_NOTIFY; import static com.android.internal.telephony.RILConstants.RIL_UNSOL_STK_CALL_SETUP; import static com.android.internal.telephony.RILConstants.RIL_UNSOL_STK_CC_ALPHA_NOTIFY; @@ -260,11 +280,13 @@ import static com.android.internal.telephony.RILConstants.RIL_UNSOL_STK_PROACTIVE_COMMAND; import static com.android.internal.telephony.RILConstants.RIL_UNSOL_STK_SESSION_END; import static com.android.internal.telephony.RILConstants.RIL_UNSOL_SUPP_SVC_NOTIFICATION; +import static com.android.internal.telephony.RILConstants.RIL_UNSOL_TRIGGER_IMS_DEREGISTRATION; import static com.android.internal.telephony.RILConstants.RIL_UNSOL_UICC_APPLICATIONS_ENABLEMENT_CHANGED; import static com.android.internal.telephony.RILConstants.RIL_UNSOL_UICC_SUBSCRIPTION_STATUS_CHANGED; import static com.android.internal.telephony.RILConstants.RIL_UNSOL_UNTHROTTLE_APN; import static com.android.internal.telephony.RILConstants.RIL_UNSOL_VOICE_RADIO_TECH_CHANGED; +import android.annotation.NonNull; import android.annotation.Nullable; import android.net.InetAddresses; import android.net.LinkAddress; @@ -298,8 +320,11 @@ import android.telephony.CellSignalStrengthTdscdma; import android.telephony.CellSignalStrengthWcdma; import android.telephony.ClosedSubscriberGroupInfo; +import android.telephony.DomainSelectionService; +import android.telephony.EmergencyRegResult; import android.telephony.LinkCapacityEstimate; import android.telephony.ModemInfo; +import android.telephony.NetworkRegistrationInfo; import android.telephony.PhoneCapability; import android.telephony.PhoneNumberUtils; import android.telephony.PhysicalChannelConfig; @@ -326,6 +351,12 @@ import android.telephony.data.RouteSelectionDescriptor; import android.telephony.data.TrafficDescriptor; import android.telephony.data.UrspRule; +import android.telephony.ims.RegistrationManager; +import android.telephony.ims.feature.ConnectionFailureInfo; +import android.telephony.ims.feature.MmTelFeature; +import android.telephony.ims.stub.ImsRegistrationImplBase; +import android.telephony.ims.stub.ImsRegistrationImplBase.ImsDeregistrationReason; +import android.telephony.satellite.SatelliteManager; import android.text.TextUtils; import android.util.ArraySet; import android.util.SparseArray; @@ -339,6 +370,7 @@ import com.android.internal.telephony.cdma.sms.SmsEnvelope; import com.android.internal.telephony.data.KeepaliveStatus; import com.android.internal.telephony.data.KeepaliveStatus.KeepaliveStatusCode; +import com.android.internal.telephony.imsphone.ImsCallInfo; import com.android.internal.telephony.uicc.AdnCapacity; import com.android.internal.telephony.uicc.IccCardApplicationStatus; import com.android.internal.telephony.uicc.IccCardStatus; @@ -346,6 +378,7 @@ import com.android.internal.telephony.uicc.IccSlotPortMapping; import com.android.internal.telephony.uicc.IccSlotStatus; import com.android.internal.telephony.uicc.IccUtils; +import com.android.internal.telephony.uicc.PortUtils; import com.android.internal.telephony.uicc.SimPhonebookRecord; import com.android.telephony.Rlog; @@ -1441,8 +1474,9 @@ if (ComprehensionTlvTag.TEXT_STRING.value() == ctlv.getTag()) { byte[] target = Arrays.copyOfRange(ctlv.getRawValue(), from, ctlv.getValueIndex() + ctlv.getLength()); - terminalResponse = terminalResponse.toLowerCase().replace( - IccUtils.bytesToHexString(target).toLowerCase(), "********"); + terminalResponse = terminalResponse.toLowerCase(Locale.ROOT).replace( + IccUtils.bytesToHexString(target).toLowerCase(Locale.ROOT), + "********"); } // The text string tag and the length field should also be hidden. from = ctlv.getValueIndex() + ctlv.getLength(); @@ -1660,12 +1694,10 @@ if ((networkTypeBitmask & TelephonyManager.NETWORK_TYPE_BITMASK_IWLAN) != 0) { raf |= android.hardware.radio.RadioAccessFamily.IWLAN; } - if ((networkTypeBitmask & TelephonyManager.NETWORK_TYPE_BITMASK_LTE) != 0) { + if ((networkTypeBitmask & TelephonyManager.NETWORK_TYPE_BITMASK_LTE) != 0 + || (networkTypeBitmask & TelephonyManager.NETWORK_TYPE_BITMASK_LTE_CA) != 0) { raf |= android.hardware.radio.RadioAccessFamily.LTE; } - if ((networkTypeBitmask & TelephonyManager.NETWORK_TYPE_BITMASK_LTE_CA) != 0) { - raf |= android.hardware.radio.RadioAccessFamily.LTE_CA; - } if ((networkTypeBitmask & TelephonyManager.NETWORK_TYPE_BITMASK_NR) != 0) { raf |= android.hardware.radio.RadioAccessFamily.NR; } @@ -1801,10 +1833,12 @@ * @param p2 p2 * @param p3 p3 * @param data data + * @param radioHalVersion radio hal version * @return The converted SimApdu */ public static android.hardware.radio.sim.SimApdu convertToHalSimApduAidl(int channel, int cla, - int instruction, int p1, int p2, int p3, String data) { + int instruction, int p1, int p2, int p3, String data, boolean isEs10Command, + HalVersion radioHalVersion) { android.hardware.radio.sim.SimApdu msg = new android.hardware.radio.sim.SimApdu(); msg.sessionId = channel; msg.cla = cla; @@ -1813,6 +1847,9 @@ msg.p2 = p2; msg.p3 = p3; msg.data = convertNullToEmptyString(data); + if (radioHalVersion.greaterOrEqual(RIL.RADIO_HAL_VERSION_2_1)) { + msg.isEs10 = isEs10Command; + } return msg; } @@ -3256,7 +3293,8 @@ return new CellSignalStrengthNr(CellSignalStrengthNr.flip(ss.base.csiRsrp), CellSignalStrengthNr.flip(ss.base.csiRsrq), ss.base.csiSinr, ss.csiCqiTableIndex, ss.csiCqiReport, CellSignalStrengthNr.flip(ss.base.ssRsrp), - CellSignalStrengthNr.flip(ss.base.ssRsrq), ss.base.ssSinr); + CellSignalStrengthNr.flip(ss.base.ssRsrq), ss.base.ssSinr, + CellInfo.UNAVAILABLE); } return null; } @@ -3271,7 +3309,7 @@ return new CellSignalStrengthNr(CellSignalStrengthNr.flip(ss.csiRsrp), CellSignalStrengthNr.flip(ss.csiRsrq), ss.csiSinr, ss.csiCqiTableIndex, primitiveArrayToArrayList(ss.csiCqiReport), CellSignalStrengthNr.flip(ss.ssRsrp), - CellSignalStrengthNr.flip(ss.ssRsrq), ss.ssSinr); + CellSignalStrengthNr.flip(ss.ssRsrq), ss.ssSinr, ss.timingAdvance); } private static ClosedSubscriberGroupInfo convertHalClosedSubscriberGroupInfo( @@ -3835,9 +3873,13 @@ convertHalQosBandwidth(eps.uplink), eps.qci); case android.hardware.radio.data.Qos.nr: android.hardware.radio.data.NrQos nr = qos.getNr(); + int averagingWindowMs = nr.averagingWindowMillis; + if (averagingWindowMs + == android.hardware.radio.data.NrQos.AVERAGING_WINDOW_UNKNOWN) { + averagingWindowMs = nr.averagingWindowMs; + } return new NrQos(convertHalQosBandwidth(nr.downlink), - convertHalQosBandwidth(nr.uplink), nr.qfi, nr.fiveQi, - nr.averagingWindowMs); + convertHalQosBandwidth(nr.uplink), nr.qfi, nr.fiveQi, averagingWindowMs); default: return null; } @@ -4331,6 +4373,7 @@ android.hardware.radio.sim.CardStatus cardStatus) { IccCardStatus iccCardStatus = new IccCardStatus(); iccCardStatus.setCardState(cardStatus.cardState); + iccCardStatus.setMultipleEnabledProfilesMode(cardStatus.supportedMepMode); iccCardStatus.setUniversalPinState(cardStatus.universalPinState); iccCardStatus.mGsmUmtsSubscriptionAppIndex = cardStatus.gsmUmtsSubscriptionAppIndex; iccCardStatus.mCdmaSubscriptionAppIndex = cardStatus.cdmaSubscriptionAppIndex; @@ -4358,7 +4401,9 @@ } IccSlotPortMapping slotPortMapping = new IccSlotPortMapping(); slotPortMapping.mPhysicalSlotIndex = cardStatus.slotMap.physicalSlotId; - slotPortMapping.mPortIndex = cardStatus.slotMap.portId; + slotPortMapping.mPortIndex = PortUtils.convertFromHalPortIndex( + cardStatus.slotMap.physicalSlotId, cardStatus.slotMap.portId, + iccCardStatus.mCardState, iccCardStatus.mSupportedMepMode); iccCardStatus.mSlotPortMapping = slotPortMapping; return iccCardStatus; } @@ -4474,6 +4519,7 @@ } iccSlotStatus.atr = slotStatus.atr; iccSlotStatus.eid = slotStatus.eid; + iccSlotStatus.setMultipleEnabledProfilesMode(slotStatus.supportedMepMode); response.add(iccSlotStatus); } return response; @@ -4541,7 +4587,8 @@ int logicalSlotIdx = mapping.getLogicalSlotIndex(); res[logicalSlotIdx] = new android.hardware.radio.config.SlotPortMapping(); res[logicalSlotIdx].physicalSlotId = mapping.getPhysicalSlotIndex(); - res[logicalSlotIdx].portId = mapping.getPortIndex(); + res[logicalSlotIdx].portId = PortUtils.convertToHalPortIndex( + mapping.getPhysicalSlotIndex(), mapping.getPortIndex()); } return res; } @@ -4566,12 +4613,14 @@ public static PhoneCapability convertHalPhoneCapability(int[] deviceNrCapabilities, Object o) { int maxActiveVoiceCalls = 0; int maxActiveData = 0; + int maxActiveInternetData = 0; boolean validationBeforeSwitchSupported = false; List<ModemInfo> logicalModemList = new ArrayList<>(); if (o instanceof android.hardware.radio.config.PhoneCapability) { final android.hardware.radio.config.PhoneCapability phoneCapability = (android.hardware.radio.config.PhoneCapability) o; maxActiveData = phoneCapability.maxActiveData; + maxActiveInternetData = phoneCapability.maxActiveInternetData; validationBeforeSwitchSupported = phoneCapability.isInternetLingeringSupported; for (int modemId : phoneCapability.logicalModemIds) { logicalModemList.add(new ModemInfo(modemId)); @@ -4580,16 +4629,249 @@ final android.hardware.radio.config.V1_1.PhoneCapability phoneCapability = (android.hardware.radio.config.V1_1.PhoneCapability) o; maxActiveData = phoneCapability.maxActiveData; + maxActiveInternetData = phoneCapability.maxActiveInternetData; validationBeforeSwitchSupported = phoneCapability.isInternetLingeringSupported; for (android.hardware.radio.config.V1_1.ModemInfo modemInfo : phoneCapability.logicalModemList) { logicalModemList.add(new ModemInfo(modemInfo.modemId)); } } + // maxActiveInternetData defines how many logical modems can have internet PDN connections + // simultaneously. For L+L DSDS modem it’s 1, and for DSDA modem it’s 2. + maxActiveVoiceCalls = maxActiveInternetData; return new PhoneCapability(maxActiveVoiceCalls, maxActiveData, logicalModemList, validationBeforeSwitchSupported, deviceNrCapabilities); } + /** + * Convert network scan type + * @param scanType The network scan type + * @return The converted EmergencyScanType + */ + public static int convertEmergencyScanType(int scanType) { + switch (scanType) { + case DomainSelectionService.SCAN_TYPE_LIMITED_SERVICE: + return android.hardware.radio.network.EmergencyScanType.LIMITED_SERVICE; + case DomainSelectionService.SCAN_TYPE_FULL_SERVICE: + return android.hardware.radio.network.EmergencyScanType.FULL_SERVICE; + default: + return android.hardware.radio.network.EmergencyScanType.NO_PREFERENCE; + } + } + + /** + * Convert to EmergencyNetworkScanTrigger + * @param accessNetwork The list of access network types + * @param scanType The network scan type + * @return The converted EmergencyNetworkScanTrigger + */ + public static android.hardware.radio.network.EmergencyNetworkScanTrigger + convertEmergencyNetworkScanTrigger(@NonNull int[] accessNetwork, int scanType) { + int[] halAccessNetwork = new int[accessNetwork.length]; + for (int i = 0; i < accessNetwork.length; i++) { + halAccessNetwork[i] = convertToHalAccessNetworkAidl(accessNetwork[i]); + } + + android.hardware.radio.network.EmergencyNetworkScanTrigger scanRequest = + new android.hardware.radio.network.EmergencyNetworkScanTrigger(); + + scanRequest.accessNetwork = halAccessNetwork; + scanRequest.scanType = convertEmergencyScanType(scanType); + return scanRequest; + } + + /** + * Convert EmergencyRegResult.aidl to EmergencyRegResult. + * @param halResult EmergencyRegResult.aidl in HAL. + * @return Converted EmergencyRegResult. + */ + public static EmergencyRegResult convertHalEmergencyRegResult( + android.hardware.radio.network.EmergencyRegResult halResult) { + return new EmergencyRegResult( + halResult.accessNetwork, + convertHalRegState(halResult.regState), + halResult.emcDomain, + halResult.isVopsSupported, + halResult.isEmcBearerSupported, + halResult.nwProvidedEmc, + halResult.nwProvidedEmf, + halResult.mcc, + halResult.mnc, + getCountryCodeForMccMnc(halResult.mcc, halResult.mnc)); + } + + private static @NonNull String getCountryCodeForMccMnc( + @NonNull String mcc, @NonNull String mnc) { + if (TextUtils.isEmpty(mcc)) return ""; + if (TextUtils.isEmpty(mnc)) mnc = "000"; + String operatorNumeric = TextUtils.concat(mcc, mnc).toString(); + + MccTable.MccMnc mccMnc = MccTable.MccMnc.fromOperatorNumeric(operatorNumeric); + return MccTable.geoCountryCodeForMccMnc(mccMnc); + } + + /** + * Convert RegResult.aidl to RegistrationState. + * @param halRegState RegResult in HAL. + * @return Converted RegistrationState. + */ + public static @NetworkRegistrationInfo.RegistrationState int convertHalRegState( + int halRegState) { + switch (halRegState) { + case android.hardware.radio.network.RegState.NOT_REG_MT_NOT_SEARCHING_OP: + case android.hardware.radio.network.RegState.NOT_REG_MT_NOT_SEARCHING_OP_EM: + return NetworkRegistrationInfo.REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING; + case android.hardware.radio.network.RegState.REG_HOME: + return NetworkRegistrationInfo.REGISTRATION_STATE_HOME; + case android.hardware.radio.network.RegState.NOT_REG_MT_SEARCHING_OP: + case android.hardware.radio.network.RegState.NOT_REG_MT_SEARCHING_OP_EM: + return NetworkRegistrationInfo.REGISTRATION_STATE_NOT_REGISTERED_SEARCHING; + case android.hardware.radio.network.RegState.REG_DENIED: + case android.hardware.radio.network.RegState.REG_DENIED_EM: + return NetworkRegistrationInfo.REGISTRATION_STATE_DENIED; + case android.hardware.radio.network.RegState.UNKNOWN: + case android.hardware.radio.network.RegState.UNKNOWN_EM: + return NetworkRegistrationInfo.REGISTRATION_STATE_UNKNOWN; + case android.hardware.radio.network.RegState.REG_ROAMING: + return NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING; + default: + return NetworkRegistrationInfo.REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING; + } + } + + /** Converts the array of network types to readable String array */ + public static @NonNull String accessNetworkTypesToString( + @NonNull @AccessNetworkConstants.RadioAccessNetworkType int[] accessNetworkTypes) { + int length = accessNetworkTypes.length; + StringBuilder sb = new StringBuilder("{"); + if (length > 0) { + sb.append(Arrays.stream(accessNetworkTypes) + .mapToObj(RILUtils::accessNetworkTypeToString) + .collect(Collectors.joining(","))); + } + sb.append("}"); + return sb.toString(); + } + + private static @NonNull String accessNetworkTypeToString( + @AccessNetworkConstants.RadioAccessNetworkType int accessNetworkType) { + switch (accessNetworkType) { + case AccessNetworkConstants.AccessNetworkType.UNKNOWN: return "UNKNOWN"; + case AccessNetworkConstants.AccessNetworkType.GERAN: return "GERAN"; + case AccessNetworkConstants.AccessNetworkType.UTRAN: return "UTRAN"; + case AccessNetworkConstants.AccessNetworkType.EUTRAN: return "EUTRAN"; + case AccessNetworkConstants.AccessNetworkType.CDMA2000: return "CDMA2000"; + case AccessNetworkConstants.AccessNetworkType.IWLAN: return "IWLAN"; + case AccessNetworkConstants.AccessNetworkType.NGRAN: return "NGRAN"; + default: return Integer.toString(accessNetworkType); + } + } + + /** Converts scan type to readable String */ + public static @NonNull String scanTypeToString( + @DomainSelectionService.EmergencyScanType int scanType) { + switch (scanType) { + case DomainSelectionService.SCAN_TYPE_LIMITED_SERVICE: + return "LIMITED_SERVICE"; + case DomainSelectionService.SCAN_TYPE_FULL_SERVICE: + return "FULL_SERVICE"; + default: + return "NO_PREFERENCE"; + } + } + + /** Convert IMS deregistration reason */ + public static @ImsDeregistrationReason int convertHalDeregistrationReason(int reason) { + switch (reason) { + case android.hardware.radio.ims.ImsDeregistrationReason.REASON_SIM_REMOVED: + return ImsRegistrationImplBase.REASON_SIM_REMOVED; + case android.hardware.radio.ims.ImsDeregistrationReason.REASON_SIM_REFRESH: + return ImsRegistrationImplBase.REASON_SIM_REFRESH; + case android.hardware.radio.ims.ImsDeregistrationReason + .REASON_ALLOWED_NETWORK_TYPES_CHANGED: + return ImsRegistrationImplBase.REASON_ALLOWED_NETWORK_TYPES_CHANGED; + default: + return ImsRegistrationImplBase.REASON_UNKNOWN; + } + } + + /** + * Convert the IMS traffic type. + * @param trafficType IMS traffic type like registration, voice, video, SMS, emergency, and etc. + * @return The converted IMS traffic type. + */ + public static int convertImsTrafficType(@MmTelFeature.ImsTrafficType int trafficType) { + switch (trafficType) { + case MmTelFeature.IMS_TRAFFIC_TYPE_EMERGENCY: + return android.hardware.radio.ims.ImsTrafficType.EMERGENCY; + case MmTelFeature.IMS_TRAFFIC_TYPE_EMERGENCY_SMS: + return android.hardware.radio.ims.ImsTrafficType.EMERGENCY_SMS; + case MmTelFeature.IMS_TRAFFIC_TYPE_VOICE: + return android.hardware.radio.ims.ImsTrafficType.VOICE; + case MmTelFeature.IMS_TRAFFIC_TYPE_VIDEO: + return android.hardware.radio.ims.ImsTrafficType.VIDEO; + case MmTelFeature.IMS_TRAFFIC_TYPE_SMS: + return android.hardware.radio.ims.ImsTrafficType.SMS; + case MmTelFeature.IMS_TRAFFIC_TYPE_REGISTRATION: + return android.hardware.radio.ims.ImsTrafficType.REGISTRATION; + } + return android.hardware.radio.ims.ImsTrafficType.UT_XCAP; + } + + /** + * Convert the IMS traffic direction. + * @param trafficDirection Indicates the traffic direction. + * @return The converted IMS traffic direction. + */ + public static int convertImsTrafficDirection( + @MmTelFeature.ImsTrafficDirection int trafficDirection) { + switch (trafficDirection) { + case MmTelFeature.IMS_TRAFFIC_DIRECTION_INCOMING: + return android.hardware.radio.ims.ImsCall.Direction.INCOMING; + default: + return android.hardware.radio.ims.ImsCall.Direction.OUTGOING; + } + } + + /** + * Convert the IMS connection failure reason. + * @param halReason Specifies the reason that IMS connection failed. + * @return The converted IMS connection failure reason. + */ + public static @ConnectionFailureInfo.FailureReason int convertHalConnectionFailureReason( + int halReason) { + switch (halReason) { + case android.hardware.radio.ims.ConnectionFailureInfo + .ConnectionFailureReason.REASON_ACCESS_DENIED: + return ConnectionFailureInfo.REASON_ACCESS_DENIED; + case android.hardware.radio.ims.ConnectionFailureInfo + .ConnectionFailureReason.REASON_NAS_FAILURE: + return ConnectionFailureInfo.REASON_NAS_FAILURE; + case android.hardware.radio.ims.ConnectionFailureInfo + .ConnectionFailureReason.REASON_RACH_FAILURE: + return ConnectionFailureInfo.REASON_RACH_FAILURE; + case android.hardware.radio.ims.ConnectionFailureInfo + .ConnectionFailureReason.REASON_RLC_FAILURE: + return ConnectionFailureInfo.REASON_RLC_FAILURE; + case android.hardware.radio.ims.ConnectionFailureInfo + .ConnectionFailureReason.REASON_RRC_REJECT: + return ConnectionFailureInfo.REASON_RRC_REJECT; + case android.hardware.radio.ims.ConnectionFailureInfo + .ConnectionFailureReason.REASON_RRC_TIMEOUT: + return ConnectionFailureInfo.REASON_RRC_TIMEOUT; + case android.hardware.radio.ims.ConnectionFailureInfo + .ConnectionFailureReason.REASON_NO_SERVICE: + return ConnectionFailureInfo.REASON_NO_SERVICE; + case android.hardware.radio.ims.ConnectionFailureInfo + .ConnectionFailureReason.REASON_PDN_NOT_AVAILABLE: + return ConnectionFailureInfo.REASON_PDN_NOT_AVAILABLE; + case android.hardware.radio.ims.ConnectionFailureInfo + .ConnectionFailureReason.REASON_RF_BUSY: + return ConnectionFailureInfo.REASON_RF_BUSY; + } + return ConnectionFailureInfo.REASON_UNSPECIFIED; + } + /** Append the data to the end of an ArrayList */ public static void appendPrimitiveArrayToArrayList(byte[] src, ArrayList<Byte> dst) { for (byte b : src) { @@ -4983,6 +5265,9 @@ return "GET_SIM_PHONEBOOK_RECORDS"; case RIL_REQUEST_UPDATE_SIM_PHONEBOOK_RECORD: return "UPDATE_SIM_PHONEBOOK_RECORD"; + case RIL_REQUEST_DEVICE_IMEI: + return "DEVICE_IMEI"; + /* The following requests are not defined in RIL.h */ case RIL_REQUEST_GET_SLOT_STATUS: return "GET_SLOT_STATUS"; case RIL_REQUEST_SET_LOGICAL_TO_PHYSICAL_SLOT_MAPPING: @@ -5041,6 +5326,36 @@ return "SET_USAGE_SETTING"; case RIL_REQUEST_GET_USAGE_SETTING: return "GET_USAGE_SETTING"; + case RIL_REQUEST_SET_EMERGENCY_MODE: + return "SET_EMERGENCY_MODE"; + case RIL_REQUEST_TRIGGER_EMERGENCY_NETWORK_SCAN: + return "TRIGGER_EMERGENCY_NETWORK_SCAN"; + case RIL_REQUEST_CANCEL_EMERGENCY_NETWORK_SCAN: + return "CANCEL_EMERGENCY_NETWORK_SCAN"; + case RIL_REQUEST_EXIT_EMERGENCY_MODE: + return "EXIT_EMERGENCY_MODE"; + case RIL_REQUEST_SET_SRVCC_CALL_INFO: + return "SET_SRVCC_CALL_INFO"; + case RIL_REQUEST_UPDATE_IMS_REGISTRATION_INFO: + return "UPDATE_IMS_REGISTRATION_INFO"; + case RIL_REQUEST_START_IMS_TRAFFIC: + return "START_IMS_TRAFFIC"; + case RIL_REQUEST_STOP_IMS_TRAFFIC: + return "STOP_IMS_TRAFFIC"; + case RIL_REQUEST_SEND_ANBR_QUERY: + return "SEND_ANBR_QUERY"; + case RIL_REQUEST_TRIGGER_EPS_FALLBACK: + return "TRIGGER_EPS_FALLBACK"; + case RIL_REQUEST_SET_NULL_CIPHER_AND_INTEGRITY_ENABLED: + return "SET_NULL_CIPHER_AND_INTEGRITY_ENABLED"; + case RIL_REQUEST_IS_NULL_CIPHER_AND_INTEGRITY_ENABLED: + return "IS_NULL_CIPHER_AND_INTEGRITY_ENABLED"; + case RIL_REQUEST_UPDATE_IMS_CALL_STATUS: + return "UPDATE_IMS_CALL_STATUS"; + case RIL_REQUEST_SET_N1_MODE_ENABLED: + return "SET_N1_MODE_ENABLED"; + case RIL_REQUEST_IS_N1_MODE_ENABLED: + return "IS_N1_MODE_ENABLED"; default: return "<unknown request " + request + ">"; } @@ -5161,6 +5476,9 @@ return "UNSOL_RESPONSE_SIM_PHONEBOOK_CHANGED"; case RIL_UNSOL_RESPONSE_SIM_PHONEBOOK_RECORDS_RECEIVED: return "UNSOL_RESPONSE_SIM_PHONEBOOK_RECORDS_RECEIVED"; + case RIL_UNSOL_SLICING_CONFIG_CHANGED: + return "UNSOL_SLICING_CONFIG_CHANGED"; + /* The follow unsols are not defined in RIL.h */ case RIL_UNSOL_ICC_SLOT_STATUS: return "UNSOL_ICC_SLOT_STATUS"; case RIL_UNSOL_PHYSICAL_CHANNEL_CONFIG: @@ -5173,8 +5491,16 @@ return "UNSOL_REGISTRATION_FAILED"; case RIL_UNSOL_BARRING_INFO_CHANGED: return "UNSOL_BARRING_INFO_CHANGED"; + case RIL_UNSOL_EMERGENCY_NETWORK_SCAN_RESULT: + return "UNSOL_EMERGENCY_NETWORK_SCAN_RESULT"; + case RIL_UNSOL_TRIGGER_IMS_DEREGISTRATION: + return "UNSOL_TRIGGER_IMS_DEREGISTRATION"; + case RIL_UNSOL_CONNECTION_SETUP_FAILURE: + return "UNSOL_CONNECTION_SETUP_FAILURE"; + case RIL_UNSOL_NOTIFY_ANBR: + return "UNSOL_NOTIFY_ANBR"; default: - return "<unknown response>"; + return "<unknown response " + response + ">"; } } @@ -5321,6 +5647,302 @@ return sb.toString(); } + /** + * Converts the list of call information for Single Radio Voice Call Continuity(SRVCC). + * + * @param srvccConnections The list of call information for SRVCC. + * @return The converted list of call information. + */ + public static android.hardware.radio.ims.SrvccCall[] convertToHalSrvccCall( + SrvccConnection[] srvccConnections) { + if (srvccConnections == null) { + return new android.hardware.radio.ims.SrvccCall[0]; + } + + int length = srvccConnections.length; + android.hardware.radio.ims.SrvccCall[] srvccCalls = + new android.hardware.radio.ims.SrvccCall[length]; + + for (int i = 0; i < length; i++) { + srvccCalls[i] = new android.hardware.radio.ims.SrvccCall(); + srvccCalls[i].index = i + 1; + srvccCalls[i].callType = convertSrvccCallType(srvccConnections[i].getType()); + srvccCalls[i].callState = convertCallState(srvccConnections[i].getState()); + srvccCalls[i].callSubstate = + convertSrvccCallSubState(srvccConnections[i].getSubState()); + srvccCalls[i].ringbackToneType = + convertSrvccCallRingbackToneType(srvccConnections[i].getRingbackToneType()); + srvccCalls[i].isMpty = srvccConnections[i].isMultiParty(); + srvccCalls[i].isMT = srvccConnections[i].isIncoming(); + srvccCalls[i].number = TextUtils.emptyIfNull(srvccConnections[i].getNumber()); + srvccCalls[i].numPresentation = + convertPresentation(srvccConnections[i].getNumberPresentation()); + srvccCalls[i].name = TextUtils.emptyIfNull(srvccConnections[i].getName()); + srvccCalls[i].namePresentation = + convertPresentation(srvccConnections[i].getNamePresentation()); + } + + return srvccCalls; + } + + /** + * Converts the call type. + * + * @param type The call type. + * @return The converted call type. + */ + public static int convertSrvccCallType(int type) { + switch (type) { + case SrvccConnection.CALL_TYPE_NORMAL: + return android.hardware.radio.ims.SrvccCall.CallType.NORMAL; + case SrvccConnection.CALL_TYPE_EMERGENCY: + return android.hardware.radio.ims.SrvccCall.CallType.EMERGENCY; + default: + throw new RuntimeException("illegal call type " + type); + } + } + + /** + * Converts the call state. + * + * @param state The call state. + * @return The converted call state. + */ + public static int convertCallState(Call.State state) { + switch (state) { + case ACTIVE: return android.hardware.radio.voice.Call.STATE_ACTIVE; + case HOLDING: return android.hardware.radio.voice.Call.STATE_HOLDING; + case DIALING: return android.hardware.radio.voice.Call.STATE_DIALING; + case ALERTING: return android.hardware.radio.voice.Call.STATE_ALERTING; + case INCOMING: return android.hardware.radio.voice.Call.STATE_INCOMING; + case WAITING: return android.hardware.radio.voice.Call.STATE_WAITING; + default: + throw new RuntimeException("illegal state " + state); + } + } + + /** + * Converts the substate of a call. + * + * @param state The substate of a call. + * @return The converted substate. + */ + public static int convertSrvccCallSubState(int state) { + switch (state) { + case SrvccConnection.SUBSTATE_NONE: + return android.hardware.radio.ims.SrvccCall.CallSubState.NONE; + case SrvccConnection.SUBSTATE_PREALERTING: + return android.hardware.radio.ims.SrvccCall.CallSubState.PREALERTING; + default: + throw new RuntimeException("illegal substate " + state); + } + } + + /** + * Converts the ringback tone type. + * + * @param type The ringback tone type. + * @return The converted ringback tone type. + */ + public static int convertSrvccCallRingbackToneType(int type) { + switch (type) { + case SrvccConnection.TONE_NONE: + return android.hardware.radio.ims.SrvccCall.ToneType.NONE; + case SrvccConnection.TONE_LOCAL: + return android.hardware.radio.ims.SrvccCall.ToneType.LOCAL; + case SrvccConnection.TONE_NETWORK: + return android.hardware.radio.ims.SrvccCall.ToneType.NETWORK; + default: + throw new RuntimeException("illegal ringback tone type " + type); + } + } + + /** + * Converts the number presentation type for caller id display. + * + * @param presentation The number presentation type. + * @return The converted presentation type. + */ + public static int convertPresentation(int presentation) { + switch (presentation) { + case PhoneConstants.PRESENTATION_ALLOWED: + return android.hardware.radio.voice.Call.PRESENTATION_ALLOWED; + case PhoneConstants.PRESENTATION_RESTRICTED: + return android.hardware.radio.voice.Call.PRESENTATION_RESTRICTED; + case PhoneConstants.PRESENTATION_UNKNOWN: + return android.hardware.radio.voice.Call.PRESENTATION_UNKNOWN; + case PhoneConstants.PRESENTATION_PAYPHONE: + return android.hardware.radio.voice.Call.PRESENTATION_PAYPHONE; + default: + throw new RuntimeException("illegal presentation " + presentation); + } + } + + /** + * Converts IMS registration state. + * + * @param state The IMS registration state. + * @return The converted HAL IMS registration state. + */ + public static int convertImsRegistrationState(int state) { + switch (state) { + case RegistrationManager.REGISTRATION_STATE_NOT_REGISTERED: + return android.hardware.radio.ims.ImsRegistrationState.NOT_REGISTERED; + case RegistrationManager.REGISTRATION_STATE_REGISTERED: + return android.hardware.radio.ims.ImsRegistrationState.REGISTERED; + default: + throw new RuntimeException("illegal state " + state); + } + } + + /** + * Converts IMS service radio technology. + * + * @param imsRadioTech The IMS service radio technology. + * @return The converted HAL access network type. + */ + + public static int convertImsRegistrationTech( + @ImsRegistrationImplBase.ImsRegistrationTech int imsRadioTech) { + switch (imsRadioTech) { + case ImsRegistrationImplBase.REGISTRATION_TECH_LTE: + return android.hardware.radio.AccessNetwork.EUTRAN; + case ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN: + return android.hardware.radio.AccessNetwork.IWLAN; + case ImsRegistrationImplBase.REGISTRATION_TECH_NR: + return android.hardware.radio.AccessNetwork.NGRAN; + case ImsRegistrationImplBase.REGISTRATION_TECH_3G: + return android.hardware.radio.AccessNetwork.UTRAN; + default: + return android.hardware.radio.AccessNetwork.UNKNOWN; + } + } + + /** + * Converts IMS capabilities. + * + * @param capabilities The IMS capabilities. + * @return The converted HAL IMS capabilities. + */ + public static int convertImsCapability(int capabilities) { + int halCapabilities = android.hardware.radio.ims.ImsRegistration.IMS_MMTEL_CAPABILITY_NONE; + if ((capabilities & CommandsInterface.IMS_MMTEL_CAPABILITY_VOICE) > 0) { + halCapabilities |= + android.hardware.radio.ims.ImsRegistration.IMS_MMTEL_CAPABILITY_VOICE; + } + if ((capabilities & CommandsInterface.IMS_MMTEL_CAPABILITY_VIDEO) > 0) { + halCapabilities |= + android.hardware.radio.ims.ImsRegistration.IMS_MMTEL_CAPABILITY_VIDEO; + } + if ((capabilities & CommandsInterface.IMS_MMTEL_CAPABILITY_SMS) > 0) { + halCapabilities |= android.hardware.radio.ims.ImsRegistration.IMS_MMTEL_CAPABILITY_SMS; + } + if ((capabilities & CommandsInterface.IMS_RCS_CAPABILITIES) > 0) { + halCapabilities |= android.hardware.radio.ims.ImsRegistration.IMS_RCS_CAPABILITIES; + } + return halCapabilities; + } + + /** Converts the ImsCallInfo instances to HAL ImsCall instances. */ + public static android.hardware.radio.ims.ImsCall[] convertImsCallInfo( + List<ImsCallInfo> imsCallInfos) { + if (imsCallInfos == null) { + return new android.hardware.radio.ims.ImsCall[0]; + } + + int length = 0; + for (int i = 0; i < imsCallInfos.size(); i++) { + if (imsCallInfos.get(i) != null) length++; + } + if (length == 0) { + return new android.hardware.radio.ims.ImsCall[0]; + } + + android.hardware.radio.ims.ImsCall[] halInfos = + new android.hardware.radio.ims.ImsCall[length]; + + int index = 0; + for (int i = 0; i < imsCallInfos.size(); i++) { + ImsCallInfo info = imsCallInfos.get(i); + if (info == null) continue; + + halInfos[index] = new android.hardware.radio.ims.ImsCall(); + halInfos[index].index = info.getIndex(); + halInfos[index].callState = convertToHalImsCallState(info.getCallState()); + halInfos[index].callType = info.isEmergencyCall() + ? android.hardware.radio.ims.ImsCall.CallType.EMERGENCY + : android.hardware.radio.ims.ImsCall.CallType.NORMAL; + halInfos[index].accessNetwork = convertToHalAccessNetworkAidl(info.getCallRadioTech()); + halInfos[index].direction = info.isIncoming() + ? android.hardware.radio.ims.ImsCall.Direction.INCOMING + : android.hardware.radio.ims.ImsCall.Direction.OUTGOING; + halInfos[index].isHeldByRemote = info.isHeldByRemote(); + index++; + } + + return halInfos; + } + + /** + * Convert satellite-related errors from CommandException.Error to + * SatelliteManager.SatelliteServiceResult. + * @param error The satellite error. + * @return The converted SatelliteServiceResult. + */ + @SatelliteManager.SatelliteError + public static int convertToSatelliteError( + CommandException.Error error) { + switch (error) { + case INTERNAL_ERR: + //fallthrough to SYSTEM_ERR + case MODEM_ERR: + //fallthrough to SYSTEM_ERR + case SYSTEM_ERR: + return SatelliteManager.SATELLITE_MODEM_ERROR; + case INVALID_ARGUMENTS: + return SatelliteManager.SATELLITE_INVALID_ARGUMENTS; + case INVALID_MODEM_STATE: + return SatelliteManager.SATELLITE_INVALID_MODEM_STATE; + case RADIO_NOT_AVAILABLE: + return SatelliteManager.SATELLITE_RADIO_NOT_AVAILABLE; + case REQUEST_NOT_SUPPORTED: + return SatelliteManager.SATELLITE_REQUEST_NOT_SUPPORTED; + case NO_MEMORY: + //fallthrough to NO_RESOURCES + case NO_RESOURCES: + return SatelliteManager.SATELLITE_NO_RESOURCES; + case NETWORK_ERR: + return SatelliteManager.SATELLITE_NETWORK_ERROR; + case NO_NETWORK_FOUND: + return SatelliteManager.SATELLITE_NOT_REACHABLE; + case ABORTED: + return SatelliteManager.SATELLITE_REQUEST_ABORTED; + case ACCESS_BARRED: + return SatelliteManager.SATELLITE_ACCESS_BARRED; + default: + return SatelliteManager.SATELLITE_ERROR; + } + } + + /** + * Converts the call state to HAL IMS call state. + * + * @param state The {@link Call.State}. + * @return The converted {@link android.hardware.radio.ims.ImsCall.CallState}. + */ + private static int convertToHalImsCallState(Call.State state) { + switch (state) { + case ACTIVE: return android.hardware.radio.ims.ImsCall.CallState.ACTIVE; + case HOLDING: return android.hardware.radio.ims.ImsCall.CallState.HOLDING; + case DIALING: return android.hardware.radio.ims.ImsCall.CallState.DIALING; + case ALERTING: return android.hardware.radio.ims.ImsCall.CallState.ALERTING; + case INCOMING: return android.hardware.radio.ims.ImsCall.CallState.INCOMING; + case WAITING: return android.hardware.radio.ims.ImsCall.CallState.WAITING; + case DISCONNECTING: return android.hardware.radio.ims.ImsCall.CallState.DISCONNECTING; + default: return android.hardware.radio.ims.ImsCall.CallState.DISCONNECTED; + } + } + private static void logd(String log) { Rlog.d("RILUtils", log); }
diff --git a/src/java/com/android/internal/telephony/RadioConfig.java b/src/java/com/android/internal/telephony/RadioConfig.java index 9ac03b1..3e2be1d 100644 --- a/src/java/com/android/internal/telephony/RadioConfig.java +++ b/src/java/com/android/internal/telephony/RadioConfig.java
@@ -36,6 +36,7 @@ import android.os.Registrant; import android.os.RemoteException; import android.os.ServiceManager; +import android.os.Trace; import android.os.WorkSource; import android.telephony.TelephonyManager; import android.telephony.UiccSlotMapping; @@ -44,7 +45,6 @@ import com.android.telephony.Rlog; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import java.util.NoSuchElementException; import java.util.concurrent.atomic.AtomicLong; @@ -342,6 +342,9 @@ private RILRequest obtainRequest(int request, Message result, WorkSource workSource) { RILRequest rr = RILRequest.obtain(request, result, workSource); + Trace.asyncTraceForTrackBegin( + Trace.TRACE_TAG_NETWORK, "RIL", RILUtils.requestToString(rr.mRequest), rr.mSerial); + synchronized (mRequestList) { mRequestList.append(rr.mSerial, rr); } @@ -352,7 +355,10 @@ RILRequest rr; synchronized (mRequestList) { rr = mRequestList.get(serial); + if (rr != null) { + Trace.asyncTraceForTrackEnd( + Trace.TRACE_TAG_NETWORK, "RIL", rr.mSerial); mRequestList.remove(serial); } } @@ -636,4 +642,9 @@ private static void loge(String log) { Rlog.e(TAG, log); } + + @Override + public String toString() { + return "RadioConfig[" + "mRadioConfigProxy=" + mRadioConfigProxy + ']'; + } }
diff --git a/src/java/com/android/internal/telephony/RadioConfigIndicationAidl.java b/src/java/com/android/internal/telephony/RadioConfigIndicationAidl.java index 851a8ef..76e6d5e 100644 --- a/src/java/com/android/internal/telephony/RadioConfigIndicationAidl.java +++ b/src/java/com/android/internal/telephony/RadioConfigIndicationAidl.java
@@ -17,6 +17,7 @@ package com.android.internal.telephony; import android.os.AsyncResult; +import android.os.Trace; import com.android.internal.telephony.uicc.IccSlotStatus; import com.android.telephony.Rlog; @@ -43,7 +44,7 @@ public void simSlotsStatusChanged( int type, android.hardware.radio.config.SimSlotStatus[] slotStatus) { ArrayList<IccSlotStatus> ret = RILUtils.convertHalSlotStatus(slotStatus); - logd("[UNSL]< UNSOL_SIM_SLOT_STATUS_CHANGED " + ret.toString()); + logd("UNSOL_SIM_SLOT_STATUS_CHANGED " + ret.toString()); if (mRadioConfig.mSimSlotStatusRegistrant != null) { mRadioConfig.mSimSlotStatusRegistrant.notifyRegistrant( new AsyncResult(null, ret, null)); @@ -51,7 +52,8 @@ } private static void logd(String log) { - Rlog.d(TAG, log); + Rlog.d(TAG, "[UNSL]< " + log); + Trace.instantForTrack(Trace.TRACE_TAG_NETWORK, "RIL", log); } @Override
diff --git a/src/java/com/android/internal/telephony/RadioConfigIndicationHidl.java b/src/java/com/android/internal/telephony/RadioConfigIndicationHidl.java index 23a676b..3b5330b 100644 --- a/src/java/com/android/internal/telephony/RadioConfigIndicationHidl.java +++ b/src/java/com/android/internal/telephony/RadioConfigIndicationHidl.java
@@ -17,6 +17,7 @@ package com.android.internal.telephony; import android.os.AsyncResult; +import android.os.Trace; import com.android.internal.telephony.uicc.IccSlotStatus; import com.android.telephony.Rlog; @@ -42,7 +43,7 @@ public void simSlotsStatusChanged(int indicationType, ArrayList<android.hardware.radio.config.V1_0.SimSlotStatus> slotStatus) { ArrayList<IccSlotStatus> ret = RILUtils.convertHalSlotStatus(slotStatus); - logd("[UNSL]< UNSOL_SIM_SLOT_STATUS_CHANGED " + ret.toString()); + logd("UNSOL_SIM_SLOT_STATUS_CHANGED " + ret.toString()); if (mRadioConfig.mSimSlotStatusRegistrant != null) { mRadioConfig.mSimSlotStatusRegistrant.notifyRegistrant( new AsyncResult(null, ret, null)); @@ -55,7 +56,7 @@ public void simSlotsStatusChanged_1_2(int indicationType, ArrayList<android.hardware.radio.config.V1_2.SimSlotStatus> slotStatus) { ArrayList<IccSlotStatus> ret = RILUtils.convertHalSlotStatus(slotStatus); - logd("[UNSL]< UNSOL_SIM_SLOT_STATUS_CHANGED " + ret.toString()); + logd("UNSOL_SIM_SLOT_STATUS_CHANGED " + ret.toString()); if (mRadioConfig.mSimSlotStatusRegistrant != null) { mRadioConfig.mSimSlotStatusRegistrant.notifyRegistrant( new AsyncResult(null, ret, null)); @@ -63,6 +64,7 @@ } private static void logd(String log) { - Rlog.d(TAG, log); + Rlog.d(TAG, "[UNSL]< " + log); + Trace.instantForTrack(Trace.TRACE_TAG_NETWORK, "RIL", log); } }
diff --git a/src/java/com/android/internal/telephony/RadioConfigProxy.java b/src/java/com/android/internal/telephony/RadioConfigProxy.java index a8601f1..edeb558 100644 --- a/src/java/com/android/internal/telephony/RadioConfigProxy.java +++ b/src/java/com/android/internal/telephony/RadioConfigProxy.java
@@ -336,4 +336,11 @@ mRadioConfig.obtainMessage(RadioConfig.EVENT_AIDL_SERVICE_DEAD)); } } + + @Override + public String toString() { + return "RadioConfigProxy[" + + "mRadioHalVersion=" + mRadioHalVersion + + ", mRadioConfigHalVersion=" + mRadioConfigHalVersion + ']'; + } }
diff --git a/src/java/com/android/internal/telephony/RadioDataProxy.java b/src/java/com/android/internal/telephony/RadioDataProxy.java index cbc762a..9671077 100644 --- a/src/java/com/android/internal/telephony/RadioDataProxy.java +++ b/src/java/com/android/internal/telephony/RadioDataProxy.java
@@ -45,12 +45,22 @@ * Set IRadioData as the AIDL implementation for RadioServiceProxy * @param halVersion Radio HAL version * @param data IRadioData implementation + * + * @return updated HAL version */ - public void setAidl(HalVersion halVersion, android.hardware.radio.data.IRadioData data) { - mHalVersion = halVersion; + public HalVersion setAidl(HalVersion halVersion, android.hardware.radio.data.IRadioData data) { + HalVersion version = halVersion; + try { + version = RIL.getServiceHalVersion(data.getInterfaceVersion()); + } catch (RemoteException e) { + Rlog.e(TAG, "setAidl: " + e); + } + mHalVersion = version; mDataProxy = data; mIsAidl = true; - Rlog.d(TAG, "AIDL initialized"); + + Rlog.d(TAG, "AIDL initialized mHalVersion=" + mHalVersion); + return mHalVersion; } /**
diff --git a/src/java/com/android/internal/telephony/RadioImsProxy.java b/src/java/com/android/internal/telephony/RadioImsProxy.java new file mode 100644 index 0000000..cde2e06 --- /dev/null +++ b/src/java/com/android/internal/telephony/RadioImsProxy.java
@@ -0,0 +1,194 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.telephony; + +import android.os.RemoteException; +import android.telephony.Rlog; + +/** + * A holder for IRadioIms. + * Use getAidl to get IRadioIms and call the AIDL implementations of the HAL APIs. + */ +public class RadioImsProxy extends RadioServiceProxy { + private static final String TAG = "RadioImsProxy"; + private volatile android.hardware.radio.ims.IRadioIms mImsProxy = null; + + /** + * Sets IRadioIms as the AIDL implementation for RadioServiceProxy. + * @param halVersion Radio HAL version. + * @param ims IRadioIms implementation. + * + * @return updated HAL version. + */ + public HalVersion setAidl(HalVersion halVersion, android.hardware.radio.ims.IRadioIms ims) { + HalVersion version = halVersion; + try { + version = RIL.getServiceHalVersion(ims.getInterfaceVersion()); + } catch (RemoteException e) { + Rlog.e(TAG, "setAidl: " + e); + } + mHalVersion = version; + mImsProxy = ims; + mIsAidl = true; + + Rlog.d(TAG, "AIDL initialized mHalVersion=" + mHalVersion); + return mHalVersion; + } + + /** + * Gets the AIDL implementation of RadioImsProxy. + * @return IRadioIms implementation. + */ + public android.hardware.radio.ims.IRadioIms getAidl() { + return mImsProxy; + } + + /** + * Resets RadioImsProxy. + */ + @Override + public void clear() { + super.clear(); + mImsProxy = null; + } + + /** + * Checks whether a RadioIms implementation exists. + * @return true if there is neither a HIDL nor AIDL implementation. + */ + @Override + public boolean isEmpty() { + return mRadioProxy == null && mImsProxy == null; + } + + /** + * No implementation in IRadioIms. + * @throws RemoteException. + */ + @Override + public void responseAcknowledgement() throws RemoteException { + /* Currently, IRadioIms doesn't support the following response types: + * - RadioIndicationType.UNSOLICITED_ACK_EXP + * - RadioResponseType.SOLICITED_ACK_EXP */ + // no-op + } + + /** + * Calls IRadioIms#setSrvccCallInfo. + * @param serial Serial number of request. + * @param srvccCalls The list of call information. + * @throws RemoteException. + */ + public void setSrvccCallInfo(int serial, + android.hardware.radio.ims.SrvccCall[] srvccCalls) throws RemoteException { + if (isEmpty()) return; + if (isAidl()) { + mImsProxy.setSrvccCallInfo(serial, srvccCalls); + } + } + + /** + * Calls IRadioIms#updateImsRegistrationInfo. + * @param serial Serial number of request. + * @param registrationInfo The registration state information. + * @throws RemoteException. + */ + public void updateImsRegistrationInfo(int serial, + android.hardware.radio.ims.ImsRegistration registrationInfo) throws RemoteException { + if (isEmpty()) return; + if (isAidl()) { + mImsProxy.updateImsRegistrationInfo(serial, registrationInfo); + } + } + + /** + * Calls IRadioIms#startImsTraffic. + * @param serial Serial number of request. + * @param token A nonce to identify the request. + * @param trafficType IMS traffic type like registration, voice, video, SMS, emergency, and etc. + * @param accessNetworkType The type of underlying radio access network used. + * @param trafficDirection Indicates whether traffic is originated by mobile originated or + * mobile terminated use case eg. MO/MT call/SMS etc. + * @throws RemoteException. + */ + public void startImsTraffic(int serial, int token, int trafficType, int accessNetworkType, + int trafficDirection) throws RemoteException { + if (isEmpty()) return; + if (isAidl()) { + mImsProxy.startImsTraffic(serial, + token, trafficType, accessNetworkType, trafficDirection); + } + } + + /** + * Calls IRadioIms#stopImsTraffic. + * @param serial Serial number of request. + * @param token The token assigned by startImsTraffic. + * @throws RemoteException. + */ + public void stopImsTraffic(int serial, int token) + throws RemoteException { + if (isEmpty()) return; + if (isAidl()) { + mImsProxy.stopImsTraffic(serial, token); + } + } + + /** + * Calls IRadioIms#triggerEpsFallback. + * @param serial Serial number of request. + * @param reason Specifies the reason for EPS fallback. + * @throws RemoteException. + */ + public void triggerEpsFallback(int serial, int reason) + throws RemoteException { + if (isEmpty()) return; + if (isAidl()) { + mImsProxy.triggerEpsFallback(serial, reason); + } + } + + /** + * Calls IRadioIms#sendAnbrQuery. + * @param serial Serial number of request. + * @param mediaType Media type is used to identify media stream such as audio or video. + * @param direction Direction of this packet stream (e.g. uplink or downlink). + * @param bitsPerSecond The bit rate requested by the opponent UE. + * @throws RemoteException. + */ + public void sendAnbrQuery(int serial, int mediaType, int direction, int bitsPerSecond) + throws RemoteException { + if (isEmpty()) return; + if (isAidl()) { + mImsProxy.sendAnbrQuery(serial, mediaType, direction, bitsPerSecond); + } + } + + /** + * Call IRadioIms#updateImsCallStatus + * @param serial Serial number of request. + * @param imsCalls The list of call status information. + * @throws RemoteException. + */ + public void updateImsCallStatus(int serial, + android.hardware.radio.ims.ImsCall[] imsCalls) throws RemoteException { + if (isEmpty()) return; + if (isAidl()) { + mImsProxy.updateImsCallStatus(serial, imsCalls); + } + } +}
diff --git a/src/java/com/android/internal/telephony/RadioIndication.java b/src/java/com/android/internal/telephony/RadioIndication.java index c7244be..4f75412 100644 --- a/src/java/com/android/internal/telephony/RadioIndication.java +++ b/src/java/com/android/internal/telephony/RadioIndication.java
@@ -16,7 +16,7 @@ package com.android.internal.telephony; -import static android.telephony.TelephonyManager.UNKNOWN_CARRIER_ID; +import static android.telephony.TelephonyManager.HAL_SERVICE_RADIO; import static com.android.internal.telephony.RILConstants.RIL_UNSOL_CALL_RING; import static com.android.internal.telephony.RILConstants.RIL_UNSOL_CARRIER_INFO_IMSI_ENCRYPTION; @@ -136,21 +136,21 @@ * @param radioState android.hardware.radio.V1_0.RadioState */ public void radioStateChanged(int indicationType, int radioState) { - mRil.processIndication(RIL.RADIO_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_RADIO, indicationType); int state = RILUtils.convertHalRadioState(radioState); - if (RIL.RILJ_LOGD) { - mRil.unsljLogMore(RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED, "radioStateChanged: " + - state); + if (mRil.isLogOrTrace()) { + mRil.unsljLogMore( + RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED, "radioStateChanged: " + state); } mRil.setRadioState(state, false /* forceNotifyRegistrants */); } public void callStateChanged(int indicationType) { - mRil.processIndication(RIL.RADIO_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_RADIO, indicationType); - if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED); + if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED); mRil.mCallStateRegistrants.notifyRegistrants(); } @@ -160,18 +160,18 @@ * @param indicationType RadioIndicationType */ public void networkStateChanged(int indicationType) { - mRil.processIndication(RIL.RADIO_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_RADIO, indicationType); - if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_RESPONSE_NETWORK_STATE_CHANGED); + if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_RESPONSE_NETWORK_STATE_CHANGED); mRil.mNetworkStateRegistrants.notifyRegistrants(); } public void newSms(int indicationType, ArrayList<Byte> pdu) { - mRil.processIndication(RIL.RADIO_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_RADIO, indicationType); byte[] pduArray = RILUtils.arrayListToPrimitiveArray(pdu); - if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_RESPONSE_NEW_SMS); + if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_RESPONSE_NEW_SMS); SmsMessageBase smsb = com.android.internal.telephony.gsm.SmsMessage.createFromPdu(pduArray); if (mRil.mGsmSmsRegistrant != null) { @@ -181,10 +181,10 @@ } public void newSmsStatusReport(int indicationType, ArrayList<Byte> pdu) { - mRil.processIndication(RIL.RADIO_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_RADIO, indicationType); byte[] pduArray = RILUtils.arrayListToPrimitiveArray(pdu); - if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_RESPONSE_NEW_SMS_STATUS_REPORT); + if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_RESPONSE_NEW_SMS_STATUS_REPORT); if (mRil.mSmsStatusRegistrant != null) { mRil.mSmsStatusRegistrant.notifyRegistrant(new AsyncResult(null, pduArray, null)); @@ -192,9 +192,9 @@ } public void newSmsOnSim(int indicationType, int recordNumber) { - mRil.processIndication(RIL.RADIO_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_RADIO, indicationType); - if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_RESPONSE_NEW_SMS_ON_SIM); + if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_RESPONSE_NEW_SMS_ON_SIM); if (mRil.mSmsOnSimRegistrant != null) { mRil.mSmsOnSimRegistrant.notifyRegistrant(new AsyncResult(null, recordNumber, null)); @@ -202,9 +202,9 @@ } public void onUssd(int indicationType, int ussdModeType, String msg) { - mRil.processIndication(RIL.RADIO_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_RADIO, indicationType); - if (RIL.RILJ_LOGD) mRil.unsljLogMore(RIL_UNSOL_ON_USSD, "" + ussdModeType); + if (mRil.isLogOrTrace()) mRil.unsljLogMore(RIL_UNSOL_ON_USSD, "" + ussdModeType); // todo: Clean this up with a parcelable class for better self-documentation String[] resp = new String[2]; @@ -216,9 +216,9 @@ } public void nitzTimeReceived(int indicationType, String nitzTime, long receivedTime) { - mRil.processIndication(RIL.RADIO_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_RADIO, indicationType); - if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_NITZ_TIME_RECEIVED, nitzTime); + if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_NITZ_TIME_RECEIVED, nitzTime); // todo: Clean this up with a parcelable class for better self-documentation Object[] result = new Object[2]; @@ -228,7 +228,7 @@ boolean ignoreNitz = TelephonyProperties.ignore_nitz().orElse(false); if (ignoreNitz) { - if (RIL.RILJ_LOGD) mRil.riljLog("ignoring UNSOL_NITZ_TIME_RECEIVED"); + if (mRil.isLogOrTrace()) mRil.riljLog("ignoring UNSOL_NITZ_TIME_RECEIVED"); } else { if (mRil.mNITZTimeRegistrant != null) { mRil.mNITZTimeRegistrant.notifyRegistrant(new AsyncResult (null, result, null)); @@ -241,13 +241,13 @@ public void currentSignalStrength(int indicationType, android.hardware.radio.V1_0.SignalStrength signalStrength) { - mRil.processIndication(RIL.RADIO_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_RADIO, indicationType); SignalStrength ssInitial = RILUtils.convertHalSignalStrength(signalStrength); SignalStrength ss = mRil.fixupSignalStrength10(ssInitial); // Note this is set to "verbose" because it happens frequently - if (RIL.RILJ_LOGV) mRil.unsljLogvRet(RIL_UNSOL_SIGNAL_STRENGTH, ss); + if (mRil.isLogvOrTrace()) mRil.unsljLogvRet(RIL_UNSOL_SIGNAL_STRENGTH, ss); if (mRil.mSignalStrengthRegistrant != null) { mRil.mSignalStrengthRegistrant.notifyRegistrant(new AsyncResult (null, ss, null)); @@ -259,11 +259,11 @@ */ public void currentLinkCapacityEstimate(int indicationType, android.hardware.radio.V1_2.LinkCapacityEstimate lce) { - mRil.processIndication(RIL.RADIO_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_RADIO, indicationType); List<LinkCapacityEstimate> response = RILUtils.convertHalLceData(lce); - if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_LCEDATA_RECV, response); + if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_LCEDATA_RECV, response); if (mRil.mLceInfoRegistrants != null) { mRil.mLceInfoRegistrants.notifyRegistrants(new AsyncResult(null, response, null)); @@ -275,11 +275,11 @@ */ public void currentLinkCapacityEstimate_1_6(int indicationType, android.hardware.radio.V1_6.LinkCapacityEstimate lce) { - mRil.processIndication(RIL.RADIO_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_RADIO, indicationType); List<LinkCapacityEstimate> response = RILUtils.convertHalLceData(lce); - if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_LCEDATA_RECV, response); + if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_LCEDATA_RECV, response); if (mRil.mLceInfoRegistrants != null) { mRil.mLceInfoRegistrants.notifyRegistrants(new AsyncResult(null, response, null)); @@ -291,11 +291,11 @@ */ public void currentSignalStrength_1_2(int indicationType, android.hardware.radio.V1_2.SignalStrength signalStrength) { - mRil.processIndication(RIL.RADIO_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_RADIO, indicationType); SignalStrength ss = RILUtils.convertHalSignalStrength(signalStrength); // Note this is set to "verbose" because it happens frequently - if (RIL.RILJ_LOGV) mRil.unsljLogvRet(RIL_UNSOL_SIGNAL_STRENGTH, ss); + if (mRil.isLogvOrTrace()) mRil.unsljLogvRet(RIL_UNSOL_SIGNAL_STRENGTH, ss); if (mRil.mSignalStrengthRegistrant != null) { mRil.mSignalStrengthRegistrant.notifyRegistrant(new AsyncResult(null, ss, null)); @@ -308,11 +308,11 @@ public void currentSignalStrength_1_4(int indicationType, android.hardware.radio.V1_4.SignalStrength signalStrength) { - mRil.processIndication(RIL.RADIO_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_RADIO, indicationType); SignalStrength ss = RILUtils.convertHalSignalStrength(signalStrength); - if (RIL.RILJ_LOGV) mRil.unsljLogvRet(RIL_UNSOL_SIGNAL_STRENGTH, ss); + if (mRil.isLogvOrTrace()) mRil.unsljLogvRet(RIL_UNSOL_SIGNAL_STRENGTH, ss); if (mRil.mSignalStrengthRegistrant != null) { mRil.mSignalStrengthRegistrant.notifyRegistrant(new AsyncResult(null, ss, null)); @@ -325,11 +325,11 @@ public void currentSignalStrength_1_6(int indicationType, android.hardware.radio.V1_6.SignalStrength signalStrength) { - mRil.processIndication(RIL.RADIO_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_RADIO, indicationType); SignalStrength ss = RILUtils.convertHalSignalStrength(signalStrength); - if (RIL.RILJ_LOGV) mRil.unsljLogvRet(RIL_UNSOL_SIGNAL_STRENGTH, ss); + if (mRil.isLogvOrTrace()) mRil.unsljLogvRet(RIL_UNSOL_SIGNAL_STRENGTH, ss); if (mRil.mSignalStrengthRegistrant != null) { mRil.mSignalStrengthRegistrant.notifyRegistrant(new AsyncResult(null, ss, null)); @@ -341,7 +341,7 @@ */ public void currentPhysicalChannelConfigs_1_4(int indicationType, ArrayList<android.hardware.radio.V1_4.PhysicalChannelConfig> configs) { - mRil.processIndication(RIL.RADIO_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_RADIO, indicationType); physicalChannelConfigsIndication(configs); } @@ -350,7 +350,7 @@ */ public void currentPhysicalChannelConfigs_1_6(int indicationType, ArrayList<android.hardware.radio.V1_6.PhysicalChannelConfig> configs) { - mRil.processIndication(RIL.RADIO_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_RADIO, indicationType); physicalChannelConfigsIndication(configs); } @@ -359,7 +359,7 @@ */ public void currentPhysicalChannelConfigs(int indicationType, ArrayList<android.hardware.radio.V1_2.PhysicalChannelConfig> configs) { - mRil.processIndication(RIL.RADIO_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_RADIO, indicationType); physicalChannelConfigsIndication(configs); } @@ -368,7 +368,7 @@ */ public void currentEmergencyNumberList(int indicationType, ArrayList<android.hardware.radio.V1_4.EmergencyNumber> emergencyNumberList) { - mRil.processIndication(RIL.RADIO_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_RADIO, indicationType); List<EmergencyNumber> response = new ArrayList<>(emergencyNumberList.size()); for (android.hardware.radio.V1_4.EmergencyNumber emergencyNumberHal @@ -380,7 +380,7 @@ response.add(emergencyNumber); } - if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_EMERGENCY_NUMBER_LIST, response); + if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_EMERGENCY_NUMBER_LIST, response); // Cache emergency number list from last indication. mRil.cacheEmergencyNumberListIndication(response); @@ -422,7 +422,7 @@ } public void suppSvcNotify(int indicationType, SuppSvcNotification suppSvcNotification) { - mRil.processIndication(RIL.RADIO_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_RADIO, indicationType); SuppServiceNotification notification = new SuppServiceNotification(); notification.notificationType = suppSvcNotification.isMT ? 1 : 0; @@ -431,7 +431,9 @@ notification.type = suppSvcNotification.type; notification.number = suppSvcNotification.number; - if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_SUPP_SVC_NOTIFICATION, notification); + if (mRil.isLogOrTrace()) { + mRil.unsljLogRet(RIL_UNSOL_SUPP_SVC_NOTIFICATION, notification); + } if (mRil.mSsnRegistrant != null) { mRil.mSsnRegistrant.notifyRegistrant(new AsyncResult (null, notification, null)); @@ -439,9 +441,9 @@ } public void stkSessionEnd(int indicationType) { - mRil.processIndication(RIL.RADIO_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_RADIO, indicationType); - if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_STK_SESSION_END); + if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_STK_SESSION_END); if (mRil.mCatSessionEndRegistrant != null) { mRil.mCatSessionEndRegistrant.notifyRegistrant(new AsyncResult (null, null, null)); @@ -449,9 +451,9 @@ } public void stkProactiveCommand(int indicationType, String cmd) { - mRil.processIndication(RIL.RADIO_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_RADIO, indicationType); - if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_STK_PROACTIVE_COMMAND); + if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_STK_PROACTIVE_COMMAND); if (mRil.mCatProCmdRegistrant != null) { mRil.mCatProCmdRegistrant.notifyRegistrant(new AsyncResult (null, cmd, null)); @@ -459,9 +461,9 @@ } public void stkEventNotify(int indicationType, String cmd) { - mRil.processIndication(RIL.RADIO_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_RADIO, indicationType); - if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_STK_EVENT_NOTIFY); + if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_STK_EVENT_NOTIFY); if (mRil.mCatEventRegistrant != null) { mRil.mCatEventRegistrant.notifyRegistrant(new AsyncResult (null, cmd, null)); @@ -469,9 +471,9 @@ } public void stkCallSetup(int indicationType, long timeout) { - mRil.processIndication(RIL.RADIO_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_RADIO, indicationType); - if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_STK_CALL_SETUP, timeout); + if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_STK_CALL_SETUP, timeout); if (mRil.mCatCallSetUpRegistrant != null) { mRil.mCatCallSetUpRegistrant.notifyRegistrant(new AsyncResult (null, timeout, null)); @@ -479,9 +481,9 @@ } public void simSmsStorageFull(int indicationType) { - mRil.processIndication(RIL.RADIO_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_RADIO, indicationType); - if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_SIM_SMS_STORAGE_FULL); + if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_SIM_SMS_STORAGE_FULL); if (mRil.mIccSmsFullRegistrant != null) { mRil.mIccSmsFullRegistrant.notifyRegistrant(); @@ -489,20 +491,20 @@ } public void simRefresh(int indicationType, SimRefreshResult refreshResult) { - mRil.processIndication(RIL.RADIO_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_RADIO, indicationType); IccRefreshResponse response = new IccRefreshResponse(); response.refreshResult = refreshResult.type; response.efId = refreshResult.efId; response.aid = refreshResult.aid; - if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_SIM_REFRESH, response); + if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_SIM_REFRESH, response); mRil.mIccRefreshRegistrants.notifyRegistrants(new AsyncResult (null, response, null)); } public void callRing(int indicationType, boolean isGsm, CdmaSignalInfoRecord record) { - mRil.processIndication(RIL.RADIO_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_RADIO, indicationType); char response[] = null; @@ -517,7 +519,7 @@ mRil.writeMetricsCallRing(response); } - if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_CALL_RING, response); + if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_CALL_RING, response); if (mRil.mRingRegistrant != null) { mRil.mRingRegistrant.notifyRegistrant(new AsyncResult (null, response, null)); @@ -525,17 +527,17 @@ } public void simStatusChanged(int indicationType) { - mRil.processIndication(RIL.RADIO_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_RADIO, indicationType); - if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_RESPONSE_SIM_STATUS_CHANGED); + if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_RESPONSE_SIM_STATUS_CHANGED); mRil.mIccStatusChangedRegistrants.notifyRegistrants(); } public void cdmaNewSms(int indicationType, CdmaSmsMessage msg) { - mRil.processIndication(RIL.RADIO_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_RADIO, indicationType); - if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_RESPONSE_CDMA_NEW_SMS); + if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_RESPONSE_CDMA_NEW_SMS); SmsMessage sms = new SmsMessage(RILUtils.convertHalCdmaSmsMessage(msg)); if (mRil.mCdmaSmsRegistrant != null) { @@ -544,12 +546,12 @@ } public void newBroadcastSms(int indicationType, ArrayList<Byte> data) { - mRil.processIndication(RIL.RADIO_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_RADIO, indicationType); byte[] response = RILUtils.arrayListToPrimitiveArray(data); - if (RIL.RILJ_LOGD) { - mRil.unsljLogvRet(RIL_UNSOL_RESPONSE_NEW_BROADCAST_SMS, - IccUtils.bytesToHexString(response)); + if (mRil.isLogOrTrace()) { + mRil.unsljLogvRet( + RIL_UNSOL_RESPONSE_NEW_BROADCAST_SMS, IccUtils.bytesToHexString(response)); } if (mRil.mGsmBroadcastSmsRegistrant != null) { @@ -558,9 +560,9 @@ } public void cdmaRuimSmsStorageFull(int indicationType) { - mRil.processIndication(RIL.RADIO_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_RADIO, indicationType); - if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_CDMA_RUIM_SMS_STORAGE_FULL); + if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_CDMA_RUIM_SMS_STORAGE_FULL); if (mRil.mIccSmsFullRegistrant != null) { mRil.mIccSmsFullRegistrant.notifyRegistrant(); @@ -568,9 +570,9 @@ } public void restrictedStateChanged(int indicationType, int state) { - mRil.processIndication(RIL.RADIO_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_RADIO, indicationType); - if (RIL.RILJ_LOGD) mRil.unsljLogvRet(RIL_UNSOL_RESTRICTED_STATE_CHANGED, state); + if (mRil.isLogOrTrace()) mRil.unsljLogvRet(RIL_UNSOL_RESTRICTED_STATE_CHANGED, state); if (mRil.mRestrictedStateRegistrant != null) { mRil.mRestrictedStateRegistrant.notifyRegistrant(new AsyncResult (null, state, null)); @@ -578,9 +580,9 @@ } public void enterEmergencyCallbackMode(int indicationType) { - mRil.processIndication(RIL.RADIO_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_RADIO, indicationType); - if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_ENTER_EMERGENCY_CALLBACK_MODE); + if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_ENTER_EMERGENCY_CALLBACK_MODE); if (mRil.mEmergencyCallbackModeRegistrant != null) { mRil.mEmergencyCallbackModeRegistrant.notifyRegistrant(); @@ -588,7 +590,7 @@ } public void cdmaCallWaiting(int indicationType, CdmaCallWaiting callWaitingRecord) { - mRil.processIndication(RIL.RADIO_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_RADIO, indicationType); // todo: create a CdmaCallWaitingNotification constructor that takes in these fields to make // sure no fields are missing @@ -605,26 +607,28 @@ notification.numberType = callWaitingRecord.numberType; notification.numberPlan = callWaitingRecord.numberPlan; - if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_CDMA_CALL_WAITING, notification); + if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_CDMA_CALL_WAITING, notification); mRil.mCallWaitingInfoRegistrants.notifyRegistrants( new AsyncResult (null, notification, null)); } public void cdmaOtaProvisionStatus(int indicationType, int status) { - mRil.processIndication(RIL.RADIO_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_RADIO, indicationType); int response[] = new int[1]; response[0] = status; - if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_CDMA_OTA_PROVISION_STATUS, response); + if (mRil.isLogOrTrace()) { + mRil.unsljLogRet(RIL_UNSOL_CDMA_OTA_PROVISION_STATUS, response); + } mRil.mOtaProvisionRegistrants.notifyRegistrants(new AsyncResult (null, response, null)); } public void cdmaInfoRec(int indicationType, android.hardware.radio.V1_0.CdmaInformationRecords records) { - mRil.processIndication(RIL.RADIO_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_RADIO, indicationType); int numberOfInfoRecs = records.infoRec.size(); for (int i = 0; i < numberOfInfoRecs; i++) { @@ -712,63 +716,67 @@ + CdmaInformationRecords.idToString(id) + " "); } - if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_CDMA_INFO_REC, cdmaInformationRecords); + if (mRil.isLogOrTrace()) { + mRil.unsljLogRet(RIL_UNSOL_CDMA_INFO_REC, cdmaInformationRecords); + } mRil.notifyRegistrantsCdmaInfoRec(cdmaInformationRecords); } } public void indicateRingbackTone(int indicationType, boolean start) { - mRil.processIndication(RIL.RADIO_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_RADIO, indicationType); - if (RIL.RILJ_LOGD) mRil.unsljLogvRet(RIL_UNSOL_RINGBACK_TONE, start); + if (mRil.isLogOrTrace()) mRil.unsljLogvRet(RIL_UNSOL_RINGBACK_TONE, start); mRil.mRingbackToneRegistrants.notifyRegistrants(new AsyncResult(null, start, null)); } public void resendIncallMute(int indicationType) { - mRil.processIndication(RIL.RADIO_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_RADIO, indicationType); - if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_RESEND_INCALL_MUTE); + if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_RESEND_INCALL_MUTE); mRil.mResendIncallMuteRegistrants.notifyRegistrants(); } public void cdmaSubscriptionSourceChanged(int indicationType, int cdmaSource) { - mRil.processIndication(RIL.RADIO_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_RADIO, indicationType); int response[] = new int[1]; response[0] = cdmaSource; - if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_CDMA_SUBSCRIPTION_SOURCE_CHANGED, response); + if (mRil.isLogOrTrace()) { + mRil.unsljLogRet(RIL_UNSOL_CDMA_SUBSCRIPTION_SOURCE_CHANGED, response); + } mRil.mCdmaSubscriptionChangedRegistrants.notifyRegistrants( new AsyncResult (null, response, null)); } public void cdmaPrlChanged(int indicationType, int version) { - mRil.processIndication(RIL.RADIO_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_RADIO, indicationType); int response[] = new int[1]; response[0] = version; - if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_CDMA_PRL_CHANGED, response); + if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_CDMA_PRL_CHANGED, response); mRil.mCdmaPrlChangedRegistrants.notifyRegistrants( new AsyncResult (null, response, null)); } public void exitEmergencyCallbackMode(int indicationType) { - mRil.processIndication(RIL.RADIO_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_RADIO, indicationType); - if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_EXIT_EMERGENCY_CALLBACK_MODE); + if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_EXIT_EMERGENCY_CALLBACK_MODE); mRil.mExitEmergencyCallbackModeRegistrants.notifyRegistrants(); } public void rilConnected(int indicationType) { - mRil.processIndication(RIL.RADIO_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_RADIO, indicationType); - if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_RIL_CONNECTED); + if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_RIL_CONNECTED); // Initial conditions mRil.setRadioPower(false, null); @@ -779,12 +787,14 @@ } public void voiceRadioTechChanged(int indicationType, int rat) { - mRil.processIndication(RIL.RADIO_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_RADIO, indicationType); int response[] = new int[1]; response[0] = rat; - if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_VOICE_RADIO_TECH_CHANGED, response); + if (mRil.isLogOrTrace()) { + mRil.unsljLogRet(RIL_UNSOL_VOICE_RADIO_TECH_CHANGED, response); + } mRil.mVoiceRadioTechChangedRegistrants.notifyRegistrants( new AsyncResult (null, response, null)); @@ -793,49 +803,49 @@ /** Get unsolicited message for cellInfoList */ public void cellInfoList(int indicationType, ArrayList<android.hardware.radio.V1_0.CellInfo> records) { - mRil.processIndication(RIL.RADIO_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_RADIO, indicationType); responseCellInfoList(records); } /** Get unsolicited message for cellInfoList using HAL V1_2 */ public void cellInfoList_1_2(int indicationType, ArrayList<android.hardware.radio.V1_2.CellInfo> records) { - mRil.processIndication(RIL.RADIO_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_RADIO, indicationType); responseCellInfoList(records); } /** Get unsolicited message for cellInfoList using HAL V1_4 */ public void cellInfoList_1_4(int indicationType, ArrayList<android.hardware.radio.V1_4.CellInfo> records) { - mRil.processIndication(RIL.RADIO_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_RADIO, indicationType); responseCellInfoList(records); } /** Get unsolicited message for cellInfoList using HAL V1_5 */ public void cellInfoList_1_5(int indicationType, ArrayList<android.hardware.radio.V1_5.CellInfo> records) { - mRil.processIndication(RIL.RADIO_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_RADIO, indicationType); responseCellInfoList(records); } /** Get unsolicited message for cellInfoList using HAL V1_5 */ public void cellInfoList_1_6(int indicationType, ArrayList<android.hardware.radio.V1_6.CellInfo> records) { - mRil.processIndication(RIL.RADIO_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_RADIO, indicationType); responseCellInfoList(records); } private void responseCellInfoList(ArrayList<? extends Object> records) { ArrayList<CellInfo> response = RILUtils.convertHalCellInfoList((ArrayList<Object>) records); - if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_CELL_INFO_LIST, response); + if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_CELL_INFO_LIST, response); mRil.mRilCellInfoListRegistrants.notifyRegistrants(new AsyncResult(null, response, null)); } /** Get unsolicited message for uicc applications enablement changes. */ public void uiccApplicationsEnablementChanged(int indicationType, boolean enabled) { - mRil.processIndication(RIL.RADIO_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_RADIO, indicationType); - if (RIL.RILJ_LOGD) { + if (mRil.isLogOrTrace()) { mRil.unsljLogRet(RIL_UNSOL_UICC_APPLICATIONS_ENABLEMENT_CHANGED, enabled); } @@ -873,32 +883,34 @@ } public void imsNetworkStateChanged(int indicationType) { - mRil.processIndication(RIL.RADIO_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_RADIO, indicationType); - if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_RESPONSE_IMS_NETWORK_STATE_CHANGED); + if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_RESPONSE_IMS_NETWORK_STATE_CHANGED); mRil.mImsNetworkStateChangedRegistrants.notifyRegistrants(); } public void subscriptionStatusChanged(int indicationType, boolean activate) { - mRil.processIndication(RIL.RADIO_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_RADIO, indicationType); int response[] = new int[1]; response[0] = activate ? 1 : 0; - if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_UICC_SUBSCRIPTION_STATUS_CHANGED, response); + if (mRil.isLogOrTrace()) { + mRil.unsljLogRet(RIL_UNSOL_UICC_SUBSCRIPTION_STATUS_CHANGED, response); + } mRil.mSubscriptionStatusRegistrants.notifyRegistrants( new AsyncResult (null, response, null)); } public void srvccStateNotify(int indicationType, int state) { - mRil.processIndication(RIL.RADIO_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_RADIO, indicationType); int response[] = new int[1]; response[0] = state; - if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_SRVCC_STATE_NOTIFY, response); + if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_SRVCC_STATE_NOTIFY, response); mRil.writeMetricsSrvcc(state); @@ -909,11 +921,11 @@ public void hardwareConfigChanged( int indicationType, ArrayList<android.hardware.radio.V1_0.HardwareConfig> configs) { - mRil.processIndication(RIL.RADIO_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_RADIO, indicationType); ArrayList<HardwareConfig> response = RILUtils.convertHalHardwareConfigList(configs); - if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_HARDWARE_CONFIG_CHANGED, response); + if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_HARDWARE_CONFIG_CHANGED, response); mRil.mHardwareConfigChangeRegistrants.notifyRegistrants( new AsyncResult (null, response, null)); @@ -921,18 +933,18 @@ public void radioCapabilityIndication(int indicationType, android.hardware.radio.V1_0.RadioCapability rc) { - mRil.processIndication(RIL.RADIO_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_RADIO, indicationType); RadioCapability response = RILUtils.convertHalRadioCapability(rc, mRil); - if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_RADIO_CAPABILITY, response); + if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_RADIO_CAPABILITY, response); mRil.mPhoneRadioCapabilityChangedRegistrants.notifyRegistrants( new AsyncResult (null, response, null)); } public void onSupplementaryServiceIndication(int indicationType, StkCcUnsolSsResult ss) { - mRil.processIndication(RIL.RADIO_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_RADIO, indicationType); int num; SsData ssData = new SsData(); @@ -972,7 +984,7 @@ } } - if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_ON_SS, ssData); + if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_ON_SS, ssData); if (mRil.mSsRegistrant != null) { mRil.mSsRegistrant.notifyRegistrant(new AsyncResult(null, ssData, null)); @@ -980,9 +992,9 @@ } public void stkCallControlAlphaNotify(int indicationType, String alpha) { - mRil.processIndication(RIL.RADIO_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_RADIO, indicationType); - if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_STK_CC_ALPHA_NOTIFY, alpha); + if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_STK_CC_ALPHA_NOTIFY, alpha); if (mRil.mCatCcAlphaRegistrant != null) { mRil.mCatCcAlphaRegistrant.notifyRegistrant(new AsyncResult (null, alpha, null)); @@ -990,11 +1002,11 @@ } public void lceData(int indicationType, LceDataInfo lce) { - mRil.processIndication(RIL.RADIO_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_RADIO, indicationType); List<LinkCapacityEstimate> response = RILUtils.convertHalLceData(lce); - if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_LCEDATA_RECV, response); + if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_LCEDATA_RECV, response); if (mRil.mLceInfoRegistrants != null) { mRil.mLceInfoRegistrants.notifyRegistrants(new AsyncResult(null, response, null)); @@ -1002,20 +1014,20 @@ } public void pcoData(int indicationType, PcoDataInfo pco) { - mRil.processIndication(RIL.RADIO_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_RADIO, indicationType); PcoData response = new PcoData(pco.cid, pco.bearerProto, pco.pcoId, RILUtils.arrayListToPrimitiveArray(pco.contents)); - if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_PCO_DATA, response); + if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_PCO_DATA, response); mRil.mPcoDataRegistrants.notifyRegistrants(new AsyncResult(null, response, null)); } public void modemReset(int indicationType, String reason) { - mRil.processIndication(RIL.RADIO_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_RADIO, indicationType); - if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_MODEM_RESTART, reason); + if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_MODEM_RESTART, reason); mRil.writeMetricsModemRestartEvent(reason); mRil.mModemResetRegistrants.notifyRegistrants(new AsyncResult(null, reason, null)); @@ -1026,9 +1038,11 @@ * @param indicationType RadioIndicationType */ public void carrierInfoForImsiEncryption(int indicationType) { - mRil.processIndication(RIL.RADIO_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_RADIO, indicationType); - if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_CARRIER_INFO_IMSI_ENCRYPTION, null); + if (mRil.isLogOrTrace()) { + mRil.unsljLogRet(RIL_UNSOL_CARRIER_INFO_IMSI_ENCRYPTION, null); + } mRil.mCarrierInfoForImsiEncryptionRegistrants.notifyRegistrants( new AsyncResult(null, null, null)); @@ -1041,11 +1055,12 @@ */ public void keepaliveStatus( int indicationType, android.hardware.radio.V1_1.KeepaliveStatus halStatus) { - mRil.processIndication(RIL.RADIO_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_RADIO, indicationType); - if (RIL.RILJ_LOGD) { - mRil.unsljLogRet(RIL_UNSOL_KEEPALIVE_STATUS, - "handle=" + halStatus.sessionHandle + " code=" + halStatus.code); + if (mRil.isLogOrTrace()) { + mRil.unsljLogRet( + RIL_UNSOL_KEEPALIVE_STATUS, + "handle=" + halStatus.sessionHandle + " code=" + halStatus.code); } KeepaliveStatus ks = new KeepaliveStatus( @@ -1059,9 +1074,9 @@ * @param indicationType RadioIndicationType */ public void simPhonebookChanged(int indicationType) { - mRil.processIndication(RIL.RADIO_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_RADIO, indicationType); - if (RIL.RILJ_LOGD) { + if (mRil.isLogOrTrace()) { mRil.unsljLog(RIL_UNSOL_RESPONSE_SIM_PHONEBOOK_CHANGED); } @@ -1076,7 +1091,7 @@ */ public void simPhonebookRecordsReceived(int indicationType, byte status, ArrayList<PhonebookRecordInfo> records) { - mRil.processIndication(RIL.RADIO_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_RADIO, indicationType); List<SimPhonebookRecord> simPhonebookRecords = new ArrayList<>(); @@ -1084,8 +1099,9 @@ simPhonebookRecords.add(RILUtils.convertHalPhonebookRecordInfo(record)); } - if (RIL.RILJ_LOGD) { - mRil.unsljLogRet(RIL_UNSOL_RESPONSE_SIM_PHONEBOOK_RECORDS_RECEIVED, + if (mRil.isLogOrTrace()) { + mRil.unsljLogRet( + RIL_UNSOL_RESPONSE_SIM_PHONEBOOK_RECORDS_RECEIVED, "status = " + status + " received " + records.size() + " records"); } @@ -1108,7 +1124,7 @@ android.hardware.radio.V1_5.CellIdentity cellIdentity, String chosenPlmn, @NetworkRegistrationInfo.Domain int domain, int causeCode, int additionalCauseCode) { - mRil.processIndication(RIL.RADIO_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_RADIO, indicationType); CellIdentity ci = RILUtils.convertHalCellIdentity(cellIdentity); if (ci == null || TextUtils.isEmpty(chosenPlmn) @@ -1116,7 +1132,7 @@ || (domain & ~NetworkRegistrationInfo.DOMAIN_CS_PS) != 0 || causeCode < 0 || additionalCauseCode < 0 || (causeCode == Integer.MAX_VALUE && additionalCauseCode == Integer.MAX_VALUE)) { - reportAnomaly( + AnomalyReporter.reportAnomaly( UUID.fromString("f16e5703-6105-4341-9eb3-e68189156eb4"), "Invalid registrationFailed indication"); @@ -1139,10 +1155,10 @@ public void barringInfoChanged(int indicationType, android.hardware.radio.V1_5.CellIdentity cellIdentity, ArrayList<android.hardware.radio.V1_5.BarringInfo> barringInfos) { - mRil.processIndication(RIL.RADIO_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_RADIO, indicationType); if (cellIdentity == null || barringInfos == null) { - reportAnomaly( + AnomalyReporter.reportAnomaly( UUID.fromString("645b16bb-c930-4c1c-9c5d-568696542e05"), "Invalid barringInfoChanged indication"); @@ -1208,22 +1224,29 @@ android.hardware.radio.V1_6.PhysicalChannelConfig config = (android.hardware.radio.V1_6.PhysicalChannelConfig) obj; PhysicalChannelConfig.Builder builder = new PhysicalChannelConfig.Builder(); + int band = PhysicalChannelConfig.BAND_UNKNOWN; switch (config.band.getDiscriminator()) { case Band.hidl_discriminator.geranBand: - builder.setBand(config.band.geranBand()); + band = config.band.geranBand(); break; case Band.hidl_discriminator.utranBand: - builder.setBand(config.band.utranBand()); + band = config.band.utranBand(); break; case Band.hidl_discriminator.eutranBand: - builder.setBand(config.band.eutranBand()); + band = config.band.eutranBand(); break; case Band.hidl_discriminator.ngranBand: - builder.setBand(config.band.ngranBand()); + band = config.band.ngranBand(); break; default: mRil.riljLoge("Unsupported band " + config.band.getDiscriminator()); } + if (band == PhysicalChannelConfig.BAND_UNKNOWN) { + mRil.riljLoge("Unsupported unknown band."); + return; + } else { + builder.setBand(band); + } response.add(builder.setCellConnectionStatus( RILUtils.convertHalCellConnectionStatus(config.status)) .setDownlinkChannelNumber(config.downlinkChannelNumber) @@ -1240,13 +1263,14 @@ } } } catch (IllegalArgumentException iae) { - reportAnomaly(UUID.fromString("918f0970-9aa9-4bcd-a28e-e49a83fe77d5"), - "RIL reported invalid PCC (HIDL)"); + AnomalyReporter.reportAnomaly( + UUID.fromString("918f0970-9aa9-4bcd-a28e-e49a83fe77d5"), + "RIL reported invalid PCC (HIDL)"); mRil.riljLoge("Invalid PhysicalChannelConfig " + iae); return; } - if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_PHYSICAL_CHANNEL_CONFIG, response); + if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_PHYSICAL_CHANNEL_CONFIG, response); mRil.mPhysicalChannelConfigurationRegistrants.notifyRegistrants( new AsyncResult(null, response, null)); @@ -1254,63 +1278,63 @@ private void responseNetworkScan(int indicationType, android.hardware.radio.V1_1.NetworkScanResult result) { - mRil.processIndication(RIL.RADIO_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_RADIO, indicationType); ArrayList<CellInfo> cellInfos = RILUtils.convertHalCellInfoList(new ArrayList<>(result.networkInfos)); NetworkScanResult nsr = new NetworkScanResult(result.status, result.error, cellInfos); - if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_NETWORK_SCAN_RESULT, nsr); + if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_NETWORK_SCAN_RESULT, nsr); mRil.mRilNetworkScanResultRegistrants.notifyRegistrants(new AsyncResult(null, nsr, null)); } private void responseNetworkScan_1_2(int indicationType, android.hardware.radio.V1_2.NetworkScanResult result) { - mRil.processIndication(RIL.RADIO_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_RADIO, indicationType); ArrayList<CellInfo> cellInfos = RILUtils.convertHalCellInfoList(new ArrayList<>(result.networkInfos)); NetworkScanResult nsr = new NetworkScanResult(result.status, result.error, cellInfos); - if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_NETWORK_SCAN_RESULT, nsr); + if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_NETWORK_SCAN_RESULT, nsr); mRil.mRilNetworkScanResultRegistrants.notifyRegistrants(new AsyncResult(null, nsr, null)); } private void responseNetworkScan_1_4(int indicationType, android.hardware.radio.V1_4.NetworkScanResult result) { - mRil.processIndication(RIL.RADIO_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_RADIO, indicationType); ArrayList<CellInfo> cellInfos = RILUtils.convertHalCellInfoList(new ArrayList<>(result.networkInfos)); NetworkScanResult nsr = new NetworkScanResult(result.status, result.error, cellInfos); - if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_NETWORK_SCAN_RESULT, nsr); + if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_NETWORK_SCAN_RESULT, nsr); mRil.mRilNetworkScanResultRegistrants.notifyRegistrants(new AsyncResult(null, nsr, null)); } private void responseNetworkScan_1_5(int indicationType, android.hardware.radio.V1_5.NetworkScanResult result) { - mRil.processIndication(RIL.RADIO_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_RADIO, indicationType); ArrayList<CellInfo> cellInfos = RILUtils.convertHalCellInfoList(new ArrayList<>(result.networkInfos)); NetworkScanResult nsr = new NetworkScanResult(result.status, result.error, cellInfos); - if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_NETWORK_SCAN_RESULT, nsr); + if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_NETWORK_SCAN_RESULT, nsr); mRil.mRilNetworkScanResultRegistrants.notifyRegistrants(new AsyncResult(null, nsr, null)); } private void responseNetworkScan_1_6(int indicationType, android.hardware.radio.V1_6.NetworkScanResult result) { - mRil.processIndication(RIL.RADIO_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_RADIO, indicationType); ArrayList<CellInfo> cellInfos = RILUtils.convertHalCellInfoList(new ArrayList<>(result.networkInfos)); NetworkScanResult nsr = new NetworkScanResult(result.status, result.error, cellInfos); - if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_NETWORK_SCAN_RESULT, nsr); + if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_NETWORK_SCAN_RESULT, nsr); mRil.mRilNetworkScanResultRegistrants.notifyRegistrants(new AsyncResult(null, nsr, null)); } private void responseDataCallListChanged(int indicationType, List<?> dcList) { - mRil.processIndication(RIL.RADIO_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_RADIO, indicationType); - if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_DATA_CALL_LIST_CHANGED, dcList); + if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_DATA_CALL_LIST_CHANGED, dcList); ArrayList<DataCallResponse> response = RILUtils.convertHalDataCallResultList(dcList); mRil.mDataCallListChangedRegistrants.notifyRegistrants( @@ -1318,17 +1342,11 @@ } private void responseApnUnthrottled(int indicationType, String apn) { - mRil.processIndication(RIL.RADIO_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_RADIO, indicationType); - if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_UNTHROTTLE_APN, apn); + if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_UNTHROTTLE_APN, apn); mRil.mApnUnthrottledRegistrants.notifyRegistrants( new AsyncResult(null, apn, null)); } - - private void reportAnomaly(UUID uuid, String msg) { - Phone phone = mRil.mPhoneId == null ? null : PhoneFactory.getPhone(mRil.mPhoneId); - int carrierId = phone == null ? UNKNOWN_CARRIER_ID : phone.getCarrierId(); - AnomalyReporter.reportAnomaly(uuid, msg, carrierId); - } }
diff --git a/src/java/com/android/internal/telephony/RadioInterfaceCapabilityController.java b/src/java/com/android/internal/telephony/RadioInterfaceCapabilityController.java index 04c6b54..bab4d12 100644 --- a/src/java/com/android/internal/telephony/RadioInterfaceCapabilityController.java +++ b/src/java/com/android/internal/telephony/RadioInterfaceCapabilityController.java
@@ -28,6 +28,8 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.telephony.Rlog; +import java.io.FileDescriptor; +import java.io.PrintWriter; import java.util.Collections; import java.util.Set; @@ -164,6 +166,13 @@ } } + /** + * Dump the fields of the instance + */ + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + pw.println("mRadioConfig=" + mRadioConfig); + } + private static void log(final String s) { Rlog.d(LOG_TAG, s); }
diff --git a/src/java/com/android/internal/telephony/RadioMessagingProxy.java b/src/java/com/android/internal/telephony/RadioMessagingProxy.java index e68e957..69ccf36 100644 --- a/src/java/com/android/internal/telephony/RadioMessagingProxy.java +++ b/src/java/com/android/internal/telephony/RadioMessagingProxy.java
@@ -36,13 +36,23 @@ * Set IRadioMessaging as the AIDL implementation for RadioServiceProxy * @param halVersion Radio HAL version * @param messaging IRadioMessaging implementation + * + * @return updated HAL version */ - public void setAidl(HalVersion halVersion, + public HalVersion setAidl(HalVersion halVersion, android.hardware.radio.messaging.IRadioMessaging messaging) { - mHalVersion = halVersion; + HalVersion version = halVersion; + try { + version = RIL.getServiceHalVersion(messaging.getInterfaceVersion()); + } catch (RemoteException e) { + Rlog.e(TAG, "setAidl: " + e); + } + mHalVersion = version; mMessagingProxy = messaging; mIsAidl = true; - Rlog.d(TAG, "AIDL initialized"); + + Rlog.d(TAG, "AIDL initialized mHalVersion=" + mHalVersion); + return mHalVersion; } /**
diff --git a/src/java/com/android/internal/telephony/RadioModemProxy.java b/src/java/com/android/internal/telephony/RadioModemProxy.java index 7aaf727..4178293 100644 --- a/src/java/com/android/internal/telephony/RadioModemProxy.java +++ b/src/java/com/android/internal/telephony/RadioModemProxy.java
@@ -31,13 +31,23 @@ * Set IRadioModem as the AIDL implementation for RadioServiceProxy * @param halVersion Radio HAL version * @param modem IRadioModem implementation + * + * @return updated HAL version */ - public void setAidl(HalVersion halVersion, + public HalVersion setAidl(HalVersion halVersion, android.hardware.radio.modem.IRadioModem modem) { - mHalVersion = halVersion; + HalVersion version = halVersion; + try { + version = RIL.getServiceHalVersion(modem.getInterfaceVersion()); + } catch (RemoteException e) { + Rlog.e(TAG, "setAidl: " + e); + } + mHalVersion = version; mModemProxy = modem; mIsAidl = true; - Rlog.d(TAG, "AIDL initialized"); + + Rlog.d(TAG, "AIDL initialized mHalVersion=" + mHalVersion); + return mHalVersion; } /** @@ -110,6 +120,19 @@ } /** + * Call IRadioModem#getImei + * + * @param serial Serial number of request + * @throws RemoteException + */ + public void getImei(int serial) throws RemoteException { + if (isEmpty()) return; + if (isAidl()) { + mModemProxy.getImei(serial); + } + } + + /** * Call IRadioModem#getHardwareConfig * @param serial Serial number of request * @throws RemoteException
diff --git a/src/java/com/android/internal/telephony/RadioNetworkProxy.java b/src/java/com/android/internal/telephony/RadioNetworkProxy.java index b881035..246c2e0 100644 --- a/src/java/com/android/internal/telephony/RadioNetworkProxy.java +++ b/src/java/com/android/internal/telephony/RadioNetworkProxy.java
@@ -65,13 +65,23 @@ * Set IRadioNetwork as the AIDL implementation for RadioServiceProxy * @param halVersion Radio HAL version * @param network IRadioNetwork implementation + * + * @return updated HAL version */ - public void setAidl(HalVersion halVersion, + public HalVersion setAidl(HalVersion halVersion, android.hardware.radio.network.IRadioNetwork network) { - mHalVersion = halVersion; + HalVersion version = halVersion; + try { + version = RIL.getServiceHalVersion(network.getInterfaceVersion()); + } catch (RemoteException e) { + Rlog.e(TAG, "setAidl: " + e); + } + mHalVersion = version; mNetworkProxy = network; mIsAidl = true; - Rlog.d(TAG, "AIDL initialized"); + + Rlog.d(TAG, "AIDL initialized mHalVersion=" + mHalVersion); + return mHalVersion; } /** @@ -828,4 +838,127 @@ } // Only supported on AIDL. } + + /** + * Set the Emergency Mode + * + * @param serial Serial number of the request. + * @param emcModeType Defines the radio emergency mode type. + * @throws RemoteException + */ + public void setEmergencyMode(int serial, int emcModeType) throws RemoteException { + if (isEmpty()) return; + if (isAidl()) { + mNetworkProxy.setEmergencyMode(serial, emcModeType); + } + // Only supported on AIDL. + } + + /** + * Triggers an Emergency network scan. + * + * @param serial Serial number of the request. + * @param scanRequest Contains the preferred networks and type of service to be scanned. + * @throws RemoteException + */ + public void triggerEmergencyNetworkScan(int serial, + android.hardware.radio.network.EmergencyNetworkScanTrigger scanRequest) + throws RemoteException { + if (isEmpty()) return; + if (isAidl()) { + mNetworkProxy.triggerEmergencyNetworkScan(serial, scanRequest); + } + // Only supported on AIDL. + } + + /** + * Cancels ongoing Emergency network scan + * + * @param serial Serial number of the request. + * @param resetScan Indicates how the next {@link #triggerEmergencyNetworkScan} should work. + * If {@code true}, then the modem shall start the new scan from the beginning, + * otherwise the modem shall resume from the last search. + * + * @throws RemoteException + */ + public void cancelEmergencyNetworkScan(int serial, boolean resetScan) throws RemoteException { + if (isEmpty()) return; + if (isAidl()) { + mNetworkProxy.cancelEmergencyNetworkScan(serial, resetScan); + } + // Only supported on AIDL. + } + + /** + * Exits ongoing Emergency Mode + * + * @param serial Serial number of the request. + * @throws RemoteException + */ + public void exitEmergencyMode(int serial) throws RemoteException { + if (isEmpty()) return; + if (isAidl()) { + mNetworkProxy.exitEmergencyMode(serial); + } + // Only supported on AIDL. + } + + /** + * Set if null ciphering / null integrity is permitted. + * + * @param serial Serial number of the request. + * @param enabled true if null modes are allowed, false otherwise + * @throws RemoteException + */ + public void setNullCipherAndIntegrityEnabled(int serial, + boolean enabled) throws RemoteException { + if (isEmpty()) return; + if (isAidl()) { + mNetworkProxy.setNullCipherAndIntegrityEnabled(serial, enabled); + } + // Only supported on AIDL. + } + + /** + * Get if null ciphering / null integrity is permitted. + * @param serial Serial number of the request. + * @throws RemoteException + * + */ + public void isNullCipherAndIntegrityEnabled(int serial) throws RemoteException { + if (isEmpty()) return; + if (isAidl()) { + mNetworkProxy.isNullCipherAndIntegrityEnabled(serial); + } + // Only supported on AIDL. + } + + /** + * Checks whether N1 mode is enabled. + * + * @param serial Serial number of the request. + * @throws RemoteException + */ + public void isN1ModeEnabled(int serial) throws RemoteException { + if (isEmpty()) return; + if (isAidl()) { + mNetworkProxy.isN1ModeEnabled(serial); + } + // Only supported on AIDL. + } + + /** + * Enables or disables N1 mode. + * + * @param serial Serial number of request. + * @param enable Indicates whether to enable N1 mode or not. + * @throws RemoteException + */ + public void setN1ModeEnabled(int serial, boolean enable) throws RemoteException { + if (isEmpty()) return; + if (isAidl()) { + mNetworkProxy.setN1ModeEnabled(serial, enable); + } + // Only supported on AIDL. + } }
diff --git a/src/java/com/android/internal/telephony/RadioResponse.java b/src/java/com/android/internal/telephony/RadioResponse.java index 5265be5..0bc2958 100644 --- a/src/java/com/android/internal/telephony/RadioResponse.java +++ b/src/java/com/android/internal/telephony/RadioResponse.java
@@ -2532,10 +2532,9 @@ ArrayList<NeighboringCellInfo> ret = new ArrayList<NeighboringCellInfo>(); NeighboringCellInfo cell; - int[] subId = SubscriptionManager.getSubId(mRil.mPhoneId); - int radioType = - ((TelephonyManager) mRil.mContext.getSystemService( - Context.TELEPHONY_SERVICE)).getDataNetworkType(subId[0]); + int radioType = ((TelephonyManager) mRil.mContext.getSystemService( + Context.TELEPHONY_SERVICE)).getDataNetworkType( + SubscriptionManager.getSubscriptionId(mRil.mPhoneId)); if (radioType != TelephonyManager.NETWORK_TYPE_UNKNOWN) { for (int i = 0; i < cells.size(); i++) {
diff --git a/src/java/com/android/internal/telephony/RadioServiceProxy.java b/src/java/com/android/internal/telephony/RadioServiceProxy.java index 8650dd0..4257327 100644 --- a/src/java/com/android/internal/telephony/RadioServiceProxy.java +++ b/src/java/com/android/internal/telephony/RadioServiceProxy.java
@@ -78,4 +78,9 @@ if (isEmpty()) return; if (!isAidl()) mRadioProxy.responseAcknowledgement(); } + + @Override + public String toString() { + return getClass().getSimpleName() + "[mHalVersion=" + mHalVersion + ']'; + } }
diff --git a/src/java/com/android/internal/telephony/RadioSimProxy.java b/src/java/com/android/internal/telephony/RadioSimProxy.java index c7e19c4..7c8ee7b 100644 --- a/src/java/com/android/internal/telephony/RadioSimProxy.java +++ b/src/java/com/android/internal/telephony/RadioSimProxy.java
@@ -41,12 +41,22 @@ * Set IRadioSim as the AIDL implementation for RadioServiceProxy * @param halVersion Radio HAL version * @param sim IRadioSim implementation + * + * @return updated HAL version */ - public void setAidl(HalVersion halVersion, android.hardware.radio.sim.IRadioSim sim) { - mHalVersion = halVersion; + public HalVersion setAidl(HalVersion halVersion, android.hardware.radio.sim.IRadioSim sim) { + HalVersion version = halVersion; + try { + version = RIL.getServiceHalVersion(sim.getInterfaceVersion()); + } catch (RemoteException e) { + Rlog.e(TAG, "setAidl: " + e); + } + mHalVersion = version; mSimProxy = sim; mIsAidl = true; - Rlog.d(TAG, "AIDL initialized"); + + Rlog.d(TAG, "AIDL initialized mHalVersion=" + mHalVersion); + return mHalVersion; } /** @@ -262,14 +272,24 @@ } /** - * Call IRadioSim#iccCloseLogicalChannel + * Call IRadioSim#iccCloseLogicalChannelWithSessionInfo * @param serial Serial number of request * @param channelId Channel ID of the channel to be closed + * @param isEs10 Whether the logical channel is opened for performing ES10 operations. * @throws RemoteException */ - public void iccCloseLogicalChannel(int serial, int channelId) throws RemoteException { + public void iccCloseLogicalChannel(int serial, + int channelId, boolean isEs10) throws RemoteException { if (isEmpty()) return; if (isAidl()) { + if (mHalVersion.greaterOrEqual(RIL.RADIO_HAL_VERSION_2_1)) { + android.hardware.radio.sim.SessionInfo info = + new android.hardware.radio.sim.SessionInfo(); + info.sessionId = channelId; + info.isEs10 = isEs10; + mSimProxy.iccCloseLogicalChannelWithSessionInfo(serial, info); + return; + } mSimProxy.iccCloseLogicalChannel(serial, channelId); } else { mRadioProxy.iccCloseLogicalChannel(serial, channelId); @@ -352,7 +372,8 @@ if (isEmpty()) return; if (isAidl()) { mSimProxy.iccTransmitApduBasicChannel(serial, - RILUtils.convertToHalSimApduAidl(0, cla, instruction, p1, p2, p3, data)); + RILUtils.convertToHalSimApduAidl(0, cla, instruction, p1, p2, p3, data, + false, mHalVersion)); } else { mRadioProxy.iccTransmitApduBasicChannel(serial, RILUtils.convertToHalSimApdu(0, cla, instruction, p1, p2, p3, data)); @@ -373,10 +394,29 @@ */ public void iccTransmitApduLogicalChannel(int serial, int channel, int cla, int instruction, int p1, int p2, int p3, String data) throws RemoteException { + iccTransmitApduLogicalChannel(serial, channel, cla, instruction, p1, p2, p3, data, false); + } + + /** + * Call IRadioSim#iccTransmitApduLogicalChannel + * @param serial Serial number of request + * @param channel Channel ID of the channel to use for communication + * @param cla Class of the command + * @param instruction Instruction of the command + * @param p1 P1 value of the command + * @param p2 P2 value of the command + * @param p3 P3 value of the command + * @param data Data to be sent + * @param isEs10Command APDU is an isEs10 command or not + * @throws RemoteException + */ + public void iccTransmitApduLogicalChannel(int serial, int channel, int cla, int instruction, + int p1, int p2, int p3, String data, boolean isEs10Command) throws RemoteException { if (isEmpty()) return; if (isAidl()) { mSimProxy.iccTransmitApduLogicalChannel(serial, - RILUtils.convertToHalSimApduAidl(channel, cla, instruction, p1, p2, p3, data)); + RILUtils.convertToHalSimApduAidl(channel, cla, instruction, p1, p2, p3, data, + isEs10Command, mHalVersion)); } else { mRadioProxy.iccTransmitApduLogicalChannel(serial, RILUtils.convertToHalSimApdu(channel, cla, instruction, p1, p2, p3, data)); @@ -576,7 +616,7 @@ halImsiInfo.base.expirationTime = imsiEncryptionInfo.getExpirationTime().getTime(); } for (byte b : imsiEncryptionInfo.getPublicKey().getEncoded()) { - halImsiInfo.base.carrierKey.add(new Byte(b)); + halImsiInfo.base.carrierKey.add(Byte.valueOf(b)); } halImsiInfo.keyType = (byte) imsiEncryptionInfo.getKeyType(); @@ -592,7 +632,7 @@ halImsiInfo.expirationTime = imsiEncryptionInfo.getExpirationTime().getTime(); } for (byte b : imsiEncryptionInfo.getPublicKey().getEncoded()) { - halImsiInfo.carrierKey.add(new Byte(b)); + halImsiInfo.carrierKey.add(Byte.valueOf(b)); } ((android.hardware.radio.V1_1.IRadio) mRadioProxy).setCarrierInfoForImsiEncryption(
diff --git a/src/java/com/android/internal/telephony/RadioVoiceProxy.java b/src/java/com/android/internal/telephony/RadioVoiceProxy.java index 6ac603b..7f46424 100644 --- a/src/java/com/android/internal/telephony/RadioVoiceProxy.java +++ b/src/java/com/android/internal/telephony/RadioVoiceProxy.java
@@ -35,12 +35,23 @@ * Set IRadioVoice as the AIDL implementation for RadioServiceProxy * @param halVersion Radio HAL version * @param voice IRadioVoice implementation + * + * @return updated HAL version */ - public void setAidl(HalVersion halVersion, android.hardware.radio.voice.IRadioVoice voice) { - mHalVersion = halVersion; + public HalVersion setAidl(HalVersion halVersion, + android.hardware.radio.voice.IRadioVoice voice) { + HalVersion version = halVersion; + try { + version = RIL.getServiceHalVersion(voice.getInterfaceVersion()); + } catch (RemoteException e) { + Rlog.e(TAG, "setAidl: " + e); + } + mHalVersion = version; mVoiceProxy = voice; mIsAidl = true; - Rlog.d(TAG, "AIDL initialized"); + + Rlog.d(TAG, "AIDL initialized mHalVersion=" + mHalVersion); + return mHalVersion; } /**
diff --git a/src/java/com/android/internal/telephony/RatRatcheter.java b/src/java/com/android/internal/telephony/RatRatcheter.java index 24a8ac5..aff62ae 100644 --- a/src/java/com/android/internal/telephony/RatRatcheter.java +++ b/src/java/com/android/internal/telephony/RatRatcheter.java
@@ -16,13 +16,7 @@ package com.android.internal.telephony; import android.annotation.NonNull; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.pm.PackageManager; import android.os.PersistableBundle; -import android.os.UserHandle; import android.telephony.AccessNetworkConstants; import android.telephony.Annotation.NetworkType; import android.telephony.CarrierConfigManager; @@ -80,16 +74,11 @@ /** Constructor */ public RatRatcheter(Phone phone) { mPhone = phone; - - IntentFilter intentFilter = new IntentFilter(); - intentFilter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED); - try { - Context contextAsUser = phone.getContext().createPackageContextAsUser( - phone.getContext().getPackageName(), 0, UserHandle.ALL); - contextAsUser.registerReceiver(mConfigChangedReceiver, - intentFilter, null /* broadcastPermission */, null); - } catch (PackageManager.NameNotFoundException e) { - Rlog.e(LOG_TAG, "Package name not found: " + e.getMessage()); + CarrierConfigManager ccm = mPhone.getContext().getSystemService(CarrierConfigManager.class); + if (ccm != null) { + ccm.registerCarrierConfigChangeListener( + mPhone.getContext().getMainExecutor(), + (slotIndex, subId, carrierId, specificCarrierId) -> resetRatFamilyMap()); } resetRatFamilyMap(); } @@ -152,15 +141,24 @@ synchronized (mRatFamilyMap) { // Either the two technologies are the same or their families must be non-null // and the same. + // To Fix Missing Null check + if (ss1.getNetworkRegistrationInfo(NetworkRegistrationInfo.DOMAIN_PS, + AccessNetworkConstants.TRANSPORT_TYPE_WWAN) == null + || ss2.getNetworkRegistrationInfo(NetworkRegistrationInfo.DOMAIN_PS, + AccessNetworkConstants.TRANSPORT_TYPE_WWAN) == null) { + return false; + } + int dataRat1 = ServiceState.networkTypeToRilRadioTechnology( ss1.getNetworkRegistrationInfo(NetworkRegistrationInfo.DOMAIN_PS, - AccessNetworkConstants.TRANSPORT_TYPE_WWAN) + AccessNetworkConstants.TRANSPORT_TYPE_WWAN) .getAccessNetworkTechnology()); int dataRat2 = ServiceState.networkTypeToRilRadioTechnology( ss2.getNetworkRegistrationInfo(NetworkRegistrationInfo.DOMAIN_PS, - AccessNetworkConstants.TRANSPORT_TYPE_WWAN) + AccessNetworkConstants.TRANSPORT_TYPE_WWAN) .getAccessNetworkTechnology()); + // The api getAccessNetworkTechnology@NetworkRegistrationInfo always returns LTE though // data rat is LTE CA. Because it uses mIsUsingCarrierAggregation to indicate whether // it is LTE CA or not. However, we need its actual data rat to check if they are the @@ -183,25 +181,16 @@ } } - private BroadcastReceiver mConfigChangedReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - final String action = intent.getAction(); - if (CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED.equals(action)) { - resetRatFamilyMap(); - } - } - }; - private void resetRatFamilyMap() { synchronized(mRatFamilyMap) { mRatFamilyMap.clear(); - final CarrierConfigManager configManager = (CarrierConfigManager) - mPhone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE); - if (configManager == null) return; - PersistableBundle b = configManager.getConfigForSubId(mPhone.getSubId()); - if (b == null) return; + PersistableBundle b = + CarrierConfigManager.getCarrierConfigSubset( + mPhone.getContext(), + mPhone.getSubId(), + CarrierConfigManager.KEY_RATCHET_RAT_FAMILIES); + if (b == null || b.isEmpty()) return; // Reads an array of strings, eg: // ["GPRS, EDGE", "EVDO, EVDO_A, EVDO_B", "HSPA, HSDPA, HSUPA, HSPAP"]
diff --git a/src/java/com/android/internal/telephony/RetryManager.java b/src/java/com/android/internal/telephony/RetryManager.java deleted file mode 100644 index 83864e4..0000000 --- a/src/java/com/android/internal/telephony/RetryManager.java +++ /dev/null
@@ -1,723 +0,0 @@ -/** - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.internal.telephony; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.compat.annotation.UnsupportedAppUsage; -import android.content.Context; -import android.os.Build; -import android.os.PersistableBundle; -import android.os.SystemClock; -import android.os.SystemProperties; -import android.telephony.Annotation.ApnType; -import android.telephony.CarrierConfigManager; -import android.telephony.data.ApnSetting; -import android.telephony.data.DataCallResponse; -import android.text.TextUtils; -import android.util.Pair; - -import com.android.internal.telephony.dataconnection.DataThrottler; -import com.android.internal.telephony.util.TelephonyUtils; -import com.android.telephony.Rlog; - -import java.util.ArrayList; -import java.util.Random; - -/** - * Retry manager allows a simple way to declare a series of - * retry timeouts. After creating a RetryManager the configure - * method is used to define the sequence. A simple linear series - * may be initialized using configure with three integer parameters - * The other configure method allows a series to be declared using - * a string. - *<p> - * The format of the configuration string is the apn type followed by a series of parameters - * separated by a comma. There are two name value pair parameters plus a series - * of delay times. The units of of these delay times is unspecified. - * The name value pairs which may be specified are: - *<ul> - *<li>max_retries=<value> - *<li>default_randomizationTime=<value> - *</ul> - *<p> - * apn type specifies the APN type that the retry pattern will apply for. "others" is for all other - * APN types not specified in the config. - * - * max_retries is the number of times that incrementRetryCount - * maybe called before isRetryNeeded will return false. if value - * is infinite then isRetryNeeded will always return true. - * - * default_randomizationTime will be used as the randomizationTime - * for delay times which have no supplied randomizationTime. If - * default_randomizationTime is not defined it defaults to 0. - *<p> - * The other parameters define The series of delay times and each - * may have an optional randomization value separated from the - * delay time by a colon. - *<p> - * Examples: - * <ul> - * <li>3 retries for mms with no randomization value which means its 0: - * <ul><li><code>"mms:1000, 2000, 3000"</code></ul> - * - * <li>10 retries for default APN with a 500 default randomization value for each and - * the 4..10 retries all using 3000 as the delay: - * <ul><li><code>"default:max_retries=10, default_randomization=500, 1000, 2000, 3000"</code></ul> - * - * <li>4 retries for supl APN with a 100 as the default randomization value for the first 2 values - * and the other two having specified values of 500: - * <ul><li><code>"supl:default_randomization=100, 1000, 2000, 4000:500, 5000:500"</code></ul> - * - * <li>Infinite number of retries for all other APNs with the first one at 1000, the second at 2000 - * all others will be at 3000. - * <ul><li><code>"others:max_retries=infinite,1000,2000,3000</code></ul> - * </ul> - * - * {@hide} - */ -public class RetryManager { - public static final String LOG_TAG = "RetryManager"; - public static final boolean DBG = true; - public static final boolean VDBG = false; // STOPSHIP if true - - /** - * The default retry configuration for APNs. See above for the syntax. - */ - private static final String DEFAULT_DATA_RETRY_CONFIG = "max_retries=3, 5000, 5000, 5000"; - - /** - * The APN type used for all other APNs retry configuration. - */ - private static final String OTHERS_APN_TYPE = "others"; - - /** - * The default value (in milliseconds) for delay between APN trying (mInterApnDelay) - * within the same round - */ - private static final long DEFAULT_INTER_APN_DELAY = 20000; - - /** - * The default value (in milliseconds) for delay between APN trying (mFailFastInterApnDelay) - * within the same round when we are in fail fast mode - */ - private static final long DEFAULT_INTER_APN_DELAY_FOR_PROVISIONING = 3000; - - /** - * The default value (in milliseconds) for retrying APN after disconnect - */ - private static final long DEFAULT_APN_RETRY_AFTER_DISCONNECT_DELAY = 10000; - - /** - * The value indicating retry should not occur. - */ - public static final long NO_RETRY = Long.MAX_VALUE; - - /** - * The value indicating network did not suggest any retry delay - */ - public static final long NO_SUGGESTED_RETRY_DELAY = DataCallResponse.RETRY_DURATION_UNDEFINED; - - /** - * If the network suggests a retry delay in the data call setup response, we will retry - * the current APN setting again. The maximum retry count is to prevent that network - * keeps asking device to retry data setup forever and causes power consumption issue. - */ - private static final int DEFAULT_MAX_SAME_APN_RETRY = 3; - - /** - * The delay (in milliseconds) between APN trying within the same round - */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - private long mInterApnDelay; - - /** - * The delay (in milliseconds) between APN trying within the same round when we are in - * fail fast mode - */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - private long mFailFastInterApnDelay; - - /** - * The delay (in milliseconds) for APN retrying after disconnect (e.g. Modem suddenly reports - * data call lost) - */ - private long mApnRetryAfterDisconnectDelay; - - /** - * The counter for same APN retrying. See {@link #DEFAULT_MAX_SAME_APN_RETRY} for the details. - */ - private int mSameApnRetryCount = 0; - - /** - * The maximum times that frameworks retries data setup with the same APN. This value could be - * changed via carrier config. See {@link #DEFAULT_MAX_SAME_APN_RETRY} for the details. - */ - private int mMaxSameApnRetry = DEFAULT_MAX_SAME_APN_RETRY; - - /** - * Retry record with times in milli-seconds - */ - private static class RetryRec { - long mDelayTime; - long mRandomizationTime; - - RetryRec(long delayTime, long randomizationTime) { - mDelayTime = delayTime; - mRandomizationTime = randomizationTime; - } - } - - /** - * The array of retry records - */ - private ArrayList<RetryRec> mRetryArray = new ArrayList<RetryRec>(); - - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - private Phone mPhone; - - private final DataThrottler mDataThrottler; - - /** - * Flag indicating whether retrying forever regardless the maximum retry count mMaxRetryCount - */ - private boolean mRetryForever = false; - - /** - * The maximum number of retries to attempt - */ - private int mMaxRetryCount; - - /** - * The current number of retries - */ - private int mRetryCount = 0; - - /** - * Random number generator. The random delay will be added into retry timer to avoid all devices - * around retrying the APN at the same time. - */ - private Random mRng = new Random(); - - /** - * Retry manager configuration string. See top of the detailed explanation. - */ - private String mConfig; - - /** - * The list to store APN setting candidates for data call setup. Most of the carriers only have - * one APN, but few carriers have more than one. - */ - private ArrayList<ApnSetting> mWaitingApns = new ArrayList<>(); - - /** - * Index pointing to the current trying APN from mWaitingApns - */ - private int mCurrentApnIndex = -1; - - /** - * Apn context type. - */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - private String mApnType; - - private final @ApnType int apnType; - - /** - * Retry manager constructor - * @param phone Phone object - * @param dataThrottler Data throttler - * @param apnType APN type - */ - public RetryManager(@NonNull Phone phone, @NonNull DataThrottler dataThrottler, - @ApnType int apnType) { - mPhone = phone; - mDataThrottler = dataThrottler; - this.apnType = apnType; - } - - /** - * Configure for using string which allow arbitrary - * sequences of times. See class comments for the - * string format. - * - * @return true if successful - */ - @UnsupportedAppUsage - private boolean configure(String configStr) { - // Strip quotes if present. - if ((configStr.startsWith("\"") && configStr.endsWith("\""))) { - configStr = configStr.substring(1, configStr.length() - 1); - } - - // Reset the retry manager since delay, max retry count, etc...will be reset. - reset(); - - if (DBG) log("configure: '" + configStr + "'"); - mConfig = configStr; - - if (!TextUtils.isEmpty(configStr)) { - long defaultRandomization = 0; - - if (VDBG) log("configure: not empty"); - - String strArray[] = configStr.split(","); - for (int i = 0; i < strArray.length; i++) { - if (VDBG) log("configure: strArray[" + i + "]='" + strArray[i] + "'"); - Pair<Boolean, Integer> value; - String splitStr[] = strArray[i].split("=", 2); - splitStr[0] = splitStr[0].trim(); - if (VDBG) log("configure: splitStr[0]='" + splitStr[0] + "'"); - if (splitStr.length > 1) { - splitStr[1] = splitStr[1].trim(); - if (VDBG) log("configure: splitStr[1]='" + splitStr[1] + "'"); - if (TextUtils.equals(splitStr[0], "default_randomization")) { - value = parseNonNegativeInt(splitStr[0], splitStr[1]); - if (!value.first) return false; - defaultRandomization = value.second; - } else if (TextUtils.equals(splitStr[0], "max_retries")) { - if (TextUtils.equals("infinite", splitStr[1])) { - mRetryForever = true; - } else { - value = parseNonNegativeInt(splitStr[0], splitStr[1]); - if (!value.first) return false; - mMaxRetryCount = value.second; - } - } else { - Rlog.e(LOG_TAG, "Unrecognized configuration name value pair: " - + strArray[i]); - return false; - } - } else { - /** - * Assume a retry time with an optional randomization value - * following a ":" - */ - splitStr = strArray[i].split(":", 2); - splitStr[0] = splitStr[0].trim(); - RetryRec rr = new RetryRec(0, 0); - value = parseNonNegativeInt("delayTime", splitStr[0]); - if (!value.first) return false; - rr.mDelayTime = value.second; - - // Check if optional randomization value present - if (splitStr.length > 1) { - splitStr[1] = splitStr[1].trim(); - if (VDBG) log("configure: splitStr[1]='" + splitStr[1] + "'"); - value = parseNonNegativeInt("randomizationTime", splitStr[1]); - if (!value.first) return false; - rr.mRandomizationTime = value.second; - } else { - rr.mRandomizationTime = defaultRandomization; - } - mRetryArray.add(rr); - } - } - if (mRetryArray.size() > mMaxRetryCount) { - mMaxRetryCount = mRetryArray.size(); - if (VDBG) log("configure: setting mMaxRetryCount=" + mMaxRetryCount); - } - } else { - log("configure: cleared"); - } - - if (VDBG) log("configure: true"); - return true; - } - - /** - * Configure the retry manager - */ - private void configureRetry() { - String configString = null; - String otherConfigString = null; - - try { - if (TelephonyUtils.IS_DEBUGGABLE) { - // Using system properties is easier for testing from command line. - String config = SystemProperties.get("test.data_retry_config"); - if (!TextUtils.isEmpty(config)) { - configure(config); - return; - } - } - - CarrierConfigManager configManager = (CarrierConfigManager) - mPhone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE); - PersistableBundle b = configManager.getConfigForSubId(mPhone.getSubId()); - - mInterApnDelay = b.getLong( - CarrierConfigManager.KEY_CARRIER_DATA_CALL_APN_DELAY_DEFAULT_LONG, - DEFAULT_INTER_APN_DELAY); - mFailFastInterApnDelay = b.getLong( - CarrierConfigManager.KEY_CARRIER_DATA_CALL_APN_DELAY_FASTER_LONG, - DEFAULT_INTER_APN_DELAY_FOR_PROVISIONING); - mApnRetryAfterDisconnectDelay = b.getLong( - CarrierConfigManager.KEY_CARRIER_DATA_CALL_APN_RETRY_AFTER_DISCONNECT_LONG, - DEFAULT_APN_RETRY_AFTER_DISCONNECT_DELAY); - mMaxSameApnRetry = b.getInt( - CarrierConfigManager - .KEY_CARRIER_DATA_CALL_RETRY_NETWORK_REQUESTED_MAX_COUNT_INT, - DEFAULT_MAX_SAME_APN_RETRY); - - // Load all retry patterns for all different APNs. - String[] allConfigStrings = b.getStringArray( - CarrierConfigManager.KEY_CARRIER_DATA_CALL_RETRY_CONFIG_STRINGS); - if (allConfigStrings != null) { - for (String s : allConfigStrings) { - if (!TextUtils.isEmpty(s)) { - String splitStr[] = s.split(":", 2); - if (splitStr.length == 2) { - String apnTypeStr = splitStr[0].trim(); - // Check if this retry pattern is for the APN we want. - if (apnTypeStr.equals(ApnSetting.getApnTypeString(apnType))) { - // Extract the config string. Note that an empty string is valid - // here, meaning no retry for the specified APN. - configString = splitStr[1]; - break; - } else if (apnTypeStr.equals(OTHERS_APN_TYPE)) { - // Extract the config string. Note that an empty string is valid - // here, meaning no retry for all other APNs. - otherConfigString = splitStr[1]; - } - } - } - } - } - - if (configString == null) { - if (otherConfigString != null) { - configString = otherConfigString; - } else { - // We should never reach here. If we reach here, it must be a configuration - // error bug. - log("Invalid APN retry configuration!. Use the default one now."); - configString = DEFAULT_DATA_RETRY_CONFIG; - } - } - } catch (NullPointerException ex) { - // We should never reach here unless there is a bug - log("Failed to read configuration! Use the hardcoded default value."); - - mInterApnDelay = DEFAULT_INTER_APN_DELAY; - mFailFastInterApnDelay = DEFAULT_INTER_APN_DELAY_FOR_PROVISIONING; - configString = DEFAULT_DATA_RETRY_CONFIG; - } - - if (VDBG) { - log("mInterApnDelay = " + mInterApnDelay + ", mFailFastInterApnDelay = " + - mFailFastInterApnDelay); - } - - configure(configString); - } - - /** - * Return the timer that should be used to trigger the data reconnection - */ - @UnsupportedAppUsage - private long getRetryTimer() { - int index; - if (mRetryCount < mRetryArray.size()) { - index = mRetryCount; - } else { - index = mRetryArray.size() - 1; - } - - long retVal; - if ((index >= 0) && (index < mRetryArray.size())) { - retVal = mRetryArray.get(index).mDelayTime + nextRandomizationTime(index); - } else { - retVal = 0; - } - - if (DBG) log("getRetryTimer: " + retVal); - return retVal; - } - - /** - * Parse an integer validating the value is not negative. - * @param name Name - * @param stringValue Value - * @return Pair.first == true if stringValue an integer >= 0 - */ - private Pair<Boolean, Integer> parseNonNegativeInt(String name, String stringValue) { - int value; - Pair<Boolean, Integer> retVal; - try { - value = Integer.parseInt(stringValue); - retVal = new Pair<>(validateNonNegativeInt(name, value), value); - } catch (NumberFormatException e) { - Rlog.e(LOG_TAG, name + " bad value: " + stringValue, e); - retVal = new Pair<>(false, 0); - } - if (VDBG) { - log("parseNonNegativeInt: " + name + ", " + stringValue + ", " - + retVal.first + ", " + retVal.second); - } - return retVal; - } - - /** - * Validate an integer is >= 0 and logs an error if not - * @param name Name - * @param value Value - * @return Pair.first - */ - private boolean validateNonNegativeInt(String name, long value) { - boolean retVal; - if (value < 0) { - Rlog.e(LOG_TAG, name + " bad value: is < 0"); - retVal = false; - } else { - retVal = true; - } - if (VDBG) log("validateNonNegative: " + name + ", " + value + ", " + retVal); - return retVal; - } - - /** - * Return next random number for the index - * @param index Retry index - */ - private long nextRandomizationTime(int index) { - long randomTime = mRetryArray.get(index).mRandomizationTime; - if (randomTime == 0) { - return 0; - } else { - return mRng.nextInt((int) randomTime); - } - } - - private long getNetworkSuggestedRetryDelay() { - long retryElapseTime = mDataThrottler.getRetryTime(apnType); - if (retryElapseTime == NO_RETRY || retryElapseTime == NO_SUGGESTED_RETRY_DELAY) { - return retryElapseTime; - } - - // The time from data throttler is system's elapsed time. We need to return the delta. If - // less than 0, then return 0 (i.e. retry immediately). - return Math.max(0, retryElapseTime - SystemClock.elapsedRealtime()); - } - - /** - * Get the next APN setting for data call setup. - * @return APN setting to try. {@code null} if cannot find any APN, - */ - public @Nullable ApnSetting getNextApnSetting() { - if (mWaitingApns == null || mWaitingApns.size() == 0) { - log("Waiting APN list is null or empty."); - return null; - } - - long networkSuggestedRetryDelay = getNetworkSuggestedRetryDelay(); - if (networkSuggestedRetryDelay == NO_RETRY) { - log("Network suggested no retry."); - return null; - } - - // If the network had suggested a retry delay, we should retry the current APN again - // (up to mMaxSameApnRetry times) instead of getting the next APN setting from - // our own list. If the APN waiting list has been reset before a setup data responses - // arrive (i.e. mCurrentApnIndex=-1), then ignore the network suggested retry. - if (mCurrentApnIndex != -1 && networkSuggestedRetryDelay != NO_SUGGESTED_RETRY_DELAY - && mSameApnRetryCount < mMaxSameApnRetry) { - mSameApnRetryCount++; - return mWaitingApns.get(mCurrentApnIndex); - } - - mSameApnRetryCount = 0; - - int index = mCurrentApnIndex; - // Loop through the APN list to find out the index of next non-permanent failed APN. - while (true) { - if (++index == mWaitingApns.size()) index = 0; - - // Stop if we find the non-failed APN. - if (!mWaitingApns.get(index).getPermanentFailed()) { - break; - } - - // If all APNs have permanently failed, bail out. - if (mWaitingApns.stream().allMatch(ApnSetting::getPermanentFailed)) { - return null; - } - } - - mCurrentApnIndex = index; - return mWaitingApns.get(mCurrentApnIndex); - } - - /** - * Get the delay for trying the next waiting APN from the list. - * @param failFastEnabled True if fail fast mode enabled. In this case we'll use a shorter - * delay. - * @return delay in milliseconds - */ - public long getDelayForNextApn(boolean failFastEnabled) { - - if (mWaitingApns == null || mWaitingApns.size() == 0) { - log("Waiting APN list is null or empty."); - return NO_RETRY; - } - - long networkSuggestedDelay = getNetworkSuggestedRetryDelay(); - log("Network suggested delay=" + networkSuggestedDelay + "ms"); - - if (networkSuggestedDelay == NO_RETRY) { - log("Network suggested not retrying."); - return NO_RETRY; - } - - if (networkSuggestedDelay != NO_SUGGESTED_RETRY_DELAY - && mSameApnRetryCount < mMaxSameApnRetry) { - // If the network explicitly suggests a retry delay, we should use it, even in fail fast - // mode. - log("Network suggested retry in " + networkSuggestedDelay + " ms."); - return networkSuggestedDelay; - } - - // In order to determine the delay to try next APN, we need to peek the next available APN. - // Case 1 - If we will start the next round of APN trying, - // we use the exponential-growth delay. (e.g. 5s, 10s, 30s...etc.) - // Case 2 - If we are still within the same round of APN trying, - // we use the fixed standard delay between APNs. (e.g. 20s) - - int index = mCurrentApnIndex; - while (true) { - if (++index >= mWaitingApns.size()) index = 0; - - // Stop if we find the non-failed APN. - if (!mWaitingApns.get(index).getPermanentFailed()) { - break; - } - - // If all APNs have permanently failed, bail out. - if (mWaitingApns.stream().allMatch(ApnSetting::getPermanentFailed)) { - log("All APNs have permanently failed."); - return NO_RETRY; - } - } - - long delay; - if (index <= mCurrentApnIndex) { - // Case 1, if the next APN is in the next round. - if (!mRetryForever && mRetryCount + 1 > mMaxRetryCount) { - log("Reached maximum retry count " + mMaxRetryCount + "."); - return NO_RETRY; - } - delay = getRetryTimer(); - ++mRetryCount; - } else { - // Case 2, if the next APN is still in the same round. - delay = mInterApnDelay; - } - - if (failFastEnabled && delay > mFailFastInterApnDelay) { - // If we enable fail fast mode, and the delay we got is longer than - // fail-fast delay (mFailFastInterApnDelay), use the fail-fast delay. - // If the delay we calculated is already shorter than fail-fast delay, - // then ignore fail-fast delay. - delay = mFailFastInterApnDelay; - } - - return delay; - } - - /** - * Mark the APN setting permanently failed. - * @param apn APN setting to be marked as permanently failed - * */ - public void markApnPermanentFailed(ApnSetting apn) { - if (apn != null) { - apn.setPermanentFailed(true); - } - } - - /** - * Reset the retry manager. - */ - private void reset() { - mMaxRetryCount = 0; - mRetryCount = 0; - mCurrentApnIndex = -1; - mSameApnRetryCount = 0; - mRetryArray.clear(); - } - - /** - * Set waiting APNs for retrying in case needed. - * @param waitingApns Waiting APN list - */ - public void setWaitingApns(ArrayList<ApnSetting> waitingApns) { - - if (waitingApns == null) { - log("No waiting APNs provided"); - return; - } - - mWaitingApns = waitingApns; - - // Since we replace the entire waiting APN list, we need to re-config this retry manager. - configureRetry(); - - for (ApnSetting apn : mWaitingApns) { - apn.setPermanentFailed(false); - } - - log("Setting " + mWaitingApns.size() + " waiting APNs."); - - if (VDBG) { - for (int i = 0; i < mWaitingApns.size(); i++) { - log(" [" + i + "]:" + mWaitingApns.get(i)); - } - } - } - - /** - * Get the list of waiting APNs. - * @return the list of waiting APNs - */ - public @NonNull ArrayList<ApnSetting> getWaitingApns() { - return mWaitingApns; - } - - /** - * Get the delay in milliseconds for APN retry after disconnect - * @return The delay in milliseconds - */ - public long getRetryAfterDisconnectDelay() { - return mApnRetryAfterDisconnectDelay; - } - - public String toString() { - if (mConfig == null) return ""; - return "RetryManager: apnType=" + ApnSetting.getApnTypeString(apnType) - + " mRetryCount=" - + mRetryCount + " mMaxRetryCount=" + mMaxRetryCount + " mCurrentApnIndex=" - + mCurrentApnIndex + " mSameApnRtryCount=" + mSameApnRetryCount - + " networkSuggestedDelay=" + getNetworkSuggestedRetryDelay() + " mRetryForever=" - + mRetryForever + " mInterApnDelay=" + mInterApnDelay - + " mApnRetryAfterDisconnectDelay=" + mApnRetryAfterDisconnectDelay - + " mConfig={" + mConfig + "}"; - } - - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - private void log(String s) { - Rlog.d(LOG_TAG, "[" + ApnSetting.getApnTypeString(apnType) + "] " + s); - } -}
diff --git a/src/java/com/android/internal/telephony/SMSDispatcher.java b/src/java/com/android/internal/telephony/SMSDispatcher.java index 5bc9d17..a78242a 100644 --- a/src/java/com/android/internal/telephony/SMSDispatcher.java +++ b/src/java/com/android/internal/telephony/SMSDispatcher.java
@@ -27,16 +27,22 @@ import android.app.AlertDialog; import android.app.PendingIntent; import android.app.PendingIntent.CanceledException; +import android.app.compat.CompatChanges; +import android.compat.annotation.ChangeId; +import android.compat.annotation.EnabledSince; import android.compat.annotation.UnsupportedAppUsage; +import android.content.BroadcastReceiver; import android.content.ContentResolver; import android.content.ContentValues; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; +import android.content.IntentFilter; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.res.Resources; +import android.content.res.Resources.NotFoundException; import android.database.ContentObserver; import android.net.Uri; import android.os.AsyncResult; @@ -60,6 +66,7 @@ import android.telephony.PhoneNumberUtils; import android.telephony.ServiceState; import android.telephony.SmsManager; +import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import android.text.Html; import android.text.Spanned; @@ -80,6 +87,10 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.telephony.GsmAlphabet.TextEncodingDetails; import com.android.internal.telephony.cdma.sms.UserData; +import com.android.internal.telephony.subscription.SubscriptionInfoInternal; +import com.android.internal.telephony.subscription.SubscriptionManagerService; +import com.android.internal.telephony.uicc.IccRecords; +import com.android.internal.telephony.util.TelephonyUtils; import com.android.telephony.Rlog; import java.io.FileDescriptor; @@ -139,11 +150,30 @@ /** New status report received. */ protected static final int EVENT_NEW_SMS_STATUS_REPORT = 10; + /** Retry Sending RP-SMMA Notification */ + protected static final int EVENT_RETRY_SMMA = 11; // other protected static final int EVENT_NEW_ICC_SMS = 14; protected static final int EVENT_ICC_CHANGED = 15; protected static final int EVENT_GET_IMS_SERVICE = 16; + /** Last TP - Message Reference value update to SIM */ + private static final int EVENT_TPMR_SIM_UPDATE_RESPONSE = 17; + + /** Handle SIM loaded */ + private static final int EVENT_SIM_LOADED = 18; + + /** + * When this change is enabled, more specific values of SMS sending error code + * {@link SmsManager#Result} will be returned to the SMS Apps. + * + * Refer to {@link SMSDispatcher#rilErrorToSmsManagerResult} fore more details of the new values + * of SMS sending error code that will be returned. + */ + @ChangeId + @EnabledSince(targetSdkVersion = Build.VERSION_CODES.TIRAMISU) + static final long ADD_MORE_SMS_SENDING_ERROR_CODES = 250017070L; + @UnsupportedAppUsage protected Phone mPhone; @UnsupportedAppUsage @@ -155,9 +185,18 @@ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) protected final TelephonyManager mTelephonyManager; protected final LocalLog mLocalLog = new LocalLog(16); + protected final LocalLog mSmsOutgoingErrorCodes = new LocalLog(10); /** Maximum number of times to retry sending a failed SMS. */ protected static final int MAX_SEND_RETRIES = 3; + + /** Retransmitted Flag as specified in section 6.3.1.2 in TS 124011 + * true: RP-SMMA Retried once and no more transmissions are permitted + * false: not retried at all and at least another transmission of the RP-SMMA message + * is currently permitted + */ + protected boolean mRPSmmaRetried = false; + /** Delay before next send attempt on a failed SMS, in milliseconds. */ @VisibleForTesting public static final int SEND_RETRY_DELAY = 2000; @@ -190,6 +229,9 @@ @VisibleForTesting public int mCarrierMessagingTimeout = 10 * 60 * 1000; //10 minutes + /** Used for storing last TP - Message Reference used*/ + private int mMessageRef = -1; + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) protected static int getNextConcatenatedRef() { sConcatenatedRef += 1; @@ -215,10 +257,33 @@ com.android.internal.R.bool.config_sms_capable); mSmsSendDisabled = !mTelephonyManager.getSmsSendCapableForPhone( mPhone.getPhoneId(), mSmsCapable); + IntentFilter intentFilter = new IntentFilter(); + intentFilter.addAction(Intent.ACTION_SIM_STATE_CHANGED); + mContext.registerReceiver(mBroadcastReceiver, intentFilter); Rlog.d(TAG, "SMSDispatcher: ctor mSmsCapable=" + mSmsCapable + " format=" + getFormat() + " mSmsSendDisabled=" + mSmsSendDisabled); } + private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { + @Override + public void onReceive(final Context context, Intent intent) { + Rlog.d(TAG, "Received broadcast " + intent.getAction()); + if (Intent.ACTION_SIM_STATE_CHANGED.equals(intent.getAction())) { + if (!intent.hasExtra(Intent.EXTRA_SIM_STATE)) { + Rlog.d(TAG, "Extra not found in intent."); + } else { + String simState = intent.getStringExtra(Intent.EXTRA_SIM_STATE); + if (simState.equals(Intent.SIM_STATE_LOADED)) { + Rlog.d(TAG, "SIM_STATE_CHANGED : SIM_LOADED"); + Message msg = obtainMessage(EVENT_SIM_LOADED); + msg.arg1 = getSubId(); + sendMessage(msg); + } + } + } + } + }; + /** * Observe the secure setting for updated premium sms determination rules */ @@ -262,6 +327,26 @@ protected abstract String getFormat(); /** + * Gets the maximum number of times the SMS can be retried upon Failure, + * from the {@link android.telephony.CarrierConfigManager} + * + * @return the default maximum number of times SMS can be sent + */ + protected int getMaxSmsRetryCount() { + return MAX_SEND_RETRIES; + } + + /** + * Gets the Time delay before next send attempt on a failed SMS, + * from the {@link android.telephony.CarrierConfigManager} + * + * @return the Time in miiliseconds for delay before next send attempt on a failed SMS + */ + protected int getSmsRetryDelayValue() { + return SEND_RETRY_DELAY; + } + + /** * Called when a status report is received. This should correspond to a previously successful * SEND. * @@ -280,83 +365,181 @@ @Override public void handleMessage(Message msg) { switch (msg.what) { - case EVENT_SEND_SMS_COMPLETE: - // An outbound SMS has been successfully transferred, or failed. - handleSendComplete((AsyncResult) msg.obj); - break; + case EVENT_SEND_SMS_COMPLETE: + // An outbound SMS has been successfully transferred, or failed. + handleSendComplete((AsyncResult) msg.obj); + break; - case EVENT_SEND_RETRY: - Rlog.d(TAG, "SMS retry.."); - sendRetrySms((SmsTracker) msg.obj); - break; + case EVENT_SEND_RETRY: + Rlog.d(TAG, "SMS retry.."); + sendRetrySms((SmsTracker) msg.obj); + break; - case EVENT_SEND_LIMIT_REACHED_CONFIRMATION: - handleReachSentLimit((SmsTracker[]) (msg.obj)); - break; + case EVENT_SEND_LIMIT_REACHED_CONFIRMATION: + handleReachSentLimit((SmsTracker[]) (msg.obj)); + break; - case EVENT_CONFIRM_SEND_TO_POSSIBLE_PREMIUM_SHORT_CODE: - handleConfirmShortCode(false, (SmsTracker[]) (msg.obj)); - break; + case EVENT_CONFIRM_SEND_TO_POSSIBLE_PREMIUM_SHORT_CODE: + handleConfirmShortCode(false, (SmsTracker[]) (msg.obj)); + break; - case EVENT_CONFIRM_SEND_TO_PREMIUM_SHORT_CODE: - handleConfirmShortCode(true, (SmsTracker[]) (msg.obj)); - break; + case EVENT_CONFIRM_SEND_TO_PREMIUM_SHORT_CODE: + handleConfirmShortCode(true, (SmsTracker[]) (msg.obj)); + break; - case EVENT_SEND_CONFIRMED_SMS: - { - SmsTracker[] trackers = (SmsTracker[]) msg.obj; - for (SmsTracker tracker : trackers) { - sendSms(tracker); - } - mPendingTrackerCount--; - break; - } - - case EVENT_SENDING_NOT_ALLOWED: - { - SmsTracker[] trackers = (SmsTracker[]) msg.obj; - Rlog.d(TAG, "SMSDispatcher: EVENT_SENDING_NOT_ALLOWED - " - + "sending SHORT_CODE_NEVER_ALLOWED error code."); - handleSmsTrackersFailure( - trackers, SmsManager.RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED, NO_ERROR_CODE); - break; - } - - case EVENT_STOP_SENDING: - { - SmsTracker[] trackers = (SmsTracker[]) msg.obj; - int error; - if (msg.arg1 == ConfirmDialogListener.SHORT_CODE_MSG) { - if (msg.arg2 == ConfirmDialogListener.NEVER_ALLOW) { - error = SmsManager.RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED; - Rlog.d(TAG, "SMSDispatcher: EVENT_STOP_SENDING - " - + "sending SHORT_CODE_NEVER_ALLOWED error code."); - } else { - error = SmsManager.RESULT_ERROR_SHORT_CODE_NOT_ALLOWED; - Rlog.d(TAG, "SMSDispatcher: EVENT_STOP_SENDING - " - + "sending SHORT_CODE_NOT_ALLOWED error code."); + case EVENT_SEND_CONFIRMED_SMS: { + SmsTracker[] trackers = (SmsTracker[]) msg.obj; + for (SmsTracker tracker : trackers) { + sendSms(tracker); } - } else if (msg.arg1 == ConfirmDialogListener.RATE_LIMIT) { - error = SmsManager.RESULT_ERROR_LIMIT_EXCEEDED; - Rlog.d(TAG, "SMSDispatcher: EVENT_STOP_SENDING - " - + "sending LIMIT_EXCEEDED error code."); - } else { - error = SmsManager.RESULT_UNEXPECTED_EVENT_STOP_SENDING; - Rlog.e(TAG, "SMSDispatcher: EVENT_STOP_SENDING - unexpected cases."); + mPendingTrackerCount--; + break; } - handleSmsTrackersFailure(trackers, error, NO_ERROR_CODE); - mPendingTrackerCount--; - break; + case EVENT_SENDING_NOT_ALLOWED: { + SmsTracker[] trackers = (SmsTracker[]) msg.obj; + Rlog.d(TAG, "SMSDispatcher: EVENT_SENDING_NOT_ALLOWED - " + + "sending SHORT_CODE_NEVER_ALLOWED error code."); + handleSmsTrackersFailure( + trackers, SmsManager.RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED, NO_ERROR_CODE); + break; + } + + case EVENT_STOP_SENDING: { + SmsTracker[] trackers = (SmsTracker[]) msg.obj; + int error; + if (msg.arg1 == ConfirmDialogListener.SHORT_CODE_MSG) { + if (msg.arg2 == ConfirmDialogListener.NEVER_ALLOW) { + error = SmsManager.RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED; + Rlog.d(TAG, "SMSDispatcher: EVENT_STOP_SENDING - " + + "sending SHORT_CODE_NEVER_ALLOWED error code."); + } else { + error = SmsManager.RESULT_ERROR_SHORT_CODE_NOT_ALLOWED; + Rlog.d(TAG, "SMSDispatcher: EVENT_STOP_SENDING - " + + "sending SHORT_CODE_NOT_ALLOWED error code."); + } + } else if (msg.arg1 == ConfirmDialogListener.RATE_LIMIT) { + error = SmsManager.RESULT_ERROR_LIMIT_EXCEEDED; + Rlog.d(TAG, "SMSDispatcher: EVENT_STOP_SENDING - " + + "sending LIMIT_EXCEEDED error code."); + } else { + error = SmsManager.RESULT_UNEXPECTED_EVENT_STOP_SENDING; + Rlog.e(TAG, "SMSDispatcher: EVENT_STOP_SENDING - unexpected cases."); + } + + handleSmsTrackersFailure(trackers, error, NO_ERROR_CODE); + mPendingTrackerCount--; + break; + } + + case EVENT_NEW_SMS_STATUS_REPORT: + handleStatusReport(msg.obj); + break; + case EVENT_TPMR_SIM_UPDATE_RESPONSE: + handleMessageRefStatus(msg); + break; + + case EVENT_SIM_LOADED: + /* sim TPMR value is given higher priority if both are non-negative number. + Use case: + if sim was used on another device and inserted in a new device, + that device will start sending the next TPMR after reading from the SIM. + */ + mMessageRef = getTpmrValueFromSIM(); + if (mMessageRef == -1) { + SubscriptionInfoInternal subInfo = SubscriptionManagerService.getInstance() + .getSubscriptionInfoInternal(msg.arg1); + if (subInfo != null) { + mMessageRef = subInfo.getLastUsedTPMessageReference(); + } + } + break; + + default: + Rlog.e(TAG, "handleMessage() ignoring message of unexpected type " + msg.what); + } + } + + private void handleMessageRefStatus(Message msg) { + AsyncResult ar = (AsyncResult) msg.obj; + if (ar.exception != null) { + Rlog.e(TAG, "Failed to update TP - Message reference value to SIM " + ar.exception); + } else { + Rlog.d(TAG, "TP - Message reference updated to SIM Successfully"); + } + } + + private void updateTPMessageReference() { + updateSIMLastTPMRValue(mMessageRef); + final long identity = Binder.clearCallingIdentity(); + try { + SubscriptionManagerService.getInstance() + .setLastUsedTPMessageReference(getSubId(), mMessageRef); + } catch (SecurityException e) { + Rlog.e(TAG, "Security Exception caused on messageRef updation to DB " + e.getMessage()); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + private void updateSIMLastTPMRValue(int messageRef) { + Message msg = obtainMessage(EVENT_TPMR_SIM_UPDATE_RESPONSE); + IccRecords iccRecords = getIccRecords(); + if (iccRecords != null) { + iccRecords.setSmssTpmrValue(messageRef, msg); + } + } + + private int getTpmrValueFromSIM() { + IccRecords iccRecords = getIccRecords(); + if (iccRecords != null) { + return iccRecords.getSmssTpmrValue(); + } + return -1; + } + + private IccRecords getIccRecords() { + if (mPhone != null && mPhone.getIccRecords() != null) { + return mPhone.getIccRecords(); + } + return null; + } + + /** + * Returns the next TP message Reference value incremented by 1 for every sms sent . + * once a max of 255 is reached TP message Reference is reset to 0. + * + * @return messageRef TP message Reference value + */ + public int nextMessageRef() { + if (!isMessageRefIncrementViaTelephony()) { + return 0; } - case EVENT_NEW_SMS_STATUS_REPORT: - handleStatusReport(msg.obj); - break; + mMessageRef = (mMessageRef + 1) % 256; + updateTPMessageReference(); + return mMessageRef; + } - default: - Rlog.e(TAG, "handleMessage() ignoring message of unexpected type " + msg.what); + /** + * As modem is using the last used TP-MR value present in SIM card, increment of + * messageRef(TP-MR) value should be prevented (config_stk_sms_send_support set to false) + * at telephony framework. In future, config_stk_sms_send_support flag will be enabled + * so that messageRef(TP-MR) increment will be done at framework side only. + * + * TODO:- Need to have new flag to control writing TP-MR value to SIM or shared prefrence. + */ + public boolean isMessageRefIncrementViaTelephony() { + boolean isMessageRefIncrementEnabled = false; + try { + isMessageRefIncrementEnabled = mContext.getResources().getBoolean( + com.android.internal.R.bool.config_stk_sms_send_support); + } catch (NotFoundException e) { + Rlog.e(TAG, "isMessageRefIncrementViaTelephony NotFoundException Exception"); } + + Rlog.i(TAG, "bool.config_stk_sms_send_support= " + isMessageRefIncrementEnabled); + return isMessageRefIncrementEnabled; } /** @@ -569,26 +752,50 @@ @Override public void onSendSmsComplete(int result, int messageRef) { Rlog.d(TAG, "onSendSmsComplete: result=" + result + " messageRef=" + messageRef); - if (mCallbackCalled) { - logWithLocalLog("onSendSmsComplete: unexpected call"); - AnomalyReporter.reportAnomaly(sAnomalyUnexpectedCallback, - "Unexpected onSendSmsComplete", mPhone.getCarrierId()); + if (cleanupOnSendSmsComplete("onSendSmsComplete")) { return; } - mCallbackCalled = true; + final long identity = Binder.clearCallingIdentity(); try { - mSmsSender.mCarrierMessagingServiceWrapper.disconnect(); processSendSmsResponse(mSmsSender.getSmsTracker(), result, messageRef); - mSmsSender.removeTimeout(); } finally { Binder.restoreCallingIdentity(identity); } } + /** + * This method should be called only once. + */ @Override public void onSendMultipartSmsComplete(int result, int[] messageRefs) { - Rlog.e(TAG, "Unexpected onSendMultipartSmsComplete call with result: " + result); + Rlog.d(TAG, "onSendMultipartSmsComplete: result=" + result + " messageRefs=" + + Arrays.toString(messageRefs)); + if (cleanupOnSendSmsComplete("onSendMultipartSmsComplete")) { + return; + } + + final long identity = Binder.clearCallingIdentity(); + try { + processSendMultipartSmsResponse(mSmsSender.getSmsTrackers(), result, messageRefs); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + private boolean cleanupOnSendSmsComplete(String callingFunction) { + if (mCallbackCalled) { + logWithLocalLog(callingFunction + ": unexpected call"); + AnomalyReporter.reportAnomaly(sAnomalyUnexpectedCallback, + "Unexpected " + callingFunction, mPhone.getCarrierId()); + return true; + } + + mCallbackCalled = true; + mSmsSender.removeTimeout(); + mSmsSender.mCarrierMessagingServiceWrapper.disconnect(); + + return false; } @Override @@ -663,8 +870,7 @@ } @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - void sendSmsByCarrierApp(String carrierPackageName, - MultipartSmsSenderCallback senderCallback) { + void sendSmsByCarrierApp(String carrierPackageName, SmsSenderCallback senderCallback) { super.sendSmsByCarrierApp(carrierPackageName, senderCallback); } @@ -712,69 +918,6 @@ } } - /** - * Callback for MultipartSmsSender from the carrier messaging service. - * Once the result is ready, the carrier messaging service connection is disposed. - */ - private final class MultipartSmsSenderCallback implements CarrierMessagingCallback { - private final MultipartSmsSender mSmsSender; - private boolean mCallbackCalled = false; - - MultipartSmsSenderCallback(MultipartSmsSender smsSender) { - mSmsSender = smsSender; - } - - @Override - public void onSendSmsComplete(int result, int messageRef) { - Rlog.e(TAG, "Unexpected onSendSmsComplete call with result: " + result); - } - - /** - * This method should be called only once. - */ - @Override - public void onSendMultipartSmsComplete(int result, int[] messageRefs) { - Rlog.d(TAG, "onSendMultipartSmsComplete: result=" + result + " messageRefs=" - + Arrays.toString(messageRefs)); - if (mCallbackCalled) { - logWithLocalLog("onSendMultipartSmsComplete: unexpected call"); - AnomalyReporter.reportAnomaly(sAnomalyUnexpectedCallback, - "Unexpected onSendMultipartSmsComplete", mPhone.getCarrierId()); - return; - } - mCallbackCalled = true; - mSmsSender.removeTimeout(); - mSmsSender.mCarrierMessagingServiceWrapper.disconnect(); - - if (mSmsSender.mTrackers == null) { - Rlog.e(TAG, "Unexpected onSendMultipartSmsComplete call with null trackers."); - return; - } - - final long identity = Binder.clearCallingIdentity(); - try { - processSendMultipartSmsResponse(mSmsSender.mTrackers, result, messageRefs); - } finally { - Binder.restoreCallingIdentity(identity); - } - } - - @Override - public void onReceiveSmsComplete(int result) { - Rlog.e(TAG, "Unexpected onReceiveSmsComplete call with result: " + result); - } - - @Override - public void onSendMmsComplete(int result, byte[] sendConfPdu) { - Rlog.e(TAG, "Unexpected onSendMmsComplete call with result: " + result); - } - - @Override - public void onDownloadMmsComplete(int result) { - Rlog.e(TAG, "Unexpected onDownloadMmsComplete call with result: " + result); - } - } - private void processSendMultipartSmsResponse( SmsTracker[] trackers, int result, int[] messageRefs) { if (trackers == null) { @@ -919,7 +1062,7 @@ // This is retry after failure over IMS but voice is not available. // Set retry to max allowed, so no retry is sent and cause // SmsManager.RESULT_ERROR_GENERIC_FAILURE to be returned to app. - tracker.mRetryCount = MAX_SEND_RETRIES; + tracker.mRetryCount = getMaxSmsRetryCount(); Rlog.d(TAG, "handleSendComplete: Skipping retry: " + " isIms()=" + isIms() @@ -942,7 +1085,7 @@ tracker.isFromDefaultSmsApplication(mContext), tracker.getInterval()); } else if (error == SmsManager.RESULT_RIL_SMS_SEND_FAIL_RETRY - && tracker.mRetryCount < MAX_SEND_RETRIES) { + && tracker.mRetryCount < getMaxSmsRetryCount()) { // Retry after a delay if needed. // TODO: According to TS 23.040, 9.2.3.6, we should resend // with the same TP-MR as the failed message, and @@ -954,7 +1097,7 @@ tracker.mRetryCount++; int errorCode = (smsResponse != null) ? smsResponse.mErrorCode : NO_ERROR_CODE; Message retryMsg = obtainMessage(EVENT_SEND_RETRY, tracker); - sendMessageDelayed(retryMsg, SEND_RETRY_DELAY); + sendMessageDelayed(retryMsg, getSmsRetryDelayValue()); mPhone.getSmsStats().onOutgoingSms( tracker.mImsRetry > 0 /* isOverIms */, SmsConstants.FORMAT_3GPP2.equals(getFormat()), @@ -981,8 +1124,31 @@ } @SmsManager.Result - private static int rilErrorToSmsManagerResult(CommandException.Error rilError, + private int rilErrorToSmsManagerResult(CommandException.Error rilError, SmsTracker tracker) { + mSmsOutgoingErrorCodes.log("rilError: " + rilError + + ", MessageId: " + SmsController.formatCrossStackMessageId(tracker.mMessageId)); + + ApplicationInfo appInfo = tracker.getAppInfo(); + if (appInfo == null + || !CompatChanges.isChangeEnabled(ADD_MORE_SMS_SENDING_ERROR_CODES, appInfo.uid)) { + if (rilError == CommandException.Error.INVALID_RESPONSE + || rilError == CommandException.Error.SIM_PIN2 + || rilError == CommandException.Error.SIM_PUK2 + || rilError == CommandException.Error.SUBSCRIPTION_NOT_AVAILABLE + || rilError == CommandException.Error.SIM_ERR + || rilError == CommandException.Error.INVALID_SIM_STATE + || rilError == CommandException.Error.NO_SMS_TO_ACK + || rilError == CommandException.Error.SIM_BUSY + || rilError == CommandException.Error.SIM_FULL + || rilError == CommandException.Error.NO_SUBSCRIPTION + || rilError == CommandException.Error.NO_NETWORK_FOUND + || rilError == CommandException.Error.DEVICE_IN_USE + || rilError == CommandException.Error.ABORTED) { + return SmsManager.RESULT_ERROR_GENERIC_FAILURE; + } + } + switch (rilError) { case RADIO_NOT_AVAILABLE: return SmsManager.RESULT_RIL_RADIO_NOT_AVAILABLE; @@ -1032,6 +1198,34 @@ return SmsManager.RESULT_RIL_ACCESS_BARRED; case BLOCKED_DUE_TO_CALL: return SmsManager.RESULT_RIL_BLOCKED_DUE_TO_CALL; + case INVALID_SMSC_ADDRESS: + return SmsManager.RESULT_INVALID_SMSC_ADDRESS; + case INVALID_RESPONSE: + return SmsManager.RESULT_RIL_INVALID_RESPONSE; + case SIM_PIN2: + return SmsManager.RESULT_RIL_SIM_PIN2; + case SIM_PUK2: + return SmsManager.RESULT_RIL_SIM_PUK2; + case SUBSCRIPTION_NOT_AVAILABLE: + return SmsManager.RESULT_RIL_SUBSCRIPTION_NOT_AVAILABLE; + case SIM_ERR: + return SmsManager.RESULT_RIL_SIM_ERROR; + case INVALID_SIM_STATE: + return SmsManager.RESULT_RIL_INVALID_SIM_STATE; + case NO_SMS_TO_ACK: + return SmsManager.RESULT_RIL_NO_SMS_TO_ACK; + case SIM_BUSY: + return SmsManager.RESULT_RIL_SIM_BUSY; + case SIM_FULL: + return SmsManager.RESULT_RIL_SIM_FULL; + case NO_SUBSCRIPTION: + return SmsManager.RESULT_RIL_NO_SUBSCRIPTION; + case NO_NETWORK_FOUND: + return SmsManager.RESULT_RIL_NO_NETWORK_FOUND; + case DEVICE_IN_USE: + return SmsManager.RESULT_RIL_DEVICE_IN_USE; + case ABORTED: + return SmsManager.RESULT_RIL_ABORTED; default: Rlog.d(TAG, "rilErrorToSmsManagerResult: " + rilError + " " + SmsController.formatCrossStackMessageId(tracker.mMessageId)); @@ -1133,14 +1327,15 @@ @UnsupportedAppUsage protected void sendData(String callingPackage, String destAddr, String scAddr, int destPort, byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent, boolean isForVvm) { + int messageRef = nextMessageRef(); SmsMessageBase.SubmitPduBase pdu = getSubmitPdu( - scAddr, destAddr, destPort, data, (deliveryIntent != null)); + scAddr, destAddr, destPort, data, (deliveryIntent != null), messageRef); if (pdu != null) { HashMap map = getSmsTrackerMap(destAddr, scAddr, destPort, data, pdu); SmsTracker tracker = getSmsTracker(callingPackage, map, sentIntent, deliveryIntent, getFormat(), null /*messageUri*/, false /*expectMore*/, null /*fullMessageText*/, false /*isText*/, - true /*persistMessage*/, isForVvm, 0L /* messageId */); + true /*persistMessage*/, isForVvm, 0L /* messageId */, messageRef); if (!sendSmsByCarrierApp(true /* isDataSms */, tracker)) { sendSubmitPdu(tracker); @@ -1252,18 +1447,131 @@ * Used for logging and diagnostics purposes. The id may be NULL. */ public void sendText(String destAddr, String scAddr, String text, + PendingIntent sentIntent, PendingIntent deliveryIntent, Uri messageUri, + String callingPkg, boolean persistMessage, int priority, + boolean expectMore, int validityPeriod, boolean isForVvm, + long messageId) { + sendText(destAddr, scAddr, text, sentIntent, deliveryIntent, messageUri, callingPkg, + persistMessage, priority, expectMore, validityPeriod, isForVvm, messageId, false); + } + + /** + * Send a text based SMS. + * + * @param destAddr the address to send the message to + * @param scAddr is the service center address or null to use + * the current default SMSC + * @param text the body of the message to send + * @param sentIntent if not NULL this <code>PendingIntent</code> is + * broadcast when the message is successfully sent, or failed. + * The result code will be <code>Activity.RESULT_OK<code> for success, + * or one of these errors:<br> + * <code>SmsManager.RESULT_ERROR_GENERIC_FAILURE</code><br> + * <code>SmsManager.RESULT_ERROR_RADIO_OFF</code><br> + * <code>SmsManager.RESULT_ERROR_NULL_PDU</code><br> + * <code>SmsManager.RESULT_ERROR_NO_SERVICE</code><br> + * <code>SmsManager.RESULT_ERROR_LIMIT_EXCEEDED</code><br> + * <code>SmsManager.RESULT_ERROR_FDN_CHECK_FAILURE</code><br> + * <code>SmsManager.RESULT_ERROR_SHORT_CODE_NOT_ALLOWED</code><br> + * <code>SmsManager.RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED</code><br> + * <code>SmsManager.RESULT_RADIO_NOT_AVAILABLE</code><br> + * <code>SmsManager.RESULT_NETWORK_REJECT</code><br> + * <code>SmsManager.RESULT_INVALID_ARGUMENTS</code><br> + * <code>SmsManager.RESULT_INVALID_STATE</code><br> + * <code>SmsManager.RESULT_NO_MEMORY</code><br> + * <code>SmsManager.RESULT_INVALID_SMS_FORMAT</code><br> + * <code>SmsManager.RESULT_SYSTEM_ERROR</code><br> + * <code>SmsManager.RESULT_MODEM_ERROR</code><br> + * <code>SmsManager.RESULT_NETWORK_ERROR</code><br> + * <code>SmsManager.RESULT_ENCODING_ERROR</code><br> + * <code>SmsManager.RESULT_INVALID_SMSC_ADDRESS</code><br> + * <code>SmsManager.RESULT_OPERATION_NOT_ALLOWED</code><br> + * <code>SmsManager.RESULT_INTERNAL_ERROR</code><br> + * <code>SmsManager.RESULT_NO_RESOURCES</code><br> + * <code>SmsManager.RESULT_CANCELLED</code><br> + * <code>SmsManager.RESULT_REQUEST_NOT_SUPPORTED</code><br> + * <code>SmsManager.RESULT_NO_BLUETOOTH_SERVICE</code><br> + * <code>SmsManager.RESULT_INVALID_BLUETOOTH_ADDRESS</code><br> + * <code>SmsManager.RESULT_BLUETOOTH_DISCONNECTED</code><br> + * <code>SmsManager.RESULT_UNEXPECTED_EVENT_STOP_SENDING</code><br> + * <code>SmsManager.RESULT_SMS_BLOCKED_DURING_EMERGENCY</code><br> + * <code>SmsManager.RESULT_SMS_SEND_RETRY_FAILED</code><br> + * <code>SmsManager.RESULT_REMOTE_EXCEPTION</code><br> + * <code>SmsManager.RESULT_NO_DEFAULT_SMS_APP</code><br> + * <code>SmsManager.RESULT_RIL_RADIO_NOT_AVAILABLE</code><br> + * <code>SmsManager.RESULT_RIL_SMS_SEND_FAIL_RETRY</code><br> + * <code>SmsManager.RESULT_RIL_NETWORK_REJECT</code><br> + * <code>SmsManager.RESULT_RIL_INVALID_STATE</code><br> + * <code>SmsManager.RESULT_RIL_INVALID_ARGUMENTS</code><br> + * <code>SmsManager.RESULT_RIL_NO_MEMORY</code><br> + * <code>SmsManager.RESULT_RIL_REQUEST_RATE_LIMITED</code><br> + * <code>SmsManager.RESULT_RIL_INVALID_SMS_FORMAT</code><br> + * <code>SmsManager.RESULT_RIL_SYSTEM_ERR</code><br> + * <code>SmsManager.RESULT_RIL_ENCODING_ERR</code><br> + * <code>SmsManager.RESULT_RIL_INVALID_SMSC_ADDRESS</code><br> + * <code>SmsManager.RESULT_RIL_MODEM_ERR</code><br> + * <code>SmsManager.RESULT_RIL_NETWORK_ERR</code><br> + * <code>SmsManager.RESULT_RIL_INTERNAL_ERR</code><br> + * <code>SmsManager.RESULT_RIL_REQUEST_NOT_SUPPORTED</code><br> + * <code>SmsManager.RESULT_RIL_INVALID_MODEM_STATE</code><br> + * <code>SmsManager.RESULT_RIL_NETWORK_NOT_READY</code><br> + * <code>SmsManager.RESULT_RIL_OPERATION_NOT_ALLOWED</code><br> + * <code>SmsManager.RESULT_RIL_NO_RESOURCES</code><br> + * <code>SmsManager.RESULT_RIL_CANCELLED</code><br> + * <code>SmsManager.RESULT_RIL_SIM_ABSENT</code><br> + * <code>SmsManager.RESULT_RIL_SIMULTANEOUS_SMS_AND_CALL_NOT_ALLOWED</code><br> + * <code>SmsManager.RESULT_RIL_ACCESS_BARRED</code><br> + * <code>SmsManager.RESULT_RIL_BLOCKED_DUE_TO_CALL</code><br> + * For <code>SmsManager.RESULT_ERROR_GENERIC_FAILURE</code> or any of the RESULT_RIL errors, + * the sentIntent may include the extra "errorCode" containing a radio technology specific + * value, generally only useful for troubleshooting.<br> + * The per-application based SMS control checks sentIntent. If sentIntent + * is NULL the caller will be checked against all unknown applications, + * which cause smaller number of SMS to be sent in checking period. + * @param deliveryIntent if not NULL this <code>PendingIntent</code> is + * broadcast when the message is delivered to the recipient. The + * @param messageUri optional URI of the message if it is already stored in the system + * @param callingPkg the calling package name + * @param persistMessage whether to save the sent message into SMS DB for a + * non-default SMS app. + * + * @param priority Priority level of the message + * Refer specification See 3GPP2 C.S0015-B, v2.0, table 4.5.9-1 + * --------------------------------- + * PRIORITY | Level of Priority + * --------------------------------- + * '00' | Normal + * '01' | Interactive + * '10' | Urgent + * '11' | Emergency + * ---------------------------------- + * Any Other values included Negative considered as Invalid Priority Indicator of the message. + * @param expectMore is a boolean to indicate the sending messages through same link or not. + * @param validityPeriod Validity Period of the message in mins. + * Refer specification 3GPP TS 23.040 V6.8.1 section 9.2.3.12.1. + * Validity Period(Minimum) -> 5 mins + * Validity Period(Maximum) -> 635040 mins(i.e.63 weeks). + * Any Other values included Negative considered as Invalid Validity Period of the message. + * @param messageId An id that uniquely identifies the message requested to be sent. + * Used for logging and diagnostics purposes. The id may be NULL. + * @param skipShortCodeCheck Skip check for short code type destination address. + */ + public void sendText(String destAddr, String scAddr, String text, PendingIntent sentIntent, PendingIntent deliveryIntent, Uri messageUri, String callingPkg, boolean persistMessage, int priority, boolean expectMore, int validityPeriod, boolean isForVvm, - long messageId) { + long messageId, boolean skipShortCodeCheck) { Rlog.d(TAG, "sendText id: " + SmsController.formatCrossStackMessageId(messageId)); + int messageRef = nextMessageRef(); SmsMessageBase.SubmitPduBase pdu = getSubmitPdu( - scAddr, destAddr, text, (deliveryIntent != null), null, priority, validityPeriod); + scAddr, destAddr, text, (deliveryIntent != null), null, priority, validityPeriod, + messageRef); if (pdu != null) { HashMap map = getSmsTrackerMap(destAddr, scAddr, text, pdu); SmsTracker tracker = getSmsTracker(callingPkg, map, sentIntent, deliveryIntent, getFormat(), messageUri, expectMore, text, true /*isText*/, - persistMessage, priority, validityPeriod, isForVvm, messageId); + persistMessage, priority, validityPeriod, isForVvm, messageId, messageRef, + skipShortCodeCheck); if (!sendSmsByCarrierApp(false /* isDataSms */, tracker)) { sendSubmitPdu(tracker); @@ -1319,6 +1627,14 @@ protected abstract SmsMessageBase.SubmitPduBase getSubmitPdu(String scAddr, String destAddr, int destPort, byte[] message, boolean statusReportRequested); + protected abstract SmsMessageBase.SubmitPduBase getSubmitPdu(String scAddr, String destAddr, + String message, boolean statusReportRequested, SmsHeader smsHeader, + int priority, int validityPeriod, int messageRef); + + + protected abstract SmsMessageBase.SubmitPduBase getSubmitPdu(String scAddr, String destAddr, + int destPort, byte[] message, boolean statusReportRequested, int messageRef); + /** * Calculate the number of septets needed to encode the message. This function should only be * called for individual segments of multipart message. @@ -1494,12 +1810,13 @@ if (deliveryIntents != null && deliveryIntents.size() > i) { deliveryIntent = deliveryIntents.get(i); } - + int messageRef = nextMessageRef(); trackers[i] = getNewSubmitPduTracker(callingPkg, destAddr, scAddr, parts.get(i), smsHeader, encoding, sentIntent, deliveryIntent, (i == (msgCount - 1)), unsentPartCount, anyPartFailed, messageUri, - fullMessageText, priority, expectMore, validityPeriod, messageId); + fullMessageText, priority, expectMore, validityPeriod, messageId, + messageRef); if (trackers[i] == null) { triggerSentIntentForFailure(sentIntents); return; @@ -1509,12 +1826,10 @@ String carrierPackage = getCarrierAppPackageName(); if (carrierPackage != null) { - Rlog.d(TAG, "Found carrier package " + carrierPackage - + " " + Rlog.d(TAG, "Found carrier package " + carrierPackage + " " + SmsController.formatCrossStackMessageId(getMultiTrackermessageId(trackers))); MultipartSmsSender smsSender = new MultipartSmsSender(parts, trackers); - smsSender.sendSmsByCarrierApp(carrierPackage, - new MultipartSmsSenderCallback(smsSender)); + smsSender.sendSmsByCarrierApp(carrierPackage, new SmsSenderCallback(smsSender)); } else { Rlog.v(TAG, "No carrier package. " + SmsController.formatCrossStackMessageId(getMultiTrackermessageId(trackers))); @@ -1537,7 +1852,7 @@ PendingIntent sentIntent, PendingIntent deliveryIntent, boolean lastPart, AtomicInteger unsentPartCount, AtomicBoolean anyPartFailed, Uri messageUri, String fullMessageText, int priority, boolean expectMore, int validityPeriod, - long messageId) { + long messageId, int messageRef) { if (isCdmaMo()) { UserData uData = new UserData(); uData.payloadStr = message; @@ -1567,7 +1882,7 @@ getFormat(), unsentPartCount, anyPartFailed, messageUri, smsHeader, (!lastPart || expectMore), fullMessageText, true /*isText*/, true /*persistMessage*/, priority, validityPeriod, false /* isForVvm */, - messageId); + messageId, messageRef, false); } else { Rlog.e(TAG, "CdmaSMSDispatcher.getNewSubmitPduTracker(): getSubmitPdu() returned " + "null " + SmsController.formatCrossStackMessageId(messageId)); @@ -1578,15 +1893,14 @@ com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu(scAddress, destinationAddress, message, deliveryIntent != null, SmsHeader.toByteArray(smsHeader), encoding, smsHeader.languageTable, - smsHeader.languageShiftTable, validityPeriod); + smsHeader.languageShiftTable, validityPeriod, messageRef); if (pdu != null) { - HashMap map = getSmsTrackerMap(destinationAddress, scAddress, - message, pdu); + HashMap map = getSmsTrackerMap(destinationAddress, scAddress, message, pdu); return getSmsTracker(callingPackage, map, sentIntent, deliveryIntent, getFormat(), unsentPartCount, anyPartFailed, messageUri, smsHeader, (!lastPart || expectMore), fullMessageText, true /*isText*/, false /*persistMessage*/, priority, validityPeriod, false /* isForVvm */, - messageId); + messageId, messageRef, false); } else { Rlog.e(TAG, "GsmSMSDispatcher.getNewSubmitPduTracker(): getSubmitPdu() returned " + "null " + SmsController.formatCrossStackMessageId(messageId)); @@ -1744,7 +2058,8 @@ */ boolean checkDestination(SmsTracker[] trackers) { if (mContext.checkCallingOrSelfPermission(SEND_SMS_NO_CONFIRMATION) - == PackageManager.PERMISSION_GRANTED || trackers[0].mIsForVvm) { + == PackageManager.PERMISSION_GRANTED || trackers[0].mIsForVvm + || trackers[0].mSkipShortCodeDestAddrCheck) { return true; // app is pre-approved to send to short codes } else { int rule = mPremiumSmsRule.get(); @@ -1785,6 +2100,12 @@ trackers[0].mDestAddress, networkCountryIso)); } + if (smsCategory != SmsManager.SMS_CATEGORY_NOT_SHORT_CODE) { + int xmlVersion = mSmsDispatchersController.getUsageMonitor() + .getShortCodeXmlFileVersion(); + mPhone.getSmsStats().onOutgoingShortCodeSms(smsCategory, xmlVersion); + } + if (smsCategory == SmsManager.SMS_CATEGORY_NOT_SHORT_CODE || smsCategory == SmsManager.SMS_CATEGORY_FREE_SHORT_CODE || smsCategory == SmsManager.SMS_CATEGORY_STANDARD_SHORT_CODE) { @@ -2082,6 +2403,7 @@ private Boolean mIsFromDefaultSmsApplication; private int mCarrierId; + private boolean mSkipShortCodeDestAddrCheck; // SMS anomaly uuid -- unexpected error from RIL private final UUID mAnomalyUnexpectedErrorFromRilUUID = UUID.fromString("43043600-ea7a-44d2-9ae6-a58567ac7886"); @@ -2091,7 +2413,8 @@ AtomicInteger unsentPartCount, AtomicBoolean anyPartFailed, Uri messageUri, SmsHeader smsHeader, boolean expectMore, String fullMessageText, int subId, boolean isText, boolean persistMessage, int userId, int priority, - int validityPeriod, boolean isForVvm, long messageId, int carrierId) { + int validityPeriod, boolean isForVvm, long messageId, int carrierId, + int messageRef, boolean skipShortCodeDestAddrCheck) { mData = data; mSentIntent = sentIntent; mDeliveryIntent = deliveryIntent; @@ -2102,7 +2425,7 @@ mExpectMore = expectMore; mImsRetry = 0; mUsesImsServiceForIms = false; - mMessageRef = 0; + mMessageRef = messageRef; mUnsentPartCount = unsentPartCount; mAnyPartFailed = anyPartFailed; mMessageUri = messageUri; @@ -2117,6 +2440,7 @@ mIsForVvm = isForVvm; mMessageId = messageId; mCarrierId = carrierId; + mSkipShortCodeDestAddrCheck = skipShortCodeDestAddrCheck; } public HashMap<String, Object> getData() { @@ -2131,12 +2455,21 @@ return mAppInfo != null ? mAppInfo.packageName : null; } + /** + * Get the calling Application Info + * @return Application Info + */ + public ApplicationInfo getAppInfo() { + return mAppInfo == null ? null : mAppInfo.applicationInfo; + } + /** Return if the SMS was originated from the default SMS application. */ public boolean isFromDefaultSmsApplication(Context context) { if (mIsFromDefaultSmsApplication == null) { + UserHandle userHandle = TelephonyUtils.getSubscriptionUserHandle(context, mSubId); // Perform a lazy initialization, due to the cost of the operation. - mIsFromDefaultSmsApplication = - SmsApplication.isDefaultSmsApplication(context, getAppPackageName()); + mIsFromDefaultSmsApplication = SmsApplication.isDefaultSmsApplicationAsUser(context, + getAppPackageName(), userHandle); } return mIsFromDefaultSmsApplication; } @@ -2384,7 +2717,7 @@ AtomicInteger unsentPartCount, AtomicBoolean anyPartFailed, Uri messageUri, SmsHeader smsHeader, boolean expectMore, String fullMessageText, boolean isText, boolean persistMessage, int priority, int validityPeriod, boolean isForVvm, - long messageId) { + long messageId, int messageRef, boolean skipShortCodeCheck) { // Get package info via packagemanager UserHandle callingUser = UserHandle.getUserHandleForUid(Binder.getCallingUid()); final int userId = callingUser.getIdentifier(); @@ -2401,28 +2734,30 @@ return new SmsTracker(data, sentIntent, deliveryIntent, appInfo, destAddr, format, unsentPartCount, anyPartFailed, messageUri, smsHeader, expectMore, fullMessageText, getSubId(), isText, persistMessage, userId, priority, - validityPeriod, isForVvm, messageId, mPhone.getCarrierId()); + validityPeriod, isForVvm, messageId, mPhone.getCarrierId(), messageRef, + skipShortCodeCheck); } protected SmsTracker getSmsTracker(String callingPackage, HashMap<String, Object> data, PendingIntent sentIntent, PendingIntent deliveryIntent, String format, Uri messageUri, boolean expectMore, String fullMessageText, boolean isText, boolean persistMessage, - boolean isForVvm, long messageId) { + boolean isForVvm, long messageId, int messageRef) { return getSmsTracker(callingPackage, data, sentIntent, deliveryIntent, format, null/*unsentPartCount*/, null/*anyPartFailed*/, messageUri, null/*smsHeader*/, expectMore, fullMessageText, isText, persistMessage, SMS_MESSAGE_PRIORITY_NOT_SPECIFIED, SMS_MESSAGE_PERIOD_NOT_SPECIFIED, isForVvm, - messageId); + messageId, messageRef, false); } protected SmsTracker getSmsTracker(String callingPackage, HashMap<String, Object> data, PendingIntent sentIntent, PendingIntent deliveryIntent, String format, Uri messageUri, boolean expectMore, String fullMessageText, boolean isText, boolean persistMessage, - int priority, int validityPeriod, boolean isForVvm, long messageId) { + int priority, int validityPeriod, boolean isForVvm, long messageId, int messageRef, + boolean skipShortCodeCheck) { return getSmsTracker(callingPackage, data, sentIntent, deliveryIntent, format, null/*unsentPartCount*/, null/*anyPartFailed*/, messageUri, null/*smsHeader*/, expectMore, fullMessageText, isText, persistMessage, priority, validityPeriod, - isForVvm, messageId); + isForVvm, messageId, messageRef, skipShortCodeCheck); } protected HashMap<String, Object> getSmsTrackerMap(String destAddr, String scAddr, @@ -2591,7 +2926,7 @@ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) protected int getSubId() { - return SubscriptionController.getInstance().getSubIdUsingPhoneId(mPhone.getPhoneId()); + return SubscriptionManager.getSubscriptionId(mPhone.getPhoneId()); } @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @@ -2641,10 +2976,17 @@ IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, " "); pw.println(TAG); pw.increaseIndent(); + pw.println("mLocalLog:"); pw.increaseIndent(); mLocalLog.dump(fd, pw, args); pw.decreaseIndent(); + + pw.println("mSmsOutgoingErrorCodes:"); + pw.increaseIndent(); + mSmsOutgoingErrorCodes.dump(fd, pw, args); + pw.decreaseIndent(); + pw.decreaseIndent(); } }
diff --git a/src/java/com/android/internal/telephony/ServiceStateTracker.java b/src/java/com/android/internal/telephony/ServiceStateTracker.java old mode 100755 new mode 100644 index 751d59a..50eea7f --- a/src/java/com/android/internal/telephony/ServiceStateTracker.java +++ b/src/java/com/android/internal/telephony/ServiceStateTracker.java
@@ -37,7 +37,6 @@ import android.content.SharedPreferences; import android.content.res.Resources; import android.hardware.radio.V1_0.CellInfoType; -import android.net.NetworkCapabilities; import android.os.AsyncResult; import android.os.BaseBundle; import android.os.Build; @@ -72,7 +71,6 @@ import android.telephony.RadioAccessFamily; import android.telephony.ServiceState; import android.telephony.ServiceState.RilRadioTechnology; -import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener; import android.telephony.TelephonyManager; @@ -93,11 +91,14 @@ import com.android.internal.telephony.cdnr.CarrierDisplayNameData; import com.android.internal.telephony.cdnr.CarrierDisplayNameResolver; import com.android.internal.telephony.data.AccessNetworksManager; +import com.android.internal.telephony.data.AccessNetworksManager.AccessNetworksManagerCallback; import com.android.internal.telephony.data.DataNetwork; import com.android.internal.telephony.data.DataNetworkController.DataNetworkControllerCallback; -import com.android.internal.telephony.dataconnection.DataConnection; +import com.android.internal.telephony.imsphone.ImsPhone; import com.android.internal.telephony.metrics.ServiceStateStats; import com.android.internal.telephony.metrics.TelephonyMetrics; +import com.android.internal.telephony.subscription.SubscriptionInfoInternal; +import com.android.internal.telephony.subscription.SubscriptionManagerService; import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppState; import com.android.internal.telephony.uicc.IccCardStatus.CardState; import com.android.internal.telephony.uicc.IccRecords; @@ -146,7 +147,7 @@ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) private UiccController mUiccController = null; @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - private UiccCardApplication mUiccApplcation = null; + private UiccCardApplication mUiccApplication = null; @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) private IccRecords mIccRecords = null; @@ -182,13 +183,12 @@ private final Set<Integer> mRadioPowerOffReasons = new HashSet(); - // TODO - this should not be public, right now used externally GsmConnetion. + // TODO - this should not be public, right now used externally GsmConnection. public RestrictedState mRestrictedState; /** - * A unique identifier to track requests associated with a poll - * and ignore stale responses. The value is a count-down of - * expected responses in this pollingContext. + * A unique identifier to track requests associated with a poll and ignore stale responses. + * The value is a count-down of expected responses in this pollingContext. */ @VisibleForTesting public int[] mPollingContext; @@ -217,13 +217,12 @@ private RegistrantList mNrStateChangedRegistrants = new RegistrantList(); private RegistrantList mNrFrequencyChangedRegistrants = new RegistrantList(); private RegistrantList mCssIndicatorChangedRegistrants = new RegistrantList(); - private final RegistrantList mBandwidthChangedRegistrants = new RegistrantList(); private final RegistrantList mAirplaneModeChangedRegistrants = new RegistrantList(); private final RegistrantList mAreaCodeChangedRegistrants = new RegistrantList(); - /* Radio power off pending flag and tag counter */ - private boolean mPendingRadioPowerOffAfterDataOff = false; - private int mPendingRadioPowerOffAfterDataOffTag = 0; + /* Radio power off pending flag */ + // @GuardedBy("this") + private volatile boolean mPendingRadioPowerOffAfterDataOff = false; /** Waiting period before recheck gprs and voice registration. */ public static final int DEFAULT_GPRS_CHECK_PERIOD_MILLIS = 60 * 1000; @@ -280,12 +279,10 @@ protected static final int EVENT_RADIO_POWER_OFF_DONE = 54; protected static final int EVENT_PHYSICAL_CHANNEL_CONFIG = 55; protected static final int EVENT_CELL_LOCATION_RESPONSE = 56; - protected static final int EVENT_CARRIER_CONFIG_CHANGED = 57; private static final int EVENT_POLL_STATE_REQUEST = 58; // Timeout event used when delaying radio power off to wait for IMS deregistration to happen. private static final int EVENT_POWER_OFF_RADIO_IMS_DEREG_TIMEOUT = 62; protected static final int EVENT_RESET_LAST_KNOWN_CELL_IDENTITY = 63; - private static final int EVENT_REGISTER_DATA_NETWORK_EXISTING_CHANGED = 64; // Telecom has un/registered a PhoneAccount that provides OTT voice calling capability, e.g. // wi-fi calling. protected static final int EVENT_TELECOM_VOICE_SERVICE_STATE_OVERRIDE_CHANGED = 65; @@ -312,7 +309,7 @@ // Show PLMN only and only if this bit is set. public static final int CARRIER_NAME_DISPLAY_BITMASK_SHOW_PLMN = 1 << 1; - private List<Message> mPendingCellInfoRequests = new LinkedList<Message>(); + private List<Message> mPendingCellInfoRequests = new LinkedList<>(); // @GuardedBy("mPendingCellInfoRequests") private boolean mIsPendingCellInfoRequest = false; @@ -323,14 +320,10 @@ private CarrierDisplayNameResolver mCdnr; private boolean mImsRegistrationOnOff = false; - /** Radio is disabled by carrier. Radio power will not be override if this field is set */ - private boolean mRadioDisabledByCarrier = false; @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) private boolean mDeviceShuttingDown = false; /** Keep track of SPN display rules, so we only broadcast intent if something changes. */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - private boolean mSpnUpdatePending = false; - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) private String mCurSpn = null; @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) private String mCurDataSpn = null; @@ -349,13 +342,11 @@ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) private SubscriptionManager mSubscriptionManager; - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - private SubscriptionController mSubscriptionController; + private SubscriptionManagerService mSubscriptionManagerService; @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) private final SstSubscriptionsChangedListener mOnSubscriptionsChangedListener = new SstSubscriptionsChangedListener(); - private final RatRatcheter mRatRatcheter; private final LocaleTracker mLocaleTracker; @@ -368,6 +359,7 @@ private final LocalLog mCdnrLogs = new LocalLog(64); private Pattern mOperatorNameStringPattern; + private PersistableBundle mCarrierConfig; private class SstSubscriptionsChangedListener extends OnSubscriptionsChangedListener { @@ -387,7 +379,6 @@ // If not, then the subId has changed, so we need to remember the old subId, // even if the new subId is invalid (likely). mPrevSubId = mSubId; - mSubId = curSubId; // Update voicemail count and notify message waiting changed regardless of // whether the new subId is valid. This is an exception to the general logic @@ -396,72 +387,64 @@ // which seems desirable. mPhone.updateVoiceMail(); - if (!SubscriptionManager.isValidSubscriptionId(mSubId)) { + if (!SubscriptionManager.isValidSubscriptionId(curSubId)) { if (SubscriptionManager.isValidSubscriptionId(mPrevSubId)) { // just went from valid to invalid subId, so notify phone state listeners // with final broadcast mPhone.notifyServiceStateChangedForSubId(mOutOfServiceSS, ServiceStateTracker.this.mPrevSubId); } - // If the new subscription ID isn't valid, then we don't need to do all the - // UI updating, so we're done. - return; + } else { + Context context = mPhone.getContext(); + + mPhone.notifyPhoneStateChanged(); + + if (!SubscriptionManager.isValidSubscriptionId(mPrevSubId)) { + // just went from invalid to valid subId, so notify with current service + // state in case our service state was never broadcasted (we don't notify + // service states when the subId is invalid) + mPhone.notifyServiceStateChanged(mPhone.getServiceState()); + } + + boolean restoreSelection = !context.getResources().getBoolean( + com.android.internal.R.bool.skip_restoring_network_selection); + mPhone.sendSubscriptionSettings(restoreSelection); + + setDataNetworkTypeForPhone(mSS.getRilDataRadioTechnology()); + + // Remove old network selection sharedPreferences since SP key names are now + // changed to include subId. This will be done only once when upgrading from an + // older build that did not include subId in the names. + SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences( + context); + String oldNetworkSelection = sp.getString( + Phone.NETWORK_SELECTION_KEY, ""); + String oldNetworkSelectionName = sp.getString( + Phone.NETWORK_SELECTION_NAME_KEY, ""); + String oldNetworkSelectionShort = sp.getString( + Phone.NETWORK_SELECTION_SHORT_KEY, ""); + if (!TextUtils.isEmpty(oldNetworkSelection) + || !TextUtils.isEmpty(oldNetworkSelectionName) + || !TextUtils.isEmpty(oldNetworkSelectionShort)) { + SharedPreferences.Editor editor = sp.edit(); + editor.putString(Phone.NETWORK_SELECTION_KEY + curSubId, + oldNetworkSelection); + editor.putString(Phone.NETWORK_SELECTION_NAME_KEY + curSubId, + oldNetworkSelectionName); + editor.putString(Phone.NETWORK_SELECTION_SHORT_KEY + curSubId, + oldNetworkSelectionShort); + editor.remove(Phone.NETWORK_SELECTION_KEY); + editor.remove(Phone.NETWORK_SELECTION_NAME_KEY); + editor.remove(Phone.NETWORK_SELECTION_SHORT_KEY); + editor.commit(); + } + + // Once sub id becomes valid, we need to update the service provider name + // displayed on the UI again. The old SPN update intents sent to + // MobileSignalController earlier were actually ignored due to invalid sub id. + updateSpnDisplay(); } - - Context context = mPhone.getContext(); - - mPhone.notifyPhoneStateChanged(); - - if (!SubscriptionManager.isValidSubscriptionId(mPrevSubId)) { - // just went from invalid to valid subId, so notify with current service - // state in case our service state was never broadcasted (we don't notify - // service states when the subId is invalid) - mPhone.notifyServiceStateChanged(mPhone.getServiceState()); - } - - boolean restoreSelection = !context.getResources().getBoolean( - com.android.internal.R.bool.skip_restoring_network_selection); - mPhone.sendSubscriptionSettings(restoreSelection); - - setDataNetworkTypeForPhone(mSS.getRilDataRadioTechnology()); - - if (mSpnUpdatePending) { - mSubscriptionController.setPlmnSpn(mPhone.getPhoneId(), mCurShowPlmn, - mCurPlmn, mCurShowSpn, mCurSpn); - mSpnUpdatePending = false; - } - - // Remove old network selection sharedPreferences since SP key names are now - // changed to include subId. This will be done only once when upgrading from an - // older build that did not include subId in the names. - SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences( - context); - String oldNetworkSelection = sp.getString( - Phone.NETWORK_SELECTION_KEY, ""); - String oldNetworkSelectionName = sp.getString( - Phone.NETWORK_SELECTION_NAME_KEY, ""); - String oldNetworkSelectionShort = sp.getString( - Phone.NETWORK_SELECTION_SHORT_KEY, ""); - if (!TextUtils.isEmpty(oldNetworkSelection) - || !TextUtils.isEmpty(oldNetworkSelectionName) - || !TextUtils.isEmpty(oldNetworkSelectionShort)) { - SharedPreferences.Editor editor = sp.edit(); - editor.putString(Phone.NETWORK_SELECTION_KEY + mSubId, - oldNetworkSelection); - editor.putString(Phone.NETWORK_SELECTION_NAME_KEY + mSubId, - oldNetworkSelectionName); - editor.putString(Phone.NETWORK_SELECTION_SHORT_KEY + mSubId, - oldNetworkSelectionShort); - editor.remove(Phone.NETWORK_SELECTION_KEY); - editor.remove(Phone.NETWORK_SELECTION_NAME_KEY); - editor.remove(Phone.NETWORK_SELECTION_SHORT_KEY); - editor.commit(); - } - - // Once sub id becomes valid, we need to update the service provider name - // displayed on the UI again. The old SPN update intents sent to - // MobileSignalController earlier were actually ignored due to invalid sub id. - updateSpnDisplay(); + mSubId = curSubId; } }; @@ -560,15 +543,13 @@ @Override public void onReceive(Context context, Intent intent) { final String action = intent.getAction(); - if (action.equals(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)) { - int phoneId = intent.getExtras().getInt(CarrierConfigManager.EXTRA_SLOT_INDEX); - // Ignore the carrier config changed if the phoneId is not matched. - if (phoneId == mPhone.getPhoneId()) { - sendEmptyMessage(EVENT_CARRIER_CONFIG_CHANGED); - } - } else if (action.equals(Intent.ACTION_LOCALE_CHANGED)) { + if (action.equals(Intent.ACTION_LOCALE_CHANGED)) { + log("ACTION_LOCALE_CHANGED"); // Update emergency string or operator name, polling service state. pollState(); + // Depends on modem, ServiceState is not necessarily updated, so make sure updating + // SPN. + updateSpnDisplay(); } else if (action.equals(TelephonyManager.ACTION_NETWORK_COUNTRY_CHANGED)) { String lastKnownNetworkCountry = intent.getStringExtra( TelephonyManager.EXTRA_LAST_KNOWN_NETWORK_COUNTRY); @@ -579,6 +560,10 @@ } }; + private final CarrierConfigManager.CarrierConfigChangeListener mCarrierConfigChangeListener = + (slotIndex, subId, carrierId, specificCarrierId) -> + onCarrierConfigurationChanged(slotIndex); + //CDMA // Min values used to by getOtasp() public static final String UNACTIVATED_MIN2_VALUE = "000000"; @@ -620,11 +605,17 @@ private int mLastKnownAreaCode = CellInfo.UNAVAILABLE; /** - * Indicating if there is any data network existing. This is used in airplane mode turning on - * scenario, where service state tracker should wait all data disconnected before powering - * down the modem. + * Data network controller callback for all data disconnected. This is used when turning on + * airplane mode, where service state tracker should wait for all data disconnected on all + * subscriptions before powering down the modem. */ - private boolean mAnyDataExisting = false; + private DataNetworkControllerCallback mDataDisconnectedCallback; + + /** + * AccessNetworksManagerCallback is used for preferred on the IWLAN when preferred transport + * type changed in AccessNetworksManager. + */ + private AccessNetworksManagerCallback mAccessNetworksManagerCallback = null; public ServiceStateTracker(GsmCdmaPhone phone, CommandsInterface ci) { mNitzState = TelephonyComponentFactory.getInstance() @@ -655,15 +646,20 @@ mCi.registerForCellInfoList(this, EVENT_UNSOL_CELL_INFO_LIST, null); mCi.registerForPhysicalChannelConfiguration(this, EVENT_PHYSICAL_CHANNEL_CONFIG, null); - mSubscriptionController = SubscriptionController.getInstance(); + mSubscriptionManagerService = SubscriptionManagerService.getInstance(); mSubscriptionManager = SubscriptionManager.from(phone.getContext()); mSubscriptionManager.addOnSubscriptionsChangedListener( new android.os.HandlerExecutor(this), mOnSubscriptionsChangedListener); mRestrictedState = new RestrictedState(); + mCarrierConfig = getCarrierConfig(); + CarrierConfigManager ccm = mPhone.getContext().getSystemService(CarrierConfigManager.class); + // Callback which directly handle config change should be executed in handler thread + ccm.registerCarrierConfigChangeListener(this::post, mCarrierConfigChangeListener); + mAccessNetworksManager = mPhone.getAccessNetworksManager(); mOutOfServiceSS = new ServiceState(); - mOutOfServiceSS.setOutOfService(mAccessNetworksManager.isInLegacyMode(), false); + mOutOfServiceSS.setOutOfService(false); for (int transportType : mAccessNetworksManager.getAvailableTransports()) { mRegStateManagers.append(transportType, new NetworkRegistrationManager( @@ -686,12 +682,11 @@ Settings.Global.ENABLE_CELLULAR_ON_BOOT, 1); mDesiredPowerState = (enableCellularOnBoot > 0) && ! (airplaneMode > 0); if (!mDesiredPowerState) { - mRadioPowerOffReasons.add(Phone.RADIO_POWER_REASON_USER); + mRadioPowerOffReasons.add(TelephonyManager.RADIO_POWER_REASON_USER); } mRadioPowerLog.log("init : airplane mode = " + airplaneMode + " enableCellularOnBoot = " + enableCellularOnBoot); - mPhone.getCarrierActionAgent().registerForCarrierAction(CARRIER_ACTION_SET_RADIO_ENABLED, this, EVENT_RADIO_POWER_FROM_CARRIER, null, false); @@ -699,7 +694,6 @@ Context context = mPhone.getContext(); IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_LOCALE_CHANGED); - filter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED); filter.addAction(TelephonyManager.ACTION_NETWORK_COUNTRY_CHANGED); context.registerReceiver(mIntentReceiver, filter); @@ -721,8 +715,31 @@ registerForImsCapabilityChanged(mCSST, CarrierServiceStateTracker.CARRIER_EVENT_IMS_CAPABILITIES_CHANGED, null); - if (mPhone.isUsingNewDataStack()) { - sendEmptyMessage(EVENT_REGISTER_DATA_NETWORK_EXISTING_CHANGED); + mDataDisconnectedCallback = new DataNetworkControllerCallback(this::post) { + @Override + public void onAnyDataNetworkExistingChanged(boolean anyDataExisting) { + log("onAnyDataNetworkExistingChanged: anyDataExisting=" + anyDataExisting); + if (!anyDataExisting) { + sendEmptyMessage(EVENT_ALL_DATA_DISCONNECTED); + } + } + }; + + mAccessNetworksManagerCallback = new AccessNetworksManagerCallback(this::post) { + @Override + public void onPreferredTransportChanged(int networkCapability) { + // Check if preferred on IWLAN was changed in ServiceState. + boolean isIwlanPreferred = mAccessNetworksManager.isAnyApnOnIwlan(); + if (mSS.isIwlanPreferred() != isIwlanPreferred) { + log("onPreferredTransportChanged: IwlanPreferred is changed to " + + isIwlanPreferred); + mSS.setIwlanPreferred(isIwlanPreferred); + mPhone.notifyServiceStateChanged(mPhone.getServiceState()); + } + } + }; + if (mAccessNetworksManagerCallback != null) { + mAccessNetworksManager.registerCallback(mAccessNetworksManagerCallback); } } @@ -759,9 +776,9 @@ } mSS = new ServiceState(); - mSS.setOutOfService(mAccessNetworksManager.isInLegacyMode(), false); + mSS.setOutOfService(false); mNewSS = new ServiceState(); - mNewSS.setOutOfService(mAccessNetworksManager.isInLegacyMode(), false); + mNewSS.setOutOfService(false); mLastCellInfoReqTime = 0; mLastCellInfoList = null; mStartedGprsRegCheck = false; @@ -854,17 +871,28 @@ mPhone.getCarrierActionAgent().unregisterForCarrierAction(this, CARRIER_ACTION_SET_RADIO_ENABLED); mPhone.getContext().unregisterReceiver(mIntentReceiver); + CarrierConfigManager ccm = mPhone.getContext().getSystemService(CarrierConfigManager.class); + if (ccm != null && mCarrierConfigChangeListener != null) { + ccm.unregisterCarrierConfigChangeListener(mCarrierConfigChangeListener); + } if (mCSST != null) { mCSST.dispose(); mCSST = null; } + if (mAccessNetworksManagerCallback != null) { + mAccessNetworksManager.unregisterCallback(mAccessNetworksManagerCallback); + mAccessNetworksManagerCallback = null; + } } @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public boolean getDesiredPowerState() { return mDesiredPowerState; } - public boolean getPowerStateFromCarrier() { return !mRadioDisabledByCarrier; } + + public boolean getPowerStateFromCarrier() { + return !mRadioPowerOffReasons.contains(TelephonyManager.RADIO_POWER_REASON_CARRIER); + } public List<PhysicalChannelConfig> getPhysicalChannelConfigList() { return mLastPhysicalChannelConfigList; @@ -898,7 +926,7 @@ if (nrs != null) { int rat = ServiceState.networkTypeToRilRadioTechnology( nrs.getAccessNetworkTechnology()); - int drs = regCodeToServiceState(nrs.getRegistrationState()); + int drs = regCodeToServiceState(nrs.getNetworkRegistrationState()); return new Pair<>(drs, rat); } return null; @@ -1054,7 +1082,7 @@ * @return the current reasons for which the radio is off. */ public Set<Integer> getRadioPowerOffReasons() { - return mRadioPowerOffReasons; + return Set.copyOf(mRadioPowerOffReasons); } /** @@ -1080,14 +1108,14 @@ public void setRadioPower(boolean power, boolean forEmergencyCall, boolean isSelectedPhoneForEmergencyCall, boolean forceApply) { setRadioPowerForReason(power, forEmergencyCall, isSelectedPhoneForEmergencyCall, forceApply, - Phone.RADIO_POWER_REASON_USER); + TelephonyManager.RADIO_POWER_REASON_USER); } /** * Turn on or off radio power with option to specify whether it's for emergency call and specify * a reason for setting the power state. - * More details check {@link PhoneInternalInterface#setRadioPower( - * boolean, boolean, boolean, boolean, int)}. + * More details check {@link + * PhoneInternalInterface#setRadioPowerForReason(boolean, boolean, boolean, boolean, int)}. */ public void setRadioPowerForReason(boolean power, boolean forEmergencyCall, boolean isSelectedPhoneForEmergencyCall, boolean forceApply, int reason) { @@ -1110,7 +1138,7 @@ if (power && !mRadioPowerOffReasons.isEmpty()) { log("setRadioPowerForReason " + "power: " + power + " forEmergencyCall= " + forEmergencyCall + " isSelectedPhoneForEmergencyCall: " - + isSelectedPhoneForEmergencyCall + " forceApply " + forceApply + "reason:" + + isSelectedPhoneForEmergencyCall + " forceApply " + forceApply + " reason: " + reason + " will not power on the radio as it is powered off for the " + "following reasons: " + mRadioPowerOffReasons + "."); return; @@ -1121,23 +1149,6 @@ } /** - * Radio power set from carrier action. if set to false means carrier desire to turn radio off - * and radio wont be re-enabled unless carrier explicitly turn it back on. - * @param enable indicate if radio power is enabled or disabled from carrier action. - */ - public void setRadioPowerFromCarrier(boolean enable) { - boolean disableByCarrier = !enable; - if (mRadioDisabledByCarrier == disableByCarrier) { - log("setRadioPowerFromCarrier mRadioDisabledByCarrier is already " - + disableByCarrier + " Do nothing."); - return; - } - - mRadioDisabledByCarrier = disableByCarrier; - setPowerStateToDesired(); - } - - /** * These two flags manage the behavior of the cell lock -- the * lock should be held if either flag is true. The intention is * to allow temporary acquisition of the lock to get a single @@ -1189,22 +1200,9 @@ switch (msg.what) { case EVENT_SET_RADIO_POWER_OFF: synchronized(this) { - if (mPhone.isUsingNewDataStack()) { - mPendingRadioPowerOffAfterDataOff = false; - log("Wait for all data networks torn down timed out. Power off now."); - hangupAndPowerOff(); - return; - } - if (mPendingRadioPowerOffAfterDataOff && - (msg.arg1 == mPendingRadioPowerOffAfterDataOffTag)) { - if (DBG) log("EVENT_SET_RADIO_OFF, turn radio off now."); - hangupAndPowerOff(); - mPendingRadioPowerOffAfterDataOffTag += 1; - mPendingRadioPowerOffAfterDataOff = false; - } else { - log("EVENT_SET_RADIO_OFF is stale arg1=" + msg.arg1 + - "!= tag=" + mPendingRadioPowerOffAfterDataOffTag); - } + mPendingRadioPowerOffAfterDataOff = false; + log("Wait for all data networks torn down timed out. Power off now."); + hangupAndPowerOff(); } break; @@ -1223,8 +1221,8 @@ mCdnr.updateEfFromUsim(null /* Usim */); } onUpdateIccAvailability(); - if (mUiccApplcation == null - || mUiccApplcation.getState() != AppState.APPSTATE_READY) { + if (mUiccApplication == null + || mUiccApplication.getState() != AppState.APPSTATE_READY) { mIsSimReady = false; updateSpnDisplay(); } @@ -1471,45 +1469,28 @@ } break; - case EVENT_REGISTER_DATA_NETWORK_EXISTING_CHANGED: { - mPhone.getDataNetworkController().registerDataNetworkControllerCallback( - new DataNetworkControllerCallback(this::post) { - @Override - public void onAnyDataNetworkExistingChanged(boolean anyDataExisting) { - if (mAnyDataExisting != anyDataExisting) { - mAnyDataExisting = anyDataExisting; - log("onAnyDataNetworkExistingChanged: anyDataExisting=" - + anyDataExisting); - if (!mAnyDataExisting) { - sendEmptyMessage(EVENT_ALL_DATA_DISCONNECTED); - } - } - } - }); - break; - } case EVENT_ALL_DATA_DISCONNECTED: - if (mPhone.isUsingNewDataStack()) { - log("EVENT_ALL_DATA_DISCONNECTED"); - if (mPendingRadioPowerOffAfterDataOff) { + log("EVENT_ALL_DATA_DISCONNECTED"); + synchronized (this) { + if (!mPendingRadioPowerOffAfterDataOff) return; + boolean areAllDataDisconnectedOnAllPhones = true; + for (Phone phone : PhoneFactory.getPhones()) { + if (phone.getDataNetworkController().areAllDataDisconnected()) { + phone.getDataNetworkController() + .unregisterDataNetworkControllerCallback( + mDataDisconnectedCallback); + } else { + log("Still waiting for all data disconnected on phone: " + + phone.getSubId()); + areAllDataDisconnectedOnAllPhones = false; + } + } + if (areAllDataDisconnectedOnAllPhones) { mPendingRadioPowerOffAfterDataOff = false; removeMessages(EVENT_SET_RADIO_POWER_OFF); - if (DBG) log("EVENT_ALL_DATA_DISCONNECTED, turn radio off now."); + if (DBG) log("Data disconnected for all phones, turn radio off now."); hangupAndPowerOff(); } - return; - } - int dds = SubscriptionManager.getDefaultDataSubscriptionId(); - ProxyController.getInstance().unregisterForAllDataDisconnected(dds, this); - synchronized(this) { - if (mPendingRadioPowerOffAfterDataOff) { - if (DBG) log("EVENT_ALL_DATA_DISCONNECTED, turn radio off now."); - hangupAndPowerOff(); - mPendingRadioPowerOffAfterDataOffTag += 1; - mPendingRadioPowerOffAfterDataOff = false; - } else { - log("EVENT_ALL_DATA_DISCONNECTED is stale"); - } } break; @@ -1666,7 +1647,8 @@ if (ar.exception == null) { boolean enable = (boolean) ar.result; if (DBG) log("EVENT_RADIO_POWER_FROM_CARRIER: " + enable); - setRadioPowerFromCarrier(enable); + setRadioPowerForReason(enable, false, false, false, + TelephonyManager.RADIO_POWER_REASON_CARRIER); } break; @@ -1675,8 +1657,8 @@ if (ar.exception == null) { List<PhysicalChannelConfig> list = (List<PhysicalChannelConfig>) ar.result; if (VDBG) { - log("EVENT_PHYSICAL_CHANNEL_CONFIG: size=" + list.size() + " list=" - + list); + log("EVENT_PHYSICAL_CHANNEL_CONFIG: list=" + list + + (list == null ? "" : ", list.size()=" + list.size())); } mLastPhysicalChannelConfigList = list; boolean hasChanged = false; @@ -1695,9 +1677,14 @@ // Notify NR frequency, NR connection status or bandwidths changed. if (hasChanged) { mPhone.notifyServiceStateChanged(mPhone.getServiceState()); + mServiceStateChangedRegistrants.notifyRegistrants(); TelephonyMetrics.getInstance().writeServiceStateChanged( mPhone.getPhoneId(), mSS); mPhone.getVoiceCallSessionStats().onServiceStateChanged(mSS); + ImsPhone imsPhone = (ImsPhone) mPhone.getImsPhone(); + if (imsPhone != null) { + imsPhone.getImsStats().onServiceStateChanged(mSS); + } mServiceStateStats.onServiceStateChanged(mSS); } } @@ -1718,10 +1705,6 @@ rspRspMsg.sendToTarget(); break; - case EVENT_CARRIER_CONFIG_CHANGED: - onCarrierConfigChanged(); - break; - case EVENT_POLL_STATE_REQUEST: pollStateInternal(false); break; @@ -2106,16 +2089,20 @@ if (physicalChannelConfigs != null) { for (PhysicalChannelConfig config : physicalChannelConfigs) { if (isNrPhysicalChannelConfig(config) && isInternetPhysicalChannelConfig(config)) { - // Update the NR frequency range if there is an internet data connection + // Update the NR frequency range if there is an active internet data connection // associated with this NR physical channel channel config. - newFrequencyRange = ServiceState.getBetterNRFrequencyRange( - newFrequencyRange, config.getFrequencyRange()); - break; + // If there are multiple valid configs, use the highest frequency range value. + newFrequencyRange = Math.max(newFrequencyRange, config.getFrequencyRange()); } } } boolean hasChanged = newFrequencyRange != ss.getNrFrequencyRange(); + if (hasChanged) { + log(String.format("NR frequency range changed from %s to %s.", + ServiceState.frequencyRangeToString(ss.getNrFrequencyRange()), + ServiceState.frequencyRangeToString(newFrequencyRange))); + } ss.setNrFrequencyRange(newFrequencyRange); return hasChanged; } @@ -2146,6 +2133,11 @@ } boolean hasChanged = newNrState != oldNrState; + if (hasChanged) { + log(String.format("NR state changed from %s to %s.", + NetworkRegistrationInfo.nrStateToString(oldNrState), + NetworkRegistrationInfo.nrStateToString(newNrState))); + } regInfo.setNrState(newNrState); ss.addNetworkRegistrationInfo(regInfo); return hasChanged; @@ -2157,18 +2149,8 @@ private boolean isInternetPhysicalChannelConfig(PhysicalChannelConfig config) { for (int cid : config.getContextIds()) { - if (mPhone.isUsingNewDataStack()) { - if (mPhone.getDataNetworkController().isInternetNetwork(cid)) { - return true; - } - } else { - DataConnection dc = mPhone.getDcTracker( - AccessNetworkConstants.TRANSPORT_TYPE_WWAN) - .getDataConnectionByContextId(cid); - if (dc != null && dc.getNetworkCapabilities().hasCapability( - NetworkCapabilities.NET_CAPABILITY_INTERNET)) { - return true; - } + if (mPhone.getDataNetworkController().isInternetNetwork(cid)) { + return true; } } return false; @@ -2195,14 +2177,14 @@ if (wlanPsRegState != null && wlanPsRegState.getAccessNetworkTechnology() == TelephonyManager.NETWORK_TYPE_IWLAN - && wlanPsRegState.getRegistrationState() + && wlanPsRegState.getNetworkRegistrationState() == NetworkRegistrationInfo.REGISTRATION_STATE_HOME && isIwlanPreferred) { serviceState.setDataRegState(ServiceState.STATE_IN_SERVICE); } else if (wwanPsRegState != null) { // If the device is not camped on IWLAN, then we use cellular PS registration state // to compute reg state and rat. - int regState = wwanPsRegState.getRegistrationState(); + int regState = wwanPsRegState.getNetworkRegistrationState(); serviceState.setDataRegState(regCodeToServiceState(regState)); } if (DBG) { @@ -2218,10 +2200,8 @@ VoiceSpecificRegistrationInfo voiceSpecificStates = networkRegState.getVoiceSpecificInfo(); - int registrationState = networkRegState.getRegistrationState(); + int registrationState = networkRegState.getNetworkRegistrationState(); int cssIndicator = voiceSpecificStates.cssSupported ? 1 : 0; - int newVoiceRat = ServiceState.networkTypeToRilRadioTechnology( - networkRegState.getAccessNetworkTechnology()); mNewSS.setVoiceRegState(regCodeToServiceState(registrationState)); mNewSS.setCssIndicator(cssIndicator); mNewSS.addNetworkRegistrationInfo(networkRegState); @@ -2254,7 +2234,7 @@ && !isRoamIndForHomeSystem(roamingIndicator); mNewSS.setVoiceRoaming(cdmaRoaming); mRoamingIndicator = roamingIndicator; - mIsInPrl = (systemIsInPrl == 0) ? false : true; + mIsInPrl = systemIsInPrl != 0; mDefaultRoamingIndicator = defaultRoamingIndicator; int systemId = 0; @@ -2300,7 +2280,7 @@ mNewSS.addNetworkRegistrationInfo(networkRegState); DataSpecificRegistrationInfo dataSpecificStates = networkRegState.getDataSpecificInfo(); - int registrationState = networkRegState.getRegistrationState(); + int registrationState = networkRegState.getNetworkRegistrationState(); int serviceState = regCodeToServiceState(registrationState); int newDataRat = ServiceState.networkTypeToRilRadioTechnology( networkRegState.getAccessNetworkTechnology()); @@ -2631,8 +2611,7 @@ */ private boolean isRoamIndForHomeSystem(int roamInd) { // retrieve the carrier-specified list of ERIs for home system - final PersistableBundle config = getCarrierConfig(); - int[] homeRoamIndicators = config.getIntArray(CarrierConfigManager + int[] homeRoamIndicators = mCarrierConfig.getIntArray(CarrierConfigManager .KEY_CDMA_ENHANCED_ROAMING_INDICATOR_FOR_HOME_NETWORK_INT_ARRAY); log("isRoamIndForHomeSystem: homeRoamIndicators=" + Arrays.toString(homeRoamIndicators)); @@ -2661,8 +2640,6 @@ */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) protected void updateRoamingState() { - PersistableBundle bundle = getCarrierConfig(); - if (mPhone.isPhoneTypeGsm()) { /** * Since the roaming state of gsm service (from +CREG) and @@ -2687,14 +2664,14 @@ roaming = false; } - if (alwaysOnHomeNetwork(bundle)) { + if (alwaysOnHomeNetwork(mCarrierConfig)) { log("updateRoamingState: carrier config override always on home network"); roaming = false; - } else if (isNonRoamingInGsmNetwork(bundle, mNewSS.getOperatorNumeric())) { + } else if (isNonRoamingInGsmNetwork(mCarrierConfig, mNewSS.getOperatorNumeric())) { log("updateRoamingState: carrier config override set non roaming:" + mNewSS.getOperatorNumeric()); roaming = false; - } else if (isRoamingInGsmNetwork(bundle, mNewSS.getOperatorNumeric())) { + } else if (isRoamingInGsmNetwork(mCarrierConfig, mNewSS.getOperatorNumeric())) { log("updateRoamingState: carrier config override set roaming:" + mNewSS.getOperatorNumeric()); roaming = true; @@ -2704,16 +2681,16 @@ } else { String systemId = Integer.toString(mNewSS.getCdmaSystemId()); - if (alwaysOnHomeNetwork(bundle)) { + if (alwaysOnHomeNetwork(mCarrierConfig)) { log("updateRoamingState: carrier config override always on home network"); setRoamingOff(); - } else if (isNonRoamingInGsmNetwork(bundle, mNewSS.getOperatorNumeric()) - || isNonRoamingInCdmaNetwork(bundle, systemId)) { + } else if (isNonRoamingInGsmNetwork(mCarrierConfig, mNewSS.getOperatorNumeric()) + || isNonRoamingInCdmaNetwork(mCarrierConfig, systemId)) { log("updateRoamingState: carrier config override set non-roaming:" + mNewSS.getOperatorNumeric() + ", " + systemId); setRoamingOff(); - } else if (isRoamingInGsmNetwork(bundle, mNewSS.getOperatorNumeric()) - || isRoamingInCdmaNetwork(bundle, systemId)) { + } else if (isRoamingInGsmNetwork(mCarrierConfig, mNewSS.getOperatorNumeric()) + || isRoamingInCdmaNetwork(mCarrierConfig, systemId)) { log("updateRoamingState: carrier config override set roaming:" + mNewSS.getOperatorNumeric() + ", " + systemId); setRoamingOn(); @@ -2744,24 +2721,21 @@ if (!mPhone.isPhoneTypeGsm() && !mSS.getRoaming()) { boolean hasBrandOverride = mUiccController.getUiccPort(getPhoneId()) != null && mUiccController.getUiccPort(getPhoneId()).getOperatorBrandOverride() != null; - if (!hasBrandOverride) { - PersistableBundle config = getCarrierConfig(); - if (config.getBoolean( + if (!hasBrandOverride && mCarrierConfig.getBoolean( CarrierConfigManager.KEY_CDMA_HOME_REGISTERED_PLMN_NAME_OVERRIDE_BOOL)) { - String operator = config.getString( - CarrierConfigManager.KEY_CDMA_HOME_REGISTERED_PLMN_NAME_STRING); - log("updateOperatorNameFromCarrierConfig: changing from " - + mSS.getOperatorAlpha() + " to " + operator); - // override long and short operator name, keeping numeric the same - mSS.setOperatorName(operator, operator, mSS.getOperatorNumeric()); - } + String operator = mCarrierConfig.getString( + CarrierConfigManager.KEY_CDMA_HOME_REGISTERED_PLMN_NAME_STRING); + log("updateOperatorNameFromCarrierConfig: changing from " + + mSS.getOperatorAlpha() + " to " + operator); + // override long and short operator name, keeping numeric the same + mSS.setOperatorName(operator, operator, mSS.getOperatorNumeric()); } } } private void notifySpnDisplayUpdate(CarrierDisplayNameData data) { int subId = mPhone.getSubId(); - // Update ACTION_SERVICE_PROVIDERS_UPDATED IFF any value changes + // Update ACTION_SERVICE_PROVIDERS_UPDATED if any value changes if (mSubId != subId || data.shouldShowPlmn() != mCurShowPlmn || data.shouldShowSpn() != mCurShowSpn @@ -2791,13 +2765,12 @@ SubscriptionManager.putPhoneIdAndSubIdExtra(intent, mPhone.getPhoneId()); mPhone.getContext().sendStickyBroadcastAsUser(intent, UserHandle.ALL); - if (!mSubscriptionController.setPlmnSpn(mPhone.getPhoneId(), - data.shouldShowPlmn(), data.getPlmn(), data.shouldShowSpn(), data.getSpn())) { - mSpnUpdatePending = true; + if (SubscriptionManager.isValidSubscriptionId(subId)) { + mSubscriptionManagerService.setCarrierName(subId, TextUtils.emptyIfNull( + getCarrierName(data.shouldShowPlmn(), data.getPlmn(), + data.shouldShowSpn(), data.getSpn()))); } } - - mSubId = subId; mCurShowSpn = data.shouldShowSpn(); mCurShowPlmn = data.shouldShowPlmn(); mCurSpn = data.getSpn(); @@ -2805,6 +2778,26 @@ mCurPlmn = data.getPlmn(); } + @NonNull + private String getCarrierName(boolean showPlmn, String plmn, boolean showSpn, String spn) { + String carrierName = ""; + if (showPlmn) { + carrierName = plmn; + if (showSpn) { + // Need to show both plmn and spn if both are not same. + if (!Objects.equals(spn, plmn)) { + String separator = mPhone.getContext().getString( + com.android.internal.R.string.kg_text_message_separator).toString(); + carrierName = new StringBuilder().append(carrierName).append(separator) + .append(spn).toString(); + } + } + } else if (showSpn) { + carrierName = spn; + } + return carrierName; + } + private void updateSpnDisplayCdnr() { log("updateSpnDisplayCdnr+"); CarrierDisplayNameData data = mCdnr.getCarrierDisplayNameData(); @@ -2815,8 +2808,8 @@ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @VisibleForTesting public void updateSpnDisplay() { - PersistableBundle config = getCarrierConfig(); - if (config.getBoolean(CarrierConfigManager.KEY_ENABLE_CARRIER_DISPLAY_NAME_RESOLVER_BOOL)) { + if (mCarrierConfig.getBoolean( + CarrierConfigManager.KEY_ENABLE_CARRIER_DISPLAY_NAME_RESOLVER_BOOL)) { updateSpnDisplayCdnr(); } else { updateSpnDisplayLegacy(); @@ -2829,14 +2822,15 @@ String spn = null; String dataSpn = null; boolean showSpn = false; - String plmn = null; - boolean showPlmn = false; + String plmn; + boolean showPlmn; String wfcVoiceSpnFormat = null; String wfcDataSpnFormat = null; String wfcFlightSpnFormat = null; int combinedRegState = getCombinedRegState(mSS); if (mPhone.getImsPhone() != null && mPhone.getImsPhone().isWifiCallingEnabled() + && mPhone.isImsRegistered() && (combinedRegState == ServiceState.STATE_IN_SERVICE && mSS.getDataNetworkType() == TelephonyManager.NETWORK_TYPE_IWLAN)) { // In Wi-Fi Calling mode (connected to WiFi and WFC enabled), @@ -2847,20 +2841,17 @@ // // 2) Show PLMN + Wi-Fi Calling if there is no valid SPN in case 1 - int voiceIdx = 0; - int dataIdx = 0; - int flightModeIdx = -1; - boolean useRootLocale = false; + int voiceIdx; + int dataIdx; + int flightModeIdx; + boolean useRootLocale; - PersistableBundle bundle = getCarrierConfig(); - - voiceIdx = bundle.getInt(CarrierConfigManager.KEY_WFC_SPN_FORMAT_IDX_INT); - dataIdx = bundle.getInt( - CarrierConfigManager.KEY_WFC_DATA_SPN_FORMAT_IDX_INT); - flightModeIdx = bundle.getInt( + voiceIdx = mCarrierConfig.getInt(CarrierConfigManager.KEY_WFC_SPN_FORMAT_IDX_INT); + dataIdx = mCarrierConfig.getInt(CarrierConfigManager.KEY_WFC_DATA_SPN_FORMAT_IDX_INT); + flightModeIdx = mCarrierConfig.getInt( CarrierConfigManager.KEY_WFC_FLIGHT_MODE_SPN_FORMAT_IDX_INT); useRootLocale = - bundle.getBoolean(CarrierConfigManager.KEY_WFC_SPN_USE_ROOT_LOCALE); + mCarrierConfig.getBoolean(CarrierConfigManager.KEY_WFC_SPN_USE_ROOT_LOCALE); String[] wfcSpnFormats = SubscriptionManager.getResourcesForSubId(mPhone.getContext(), mPhone.getSubId(), useRootLocale) @@ -2891,21 +2882,19 @@ && (mPhone.getImsPhone() != null) && (mPhone.getImsPhone().getImsRegistrationTech() == ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM)) { - // In Cros SIM Calling mode show SPN or PLMN + Cross SIM Calling + // In Cross SIM Calling mode show SPN or PLMN + Cross SIM Calling // // 1) Show SPN + Cross SIM Calling If SIM has SPN and SPN display condition // is satisfied or SPN override is enabled for this carrier // // 2) Show PLMN + Cross SIM Calling if there is no valid SPN in case 1 - PersistableBundle bundle = getCarrierConfig(); int crossSimSpnFormatIdx = - bundle.getInt(CarrierConfigManager.KEY_CROSS_SIM_SPN_FORMAT_INT); + mCarrierConfig.getInt(CarrierConfigManager.KEY_CROSS_SIM_SPN_FORMAT_INT); boolean useRootLocale = - bundle.getBoolean(CarrierConfigManager.KEY_WFC_SPN_USE_ROOT_LOCALE); + mCarrierConfig.getBoolean(CarrierConfigManager.KEY_WFC_SPN_USE_ROOT_LOCALE); String[] crossSimSpnFormats = SubscriptionManager.getResourcesForSubId( - mPhone.getContext(), - mPhone.getSubId(), useRootLocale) + mPhone.getContext(), mPhone.getSubId(), useRootLocale) .getStringArray(R.array.crossSimSpnFormats); if (crossSimSpnFormatIdx < 0 || crossSimSpnFormatIdx >= crossSimSpnFormats.length) { @@ -2935,7 +2924,6 @@ // EXTRA_SHOW_PLMN = true // EXTRA_PLMN = null - IccRecords iccRecords = mIccRecords; int rule = getCarrierNameDisplayBitmask(mSS); boolean noService = false; if (combinedRegState == ServiceState.STATE_OUT_OF_SERVICE @@ -2951,8 +2939,7 @@ } else { // No service at all plmn = Resources.getSystem() - .getText( - com.android.internal.R.string.lockscreen_carrier_default) + .getText(com.android.internal.R.string.lockscreen_carrier_default) .toString(); noService = true; } @@ -2995,8 +2982,7 @@ } else if (!TextUtils.isEmpty(plmn)) { // Show PLMN + Cross-SIM Calling if there is no valid SPN in the above case String originalPlmn = plmn.trim(); - PersistableBundle config = getCarrierConfig(); - if (mIccRecords != null && config.getBoolean( + if (mIccRecords != null && mCarrierConfig.getBoolean( CarrierConfigManager.KEY_WFC_CARRIER_NAME_OVERRIDE_BY_PNN_BOOL)) { originalPlmn = mIccRecords.getPnnHomeName(); } @@ -3021,8 +3007,7 @@ // Show PLMN + Wi-Fi Calling if there is no valid SPN in the above case String originalPlmn = plmn.trim(); - PersistableBundle config = getCarrierConfig(); - if (mIccRecords != null && config.getBoolean( + if (mIccRecords != null && mCarrierConfig.getBoolean( CarrierConfigManager.KEY_WFC_CARRIER_NAME_OVERRIDE_BY_PNN_BOOL)) { originalPlmn = mIccRecords.getPnnHomeName(); } @@ -3105,12 +3090,12 @@ protected void setPowerStateToDesired(boolean forEmergencyCall, boolean isSelectedPhoneForEmergencyCall, boolean forceApply) { if (DBG) { - String tmpLog = "setPowerStateToDesired: mDeviceShuttingDown=" + mDeviceShuttingDown + - ", mDesiredPowerState=" + mDesiredPowerState + - ", getRadioState=" + mCi.getRadioState() + - ", mRadioDisabledByCarrier=" + mRadioDisabledByCarrier + - ", IMS reg state=" + mImsRegistrationOnOff + - ", pending radio off=" + hasMessages(EVENT_POWER_OFF_RADIO_IMS_DEREG_TIMEOUT); + String tmpLog = "setPowerStateToDesired: mDeviceShuttingDown=" + mDeviceShuttingDown + + ", mDesiredPowerState=" + mDesiredPowerState + + ", getRadioState=" + mCi.getRadioState() + + ", mRadioPowerOffReasons=" + mRadioPowerOffReasons + + ", IMS reg state=" + mImsRegistrationOnOff + + ", pending radio off=" + hasMessages(EVENT_POWER_OFF_RADIO_IMS_DEREG_TIMEOUT); log(tmpLog); mRadioPowerLog.log(tmpLog); } @@ -3122,22 +3107,13 @@ } // If we want it on and it's off, turn it on - if (mDesiredPowerState && !mRadioDisabledByCarrier + if (mDesiredPowerState && mRadioPowerOffReasons.isEmpty() && (forceApply || mCi.getRadioState() == TelephonyManager.RADIO_POWER_OFF)) { mCi.setRadioPower(true, forEmergencyCall, isSelectedPhoneForEmergencyCall, null); - } else if ((!mDesiredPowerState || mRadioDisabledByCarrier) && mCi.getRadioState() + } else if ((!mDesiredPowerState || !mRadioPowerOffReasons.isEmpty()) && mCi.getRadioState() == TelephonyManager.RADIO_POWER_ON) { - // If it's on and available and we want it off gracefully - if (!mPhone.isUsingNewDataStack() && mImsRegistrationOnOff - && getRadioPowerOffDelayTimeoutForImsRegistration() > 0) { - if (DBG) log("setPowerStateToDesired: delaying power off until IMS dereg."); - startDelayRadioOffWaitingForImsDeregTimeout(); - // Return early here as we do not want to hit the cancel timeout code below. - return; - } else { - if (DBG) log("setPowerStateToDesired: powerOffRadioSafely()"); - powerOffRadioSafely(); - } + if (DBG) log("setPowerStateToDesired: powerOffRadioSafely()"); + powerOffRadioSafely(); } else if (mDeviceShuttingDown && (mCi.getRadioState() != TelephonyManager.RADIO_POWER_UNAVAILABLE)) { // !mDesiredPowerState condition above will happen first if the radio is on, so we will @@ -3151,7 +3127,6 @@ /** * Cancel the EVENT_POWER_OFF_RADIO_DELAYED event if it is currently pending to be completed. - * @return true if there was a pending timeout message in the queue, false otherwise. */ private void cancelDelayRadioOffWaitingForImsDeregTimeout() { if (hasMessages(EVENT_POWER_OFF_RADIO_IMS_DEREG_TIMEOUT)) { @@ -3160,21 +3135,6 @@ } } - /** - * Start a timer to turn off the radio if IMS does not move to deregistered after the - * radio power off event occurred. If this event already exists in the message queue, then - * ignore the new request and use the existing one. - */ - private void startDelayRadioOffWaitingForImsDeregTimeout() { - if (hasMessages(EVENT_POWER_OFF_RADIO_IMS_DEREG_TIMEOUT)) { - if (DBG) log("startDelayRadioOffWaitingForImsDeregTimeout: timer exists, ignoring"); - return; - } - if (DBG) log("startDelayRadioOffWaitingForImsDeregTimeout: starting timer"); - sendEmptyMessageDelayed(EVENT_POWER_OFF_RADIO_IMS_DEREG_TIMEOUT, - getRadioPowerOffDelayTimeoutForImsRegistration()); - } - protected void onUpdateIccAvailability() { if (mUiccController == null ) { return; @@ -3182,7 +3142,7 @@ UiccCardApplication newUiccApplication = getUiccCardApplication(); - if (mUiccApplcation != newUiccApplication) { + if (mUiccApplication != newUiccApplication) { // Remove the EF records that come from UICC if (mIccRecords instanceof SIMRecords) { @@ -3191,26 +3151,26 @@ mCdnr.updateEfFromRuim(null /* ruim */); } - if (mUiccApplcation != null) { + if (mUiccApplication != null) { log("Removing stale icc objects."); - mUiccApplcation.unregisterForReady(this); + mUiccApplication.unregisterForReady(this); if (mIccRecords != null) { mIccRecords.unregisterForRecordsLoaded(this); } mIccRecords = null; - mUiccApplcation = null; + mUiccApplication = null; } if (newUiccApplication != null) { log("New card found"); - mUiccApplcation = newUiccApplication; - mIccRecords = mUiccApplcation.getIccRecords(); + mUiccApplication = newUiccApplication; + mIccRecords = mUiccApplication.getIccRecords(); if (mPhone.isPhoneTypeGsm()) { - mUiccApplcation.registerForReady(this, EVENT_SIM_READY, null); + mUiccApplication.registerForReady(this, EVENT_SIM_READY, null); if (mIccRecords != null) { mIccRecords.registerForRecordsLoaded(this, EVENT_SIM_RECORDS_LOADED, null); } } else if (mIsSubscriptionFromRuim) { - mUiccApplcation.registerForReady(this, EVENT_RUIM_READY, null); + mUiccApplication.registerForReady(this, EVENT_RUIM_READY, null); if (mIccRecords != null) { mIccRecords.registerForRecordsLoaded(this, EVENT_RUIM_RECORDS_LOADED, null); } @@ -3297,7 +3257,6 @@ + " mImsRegistrationOnOff=" + mImsRegistrationOnOff + "}"); - if (mImsRegistrationOnOff && !registered) { // moving to deregistered, only send this event if we need to re-evaluate if (getRadioPowerOffDelayTimeoutForImsRegistration() > 0) { @@ -3310,6 +3269,9 @@ } } mImsRegistrationOnOff = registered; + + // It's possible ServiceState changes did not trigger SPN display update; we update it here. + updateSpnDisplay(); } public void onImsCapabilityChanged() { @@ -3346,7 +3308,7 @@ nri = mNewSS.getNetworkRegistrationInfo( NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WLAN); - mNewSS.setOutOfService(mAccessNetworksManager.isInLegacyMode(), false); + mNewSS.setOutOfService(false); // Add the IWLAN registration info back to service state. if (nri != null) { mNewSS.addNetworkRegistrationInfo(nri); @@ -3363,7 +3325,7 @@ nri = mNewSS.getNetworkRegistrationInfo( NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WLAN); - mNewSS.setOutOfService(mAccessNetworksManager.isInLegacyMode(), true); + mNewSS.setOutOfService(true); // Add the IWLAN registration info back to service state. if (nri != null) { mNewSS.addNetworkRegistrationInfo(nri); @@ -3455,20 +3417,21 @@ useDataRegStateForDataOnlyDevices(); processIwlanRegistrationInfo(); - if (TelephonyUtils.IS_DEBUGGABLE && mPhone.mTelephonyTester != null) { - mPhone.mTelephonyTester.overrideServiceState(mNewSS); + updateNrFrequencyRangeFromPhysicalChannelConfigs(mLastPhysicalChannelConfigList, mNewSS); + updateNrStateFromPhysicalChannelConfigs(mLastPhysicalChannelConfigList, mNewSS); + + if (TelephonyUtils.IS_DEBUGGABLE && mPhone.getTelephonyTester() != null) { + mPhone.getTelephonyTester().overrideServiceState(mNewSS); } NetworkRegistrationInfo networkRegState = mNewSS.getNetworkRegistrationInfo( NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN); - updateNrFrequencyRangeFromPhysicalChannelConfigs(mLastPhysicalChannelConfigList, mNewSS); - updateNrStateFromPhysicalChannelConfigs(mLastPhysicalChannelConfigList, mNewSS); setPhyCellInfoFromCellIdentity(mNewSS, networkRegState.getCellIdentity()); if (DBG) { - log("Poll ServiceState done: " - + " oldSS=[" + mSS + "] newSS=[" + mNewSS + "]" - + " oldMaxDataCalls=" + mMaxDataCalls + log("Poll ServiceState done: oldSS=" + mSS); + log("Poll ServiceState done: newSS=" + mNewSS); + log("Poll ServiceState done: oldMaxDataCalls=" + mMaxDataCalls + " mNewMaxDataCalls=" + mNewMaxDataCalls + " oldReasonDataDenied=" + mReasonDataDenied + " mNewReasonDataDenied=" + mNewReasonDataDenied); @@ -3489,14 +3452,10 @@ mSS.getState() == ServiceState.STATE_POWER_OFF && mNewSS.getState() != ServiceState.STATE_POWER_OFF; - SparseBooleanArray hasDataAttached = new SparseBooleanArray( - mAccessNetworksManager.getAvailableTransports().length); - SparseBooleanArray hasDataDetached = new SparseBooleanArray( - mAccessNetworksManager.getAvailableTransports().length); - SparseBooleanArray hasRilDataRadioTechnologyChanged = new SparseBooleanArray( - mAccessNetworksManager.getAvailableTransports().length); - SparseBooleanArray hasDataRegStateChanged = new SparseBooleanArray( - mAccessNetworksManager.getAvailableTransports().length); + SparseBooleanArray hasDataAttached = new SparseBooleanArray(); + SparseBooleanArray hasDataDetached = new SparseBooleanArray(); + SparseBooleanArray hasRilDataRadioTechnologyChanged = new SparseBooleanArray(); + SparseBooleanArray hasDataRegStateChanged = new SparseBooleanArray(); boolean anyDataRegChanged = false; boolean anyDataRatChanged = false; boolean hasAlphaRawChanged = @@ -3510,11 +3469,6 @@ NetworkRegistrationInfo newNrs = mNewSS.getNetworkRegistrationInfo( NetworkRegistrationInfo.DOMAIN_PS, transport); - // If the previously it was not in service, and now it's in service, trigger the - // attached event. Also if airplane mode was just turned on, and data is already in - // service, we need to trigger the attached event again so that DcTracker can setup - // data on all connectable APNs again (because we've already torn down all data - // connections just before airplane mode turned on) boolean changed = (oldNrs == null || !oldNrs.isInService() || hasAirplaneModeOnChanged) && (newNrs != null && newNrs.isInService()); hasDataAttached.put(transport, changed); @@ -3539,9 +3493,9 @@ anyDataRatChanged = true; } - int oldRegState = oldNrs != null ? oldNrs.getRegistrationState() + int oldRegState = oldNrs != null ? oldNrs.getNetworkRegistrationState() : NetworkRegistrationInfo.REGISTRATION_STATE_UNKNOWN; - int newRegState = newNrs != null ? newNrs.getRegistrationState() + int newRegState = newNrs != null ? newNrs.getNetworkRegistrationState() : NetworkRegistrationInfo.REGISTRATION_STATE_UNKNOWN; hasDataRegStateChanged.put(transport, oldRegState != newRegState); if (oldRegState != newRegState) { @@ -3598,8 +3552,6 @@ boolean hasCssIndicatorChanged = (mSS.getCssIndicator() != mNewSS.getCssIndicator()); - boolean hasBandwidthChanged = mSS.getCellBandwidths() != mNewSS.getCellBandwidths(); - boolean has4gHandoff = false; boolean hasMultiApnSupport = false; boolean hasLostMultiApnSupport = false; @@ -3643,7 +3595,6 @@ + " hasCssIndicatorChanged = " + hasCssIndicatorChanged + " hasNrFrequencyRangeChanged = " + hasNrFrequencyRangeChanged + " hasNrStateChanged = " + hasNrStateChanged - + " hasBandwidthChanged = " + hasBandwidthChanged + " hasAirplaneModeOnlChanged = " + hasAirplaneModeOnChanged); } @@ -3690,7 +3641,7 @@ ServiceState oldMergedSS = new ServiceState(mPhone.getServiceState()); mSS = new ServiceState(mNewSS); - mNewSS.setOutOfService(mAccessNetworksManager.isInLegacyMode(), false); + mNewSS.setOutOfService(false); mCellIdentity = primaryCellIdentity; if (mSS.getState() == ServiceState.STATE_IN_SERVICE && primaryCellIdentity != null) { @@ -3736,10 +3687,6 @@ mCssIndicatorChangedRegistrants.notifyRegistrants(); } - if (hasBandwidthChanged) { - mBandwidthChangedRegistrants.notifyRegistrants(); - } - if (hasRejectCauseChanged) { setNotification(CS_REJECT_CAUSE_ENABLED); } @@ -3775,8 +3722,10 @@ // incomplete. // CellIdentity can return a null MCC and MNC in CDMA String localeOperator = operatorNumeric; - if (isInvalidOperatorNumeric(operatorNumeric) - || mSS.getDataNetworkType() == TelephonyManager.NETWORK_TYPE_IWLAN) { + if (mSS.getDataNetworkType() == TelephonyManager.NETWORK_TYPE_IWLAN) { + localeOperator = null; + } + if (isInvalidOperatorNumeric(localeOperator)) { for (CellIdentity cid : prioritizedCids) { if (!TextUtils.isEmpty(cid.getPlmn())) { localeOperator = cid.getPlmn(); @@ -3817,6 +3766,10 @@ TelephonyMetrics.getInstance().writeServiceStateChanged(mPhone.getPhoneId(), mSS); mPhone.getVoiceCallSessionStats().onServiceStateChanged(mSS); + ImsPhone imsPhone = (ImsPhone) mPhone.getImsPhone(); + if (imsPhone != null) { + imsPhone.getImsStats().onServiceStateChanged(mSS); + } mServiceStateStats.onServiceStateChanged(mSS); } @@ -3982,8 +3935,9 @@ } } - if (mUiccApplcation != null && mUiccApplcation.getState() == AppState.APPSTATE_READY && - mIccRecords != null && getCombinedRegState(mSS) == ServiceState.STATE_IN_SERVICE + if (mUiccApplication != null && mUiccApplication.getState() == AppState.APPSTATE_READY + && mIccRecords != null + && getCombinedRegState(mSS) == ServiceState.STATE_IN_SERVICE && !ServiceState.isPsOnlyTech(mSS.getRilVoiceRadioTechnology())) { // SIM is found on the device. If ERI roaming is OFF, and SID/NID matches // one configured in SIM, use operator name from CSIM record. Note that ERI, SID, @@ -4014,10 +3968,9 @@ } String carrierName = mIccRecords != null ? mIccRecords.getServiceProviderName() : ""; - PersistableBundle config = getCarrierConfig(); - if (config.getBoolean(CarrierConfigManager.KEY_CARRIER_NAME_OVERRIDE_BOOL) + if (mCarrierConfig.getBoolean(CarrierConfigManager.KEY_CARRIER_NAME_OVERRIDE_BOOL) || TextUtils.isEmpty(carrierName)) { - return config.getString(CarrierConfigManager.KEY_CARRIER_NAME_STRING); + return mCarrierConfig.getString(CarrierConfigManager.KEY_CARRIER_NAME_STRING); } return carrierName; @@ -4036,7 +3989,6 @@ */ @CarrierNameDisplayBitmask public int getCarrierNameDisplayBitmask(ServiceState ss) { - PersistableBundle config = getCarrierConfig(); if (!TextUtils.isEmpty(getOperatorBrandOverride())) { // If the operator has been overridden, all PLMNs will be considered HOME PLMNs, only // show SPN. @@ -4046,7 +3998,7 @@ // This is a hack from IccRecords#getServiceProviderName(). return CARRIER_NAME_DISPLAY_BITMASK_SHOW_PLMN; } else { - boolean useRoamingFromServiceState = config.getBoolean( + boolean useRoamingFromServiceState = mCarrierConfig.getBoolean( CarrierConfigManager.KEY_SPN_DISPLAY_RULE_USE_ROAMING_FROM_SERVICE_STATE_BOOL); int carrierDisplayNameConditionFromSim = mIccRecords == null ? 0 : mIccRecords.getCarrierNameDisplayCondition(); @@ -4253,7 +4205,7 @@ } /** - * Do not set roaming state in case of oprators considered non-roaming. + * Do not set roaming state in case of operators considered non-roaming. * * Can use mcc or mcc+mnc as item of * {@link CarrierConfigManager#KEY_NON_ROAMING_OPERATOR_STRING_ARRAY}. @@ -4266,8 +4218,7 @@ private boolean isOperatorConsideredNonRoaming(ServiceState s) { String operatorNumeric = s.getOperatorNumeric(); - PersistableBundle config = getCarrierConfig(); - String[] numericArray = config.getStringArray( + String[] numericArray = mCarrierConfig.getStringArray( CarrierConfigManager.KEY_NON_ROAMING_OPERATOR_STRING_ARRAY); if (ArrayUtils.isEmpty(numericArray) || operatorNumeric == null) { @@ -4284,8 +4235,7 @@ private boolean isOperatorConsideredRoaming(ServiceState s) { String operatorNumeric = s.getOperatorNumeric(); - PersistableBundle config = getCarrierConfig(); - String[] numericArray = config.getStringArray( + String[] numericArray = mCarrierConfig.getStringArray( CarrierConfigManager.KEY_ROAMING_OPERATOR_STRING_ARRAY); if (ArrayUtils.isEmpty(numericArray) || operatorNumeric == null) { return false; @@ -4318,12 +4268,12 @@ ((state & RILConstants.RIL_RESTRICTED_STATE_CS_EMERGENCY) != 0) || ((state & RILConstants.RIL_RESTRICTED_STATE_CS_ALL) != 0) ); //ignore the normal call and data restricted state before SIM READY - if (mUiccApplcation != null && mUiccApplcation.getState() == AppState.APPSTATE_READY) { + if (mUiccApplication != null + && mUiccApplication.getState() == AppState.APPSTATE_READY) { newRs.setCsNormalRestricted( ((state & RILConstants.RIL_RESTRICTED_STATE_CS_NORMAL) != 0) || ((state & RILConstants.RIL_RESTRICTED_STATE_CS_ALL) != 0) ); - newRs.setPsRestricted( - (state & RILConstants.RIL_RESTRICTED_STATE_PS_ALL)!= 0); + newRs.setPsRestricted((state & RILConstants.RIL_RESTRICTED_STATE_PS_ALL) != 0); } if (DBG) log("onRestrictedStateChanged: new rs "+ newRs); @@ -4537,12 +4487,9 @@ } Context context = mPhone.getContext(); - SubscriptionInfo info = mSubscriptionController - .getActiveSubscriptionInfo(mPhone.getSubId(), context.getOpPackageName(), - context.getAttributionTag()); - - //if subscription is part of a group and non-primary, suppress all notifications - if (info == null || (info.isOpportunistic() && info.getGroupUuid() != null)) { + SubscriptionInfoInternal subInfo = mSubscriptionManagerService + .getSubscriptionInfoInternal(mPhone.getSubId()); + if (subInfo == null || !subInfo.isVisible()) { log("cannot setNotification on invisible subid mSubId=" + mSubId); return; } @@ -4555,10 +4502,8 @@ return; } - boolean autoCancelCsRejectNotification = false; - - PersistableBundle bundle = getCarrierConfig(); - boolean disableVoiceBarringNotification = bundle.getBoolean( + boolean autoCancelCsRejectNotification; + boolean disableVoiceBarringNotification = mCarrierConfig.getBoolean( CarrierConfigManager.KEY_DISABLE_VOICE_BARRING_NOTIFICATION_BOOL, false); if (disableVoiceBarringNotification && (notifyType == CS_ENABLED || notifyType == CS_NORMAL_ENABLED @@ -4566,7 +4511,7 @@ if (DBG) log("Voice/emergency call barred notification disabled"); return; } - autoCancelCsRejectNotification = bundle.getBoolean( + autoCancelCsRejectNotification = mCarrierConfig.getBoolean( CarrierConfigManager.KEY_AUTO_CANCEL_CS_REJECT_NOTIFICATION, false); CharSequence details = ""; @@ -4576,7 +4521,7 @@ final boolean multipleSubscriptions = (((TelephonyManager) mPhone.getContext() .getSystemService(Context.TELEPHONY_SERVICE)).getPhoneCount() > 1); - final int simNumber = mSubscriptionController.getSlotIndex(mSubId) + 1; + int simNumber = SubscriptionManager.getSlotIndex(mSubId) + 1; switch (notifyType) { case PS_ENABLED: @@ -4987,9 +4932,10 @@ * * @param h handler to notify * @param what what code of message when delivered + * @param userobj the user obj that will be passed back when notify */ - public void registerForServiceStateChanged(Handler h, int what) { - mServiceStateChangedRegistrants.addUnique(h, what, null); + public void registerForServiceStateChanged(Handler h, int what, Object userobj) { + mServiceStateChangedRegistrants.addUnique(h, what, userobj); } /** @@ -5009,79 +4955,33 @@ public void powerOffRadioSafely() { synchronized (this) { if (!mPendingRadioPowerOffAfterDataOff) { - if (mPhone.isUsingNewDataStack()) { - if (mAnyDataExisting) { - log("powerOffRadioSafely: Tear down all data networks."); - mPhone.getDataNetworkController().tearDownAllDataNetworks( - DataNetwork.TEAR_DOWN_REASON_AIRPLANE_MODE_ON); - sendEmptyMessageDelayed(EVENT_SET_RADIO_POWER_OFF, - POWER_OFF_ALL_DATA_NETWORKS_DISCONNECTED_TIMEOUT); - } else { - log("powerOffRadioSafely: No data is connected."); - sendEmptyMessage(EVENT_ALL_DATA_DISCONNECTED); - } - mPendingRadioPowerOffAfterDataOff = true; - return; + // hang up all active voice calls first + if (mPhone.isPhoneTypeGsm() && mPhone.isInCall()) { + mPhone.mCT.mRingingCall.hangupIfAlive(); + mPhone.mCT.mBackgroundCall.hangupIfAlive(); + mPhone.mCT.mForegroundCall.hangupIfAlive(); } - int dds = SubscriptionManager.getDefaultDataSubscriptionId(); - // To minimize race conditions we call cleanUpAllConnections on - // both if else paths instead of before this isDisconnected test. - if (mPhone.areAllDataDisconnected() - && (dds == mPhone.getSubId() - || (dds != mPhone.getSubId() - && ProxyController.getInstance().areAllDataDisconnected(dds)))) { - // To minimize race conditions we do this after isDisconnected - for (int transport : mAccessNetworksManager.getAvailableTransports()) { - if (mPhone.getDcTracker(transport) != null) { - mPhone.getDcTracker(transport).cleanUpAllConnections( - Phone.REASON_RADIO_TURNED_OFF); - } - } - if (DBG) { - log("powerOffRadioSafely: Data disconnected, turn off radio now."); - } - hangupAndPowerOff(); - } else { - // hang up all active voice calls first - if (mPhone.isPhoneTypeGsm() && mPhone.isInCall()) { - mPhone.mCT.mRingingCall.hangupIfAlive(); - mPhone.mCT.mBackgroundCall.hangupIfAlive(); - mPhone.mCT.mForegroundCall.hangupIfAlive(); - } - for (int transport : mAccessNetworksManager.getAvailableTransports()) { - if (mPhone.getDcTracker(transport) != null) { - mPhone.getDcTracker(transport).cleanUpAllConnections( - Phone.REASON_RADIO_TURNED_OFF); - } - } - if (dds != mPhone.getSubId() - && !ProxyController.getInstance().areAllDataDisconnected(dds)) { - if (DBG) { - log(String.format("powerOffRadioSafely: Data is active on DDS (%d)." - + " Wait for all data disconnect", dds)); - } - // Data is not disconnected on DDS. Wait for the data disconnect complete - // before sending the RADIO_POWER off. - ProxyController.getInstance().registerForAllDataDisconnected(dds, this, - EVENT_ALL_DATA_DISCONNECTED); + for (Phone phone : PhoneFactory.getPhones()) { + if (!phone.getDataNetworkController().areAllDataDisconnected()) { + log("powerOffRadioSafely: Data is active on phone " + phone.getSubId() + + ". Wait for all data disconnect."); mPendingRadioPowerOffAfterDataOff = true; + phone.getDataNetworkController().registerDataNetworkControllerCallback( + mDataDisconnectedCallback); } - Message msg = Message.obtain(this); - msg.what = EVENT_SET_RADIO_POWER_OFF; - msg.arg1 = ++mPendingRadioPowerOffAfterDataOffTag; - if (sendMessageDelayed(msg, 30000)) { - if (DBG) { - log("powerOffRadioSafely: Wait up to 30s for data to isconnect, then" - + " turn off radio."); - } - mPendingRadioPowerOffAfterDataOff = true; - } else { - log("powerOffRadioSafely: Cannot send delayed Msg, turn off radio right" - + " away."); - hangupAndPowerOff(); - mPendingRadioPowerOffAfterDataOff = false; - } + } + + // Tear down outside of the disconnected check to prevent race conditions. + mPhone.getDataNetworkController().tearDownAllDataNetworks( + DataNetwork.TEAR_DOWN_REASON_AIRPLANE_MODE_ON); + + if (mPendingRadioPowerOffAfterDataOff) { + sendEmptyMessageDelayed(EVENT_SET_RADIO_POWER_OFF, + POWER_OFF_ALL_DATA_NETWORKS_DISCONNECTED_TIMEOUT); + } else { + log("powerOffRadioSafely: No data is connected, turn off radio now."); + hangupAndPowerOff(); } } } @@ -5097,7 +4997,6 @@ if (mPendingRadioPowerOffAfterDataOff) { if (DBG) log("Process pending request to turn radio off."); hangupAndPowerOff(); - mPendingRadioPowerOffAfterDataOffTag += 1; mPendingRadioPowerOffAfterDataOff = false; return true; } @@ -5105,9 +5004,11 @@ } } - private void onCarrierConfigChanged() { - PersistableBundle config = getCarrierConfig(); - log("CarrierConfigChange " + config); + private void onCarrierConfigurationChanged(int slotIndex) { + if (slotIndex != mPhone.getPhoneId()) return; + + mCarrierConfig = getCarrierConfig(); + log("CarrierConfigChange " + mCarrierConfig); // Load the ERI based on carrier config. Carrier might have their specific ERI. if (mEriManager != null) { @@ -5115,8 +5016,8 @@ mCdnr.updateEfForEri(getOperatorNameFromEri()); } - updateOperatorNamePattern(config); - mCdnr.updateEfFromCarrierConfig(config); + updateOperatorNamePattern(mCarrierConfig); + mCdnr.updateEfFromCarrierConfig(mCarrierConfig); mPhone.notifyCallForwardingIndicator(); // Sometimes the network registration information comes before carrier config is ready. @@ -5174,8 +5075,8 @@ // Determine if the Icc card exists private boolean iccCardExists() { boolean iccCardExist = false; - if (mUiccApplcation != null) { - iccCardExist = mUiccApplcation.getState() != AppState.APPSTATE_UNKNOWN; + if (mUiccApplication != null) { + iccCardExist = mUiccApplication.getState() != AppState.APPSTATE_UNKNOWN; } return iccCardExist; } @@ -5326,18 +5227,15 @@ pw.println(" mNewSS=" + mNewSS); pw.println(" mVoiceCapable=" + mVoiceCapable); pw.println(" mRestrictedState=" + mRestrictedState); - pw.println(" mPollingContext=" + mPollingContext + " - " + - (mPollingContext != null ? mPollingContext[0] : "")); + pw.println(" mPollingContext=" + Arrays.toString(mPollingContext)); pw.println(" mDesiredPowerState=" + mDesiredPowerState); pw.println(" mRestrictedState=" + mRestrictedState); pw.println(" mPendingRadioPowerOffAfterDataOff=" + mPendingRadioPowerOffAfterDataOff); - pw.println(" mPendingRadioPowerOffAfterDataOffTag=" + mPendingRadioPowerOffAfterDataOffTag); pw.println(" mCellIdentity=" + Rlog.pii(VDBG, mCellIdentity)); pw.println(" mLastCellInfoReqTime=" + mLastCellInfoReqTime); dumpCellInfoList(pw); pw.flush(); pw.println(" mAllowedNetworkTypes=" + mAllowedNetworkTypes); - pw.println(" mAnyDataExisting=" + mAnyDataExisting); pw.println(" mMaxDataCalls=" + mMaxDataCalls); pw.println(" mNewMaxDataCalls=" + mNewMaxDataCalls); pw.println(" mReasonDataDenied=" + mReasonDataDenied); @@ -5366,8 +5264,8 @@ pw.println(" mDefaultRoamingIndicator=" + mDefaultRoamingIndicator); pw.println(" mRegistrationState=" + mRegistrationState); pw.println(" mMdn=" + mMdn); - pw.println(" mHomeSystemId=" + mHomeSystemId); - pw.println(" mHomeNetworkId=" + mHomeNetworkId); + pw.println(" mHomeSystemId=" + Arrays.toString(mHomeSystemId)); + pw.println(" mHomeNetworkId=" + Arrays.toString(mHomeNetworkId)); pw.println(" mMin=" + mMin); pw.println(" mPrlVersion=" + mPrlVersion); pw.println(" mIsMinInfoReady=" + mIsMinInfoReady); @@ -5381,9 +5279,8 @@ pw.println(" mImsRegistrationOnOff=" + mImsRegistrationOnOff); pw.println(" pending radio off event=" + hasMessages(EVENT_POWER_OFF_RADIO_IMS_DEREG_TIMEOUT)); - pw.println(" mRadioDisabledByCarrier" + mRadioDisabledByCarrier); + pw.println(" mRadioPowerOffReasons=" + mRadioPowerOffReasons); pw.println(" mDeviceShuttingDown=" + mDeviceShuttingDown); - pw.println(" mSpnUpdatePending=" + mSpnUpdatePending); pw.println(" mCellInfoMinIntervalMs=" + mCellInfoMinIntervalMs); pw.println(" mEriManager=" + mEriManager); @@ -5605,14 +5502,8 @@ return mPhone.getPhoneId(); } - /* Reset Service state when IWLAN is enabled as polling in airplane mode - * causes state to go to OUT_OF_SERVICE state instead of STATE_OFF - */ - - /** - * This method adds IWLAN registration info for legacy mode devices camped on IWLAN. It also - * makes some adjustments when the device camps on IWLAN in airplane mode. + * This method makes some adjustments when the device camps on IWLAN in airplane mode. */ private void processIwlanRegistrationInfo() { if (mCi.getRadioState() == TelephonyManager.RADIO_POWER_OFF) { @@ -5626,7 +5517,7 @@ } // operator info should be kept in SS String operator = mNewSS.getOperatorAlphaLong(); - mNewSS.setOutOfService(mAccessNetworksManager.isInLegacyMode(), true); + mNewSS.setOutOfService(true); if (resetIwlanRatVal) { mNewSS.setDataRegState(ServiceState.STATE_IN_SERVICE); NetworkRegistrationInfo nri = new NetworkRegistrationInfo.Builder() @@ -5636,17 +5527,6 @@ .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_HOME) .build(); mNewSS.addNetworkRegistrationInfo(nri); - if (mAccessNetworksManager.isInLegacyMode()) { - // If in legacy mode, simulate the behavior that IWLAN registration info - // is reported through WWAN transport. - nri = new NetworkRegistrationInfo.Builder() - .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN) - .setDomain(NetworkRegistrationInfo.DOMAIN_PS) - .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_IWLAN) - .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_HOME) - .build(); - mNewSS.addNetworkRegistrationInfo(nri); - } mNewSS.setOperatorAlphaLong(operator); // Since it's in airplane mode, cellular must be out of service. The only possible // transport for data to go through is the IWLAN transport. Setting this to true @@ -5656,31 +5536,6 @@ } return; } - - // If the device operates in legacy mode and camps on IWLAN, modem reports IWLAN as a RAT - // through WWAN registration info. To be consistent with the behavior with AP-assisted mode, - // we manually make a WLAN registration info for clients to consume. In this scenario, - // both WWAN and WLAN registration info are the IWLAN registration info and that's the - // unfortunate limitation we have when the device operates in legacy mode. In AP-assisted - // mode, the WWAN registration will correctly report the actual cellular registration info - // when the device camps on IWLAN. - if (mAccessNetworksManager.isInLegacyMode()) { - NetworkRegistrationInfo wwanNri = mNewSS.getNetworkRegistrationInfo( - NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN); - if (wwanNri != null && wwanNri.getAccessNetworkTechnology() - == TelephonyManager.NETWORK_TYPE_IWLAN) { - NetworkRegistrationInfo wlanNri = new NetworkRegistrationInfo.Builder() - .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WLAN) - .setDomain(NetworkRegistrationInfo.DOMAIN_PS) - .setRegistrationState(wwanNri.getRegistrationState()) - .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_IWLAN) - .setRejectCause(wwanNri.getRejectCause()) - .setEmergencyOnly(wwanNri.isEmergencyEnabled()) - .setAvailableServices(wwanNri.getAvailableServices()) - .build(); - mNewSS.addNetworkRegistrationInfo(wlanNri); - } - } } /** @@ -5938,25 +5793,6 @@ } /** - * Registers for cell bandwidth changed. - * @param h handler to notify - * @param what what code of message when delivered - * @param obj placed in Message.obj - */ - public void registerForBandwidthChanged(Handler h, int what, Object obj) { - Registrant r = new Registrant(h, what, obj); - mBandwidthChangedRegistrants.add(r); - } - - /** - * Unregisters for cell bandwidth changed. - * @param h handler to notify - */ - public void unregisterForBandwidthChanged(Handler h) { - mBandwidthChangedRegistrants.remove(h); - } - - /** * Get the NR data connection context ids. * * @return data connection context ids.
diff --git a/src/java/com/android/internal/telephony/SignalStrengthController.java b/src/java/com/android/internal/telephony/SignalStrengthController.java index fc378a2..59f7333 100644 --- a/src/java/com/android/internal/telephony/SignalStrengthController.java +++ b/src/java/com/android/internal/telephony/SignalStrengthController.java
@@ -16,12 +16,11 @@ package com.android.internal.telephony; +import static android.telephony.TelephonyManager.HAL_SERVICE_NETWORK; + import android.annotation.NonNull; import android.annotation.Nullable; -import android.content.BroadcastReceiver; import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; import android.os.AsyncResult; import android.os.Handler; import android.os.IBinder; @@ -46,6 +45,7 @@ import android.util.Pair; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.telephony.subscription.SubscriptionManagerService; import com.android.internal.telephony.util.ArrayUtils; import com.android.internal.util.IndentingPrintWriter; import com.android.telephony.Rlog; @@ -56,6 +56,7 @@ import java.util.Arrays; import java.util.Iterator; import java.util.List; +import java.util.NoSuchElementException; import java.util.Set; import java.util.TreeSet; import java.util.concurrent.TimeUnit; @@ -96,7 +97,6 @@ private static final int EVENT_POLL_SIGNAL_STRENGTH = 7; private static final int EVENT_SIGNAL_STRENGTH_UPDATE = 8; private static final int EVENT_POLL_SIGNAL_STRENGTH_DONE = 9; - private static final int EVENT_CARRIER_CONFIG_CHANGED = 10; @NonNull private final Phone mPhone; @@ -141,20 +141,6 @@ @NonNull private final LocalLog mLocalLog = new LocalLog(64); - private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - final String action = intent.getAction(); - if (CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED.equals(action)) { - int phoneId = intent.getExtras().getInt(CarrierConfigManager.EXTRA_SLOT_INDEX); - // Ignore the carrier config changed if the phoneId is not matched. - if (phoneId == mPhone.getPhoneId()) { - sendEmptyMessage(EVENT_CARRIER_CONFIG_CHANGED); - } - } - } - }; - public SignalStrengthController(@NonNull Phone phone) { mPhone = phone; mCi = mPhone.mCi; @@ -164,10 +150,12 @@ mCi.setOnSignalStrengthUpdate(this, EVENT_SIGNAL_STRENGTH_UPDATE, null); setSignalStrengthDefaultValues(); + CarrierConfigManager ccm = mPhone.getContext().getSystemService(CarrierConfigManager.class); mCarrierConfig = getCarrierConfig(); - IntentFilter filter = new IntentFilter(); - filter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED); - mPhone.getContext().registerReceiver(mBroadcastReceiver, filter); + // Callback which directly handle config change should be executed on handler thread + ccm.registerCarrierConfigChangeListener(this::post, + (slotIndex, subId, carrierId, specificCarrierId) -> + onCarrierConfigurationChanged(slotIndex)); } @Override @@ -227,6 +215,11 @@ while (it.hasNext()) { SignalRequestRecord srr = it.next(); if (srr.mRequest.getLiveToken().equals(record.mRequest.getLiveToken())) { + try { + srr.mRequest.getLiveToken().unlinkToDeath(srr, 0); + } catch (NoSuchElementException ignored) { + // Either never linked or has already unlinked, ignore anyway + } it.remove(); } } @@ -275,11 +268,6 @@ break; } - case EVENT_CARRIER_CONFIG_CHANGED: { - onCarrierConfigChanged(); - break; - } - default: log("Unhandled message with number: " + msg.what); break; @@ -351,7 +339,7 @@ || (curTime - mSignalStrengthUpdatedTime > SIGNAL_STRENGTH_REFRESH_THRESHOLD_IN_MS); if (!isStale) return false; - List<SubscriptionInfo> subInfoList = SubscriptionController.getInstance() + List<SubscriptionInfo> subInfoList = SubscriptionManagerService.getInstance() .getActiveSubscriptionInfoList(mPhone.getContext().getOpPackageName(), mPhone.getContext().getAttributionTag()); @@ -417,7 +405,7 @@ (lteMeasurementEnabled & CellSignalStrengthLte.USE_RSRP) != 0)); } - if (mPhone.getHalVersion().greaterOrEqual(RIL.RADIO_HAL_VERSION_1_5)) { + if (mPhone.getHalVersion(HAL_SERVICE_NETWORK).greaterOrEqual(RIL.RADIO_HAL_VERSION_1_5)) { int[] lteRsrqThresholds = mCarrierConfig.getIntArray( CarrierConfigManager.KEY_LTE_RSRQ_THRESHOLDS_INT_ARRAY); if (lteRsrqThresholds != null) { @@ -474,6 +462,18 @@ AccessNetworkConstants.AccessNetworkType.NGRAN, (nrMeasurementEnabled & CellSignalStrengthNr.USE_SSSINR) != 0)); } + + int[] wcdmaEcnoThresholds = mCarrierConfig.getIntArray( + CarrierConfigManager.KEY_WCDMA_ECNO_THRESHOLDS_INT_ARRAY); + if (wcdmaEcnoThresholds != null) { + signalThresholdInfos.add( + createSignalThresholdsInfo( + SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_ECNO, + wcdmaEcnoThresholds, + AccessNetworkConstants.AccessNetworkType.UTRAN, + false)); + } + } consolidatedAndSetReportingCriteria(signalThresholdInfos); @@ -507,7 +507,7 @@ AccessNetworkConstants.AccessNetworkType.CDMA2000, true)); - if (mPhone.getHalVersion().greaterOrEqual(RIL.RADIO_HAL_VERSION_1_5)) { + if (mPhone.getHalVersion(HAL_SERVICE_NETWORK).greaterOrEqual(RIL.RADIO_HAL_VERSION_1_5)) { signalThresholdInfos.add( createSignalThresholdsInfo( SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSRQ, @@ -540,6 +540,12 @@ AccessNetworkThresholds.NGRAN_SSSINR, AccessNetworkConstants.AccessNetworkType.NGRAN, false)); + signalThresholdInfos.add( + createSignalThresholdsInfo( + SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_ECNO, + AccessNetworkThresholds.UTRAN_ECNO, + AccessNetworkConstants.AccessNetworkType.UTRAN, + false)); } consolidatedAndSetReportingCriteria(signalThresholdInfos); @@ -552,12 +558,13 @@ for (SignalThresholdInfo signalThresholdInfo : signalThresholdInfos) { final int ran = signalThresholdInfo.getRadioAccessNetworkType(); final int measurementType = signalThresholdInfo.getSignalMeasurementType(); - final boolean isEnabledForSystem = signalThresholdInfo.isEnabled(); + final boolean isEnabledForSystem = + signalThresholdInfo.isEnabled() && shouldHonorSystemThresholds(); int[] consolidatedThresholds = getConsolidatedSignalThresholds( ran, measurementType, - isEnabledForSystem && shouldHonorSystemThresholds() + isEnabledForSystem ? signalThresholdInfo.getThresholds() : new int[]{}, ALIGNMENT_HYSTERESIS_DB); @@ -567,12 +574,14 @@ measurementType, mPhone.getSubId(), mPhone.isDeviceIdle()); + int hysteresisDb = getMinimumHysteresisDb(isEnabledForAppRequest, ran, measurementType, + consolidatedThresholds); consolidatedSignalThresholdInfos.add( new SignalThresholdInfo.Builder() .setRadioAccessNetworkType(ran) .setSignalMeasurementType(measurementType) .setHysteresisMs(REPORTING_HYSTERESIS_MILLIS) - .setHysteresisDb(REPORTING_HYSTERESIS_DB) + .setHysteresisDb(hysteresisDb) .setThresholds(consolidatedThresholds, true /*isSystem*/) .setIsEnabled(isEnabledForSystem || isEnabledForAppRequest) .build()); @@ -583,6 +592,126 @@ + consolidatedSignalThresholdInfos); } + /** + * Return the minimum hysteresis dB from all available sources: + * - system default + * - value set by client through API + * - threshold delta + */ + @VisibleForTesting + public int getMinimumHysteresisDb(boolean isEnabledForAppRequest, int ran, int measurementType, + final int[] consolidatedThresholdList) { + + int currHysteresisDb = getHysteresisDbFromCarrierConfig(ran, measurementType); + + if (isEnabledForAppRequest) { + // Get minimum hysteresisDb at api + int apiHysteresisDb = + getHysteresisDbFromSignalThresholdInfoRequests(ran, measurementType); + + // Choose minimum of hysteresisDb between api Vs current system/cc value set + currHysteresisDb = Math.min(currHysteresisDb, apiHysteresisDb); + + // Hal Req: choose hysteresis db value to be smaller of smallest of threshold delta + currHysteresisDb = computeHysteresisDbOnSmallestThresholdDelta( + currHysteresisDb, consolidatedThresholdList); + } + return currHysteresisDb; + } + + /** + * Get the hysteresis db value from Signal Requests + * Note: Based on the current use case, there does not exist multile App signal threshold info + * requests with hysteresis db value, so this logic picks the latest hysteresis db value set. + * + * TODO(b/262655157): Support Multiple App Hysteresis DB value customisation + */ + private int getHysteresisDbFromSignalThresholdInfoRequests( + @AccessNetworkConstants.RadioAccessNetworkType int ran, + @SignalThresholdInfo.SignalMeasurementType int measurement) { + int apiHysteresisDb = REPORTING_HYSTERESIS_DB; + for (SignalRequestRecord record : mSignalRequestRecords) { + for (SignalThresholdInfo info : record.mRequest.getSignalThresholdInfos()) { + if (isRanAndSignalMeasurementTypeMatch(ran, measurement, info)) { + if (info.getHysteresisDb() >= 0) { + apiHysteresisDb = info.getHysteresisDb(); + } + } + } + } + return apiHysteresisDb; + } + + private int getHysteresisDbFromCarrierConfig(int ran, int measurement) { + int configHysteresisDb = REPORTING_HYSTERESIS_DB; + String configKey = null; + + switch (ran) { + case AccessNetworkConstants.AccessNetworkType.GERAN: + if (measurement == SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI) { + configKey = CarrierConfigManager.KEY_GERAN_RSSI_HYSTERESIS_DB_INT; + } + break; + case AccessNetworkConstants.AccessNetworkType.UTRAN: + if (measurement == SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSCP) { + configKey = CarrierConfigManager.KEY_UTRAN_RSCP_HYSTERESIS_DB_INT; + } else if (measurement == SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_ECNO) { + configKey = CarrierConfigManager.KEY_UTRAN_ECNO_HYSTERESIS_DB_INT; + } + break; + case AccessNetworkConstants.AccessNetworkType.EUTRAN: + if (measurement == SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSRP) { + configKey = CarrierConfigManager.KEY_EUTRAN_RSRP_HYSTERESIS_DB_INT; + } else if (measurement == SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSRQ) { + configKey = CarrierConfigManager.KEY_EUTRAN_RSRQ_HYSTERESIS_DB_INT; + } else if (measurement == SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSNR) { + configKey = CarrierConfigManager.KEY_EUTRAN_RSSNR_HYSTERESIS_DB_INT; + } + break; + case AccessNetworkConstants.AccessNetworkType.NGRAN: + if (measurement == SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSRSRP) { + configKey = CarrierConfigManager.KEY_NGRAN_SSRSRP_HYSTERESIS_DB_INT; + } else if (measurement == SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSRSRQ) { + configKey = CarrierConfigManager.KEY_NGRAN_SSRSRQ_HYSTERESIS_DB_INT; + } else if (measurement == SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSSINR) { + configKey = CarrierConfigManager.KEY_NGRAN_SSSINR_HYSTERESIS_DB_INT; + } + break; + default: + localLog("No matching configuration"); + } + if (configKey != null) { + configHysteresisDb = mCarrierConfig.getInt(configKey, REPORTING_HYSTERESIS_DB); + } + return configHysteresisDb >= SignalThresholdInfo.HYSTERESIS_DB_MINIMUM + ? configHysteresisDb : REPORTING_HYSTERESIS_DB; + } + + /** + * This method computes the hysteresis db value between smaller of the smallest Threshold Delta + * and system / cc / api hysteresis db value determined. + * + * @param currMinHysteresisDb smaller value between system / cc / api hysteresis db value + * @param signalThresholdInfoArray consolidated threshold info with App request consolidated. + * @return current minimum hysteresis db value computed between above params. + * + */ + private int computeHysteresisDbOnSmallestThresholdDelta( + int currMinHysteresisDb, final int[] signalThresholdInfoArray) { + int index = 0; + if (signalThresholdInfoArray.length > 1) { + while (index != signalThresholdInfoArray.length - 1) { + if (signalThresholdInfoArray[index + 1] - signalThresholdInfoArray[index] + < currMinHysteresisDb) { + currMinHysteresisDb = + signalThresholdInfoArray[index + 1] - signalThresholdInfoArray[index]; + } + index++; + } + } + return currMinHysteresisDb; + } + void setSignalStrengthDefaultValues() { mSignalStrength = new SignalStrength(); mSignalStrengthUpdatedTime = System.currentTimeMillis(); @@ -731,7 +860,11 @@ && srr.mRequest.isSystemThresholdReportingRequestedWhileIdle()); } - void onDeviceIdleStateChanged(boolean isDeviceIdle) { + /** + * Get notified when device idle state changed + */ + @VisibleForTesting + public void onDeviceIdleStateChanged(boolean isDeviceIdle) { sendMessage(obtainMessage(EVENT_ON_DEVICE_IDLE_STATE_CHANGED, isDeviceIdle)); localLog("onDeviceIdleStateChanged isDeviceIdle=" + isDeviceIdle); @@ -968,7 +1101,9 @@ return earfcnPairList; } - private void onCarrierConfigChanged() { + private void onCarrierConfigurationChanged(int slotIndex) { + if (slotIndex != mPhone.getPhoneId()) return; + mCarrierConfig = getCarrierConfig(); log("Carrier Config changed."); @@ -1097,6 +1232,16 @@ 15, /* SIGNAL_STRENGTH_GOOD */ 30 /* SIGNAL_STRENGTH_GREAT */ }; + + /** + * List of dBm thresholds for UTRAN {@link AccessNetworkConstants.AccessNetworkType} ECNO + */ + public static final int[] UTRAN_ECNO = new int[]{ + -24, /* SIGNAL_STRENGTH_POOR */ + -14, /* SIGNAL_STRENGTH_MODERATE */ + -6, /* SIGNAL_STRENGTH_GOOD */ + 1 /* SIGNAL_STRENGTH_GREAT */ + }; } private static void log(String msg) {
diff --git a/src/java/com/android/internal/telephony/SimIndication.java b/src/java/com/android/internal/telephony/SimIndication.java index f3ece16..d74a9b1 100644 --- a/src/java/com/android/internal/telephony/SimIndication.java +++ b/src/java/com/android/internal/telephony/SimIndication.java
@@ -16,6 +16,8 @@ package com.android.internal.telephony; +import static android.telephony.TelephonyManager.HAL_SERVICE_SIM; + import static com.android.internal.telephony.RILConstants.RIL_UNSOL_CARRIER_INFO_IMSI_ENCRYPTION; import static com.android.internal.telephony.RILConstants.RIL_UNSOL_CDMA_SUBSCRIPTION_SOURCE_CHANGED; import static com.android.internal.telephony.RILConstants.RIL_UNSOL_RESPONSE_SIM_PHONEBOOK_CHANGED; @@ -53,9 +55,11 @@ * @param indicationType Type of radio indication */ public void carrierInfoForImsiEncryption(int indicationType) { - mRil.processIndication(RIL.SIM_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_SIM, indicationType); - if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_CARRIER_INFO_IMSI_ENCRYPTION, null); + if (mRil.isLogOrTrace()) { + mRil.unsljLogRet(RIL_UNSOL_CARRIER_INFO_IMSI_ENCRYPTION, null); + } mRil.mCarrierInfoForImsiEncryptionRegistrants.notifyRegistrants( new AsyncResult(null, null, null)); @@ -67,10 +71,12 @@ * @param cdmaSource New CdmaSubscriptionSource */ public void cdmaSubscriptionSourceChanged(int indicationType, int cdmaSource) { - mRil.processIndication(RIL.SIM_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_SIM, indicationType); int[] response = new int[]{cdmaSource}; - if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_CDMA_SUBSCRIPTION_SOURCE_CHANGED, response); + if (mRil.isLogOrTrace()) { + mRil.unsljLogRet(RIL_UNSOL_CDMA_SUBSCRIPTION_SOURCE_CHANGED, response); + } mRil.mCdmaSubscriptionChangedRegistrants.notifyRegistrants( new AsyncResult(null, response, null)); @@ -81,9 +87,9 @@ * @param indicationType Type of radio indication */ public void simPhonebookChanged(int indicationType) { - mRil.processIndication(RIL.SIM_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_SIM, indicationType); - if (RIL.RILJ_LOGD) { + if (mRil.isLogOrTrace()) { mRil.unsljLog(RIL_UNSOL_RESPONSE_SIM_PHONEBOOK_CHANGED); } @@ -98,7 +104,7 @@ */ public void simPhonebookRecordsReceived(int indicationType, byte status, android.hardware.radio.sim.PhonebookRecordInfo[] records) { - mRil.processIndication(RIL.SIM_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_SIM, indicationType); List<SimPhonebookRecord> simPhonebookRecords = new ArrayList<>(); @@ -106,8 +112,9 @@ simPhonebookRecords.add(RILUtils.convertHalPhonebookRecordInfo(record)); } - if (RIL.RILJ_LOGD) { - mRil.unsljLogRet(RIL_UNSOL_RESPONSE_SIM_PHONEBOOK_RECORDS_RECEIVED, + if (mRil.isLogOrTrace()) { + mRil.unsljLogRet( + RIL_UNSOL_RESPONSE_SIM_PHONEBOOK_RECORDS_RECEIVED, "status = " + status + " received " + records.length + " records"); } @@ -122,14 +129,14 @@ */ public void simRefresh(int indicationType, android.hardware.radio.sim.SimRefreshResult refreshResult) { - mRil.processIndication(RIL.SIM_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_SIM, indicationType); IccRefreshResponse response = new IccRefreshResponse(); response.refreshResult = refreshResult.type; response.efId = refreshResult.efId; response.aid = refreshResult.aid; - if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_SIM_REFRESH, response); + if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_SIM_REFRESH, response); mRil.mIccRefreshRegistrants.notifyRegistrants(new AsyncResult(null, response, null)); } @@ -139,9 +146,9 @@ * @param indicationType Type of radio indication */ public void simStatusChanged(int indicationType) { - mRil.processIndication(RIL.SIM_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_SIM, indicationType); - if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_RESPONSE_SIM_STATUS_CHANGED); + if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_RESPONSE_SIM_STATUS_CHANGED); mRil.mIccStatusChangedRegistrants.notifyRegistrants(); } @@ -154,9 +161,9 @@ * Refer to TS 102.223 section 9.4 for command types */ public void stkEventNotify(int indicationType, String cmd) { - mRil.processIndication(RIL.SIM_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_SIM, indicationType); - if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_STK_EVENT_NOTIFY); + if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_STK_EVENT_NOTIFY); if (mRil.mCatEventRegistrant != null) { mRil.mCatEventRegistrant.notifyRegistrant(new AsyncResult(null, cmd, null)); @@ -170,9 +177,9 @@ * Refer to TS 102.223 section 9.4 for command types */ public void stkProactiveCommand(int indicationType, String cmd) { - mRil.processIndication(RIL.SIM_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_SIM, indicationType); - if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_STK_PROACTIVE_COMMAND); + if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_STK_PROACTIVE_COMMAND); if (mRil.mCatProCmdRegistrant != null) { mRil.mCatProCmdRegistrant.notifyRegistrant(new AsyncResult(null, cmd, null)); @@ -184,9 +191,9 @@ * @param indicationType Type of radio indication */ public void stkSessionEnd(int indicationType) { - mRil.processIndication(RIL.SIM_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_SIM, indicationType); - if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_STK_SESSION_END); + if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_STK_SESSION_END); if (mRil.mCatSessionEndRegistrant != null) { mRil.mCatSessionEndRegistrant.notifyRegistrant(new AsyncResult(null, null, null)); @@ -199,11 +206,13 @@ * @param activate false for subscription deactivated, true for subscription activated */ public void subscriptionStatusChanged(int indicationType, boolean activate) { - mRil.processIndication(RIL.SIM_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_SIM, indicationType); int[] response = new int[]{activate ? 1 : 0}; - if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_UICC_SUBSCRIPTION_STATUS_CHANGED, response); + if (mRil.isLogOrTrace()) { + mRil.unsljLogRet(RIL_UNSOL_UICC_SUBSCRIPTION_STATUS_CHANGED, response); + } mRil.mSubscriptionStatusRegistrants.notifyRegistrants( new AsyncResult(null, response, null)); @@ -215,9 +224,9 @@ * @param enabled Whether uiccApplications are enabled or disabled */ public void uiccApplicationsEnablementChanged(int indicationType, boolean enabled) { - mRil.processIndication(RIL.SIM_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_SIM, indicationType); - if (RIL.RILJ_LOGD) { + if (mRil.isLogOrTrace()) { mRil.unsljLogRet(RIL_UNSOL_UICC_APPLICATIONS_ENABLEMENT_CHANGED, enabled); }
diff --git a/src/java/com/android/internal/telephony/SimResponse.java b/src/java/com/android/internal/telephony/SimResponse.java index b0099fb..1e1dbe5 100644 --- a/src/java/com/android/internal/telephony/SimResponse.java +++ b/src/java/com/android/internal/telephony/SimResponse.java
@@ -16,8 +16,11 @@ package com.android.internal.telephony; +import static android.telephony.TelephonyManager.HAL_SERVICE_SIM; + import android.hardware.radio.RadioError; import android.hardware.radio.RadioResponseInfo; +import android.hardware.radio.sim.CarrierRestrictions; import android.hardware.radio.sim.IRadioSimResponse; import android.telephony.CarrierRestrictionRules; import android.telephony.TelephonyManager; @@ -41,7 +44,7 @@ private void responseIccIo(RadioResponseInfo responseInfo, android.hardware.radio.sim.IccIoResult result) { - RILRequest rr = mRil.processResponse(RIL.SIM_SERVICE, responseInfo); + RILRequest rr = mRil.processResponse(HAL_SERVICE_SIM, responseInfo); if (rr != null) { IccIoResult ret = new IccIoResult(result.sw1, result.sw2, result.simResponse); @@ -68,7 +71,7 @@ */ public void areUiccApplicationsEnabledResponse(RadioResponseInfo responseInfo, boolean enabled) { - RILRequest rr = mRil.processResponse(RIL.SIM_SERVICE, responseInfo); + RILRequest rr = mRil.processResponse(HAL_SERVICE_SIM, responseInfo); if (rr != null) { if (responseInfo.error == RadioError.NONE) { @@ -83,7 +86,7 @@ * @param remainingAttempts Number of retries remaining, must be equal to -1 if unknown. */ public void changeIccPin2ForAppResponse(RadioResponseInfo responseInfo, int remainingAttempts) { - RadioResponse.responseInts(RIL.SIM_SERVICE, mRil, responseInfo, remainingAttempts); + RadioResponse.responseInts(HAL_SERVICE_SIM, mRil, responseInfo, remainingAttempts); } /** @@ -91,14 +94,14 @@ * @param remainingAttempts Number of retries remaining, must be equal to -1 if unknown. */ public void changeIccPinForAppResponse(RadioResponseInfo responseInfo, int remainingAttempts) { - RadioResponse.responseInts(RIL.SIM_SERVICE, mRil, responseInfo, remainingAttempts); + RadioResponse.responseInts(HAL_SERVICE_SIM, mRil, responseInfo, remainingAttempts); } /** * @param responseInfo Response info struct containing response type, serial no. and error. */ public void enableUiccApplicationsResponse(RadioResponseInfo responseInfo) { - RadioResponse.responseVoid(RIL.SIM_SERVICE, mRil, responseInfo); + RadioResponse.responseVoid(HAL_SERVICE_SIM, mRil, responseInfo); } /** @@ -109,7 +112,7 @@ public void getAllowedCarriersResponse(RadioResponseInfo responseInfo, android.hardware.radio.sim.CarrierRestrictions carrierRestrictions, int multiSimPolicy) { - RILRequest rr = mRil.processResponse(RIL.SIM_SERVICE, responseInfo); + RILRequest rr = mRil.processResponse(HAL_SERVICE_SIM, responseInfo); if (rr == null) { return; } @@ -125,7 +128,6 @@ if (!carrierRestrictions.allowedCarriersPrioritized) { carrierRestrictionDefault = CarrierRestrictionRules.CARRIER_RESTRICTION_DEFAULT_ALLOWED; } - ret = CarrierRestrictionRules.newBuilder() .setAllowedCarriers(RILUtils.convertHalCarrierList( carrierRestrictions.allowedCarriers)) @@ -133,6 +135,7 @@ carrierRestrictions.excludedCarriers)) .setDefaultCarrierRestriction(carrierRestrictionDefault) .setMultiSimPolicy(policy) + .setCarrierRestrictionStatus(carrierRestrictions.status) .build(); if (responseInfo.error == RadioError.NONE) { @@ -154,7 +157,7 @@ public void getCdmaSubscriptionResponse(RadioResponseInfo responseInfo, String mdn, String hSid, String hNid, String min, String prl) { RadioResponse.responseStrings( - RIL.SIM_SERVICE, mRil, responseInfo, mdn, hSid, hNid, min, prl); + HAL_SERVICE_SIM, mRil, responseInfo, mdn, hSid, hNid, min, prl); } /** @@ -162,7 +165,7 @@ * @param source CDMA subscription source */ public void getCdmaSubscriptionSourceResponse(RadioResponseInfo responseInfo, int source) { - RadioResponse.responseInts(RIL.SIM_SERVICE, mRil, responseInfo, source); + RadioResponse.responseInts(HAL_SERVICE_SIM, mRil, responseInfo, source); } /** @@ -171,7 +174,7 @@ * specified barring facility is active. "0" means "disabled for all" */ public void getFacilityLockForAppResponse(RadioResponseInfo responseInfo, int response) { - RadioResponse.responseInts(RIL.SIM_SERVICE, mRil, responseInfo, response); + RadioResponse.responseInts(HAL_SERVICE_SIM, mRil, responseInfo, response); } /** @@ -180,7 +183,7 @@ */ public void getIccCardStatusResponse(RadioResponseInfo responseInfo, android.hardware.radio.sim.CardStatus cardStatus) { - RILRequest rr = mRil.processResponse(RIL.SIM_SERVICE, responseInfo); + RILRequest rr = mRil.processResponse(HAL_SERVICE_SIM, responseInfo); if (rr != null) { IccCardStatus iccCardStatus = RILUtils.convertHalCardStatus(cardStatus); @@ -197,7 +200,7 @@ * @param imsi String containing the IMSI */ public void getImsiForAppResponse(RadioResponseInfo responseInfo, String imsi) { - RadioResponse.responseString(RIL.SIM_SERVICE, mRil, responseInfo, imsi); + RadioResponse.responseString(HAL_SERVICE_SIM, mRil, responseInfo, imsi); } /** @@ -207,7 +210,7 @@ public void getSimPhonebookCapacityResponse(RadioResponseInfo responseInfo, android.hardware.radio.sim.PhonebookCapacity pbCapacity) { AdnCapacity capacity = RILUtils.convertHalPhonebookCapacity(pbCapacity); - RILRequest rr = mRil.processResponse(RIL.SIM_SERVICE, responseInfo); + RILRequest rr = mRil.processResponse(HAL_SERVICE_SIM, responseInfo); if (rr != null) { if (responseInfo.error == RadioError.NONE) { RadioResponse.sendMessageResponse(rr.mResult, capacity); @@ -220,14 +223,21 @@ * @param responseInfo Response info struct containing response type, serial no. and error. */ public void getSimPhonebookRecordsResponse(RadioResponseInfo responseInfo) { - RadioResponse.responseVoid(RIL.SIM_SERVICE, mRil, responseInfo); + RadioResponse.responseVoid(HAL_SERVICE_SIM, mRil, responseInfo); } /** * @param responseInfo Response info struct containing response type, serial no. and error */ public void iccCloseLogicalChannelResponse(RadioResponseInfo responseInfo) { - RadioResponse.responseVoid(RIL.SIM_SERVICE, mRil, responseInfo); + RadioResponse.responseVoid(HAL_SERVICE_SIM, mRil, responseInfo); + } + + /** + * @param responseInfo Response info struct containing response type, serial no. and error + */ + public void iccCloseLogicalChannelWithSessionInfoResponse(RadioResponseInfo responseInfo) { + RadioResponse.responseVoid(HAL_SERVICE_SIM, mRil, responseInfo); } /** @@ -252,7 +262,7 @@ for (int i = 0; i < selectResponse.length; i++) { arr.add((int) selectResponse[i]); } - RadioResponse.responseIntArrayList(RIL.SIM_SERVICE, mRil, responseInfo, arr); + RadioResponse.responseIntArrayList(HAL_SERVICE_SIM, mRil, responseInfo, arr); } /** @@ -277,7 +287,7 @@ * @param responseInfo Response info struct containing response type, serial no. and error */ public void reportStkServiceIsRunningResponse(RadioResponseInfo responseInfo) { - RadioResponse.responseVoid(RIL.SIM_SERVICE, mRil, responseInfo); + RadioResponse.responseVoid(HAL_SERVICE_SIM, mRil, responseInfo); } /** @@ -286,7 +296,7 @@ */ public void requestIccSimAuthenticationResponse(RadioResponseInfo responseInfo, android.hardware.radio.sim.IccIoResult iccIo) { - RILRequest rr = mRil.processResponse(RIL.SIM_SERVICE, responseInfo); + RILRequest rr = mRil.processResponse(HAL_SERVICE_SIM, responseInfo); if (rr != null) { IccIoResult ret = new IccIoResult(iccIo.sw1, iccIo.sw2, @@ -315,7 +325,7 @@ * string starting with first byte of response */ public void sendEnvelopeResponse(RadioResponseInfo responseInfo, String commandResponse) { - RadioResponse.responseString(RIL.SIM_SERVICE, mRil, responseInfo, commandResponse); + RadioResponse.responseString(HAL_SERVICE_SIM, mRil, responseInfo, commandResponse); } /** @@ -331,7 +341,7 @@ * @param responseInfo Response info struct containing response type, serial no. and error */ public void sendTerminalResponseToSimResponse(RadioResponseInfo responseInfo) { - RadioResponse.responseVoid(RIL.SIM_SERVICE, mRil, responseInfo); + RadioResponse.responseVoid(HAL_SERVICE_SIM, mRil, responseInfo); } /** @@ -339,7 +349,7 @@ */ public void setAllowedCarriersResponse(RadioResponseInfo responseInfo) { int ret = TelephonyManager.SET_CARRIER_RESTRICTION_ERROR; - RILRequest rr = mRil.processResponse(RIL.SIM_SERVICE, responseInfo); + RILRequest rr = mRil.processResponse(HAL_SERVICE_SIM, responseInfo); if (rr != null) { mRil.riljLog("setAllowedCarriersResponse - error = " + responseInfo.error); @@ -355,14 +365,14 @@ * @param responseInfo Response info struct containing response type, serial no. and error */ public void setCarrierInfoForImsiEncryptionResponse(RadioResponseInfo responseInfo) { - RadioResponse.responseVoid(RIL.SIM_SERVICE, mRil, responseInfo); + RadioResponse.responseVoid(HAL_SERVICE_SIM, mRil, responseInfo); } /** * @param responseInfo Response info struct containing response type, serial no. and error */ public void setCdmaSubscriptionSourceResponse(RadioResponseInfo responseInfo) { - RadioResponse.responseVoid(RIL.SIM_SERVICE, mRil, responseInfo); + RadioResponse.responseVoid(HAL_SERVICE_SIM, mRil, responseInfo); } /** @@ -370,21 +380,21 @@ * @param retry 0 is the number of retries remaining, or -1 if unknown */ public void setFacilityLockForAppResponse(RadioResponseInfo responseInfo, int retry) { - RadioResponse.responseInts(RIL.SIM_SERVICE, mRil, responseInfo, retry); + RadioResponse.responseInts(HAL_SERVICE_SIM, mRil, responseInfo, retry); } /** * @param responseInfo Response info struct containing response type, serial no. and error */ public void setSimCardPowerResponse(RadioResponseInfo responseInfo) { - RadioResponse.responseVoid(RIL.SIM_SERVICE, mRil, responseInfo); + RadioResponse.responseVoid(HAL_SERVICE_SIM, mRil, responseInfo); } /** * @param responseInfo Response info struct containing response type, serial no. and error */ public void setUiccSubscriptionResponse(RadioResponseInfo responseInfo) { - RadioResponse.responseVoid(RIL.SIM_SERVICE, mRil, responseInfo); + RadioResponse.responseVoid(HAL_SERVICE_SIM, mRil, responseInfo); } /** @@ -392,7 +402,7 @@ * @param remainingAttempts Number of retries remaining, must be equal to -1 if unknown. */ public void supplyIccPin2ForAppResponse(RadioResponseInfo responseInfo, int remainingAttempts) { - RadioResponse.responseInts(RIL.SIM_SERVICE, mRil, responseInfo, remainingAttempts); + RadioResponse.responseInts(HAL_SERVICE_SIM, mRil, responseInfo, remainingAttempts); } /** @@ -400,7 +410,7 @@ * @param remainingAttempts Number of retries remaining, must be equal to -1 if unknown. */ public void supplyIccPinForAppResponse(RadioResponseInfo responseInfo, int remainingAttempts) { - RadioResponse.responseInts(RIL.SIM_SERVICE, mRil, responseInfo, remainingAttempts); + RadioResponse.responseInts(HAL_SERVICE_SIM, mRil, responseInfo, remainingAttempts); } /** @@ -408,7 +418,7 @@ * @param remainingAttempts Number of retries remaining, must be equal to -1 if unknown. */ public void supplyIccPuk2ForAppResponse(RadioResponseInfo responseInfo, int remainingAttempts) { - RadioResponse.responseInts(RIL.SIM_SERVICE, mRil, responseInfo, remainingAttempts); + RadioResponse.responseInts(HAL_SERVICE_SIM, mRil, responseInfo, remainingAttempts); } /** @@ -416,7 +426,7 @@ * @param remainingAttempts Number of retries remaining, must be equal to -1 if unknown. */ public void supplyIccPukForAppResponse(RadioResponseInfo responseInfo, int remainingAttempts) { - RadioResponse.responseInts(RIL.SIM_SERVICE, mRil, responseInfo, remainingAttempts); + RadioResponse.responseInts(HAL_SERVICE_SIM, mRil, responseInfo, remainingAttempts); } /** @@ -428,7 +438,7 @@ public void supplySimDepersonalizationResponse(RadioResponseInfo responseInfo, int persoType, int remainingRetries) { RadioResponse.responseInts( - RIL.SIM_SERVICE, mRil, responseInfo, persoType, remainingRetries); + HAL_SERVICE_SIM, mRil, responseInfo, persoType, remainingRetries); } /** @@ -437,7 +447,7 @@ */ public void updateSimPhonebookRecordsResponse(RadioResponseInfo responseInfo, int updatedRecordIndex) { - RadioResponse.responseInts(RIL.SIM_SERVICE, mRil, responseInfo, updatedRecordIndex); + RadioResponse.responseInts(HAL_SERVICE_SIM, mRil, responseInfo, updatedRecordIndex); } @Override
diff --git a/src/java/com/android/internal/telephony/SlidingWindowEventCounter.java b/src/java/com/android/internal/telephony/SlidingWindowEventCounter.java index 149e425..8fcc567 100644 --- a/src/java/com/android/internal/telephony/SlidingWindowEventCounter.java +++ b/src/java/com/android/internal/telephony/SlidingWindowEventCounter.java
@@ -106,7 +106,11 @@ * @return A string describing the anomaly event */ public @NonNull String getFrequencyString() { - return String.format("%d times within %d ms.", mNumOccurrences, mWindowSizeMillis); + if (mWindowSizeMillis >= 1000L) { + return mNumOccurrences + " times within " + mWindowSizeMillis / 1000L + " seconds"; + } else { + return mNumOccurrences + " times within " + mWindowSizeMillis + "ms"; + } } @Override
diff --git a/src/java/com/android/internal/telephony/SmsBroadcastUndelivered.java b/src/java/com/android/internal/telephony/SmsBroadcastUndelivered.java index 7c52a42..ecd6276 100644 --- a/src/java/com/android/internal/telephony/SmsBroadcastUndelivered.java +++ b/src/java/com/android/internal/telephony/SmsBroadcastUndelivered.java
@@ -16,6 +16,8 @@ package com.android.internal.telephony; +import static java.util.Map.entry; + import android.compat.annotation.UnsupportedAppUsage; import android.content.BroadcastReceiver; import android.content.ContentResolver; @@ -32,6 +34,7 @@ import com.android.internal.telephony.cdma.CdmaInboundSmsHandler; import com.android.internal.telephony.gsm.GsmInboundSmsHandler; import com.android.internal.telephony.metrics.TelephonyMetrics; +import com.android.internal.telephony.subscription.SubscriptionManagerService; import com.android.telephony.Rlog; import java.util.HashMap; @@ -72,19 +75,18 @@ /** Mapping from DB COLUMN to PDU_PENDING_MESSAGE_PROJECTION index */ static final Map<Integer, Integer> PDU_PENDING_MESSAGE_PROJECTION_INDEX_MAPPING = - new HashMap<Integer, Integer>() {{ - put(InboundSmsHandler.PDU_COLUMN, 0); - put(InboundSmsHandler.SEQUENCE_COLUMN, 1); - put(InboundSmsHandler.DESTINATION_PORT_COLUMN, 2); - put(InboundSmsHandler.DATE_COLUMN, 3); - put(InboundSmsHandler.REFERENCE_NUMBER_COLUMN, 4); - put(InboundSmsHandler.COUNT_COLUMN, 5); - put(InboundSmsHandler.ADDRESS_COLUMN, 6); - put(InboundSmsHandler.ID_COLUMN, 7); - put(InboundSmsHandler.MESSAGE_BODY_COLUMN, 8); - put(InboundSmsHandler.DISPLAY_ADDRESS_COLUMN, 9); - put(InboundSmsHandler.SUBID_COLUMN, 10); - }}; + Map.ofEntries( + entry(InboundSmsHandler.PDU_COLUMN, 0), + entry(InboundSmsHandler.SEQUENCE_COLUMN, 1), + entry(InboundSmsHandler.DESTINATION_PORT_COLUMN, 2), + entry(InboundSmsHandler.DATE_COLUMN, 3), + entry(InboundSmsHandler.REFERENCE_NUMBER_COLUMN, 4), + entry(InboundSmsHandler.COUNT_COLUMN, 5), + entry(InboundSmsHandler.ADDRESS_COLUMN, 6), + entry(InboundSmsHandler.ID_COLUMN, 7), + entry(InboundSmsHandler.MESSAGE_BODY_COLUMN, 8), + entry(InboundSmsHandler.DISPLAY_ADDRESS_COLUMN, 9), + entry(InboundSmsHandler.SUBID_COLUMN, 10)); private static SmsBroadcastUndelivered instance; @@ -258,10 +260,8 @@ * Send tracker to appropriate (3GPP or 3GPP2) inbound SMS handler for broadcast. */ private static void broadcastSms(InboundSmsTracker tracker) { - InboundSmsHandler handler; int subId = tracker.getSubId(); - // TODO consider other subs in this subId's group as well - int phoneId = SubscriptionController.getInstance().getPhoneId(subId); + int phoneId = SubscriptionManagerService.getInstance().getPhoneId(subId); if (!SubscriptionManager.isValidPhoneId(phoneId)) { Rlog.e(TAG, "broadcastSms: ignoring message; no phone found for subId " + subId); return; @@ -272,7 +272,7 @@ + " phoneId " + phoneId); return; } - handler = phone.getInboundSmsHandler(tracker.is3gpp2()); + InboundSmsHandler handler = phone.getInboundSmsHandler(tracker.is3gpp2()); if (handler != null) { handler.sendMessage(InboundSmsHandler.EVENT_BROADCAST_SMS, tracker); } else {
diff --git a/src/java/com/android/internal/telephony/SmsController.java b/src/java/com/android/internal/telephony/SmsController.java index 9f79161..97161f8 100644 --- a/src/java/com/android/internal/telephony/SmsController.java +++ b/src/java/com/android/internal/telephony/SmsController.java
@@ -18,6 +18,8 @@ package com.android.internal.telephony; +import static android.content.pm.PackageManager.PERMISSION_GRANTED; + import static com.android.internal.telephony.util.TelephonyUtils.checkDumpPermission; import android.annotation.Nullable; @@ -26,6 +28,7 @@ import android.app.PendingIntent; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; +import android.content.pm.PackageManager; import android.net.Uri; import android.os.BaseBundle; import android.os.Binder; @@ -40,6 +43,9 @@ import android.telephony.TelephonyFrameworkInitializer; import android.telephony.TelephonyManager; +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.telephony.subscription.SubscriptionManagerService; +import com.android.internal.telephony.util.TelephonyUtils; import com.android.internal.util.IndentingPrintWriter; import com.android.telephony.Rlog; @@ -47,6 +53,7 @@ import java.io.PrintWriter; import java.nio.charset.StandardCharsets; import java.util.List; +import java.util.Locale; /** * Implements the ISmsImplBase interface used in the SmsManager API. @@ -56,7 +63,8 @@ private final Context mContext; - protected SmsController(Context context) { + @VisibleForTesting + public SmsController(Context context) { mContext = context; ServiceRegisterer smsServiceRegisterer = TelephonyFrameworkInitializer .getTelephonyServiceManager() @@ -151,6 +159,23 @@ if (callingPackage == null) { callingPackage = getCallingPackage(); } + Rlog.d(LOG_TAG, "sendDataForSubscriber caller=" + callingPackage); + + // Check if user is associated with the subscription + if (!TelephonyPermissions.checkSubscriptionAssociatedWithUser(mContext, subId, + Binder.getCallingUserHandle(), destAddr)) { + TelephonyUtils.showSwitchToManagedProfileDialogIfAppropriate(mContext, subId, + Binder.getCallingUid(), callingPackage); + sendErrorInPendingIntent(sentIntent, SmsManager.RESULT_USER_NOT_ALLOWED); + return; + } + + // Perform FDN check + if (isNumberBlockedByFDN(subId, destAddr, callingPackage)) { + sendErrorInPendingIntent(sentIntent, SmsManager.RESULT_ERROR_FDN_CHECK_FAILURE); + return; + } + IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId); if (iccSmsIntMgr != null) { iccSmsIntMgr.sendData(callingPackage, callingAttributionTag, destAddr, scAddr, destPort, @@ -186,14 +211,74 @@ String callingAttributionTag, String destAddr, String scAddr, String text, PendingIntent sentIntent, PendingIntent deliveryIntent, boolean persistMessageForNonDefaultSmsApp, long messageId) { + sendTextForSubscriber(subId, callingPackage, callingAttributionTag, destAddr, scAddr, + text, sentIntent, deliveryIntent, persistMessageForNonDefaultSmsApp, messageId, + false, false); + + } + + /** + * @param subId Subscription Id + * @param callingAttributionTag the attribution tag of the caller + * @param destAddr the address to send the message to + * @param scAddr is the service center address or null to use + * the current default SMSC + * @param text the body of the message to send + * @param sentIntent if not NULL this <code>PendingIntent</code> is + * broadcast when the message is successfully sent, or failed. + * The result code will be <code>Activity.RESULT_OK</code> for success, or relevant errors + * the sentIntent may include the extra "errorCode" containing a radio technology specific + * value, generally only useful for troubleshooting. + * @param deliveryIntent if not NULL this <code>PendingIntent</code> is + * broadcast when the message is delivered to the recipient. The + * raw pdu of the status report is in the extended data ("pdu"). + * @param skipFdnCheck if set to true, FDN check must be skipped .This is set in case of STK sms + * + * @hide + */ + public void sendTextForSubscriber(int subId, String callingPackage, + String callingAttributionTag, String destAddr, String scAddr, String text, + PendingIntent sentIntent, PendingIntent deliveryIntent, + boolean persistMessageForNonDefaultSmsApp, long messageId, boolean skipFdnCheck, + boolean skipShortCodeCheck) { if (callingPackage == null) { callingPackage = getCallingPackage(); } + Rlog.d(LOG_TAG, "sendTextForSubscriber caller=" + callingPackage); + + if (skipFdnCheck || skipShortCodeCheck) { + if (mContext.checkCallingOrSelfPermission( + android.Manifest.permission.MODIFY_PHONE_STATE) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Requires MODIFY_PHONE_STATE permission."); + } + } if (!getSmsPermissions(subId).checkCallingCanSendText(persistMessageForNonDefaultSmsApp, callingPackage, callingAttributionTag, "Sending SMS message")) { sendErrorInPendingIntent(sentIntent, SmsManager.RESULT_ERROR_GENERIC_FAILURE); return; } + + // Check if user is associated with the subscription + boolean crossUserFullGranted = mContext.checkCallingOrSelfPermission( + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) == PERMISSION_GRANTED; + Rlog.d(LOG_TAG, "sendTextForSubscriber: caller has INTERACT_ACROSS_USERS_FULL? " + + crossUserFullGranted); + if (!crossUserFullGranted + && !TelephonyPermissions.checkSubscriptionAssociatedWithUser(mContext, subId, + Binder.getCallingUserHandle(), destAddr)) { + TelephonyUtils.showSwitchToManagedProfileDialogIfAppropriate(mContext, subId, + Binder.getCallingUid(), callingPackage); + sendErrorInPendingIntent(sentIntent, SmsManager.RESULT_USER_NOT_ALLOWED); + return; + } + + // Perform FDN check + if (!skipFdnCheck && isNumberBlockedByFDN(subId, destAddr, callingPackage)) { + sendErrorInPendingIntent(sentIntent, SmsManager.RESULT_ERROR_FDN_CHECK_FAILURE); + return; + } + long token = Binder.clearCallingIdentity(); SubscriptionInfo info; try { @@ -201,11 +286,12 @@ } finally { Binder.restoreCallingIdentity(token); } + if (isBluetoothSubscription(info)) { sendBluetoothText(info, destAddr, text, sentIntent, deliveryIntent); } else { sendIccText(subId, callingPackage, destAddr, scAddr, text, sentIntent, deliveryIntent, - persistMessageForNonDefaultSmsApp, messageId); + persistMessageForNonDefaultSmsApp, messageId, skipShortCodeCheck); } } @@ -222,16 +308,17 @@ private void sendIccText(int subId, String callingPackage, String destAddr, String scAddr, String text, PendingIntent sentIntent, PendingIntent deliveryIntent, - boolean persistMessageForNonDefaultSmsApp, long messageId) { + boolean persistMessageForNonDefaultSmsApp, long messageId, boolean skipShortCodeCheck) { Rlog.d(LOG_TAG, "sendTextForSubscriber iccSmsIntMgr" - + " Subscription: " + subId + " id: " + messageId); + + " Subscription: " + subId + " " + formatCrossStackMessageId(messageId)); IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId); if (iccSmsIntMgr != null) { iccSmsIntMgr.sendText(callingPackage, destAddr, scAddr, text, sentIntent, - deliveryIntent, persistMessageForNonDefaultSmsApp, messageId); + deliveryIntent, persistMessageForNonDefaultSmsApp, messageId, + skipShortCodeCheck); } else { Rlog.e(LOG_TAG, "sendTextForSubscriber iccSmsIntMgr is null for" - + " Subscription: " + subId + " id: " + messageId); + + " Subscription: " + subId + " " + formatCrossStackMessageId(messageId)); sendErrorInPendingIntent(sentIntent, SmsManager.RESULT_ERROR_GENERIC_FAILURE); } } @@ -259,6 +346,23 @@ if (callingPackage == null) { callingPackage = getCallingPackage(); } + Rlog.d(LOG_TAG, "sendTextForSubscriberWithOptions caller=" + callingPackage); + + // Check if user is associated with the subscription + if (!TelephonyPermissions.checkSubscriptionAssociatedWithUser(mContext, subId, + Binder.getCallingUserHandle(), destAddr)) { + TelephonyUtils.showSwitchToManagedProfileDialogIfAppropriate(mContext, subId, + Binder.getCallingUid(), callingPackage); + sendErrorInPendingIntent(sentIntent, SmsManager.RESULT_USER_NOT_ALLOWED); + return; + } + + // Perform FDN check + if (isNumberBlockedByFDN(subId, destAddr, callingPackage)) { + sendErrorInPendingIntent(sentIntent, SmsManager.RESULT_ERROR_FDN_CHECK_FAILURE); + return; + } + IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId); if (iccSmsIntMgr != null) { iccSmsIntMgr.sendTextWithOptions(callingPackage, callingAttributionTag, destAddr, @@ -281,6 +385,23 @@ if (getCallingPackage() != null) { callingPackage = getCallingPackage(); } + Rlog.d(LOG_TAG, "sendMultipartTextForSubscriber caller=" + callingPackage); + + // Check if user is associated with the subscription + if (!TelephonyPermissions.checkSubscriptionAssociatedWithUser(mContext, subId, + Binder.getCallingUserHandle(), destAddr)) { + TelephonyUtils.showSwitchToManagedProfileDialogIfAppropriate(mContext, subId, + Binder.getCallingUid(), callingPackage); + sendErrorInPendingIntents(sentIntents, SmsManager.RESULT_USER_NOT_ALLOWED); + return; + } + + // Perform FDN check + if (isNumberBlockedByFDN(subId, destAddr, callingPackage)) { + sendErrorInPendingIntents(sentIntents, SmsManager.RESULT_ERROR_FDN_CHECK_FAILURE); + return; + } + IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId); if (iccSmsIntMgr != null) { iccSmsIntMgr.sendMultipartText(callingPackage, callingAttributionTag, destAddr, scAddr, @@ -288,7 +409,7 @@ messageId); } else { Rlog.e(LOG_TAG, "sendMultipartTextForSubscriber iccSmsIntMgr is null for" - + " Subscription: " + subId + " id: " + messageId); + + " Subscription: " + subId + " " + formatCrossStackMessageId(messageId)); sendErrorInPendingIntents(sentIntents, SmsManager.RESULT_ERROR_GENERIC_FAILURE); } } @@ -301,6 +422,23 @@ if (callingPackage == null) { callingPackage = getCallingPackage(); } + Rlog.d(LOG_TAG, "sendMultipartTextForSubscriberWithOptions caller=" + callingPackage); + + // Check if user is associated with the subscription + if (!TelephonyPermissions.checkSubscriptionAssociatedWithUser(mContext, subId, + Binder.getCallingUserHandle(), destAddr)) { + TelephonyUtils.showSwitchToManagedProfileDialogIfAppropriate(mContext, subId, + Binder.getCallingUid(), callingPackage); + sendErrorInPendingIntents(sentIntents, SmsManager.RESULT_USER_NOT_ALLOWED); + return; + } + + // Perform FDN check + if (isNumberBlockedByFDN(subId, destAddr, callingPackage)) { + sendErrorInPendingIntents(sentIntents, SmsManager.RESULT_ERROR_FDN_CHECK_FAILURE); + return; + } + IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId); if (iccSmsIntMgr != null) { iccSmsIntMgr.sendMultipartTextWithOptions(callingPackage, callingAttributionTag, @@ -409,7 +547,7 @@ // Don't show the SMS SIM Pick activity if it is not foreground. boolean isCallingProcessForeground = am != null && am.getUidImportance(Binder.getCallingUid()) - == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND; + == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND; if (!isCallingProcessForeground) { Rlog.d(LOG_TAG, "isSmsSimPickActivityNeeded: calling process not foreground. " + "Suppressing activity."); @@ -481,14 +619,15 @@ @Override public int getPreferredSmsSubscription() { // If there is a default, choose that one. - int defaultSubId = SubscriptionController.getInstance().getDefaultSmsSubId(); + int defaultSubId = SubscriptionManagerService.getInstance().getDefaultSmsSubId(); + if (SubscriptionManager.isValidSubscriptionId(defaultSubId)) { return defaultSubId; } // No default, if there is only one sub active, choose that as the "preferred" sub id. long token = Binder.clearCallingIdentity(); try { - int[] activeSubs = SubscriptionController.getInstance() + int[] activeSubs = SubscriptionManagerService.getInstance() .getActiveSubIdList(true /*visibleOnly*/); if (activeSubs.length == 1) { return activeSubs[0]; @@ -519,6 +658,8 @@ throw new SecurityException("sendStoredText: Package " + callingPkg + "does not belong to " + Binder.getCallingUid()); } + Rlog.d(LOG_TAG, "sendStoredText caller=" + callingPkg); + if (iccSmsIntMgr != null) { iccSmsIntMgr.sendStoredText(callingPkg, callingAttributionTag, messageUri, scAddress, sentIntent, deliveryIntent); @@ -537,6 +678,8 @@ throw new SecurityException("sendStoredMultipartText: Package " + callingPkg + " does not belong to " + Binder.getCallingUid()); } + Rlog.d(LOG_TAG, "sendStoredMultipartText caller=" + callingPkg); + if (iccSmsIntMgr != null) { iccSmsIntMgr.sendStoredMultipartText(callingPkg, callingAttributionTag, messageUri, scAddress, sentIntents, deliveryIntents); @@ -690,6 +833,58 @@ } @Override + public void setStorageMonitorMemoryStatusOverride(int subId, boolean isStorageAvailable) { + Phone phone = getPhone(subId); + Context context; + if (phone != null) { + context = phone.getContext(); + } else { + Rlog.e(LOG_TAG, "Phone Object is Null"); + return; + } + // If it doesn't have modify phone state permission + // a SecurityException will be thrown. + if (context.checkPermission(android.Manifest + .permission.MODIFY_PHONE_STATE, Binder.getCallingPid(), + Binder.getCallingUid()) != PERMISSION_GRANTED) { + throw new SecurityException( + "setStorageMonitorMemoryStatusOverride needs MODIFY_PHONE_STATE"); + } + final long identity = Binder.clearCallingIdentity(); + try { + phone.mSmsStorageMonitor.sendMemoryStatusOverride(isStorageAvailable); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + @Override + public void clearStorageMonitorMemoryStatusOverride(int subId) { + Phone phone = getPhone(subId); + Context context; + if (phone != null) { + context = phone.getContext(); + } else { + Rlog.e(LOG_TAG, "Phone Object is Null"); + return; + } + // If it doesn't have modify phone state permission + // a SecurityException will be thrown. + if (context.checkPermission(android.Manifest + .permission.MODIFY_PHONE_STATE, Binder.getCallingPid(), + Binder.getCallingUid()) != PERMISSION_GRANTED) { + throw new SecurityException( + "clearStorageMonitorMemoryStatusOverride needs MODIFY_PHONE_STATE"); + } + final long identity = Binder.clearCallingIdentity(); + try { + phone.mSmsStorageMonitor.clearMemoryStatusOverride(); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + @Override public int checkSmsShortCodeDestination(int subId, String callingPackage, String callingFeatureId, String destAddress, String countryIso) { if (callingPackage == null) { @@ -714,6 +909,24 @@ public void sendVisualVoicemailSmsForSubscriber(String callingPackage, String callingAttributionTag, int subId, String number, int port, String text, PendingIntent sentIntent) { + Rlog.d(LOG_TAG, "sendVisualVoicemailSmsForSubscriber caller=" + callingPackage); + + // Do not send non-emergency SMS in ECBM as it forces device to exit ECBM. + if(getPhone(subId).isInEcm()) { + Rlog.d(LOG_TAG, "sendVisualVoicemailSmsForSubscriber: Do not send non-emergency " + + "SMS in ECBM as it forces device to exit ECBM."); + return; + } + + // Check if user is associated with the subscription + if (!TelephonyPermissions.checkSubscriptionAssociatedWithUser(mContext, subId, + Binder.getCallingUserHandle(), number)) { + TelephonyUtils.showSwitchToManagedProfileDialogIfAppropriate(mContext, subId, + Binder.getCallingUid(), callingPackage); + sendErrorInPendingIntent(sentIntent, SmsManager.RESULT_USER_NOT_ALLOWED); + return; + } + if (port == 0) { sendTextForSubscriberWithSelfPermissionsInternal(subId, callingPackage, callingAttributionTag, number, null, text, sentIntent, null, false, @@ -856,4 +1069,49 @@ public static String formatCrossStackMessageId(long id) { return "{x-message-id:" + id + "}"; } -} + + /** + * The following function checks if destination address or smsc is blocked due to FDN. + * @param subId subscription ID + * @param destAddr destination address of the message + * @return true if either destAddr or smscAddr is blocked due to FDN. + */ + @VisibleForTesting + public boolean isNumberBlockedByFDN(int subId, String destAddr, String callingPackage) { + int phoneId = SubscriptionManager.getPhoneId(subId); + if (!FdnUtils.isFdnEnabled(phoneId)) { + return false; + } + + // Skip FDN check for emergency numbers + TelephonyManager tm = mContext.getSystemService(TelephonyManager.class); + if (tm.isEmergencyNumber(destAddr)) { + return false; + } + + // Check if destAddr is present in FDN list + String defaultCountryIso = tm.getSimCountryIso().toUpperCase(Locale.ENGLISH); + if (FdnUtils.isNumberBlockedByFDN(phoneId, destAddr, defaultCountryIso)) { + return true; + } + + // Get SMSC address for this subscription + IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId); + String smscAddr; + if (iccSmsIntMgr != null) { + long identity = Binder.clearCallingIdentity(); + try { + smscAddr = iccSmsIntMgr.getSmscAddressFromIccEf(callingPackage); + } finally { + Binder.restoreCallingIdentity(identity); + } + } else { + Rlog.e(LOG_TAG, "getSmscAddressFromIccEfForSubscriber iccSmsIntMgr is null" + + " for Subscription: " + subId); + return true; + } + + // Check if smscAddr is present in FDN list + return FdnUtils.isNumberBlockedByFDN(phoneId, smscAddr, defaultCountryIso); + } +} \ No newline at end of file
diff --git a/src/java/com/android/internal/telephony/SmsDispatchersController.java b/src/java/com/android/internal/telephony/SmsDispatchersController.java index 53556ac..d2dfcac 100644 --- a/src/java/com/android/internal/telephony/SmsDispatchersController.java +++ b/src/java/com/android/internal/telephony/SmsDispatchersController.java
@@ -20,6 +20,8 @@ import static com.android.internal.telephony.cdma.sms.BearerData.ERROR_NONE; import static com.android.internal.telephony.cdma.sms.BearerData.ERROR_TEMPORARY; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.app.Activity; import android.app.PendingIntent; import android.app.PendingIntent.CanceledException; @@ -29,19 +31,30 @@ import android.content.IntentFilter; import android.net.Uri; import android.os.AsyncResult; +import android.os.Binder; import android.os.Handler; +import android.os.Looper; import android.os.Message; import android.os.UserManager; import android.provider.Telephony.Sms; import android.provider.Telephony.Sms.Intents; +import android.telephony.Annotation.DisconnectCauses; +import android.telephony.DomainSelectionService; +import android.telephony.NetworkRegistrationInfo; import android.telephony.ServiceState; import android.telephony.SmsManager; import android.telephony.SmsMessage; +import android.telephony.TelephonyManager; +import android.text.TextUtils; import com.android.ims.ImsManager; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.telephony.cdma.CdmaInboundSmsHandler; import com.android.internal.telephony.cdma.CdmaSMSDispatcher; +import com.android.internal.telephony.domainselection.DomainSelectionConnection; +import com.android.internal.telephony.domainselection.DomainSelectionResolver; +import com.android.internal.telephony.domainselection.EmergencySmsDomainSelectionConnection; +import com.android.internal.telephony.domainselection.SmsDomainSelectionConnection; import com.android.internal.telephony.gsm.GsmInboundSmsHandler; import com.android.internal.telephony.gsm.GsmSMSDispatcher; import com.android.telephony.Rlog; @@ -50,6 +63,8 @@ import java.io.PrintWriter; import java.util.ArrayList; import java.util.HashMap; +import java.util.List; +import java.util.concurrent.CompletableFuture; /** * @@ -115,6 +130,195 @@ new HashMap<>(); /** + * Testing interface for injecting mock DomainSelectionConnection and a flag to indicate + * whether the domain selection is supported. + */ + @VisibleForTesting + public interface DomainSelectionResolverProxy { + /** + * Returns a {@link DomainSelectionConnection} created using the specified + * context and callback. + * + * @param phone The {@link Phone} instance. + * @param selectorType The domain selector type to identify the domain selection connection. + * A {@link DomainSelectionService#SELECTOR_TYPE_SMS} is used for SMS. + * @param isEmergency A flag to indicate whether this connection is + * for an emergency SMS or not. + */ + @Nullable DomainSelectionConnection getDomainSelectionConnection(Phone phone, + @DomainSelectionService.SelectorType int selectorType, boolean isEmergency); + + /** + * Checks if the device supports the domain selection service to route the call / SMS / + * supplementary services to the appropriate domain. + * + * @return {@code true} if the domain selection is supported on the device, + * {@code false} otherwise. + */ + boolean isDomainSelectionSupported(); + } + + private DomainSelectionResolverProxy mDomainSelectionResolverProxy = + new DomainSelectionResolverProxy() { + @Override + @Nullable + public DomainSelectionConnection getDomainSelectionConnection(Phone phone, + @DomainSelectionService.SelectorType int selectorType, + boolean isEmergency) { + try { + return DomainSelectionResolver.getInstance().getDomainSelectionConnection( + phone, selectorType, isEmergency); + } catch (IllegalStateException e) { + // In general, DomainSelectionResolver is always initialized by TeleService, + // but if it's not initialized (like in unit tests), + // it returns null to perform the legacy behavior in this case. + return null; + } + } + + @Override + public boolean isDomainSelectionSupported() { + return DomainSelectionResolver.getInstance().isDomainSelectionSupported(); + } + }; + + /** Stores the sending SMS information for a pending request. */ + private class PendingRequest { + public static final int TYPE_DATA = 1; + public static final int TYPE_TEXT = 2; + public static final int TYPE_MULTIPART_TEXT = 3; + public static final int TYPE_RETRY_SMS = 4; + + public final int type; + public final SMSDispatcher.SmsTracker tracker; + public final String callingPackage; + public final String destAddr; + public final String scAddr; + public final ArrayList<PendingIntent> sentIntents; + public final ArrayList<PendingIntent> deliveryIntents; + public final boolean isForVvm; + // sendData specific + public final byte[] data; + public final int destPort; + // sendText / sendMultipartText specific + public final ArrayList<String> texts; + public final Uri messageUri; + public final boolean persistMessage; + public final int priority; + public final boolean expectMore; + public final int validityPeriod; + public final long messageId; + public final boolean skipShortCodeCheck; + + PendingRequest(int type, SMSDispatcher.SmsTracker tracker, String callingPackage, + String destAddr, String scAddr, ArrayList<PendingIntent> sentIntents, + ArrayList<PendingIntent> deliveryIntents, boolean isForVvm, byte[] data, + int destPort, ArrayList<String> texts, Uri messageUri, boolean persistMessage, + int priority, boolean expectMore, int validityPeriod, long messageId, + boolean skipShortCodeCheck) { + this.type = type; + this.tracker = tracker; + this.callingPackage = callingPackage; + this.destAddr = destAddr; + this.scAddr = scAddr; + this.sentIntents = sentIntents; + this.deliveryIntents = deliveryIntents; + this.isForVvm = isForVvm; + + this.data = data; + this.destPort = destPort; + + this.texts = texts; + this.messageUri = messageUri; + this.persistMessage = persistMessage; + this.priority = priority; + this.expectMore = expectMore; + this.validityPeriod = validityPeriod; + this.messageId = messageId; + this.skipShortCodeCheck = skipShortCodeCheck; + } + } + + /** + * Manages the {@link DomainSelectionConnection} instance and its related information. + */ + @VisibleForTesting + protected class DomainSelectionConnectionHolder + implements DomainSelectionConnection.DomainSelectionConnectionCallback { + private final boolean mEmergency; + // Manages the pending request while selecting a proper domain. + private final List<PendingRequest> mPendingRequests = new ArrayList<>(); + // Manages the domain selection connections: MO SMS or emergency SMS. + private DomainSelectionConnection mConnection; + + DomainSelectionConnectionHolder(boolean emergency) { + mEmergency = emergency; + } + + /** + * Returns a {@link DomainSelectionConnection} instance. + */ + public DomainSelectionConnection getConnection() { + return mConnection; + } + + /** + * Returns a list of {@link PendingRequest} that was added + * while the domain selection is performed. + */ + public List<PendingRequest> getPendingRequests() { + return mPendingRequests; + } + + /** + * Checks whether or not the domain selection is requested. + * If there is no pending request, the domain selection request is needed to + * select a proper domain for MO SMS. + */ + public boolean isDomainSelectionRequested() { + return !mPendingRequests.isEmpty(); + } + + /** + * Checks whether or not this holder is for an emergency SMS. + */ + public boolean isEmergency() { + return mEmergency; + } + + /** + * Clears all pending requests. + */ + public void clearAllRequests() { + mPendingRequests.clear(); + } + + /** + * Add a new pending request. + */ + public void addRequest(@NonNull PendingRequest request) { + mPendingRequests.add(request); + } + + /** + * Sets a {@link DomainSelectionConnection} instance. + */ + public void setConnection(DomainSelectionConnection connection) { + mConnection = connection; + } + + + @Override + public void onSelectionTerminated(@DisconnectCauses int cause) { + notifyDomainSelectionTerminated(this); + } + } + + /** Manages the domain selection connections: MO SMS or emergency SMS. */ + private DomainSelectionConnectionHolder mDscHolder; + private DomainSelectionConnectionHolder mEmergencyDscHolder; + + /** * Puts a delivery pending tracker to the map based on the format. * * @param tracker the tracker awaiting a delivery status report. @@ -129,6 +333,14 @@ public SmsDispatchersController(Phone phone, SmsStorageMonitor storageMonitor, SmsUsageMonitor usageMonitor) { + this(phone, storageMonitor, usageMonitor, phone.getLooper()); + } + + @VisibleForTesting + public SmsDispatchersController(Phone phone, SmsStorageMonitor storageMonitor, + SmsUsageMonitor usageMonitor, Looper looper) { + super(looper); + Rlog.d(TAG, "SmsDispatchersController created"); mContext = phone.getContext(); @@ -141,9 +353,9 @@ mImsSmsDispatcher = new ImsSmsDispatcher(phone, this, ImsManager::getConnector); mCdmaDispatcher = new CdmaSMSDispatcher(phone, this); mGsmInboundSmsHandler = GsmInboundSmsHandler.makeInboundSmsHandler(phone.getContext(), - storageMonitor, phone); + storageMonitor, phone, looper); mCdmaInboundSmsHandler = CdmaInboundSmsHandler.makeInboundSmsHandler(phone.getContext(), - storageMonitor, phone, (CdmaSMSDispatcher) mCdmaDispatcher); + storageMonitor, phone, (CdmaSMSDispatcher) mCdmaDispatcher, looper); mGsmDispatcher = new GsmSMSDispatcher(phone, this, mGsmInboundSmsHandler); SmsBroadcastUndelivered.initialize(phone.getContext(), mGsmInboundSmsHandler, mCdmaInboundSmsHandler); @@ -188,6 +400,9 @@ mCdmaDispatcher.dispose(); mGsmInboundSmsHandler.dispose(); mCdmaInboundSmsHandler.dispose(); + // Cancels the domain selection request if it's still in progress. + finishDomainSelection(mDscHolder); + finishDomainSelection(mEmergencyDscHolder); } /** @@ -242,6 +457,21 @@ } } + private String getSmscAddressFromUSIMWithPhoneIdentity(String callingPkg) { + final long identity = Binder.clearCallingIdentity(); + try { + IccSmsInterfaceManager iccSmsIntMgr = mPhone.getIccSmsInterfaceManager(); + if (iccSmsIntMgr != null) { + return iccSmsIntMgr.getSmscAddressFromIccEf(callingPkg); + } else { + Rlog.d(TAG, "getSmscAddressFromIccEf iccSmsIntMgr is null"); + } + } finally { + Binder.restoreCallingIdentity(identity); + } + return null; + } + private void reevaluateTimerStatus() { long currentTime = System.currentTimeMillis(); @@ -390,7 +620,12 @@ // SMS pdus when the phone is camping on CDMA(3gpp2) network and vice versa. android.telephony.SmsMessage msg = android.telephony.SmsMessage.createFromPdu(pdu, format); - injectSmsPdu(msg, format, callback, false /* ignoreClass */, isOverIms); + injectSmsPdu(msg, format, callback, false /* ignoreClass */, isOverIms, 0 /* unused */); + } + + @VisibleForTesting + public void setImsSmsDispatcher(ImsSmsDispatcher imsSmsDispatcher) { + mImsSmsDispatcher = imsSmsDispatcher; } /** @@ -405,7 +640,7 @@ */ @VisibleForTesting public void injectSmsPdu(SmsMessage msg, String format, SmsInjectionCallback callback, - boolean ignoreClass, boolean isOverIms) { + boolean ignoreClass, boolean isOverIms, int token) { Rlog.d(TAG, "SmsDispatchersController:injectSmsPdu"); try { if (msg == null) { @@ -427,7 +662,7 @@ Rlog.i(TAG, "SmsDispatchersController:injectSmsText Sending msg=" + msg + ", format=" + format + "to mGsmInboundSmsHandler"); mGsmInboundSmsHandler.sendMessage( - InboundSmsHandler.EVENT_INJECT_SMS, isOverIms ? 1 : 0, 0, ar); + InboundSmsHandler.EVENT_INJECT_SMS, isOverIms ? 1 : 0, token, ar); } else if (format.equals(SmsConstants.FORMAT_3GPP2)) { Rlog.i(TAG, "SmsDispatchersController:injectSmsText Sending msg=" + msg + ", format=" + format + "to mCdmaInboundSmsHandler"); @@ -445,20 +680,60 @@ } /** + * sets ImsManager object. + * + * @param imsManager holds a valid object or a null for setting + */ + public boolean setImsManager(ImsManager imsManager) { + if (mGsmInboundSmsHandler != null) { + mGsmInboundSmsHandler.setImsManager(imsManager); + return true; + } + return false; + } + + /** * Retry the message along to the radio. * * @param tracker holds the SMS message to send */ public void sendRetrySms(SMSDispatcher.SmsTracker tracker) { - String oldFormat = tracker.mFormat; boolean retryUsingImsService = false; - if (!tracker.mUsesImsServiceForIms && mImsSmsDispatcher.isAvailable()) { - // If this tracker has not been handled by ImsSmsDispatcher yet and IMS Service is - // available now, retry this failed tracker using IMS Service. - retryUsingImsService = true; + if (!tracker.mUsesImsServiceForIms) { + if (mDomainSelectionResolverProxy.isDomainSelectionSupported()) { + DomainSelectionConnectionHolder holder = getDomainSelectionConnection(false); + + // If the DomainSelectionConnection is not available, + // fallback to the legacy implementation. + if (holder != null && holder.getConnection() != null) { + sendSmsUsingDomainSelection(holder, + new PendingRequest(PendingRequest.TYPE_RETRY_SMS, tracker, + null, null, null, null, null, false, null, 0, null, null, false, + 0, false, 0, 0L, false), + "sendRetrySms"); + return; + } + } + + if (mImsSmsDispatcher.isAvailable()) { + // If this tracker has not been handled by ImsSmsDispatcher yet and IMS Service is + // available now, retry this failed tracker using IMS Service. + retryUsingImsService = true; + } } + sendRetrySms(tracker, retryUsingImsService); + } + + /** + * Retry the message along to the radio. + * + * @param tracker holds the SMS message to send + * @param retryUsingImsService a flag to indicate whether the retry SMS can use the ImsService + */ + public void sendRetrySms(SMSDispatcher.SmsTracker tracker, boolean retryUsingImsService) { + String oldFormat = tracker.mFormat; // If retryUsingImsService is true, newFormat will be IMS SMS format. Otherwise, newFormat // will be based on voice technology. String newFormat = @@ -503,7 +778,8 @@ scAddr, destAddr, text, (tracker.mDeliveryIntent != null), null); } else { pdu = com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu( - scAddr, destAddr, text, (tracker.mDeliveryIntent != null), null); + scAddr, destAddr, text, (tracker.mDeliveryIntent != null), null, + 0, 0, 0, -1, tracker.mMessageRef); } } else if (map.containsKey("data")) { byte[] data = (byte[]) map.get("data"); @@ -518,7 +794,7 @@ } else { pdu = com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu( scAddr, destAddr, destPort.intValue(), data, - (tracker.mDeliveryIntent != null)); + (tracker.mDeliveryIntent != null), tracker.mMessageRef); } } @@ -544,6 +820,23 @@ } /** + * Memory Available Event + * @param result callback message + */ + public void reportSmsMemoryStatus(Message result) { + Rlog.d(TAG, "reportSmsMemoryStatus: "); + try { + mImsSmsDispatcher.onMemoryAvailable(); + AsyncResult.forMessage(result, null, null); + result.sendToTarget(); + } catch (Exception e) { + Rlog.e(TAG, "reportSmsMemoryStatus Failed ", e); + AsyncResult.forMessage(result, null, e); + result.sendToTarget(); + } + } + + /** * SMS over IMS is supported if IMS is registered and SMS is supported on IMS. * * @return true if SMS over IMS is supported via an IMS Service or mIms is true for the older @@ -589,6 +882,347 @@ return (mCdmaDispatcher.getFormat().equals(format)); } + /** Sets a proxy interface for accessing the methods of {@link DomainSelectionResolver}. */ + @VisibleForTesting + public void setDomainSelectionResolverProxy(@NonNull DomainSelectionResolverProxy proxy) { + mDomainSelectionResolverProxy = proxy; + } + + /** + * Determines whether or not to use CDMA format for MO SMS when the domain selection uses. + * If the domain is {@link NetworkRegistrationInfo#DOMAIN_PS}, then format is based on + * IMS SMS format, otherwise format is based on current phone type. + * + * @return {@code true} if CDMA format should be used for MO SMS, {@code false} otherwise. + */ + private boolean isCdmaMo(@NetworkRegistrationInfo.Domain int domain) { + if (domain != NetworkRegistrationInfo.DOMAIN_PS) { + // IMS is not registered, use voice technology to determine SMS format. + return (PhoneConstants.PHONE_TYPE_CDMA == mPhone.getPhoneType()); + } + // IMS is registered with SMS support + return isCdmaFormat(mImsSmsDispatcher.getFormat()); + } + + /** + * Returns a {@link DomainSelectionConnectionHolder} according to the flag specified. + * + * @param emergency The flag to indicate that the domain selection is for an emergency SMS. + * @return A {@link DomainSelectionConnectionHolder} instance or null. + */ + @VisibleForTesting + @Nullable + protected DomainSelectionConnectionHolder getDomainSelectionConnectionHolder( + boolean emergency) { + return emergency ? mEmergencyDscHolder : mDscHolder; + } + + /** + * Returns a {@link DomainSelectionConnectionHolder} if the domain selection supports, + * return null otherwise. + * + * @param emergency The flag to indicate that the domain selection is for an emergency SMS. + * @return A {@link DomainSelectionConnectionHolder} that grabs the + * {@link DomainSelectionConnection} and its related information to use the domain + * selection architecture. + */ + private DomainSelectionConnectionHolder getDomainSelectionConnection(boolean emergency) { + DomainSelectionConnectionHolder holder = getDomainSelectionConnectionHolder(emergency); + DomainSelectionConnection connection = (holder != null) ? holder.getConnection() : null; + boolean created = false; + + if (connection == null) { + connection = mDomainSelectionResolverProxy.getDomainSelectionConnection( + mPhone, DomainSelectionService.SELECTOR_TYPE_SMS, emergency); + + if (connection == null) { + // Domain selection architecture is not supported. + // Use the legacy architecture. + return null; + } + + created = true; + } + + if (holder == null) { + holder = new DomainSelectionConnectionHolder(emergency); + + if (emergency) { + mEmergencyDscHolder = holder; + } else { + mDscHolder = holder; + } + } + + holder.setConnection(connection); + + return holder; + } + + /** + * Requests the domain selection for MO SMS. + * + * @param holder The {@link DomainSelectionConnectionHolder} that contains the + * {@link DomainSelectionConnection} and its related information. + */ + private void requestDomainSelection(@NonNull DomainSelectionConnectionHolder holder) { + DomainSelectionService.SelectionAttributes attr = + new DomainSelectionService.SelectionAttributes.Builder(mPhone.getPhoneId(), + mPhone.getSubId(), DomainSelectionService.SELECTOR_TYPE_SMS) + .setEmergency(holder.isEmergency()) + .build(); + + if (holder.isEmergency()) { + EmergencySmsDomainSelectionConnection emergencyConnection = + (EmergencySmsDomainSelectionConnection) holder.getConnection(); + CompletableFuture<Integer> future = + emergencyConnection.requestDomainSelection(attr, holder); + future.thenAcceptAsync((domain) -> { + if (VDBG) { + logd("requestDomainSelection(emergency): domain=" + + DomainSelectionService.getDomainName(domain)); + } + sendAllPendingRequests(holder, domain); + finishDomainSelection(holder); + }, this::post); + } else { + SmsDomainSelectionConnection connection = + (SmsDomainSelectionConnection) holder.getConnection(); + CompletableFuture<Integer> future = connection.requestDomainSelection(attr, holder); + future.thenAcceptAsync((domain) -> { + if (VDBG) { + logd("requestDomainSelection: domain=" + + DomainSelectionService.getDomainName(domain)); + } + sendAllPendingRequests(holder, domain); + finishDomainSelection(holder); + }, this::post); + } + } + + /** + * Sends a SMS after selecting the domain via the domain selection service. + * + * @param holder The {@link DomainSelectionConnectionHolder} that contains the + * {@link DomainSelectionConnection} and its related information. + * @param request The {@link PendingRequest} that stores the SMS request + * (data, text, multipart text) to be sent. + * @param logTag The log tag to display which method called this method. + */ + private void sendSmsUsingDomainSelection(@NonNull DomainSelectionConnectionHolder holder, + @NonNull PendingRequest request, @NonNull String logTag) { + boolean isDomainSelectionRequested = holder.isDomainSelectionRequested(); + // The domain selection is in progress so waits for the result of + // the domain selection by adding this request to the pending list. + holder.addRequest(request); + + if (!isDomainSelectionRequested) { + if (VDBG) { + logd("requestDomainSelection: " + logTag); + } + requestDomainSelection(holder); + } + } + + /** + * Finishes the domain selection for MO SMS. + * + * @param holder The {@link DomainSelectionConnectionHolder} object that is being finished. + */ + private void finishDomainSelection(DomainSelectionConnectionHolder holder) { + DomainSelectionConnection connection = (holder != null) ? holder.getConnection() : null; + + if (connection != null) { + // After this method is called, the domain selection service will clean up + // its resources and finish the procedure that are related to the current domain + // selection request. + connection.finishSelection(); + } + + if (holder != null) { + final List<PendingRequest> pendingRequests = holder.getPendingRequests(); + + logd("finishDomainSelection: pendingRequests=" + pendingRequests.size()); + + for (PendingRequest r : pendingRequests) { + triggerSentIntentForFailure(r.sentIntents); + } + + holder.clearAllRequests(); + holder.setConnection(null); + } + } + + /** + * Notifies the application that MO SMS is not sent by the error of domain selection. + * + * @param holder The {@link DomainSelectionConnectionHolder} object that is being terminated. + */ + private void notifyDomainSelectionTerminated(@NonNull DomainSelectionConnectionHolder holder) { + final List<PendingRequest> pendingRequests = holder.getPendingRequests(); + + logd("notifyDomainSelectionTerminated: pendingRequests=" + pendingRequests.size()); + + for (PendingRequest r : pendingRequests) { + triggerSentIntentForFailure(r.sentIntents); + } + + holder.setConnection(null); + holder.clearAllRequests(); + } + + /** + * Sends all pending requests for MO SMS. + * + * @param holder The {@link DomainSelectionConnectionHolder} object that all the pending + * requests are handled. + * @param domain The domain where the SMS is being sent, which can be one of the following: + * - {@link NetworkRegistrationInfo#DOMAIN_PS} + * - {@link NetworkRegistrationInfo#DOMAIN_CS} + */ + private void sendAllPendingRequests(@NonNull DomainSelectionConnectionHolder holder, + @NetworkRegistrationInfo.Domain int domain) { + final List<PendingRequest> pendingRequests = holder.getPendingRequests(); + + if (VDBG) { + logd("sendAllPendingRequests: domain=" + DomainSelectionService.getDomainName(domain) + + ", size=" + pendingRequests.size()); + } + + for (PendingRequest r : pendingRequests) { + switch (r.type) { + case PendingRequest.TYPE_DATA: + sendData(domain, r); + break; + case PendingRequest.TYPE_TEXT: + sendText(domain, r); + break; + case PendingRequest.TYPE_MULTIPART_TEXT: + sendMultipartText(domain, r); + break; + case PendingRequest.TYPE_RETRY_SMS: + sendRetrySms(r.tracker, (domain == NetworkRegistrationInfo.DOMAIN_PS)); + break; + default: + // Not reachable. + break; + } + } + + holder.clearAllRequests(); + } + + /** + * Sends a data based SMS to a specific application port. + * + * @param domain The domain where the SMS is being sent, which can be one of the following: + * - {@link NetworkRegistrationInfo#DOMAIN_PS} + * - {@link NetworkRegistrationInfo#DOMAIN_CS} + * @param request The pending request for MO SMS. + */ + private void sendData(@NetworkRegistrationInfo.Domain int domain, + @NonNull PendingRequest request) { + if (domain == NetworkRegistrationInfo.DOMAIN_PS) { + mImsSmsDispatcher.sendData(request.callingPackage, request.destAddr, request.scAddr, + request.destPort, request.data, request.sentIntents.get(0), + request.deliveryIntents.get(0), request.isForVvm); + } else if (isCdmaMo(domain)) { + mCdmaDispatcher.sendData(request.callingPackage, request.destAddr, request.scAddr, + request.destPort, request.data, request.sentIntents.get(0), + request.deliveryIntents.get(0), request.isForVvm); + } else { + mGsmDispatcher.sendData(request.callingPackage, request.destAddr, request.scAddr, + request.destPort, request.data, request.sentIntents.get(0), + request.deliveryIntents.get(0), request.isForVvm); + } + } + + /** + * Sends a text based SMS. + * + * @param domain The domain where the SMS is being sent, which can be one of the following: + * - {@link NetworkRegistrationInfo#DOMAIN_PS} + * - {@link NetworkRegistrationInfo#DOMAIN_CS} + * @param request The pending request for MO SMS. + */ + private void sendText(@NetworkRegistrationInfo.Domain int domain, + @NonNull PendingRequest request) { + if (domain == NetworkRegistrationInfo.DOMAIN_PS) { + mImsSmsDispatcher.sendText(request.destAddr, request.scAddr, request.texts.get(0), + request.sentIntents.get(0), request.deliveryIntents.get(0), + request.messageUri, request.callingPackage, request.persistMessage, + request.priority, false /*request.expectMore*/, request.validityPeriod, + request.isForVvm, request.messageId, request.skipShortCodeCheck); + } else { + if (isCdmaMo(domain)) { + mCdmaDispatcher.sendText(request.destAddr, request.scAddr, request.texts.get(0), + request.sentIntents.get(0), request.deliveryIntents.get(0), + request.messageUri, request.callingPackage, request.persistMessage, + request.priority, request.expectMore, request.validityPeriod, + request.isForVvm, request.messageId, request.skipShortCodeCheck); + } else { + mGsmDispatcher.sendText(request.destAddr, request.scAddr, request.texts.get(0), + request.sentIntents.get(0), request.deliveryIntents.get(0), + request.messageUri, request.callingPackage, request.persistMessage, + request.priority, request.expectMore, request.validityPeriod, + request.isForVvm, request.messageId, request.skipShortCodeCheck); + } + } + } + + /** + * Sends a multi-part text based SMS. + * + * @param domain The domain where the SMS is being sent, which can be one of the following: + * - {@link NetworkRegistrationInfo#DOMAIN_PS} + * - {@link NetworkRegistrationInfo#DOMAIN_CS} + * @param request The pending request for MO SMS. + */ + private void sendMultipartText(@NetworkRegistrationInfo.Domain int domain, + @NonNull PendingRequest request) { + if (domain == NetworkRegistrationInfo.DOMAIN_PS) { + mImsSmsDispatcher.sendMultipartText(request.destAddr, request.scAddr, request.texts, + request.sentIntents, request.deliveryIntents, request.messageUri, + request.callingPackage, request.persistMessage, request.priority, + false /*request.expectMore*/, request.validityPeriod, request.messageId); + } else { + if (isCdmaMo(domain)) { + mCdmaDispatcher.sendMultipartText(request.destAddr, request.scAddr, request.texts, + request.sentIntents, request.deliveryIntents, request.messageUri, + request.callingPackage, request.persistMessage, request.priority, + request.expectMore, request.validityPeriod, request.messageId); + } else { + mGsmDispatcher.sendMultipartText(request.destAddr, request.scAddr, request.texts, + request.sentIntents, request.deliveryIntents, request.messageUri, + request.callingPackage, request.persistMessage, request.priority, + request.expectMore, request.validityPeriod, request.messageId); + } + } + } + + private void triggerSentIntentForFailure(@NonNull PendingIntent sentIntent) { + try { + sentIntent.send(SmsManager.RESULT_ERROR_GENERIC_FAILURE); + } catch (CanceledException e) { + logd("Intent has been canceled!"); + } + } + + private void triggerSentIntentForFailure(@NonNull List<PendingIntent> sentIntents) { + for (PendingIntent sentIntent : sentIntents) { + triggerSentIntentForFailure(sentIntent); + } + } + + /** + * Creates an ArrayList object from any object. + */ + private static <T> ArrayList<T> asArrayList(T object) { + ArrayList<T> list = new ArrayList<>(); + list.add(object); + return list; + } + /** * Send a data based SMS to a specific application port. * @@ -670,6 +1304,26 @@ */ protected void sendData(String callingPackage, String destAddr, String scAddr, int destPort, byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent, boolean isForVvm) { + if (TextUtils.isEmpty(scAddr)) { + scAddr = getSmscAddressFromUSIMWithPhoneIdentity(callingPackage); + } + + if (mDomainSelectionResolverProxy.isDomainSelectionSupported()) { + DomainSelectionConnectionHolder holder = getDomainSelectionConnection(false); + + // If the DomainSelectionConnection is not available, + // fallback to the legacy implementation. + if (holder != null && holder.getConnection() != null) { + sendSmsUsingDomainSelection(holder, + new PendingRequest(PendingRequest.TYPE_DATA, null, callingPackage, + destAddr, scAddr, asArrayList(sentIntent), + asArrayList(deliveryIntent), isForVvm, data, destPort, null, null, + false, 0, false, 0, 0L, false), + "sendData"); + return; + } + } + if (mImsSmsDispatcher.isAvailable()) { mImsSmsDispatcher.sendData(callingPackage, destAddr, scAddr, destPort, data, sentIntent, deliveryIntent, isForVvm); @@ -783,19 +1437,148 @@ PendingIntent deliveryIntent, Uri messageUri, String callingPkg, boolean persistMessage, int priority, boolean expectMore, int validityPeriod, boolean isForVvm, long messageId) { + sendText(destAddr, scAddr, text, sentIntent, deliveryIntent, messageUri, callingPkg, + persistMessage, priority, expectMore, validityPeriod, isForVvm, messageId, false); + } + + /** + * Send a text based SMS. + * + * @param destAddr the address to send the message to + * @param scAddr is the service center address or null to use + * the current default SMSC + * @param text the body of the message to send + * @param sentIntent if not NULL this <code>PendingIntent</code> is + * broadcast when the message is successfully sent, or failed. + * The result code will be <code>Activity.RESULT_OK<code> for success, + * or one of these errors:<br> + * <code>SmsManager.RESULT_ERROR_GENERIC_FAILURE</code><br> + * <code>SmsManager.RESULT_ERROR_RADIO_OFF</code><br> + * <code>SmsManager.RESULT_ERROR_NULL_PDU</code><br> + * <code>SmsManager.RESULT_ERROR_NO_SERVICE</code><br> + * <code>SmsManager.RESULT_ERROR_LIMIT_EXCEEDED</code><br> + * <code>SmsManager.RESULT_ERROR_FDN_CHECK_FAILURE</code><br> + * <code>SmsManager.RESULT_ERROR_SHORT_CODE_NOT_ALLOWED</code><br> + * <code>SmsManager.RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED</code><br> + * <code>SmsManager.RESULT_RADIO_NOT_AVAILABLE</code><br> + * <code>SmsManager.RESULT_NETWORK_REJECT</code><br> + * <code>SmsManager.RESULT_INVALID_ARGUMENTS</code><br> + * <code>SmsManager.RESULT_INVALID_STATE</code><br> + * <code>SmsManager.RESULT_NO_MEMORY</code><br> + * <code>SmsManager.RESULT_INVALID_SMS_FORMAT</code><br> + * <code>SmsManager.RESULT_SYSTEM_ERROR</code><br> + * <code>SmsManager.RESULT_MODEM_ERROR</code><br> + * <code>SmsManager.RESULT_NETWORK_ERROR</code><br> + * <code>SmsManager.RESULT_ENCODING_ERROR</code><br> + * <code>SmsManager.RESULT_INVALID_SMSC_ADDRESS</code><br> + * <code>SmsManager.RESULT_OPERATION_NOT_ALLOWED</code><br> + * <code>SmsManager.RESULT_INTERNAL_ERROR</code><br> + * <code>SmsManager.RESULT_NO_RESOURCES</code><br> + * <code>SmsManager.RESULT_CANCELLED</code><br> + * <code>SmsManager.RESULT_REQUEST_NOT_SUPPORTED</code><br> + * <code>SmsManager.RESULT_NO_BLUETOOTH_SERVICE</code><br> + * <code>SmsManager.RESULT_INVALID_BLUETOOTH_ADDRESS</code><br> + * <code>SmsManager.RESULT_BLUETOOTH_DISCONNECTED</code><br> + * <code>SmsManager.RESULT_UNEXPECTED_EVENT_STOP_SENDING</code><br> + * <code>SmsManager.RESULT_SMS_BLOCKED_DURING_EMERGENCY</code><br> + * <code>SmsManager.RESULT_SMS_SEND_RETRY_FAILED</code><br> + * <code>SmsManager.RESULT_REMOTE_EXCEPTION</code><br> + * <code>SmsManager.RESULT_NO_DEFAULT_SMS_APP</code><br> + * <code>SmsManager.RESULT_RIL_RADIO_NOT_AVAILABLE</code><br> + * <code>SmsManager.RESULT_RIL_SMS_SEND_FAIL_RETRY</code><br> + * <code>SmsManager.RESULT_RIL_NETWORK_REJECT</code><br> + * <code>SmsManager.RESULT_RIL_INVALID_STATE</code><br> + * <code>SmsManager.RESULT_RIL_INVALID_ARGUMENTS</code><br> + * <code>SmsManager.RESULT_RIL_NO_MEMORY</code><br> + * <code>SmsManager.RESULT_RIL_REQUEST_RATE_LIMITED</code><br> + * <code>SmsManager.RESULT_RIL_INVALID_SMS_FORMAT</code><br> + * <code>SmsManager.RESULT_RIL_SYSTEM_ERR</code><br> + * <code>SmsManager.RESULT_RIL_ENCODING_ERR</code><br> + * <code>SmsManager.RESULT_RIL_INVALID_SMSC_ADDRESS</code><br> + * <code>SmsManager.RESULT_RIL_MODEM_ERR</code><br> + * <code>SmsManager.RESULT_RIL_NETWORK_ERR</code><br> + * <code>SmsManager.RESULT_RIL_INTERNAL_ERR</code><br> + * <code>SmsManager.RESULT_RIL_REQUEST_NOT_SUPPORTED</code><br> + * <code>SmsManager.RESULT_RIL_INVALID_MODEM_STATE</code><br> + * <code>SmsManager.RESULT_RIL_NETWORK_NOT_READY</code><br> + * <code>SmsManager.RESULT_RIL_OPERATION_NOT_ALLOWED</code><br> + * <code>SmsManager.RESULT_RIL_NO_RESOURCES</code><br> + * <code>SmsManager.RESULT_RIL_CANCELLED</code><br> + * <code>SmsManager.RESULT_RIL_SIM_ABSENT</code><br> + * <code>SmsManager.RESULT_RIL_SIMULTANEOUS_SMS_AND_CALL_NOT_ALLOWED</code><br> + * <code>SmsManager.RESULT_RIL_ACCESS_BARRED</code><br> + * <code>SmsManager.RESULT_RIL_BLOCKED_DUE_TO_CALL</code><br> + * For <code>SmsManager.RESULT_ERROR_GENERIC_FAILURE</code> or any of the RESULT_RIL errors, + * the sentIntent may include the extra "errorCode" containing a radio technology specific + * value, generally only useful for troubleshooting.<br> + * The per-application based SMS control checks sentIntent. If sentIntent + * is NULL the caller will be checked against all unknown applications, + * which cause smaller number of SMS to be sent in checking period. + * @param deliveryIntent if not NULL this <code>PendingIntent</code> is + * broadcast when the message is delivered to the recipient. The + * @param messageUri optional URI of the message if it is already stored in the system + * @param callingPkg the calling package name + * @param persistMessage whether to save the sent message into SMS DB for a + * non-default SMS app. + * @param priority Priority level of the message + * Refer specification See 3GPP2 C.S0015-B, v2.0, table 4.5.9-1 + * --------------------------------- + * PRIORITY | Level of Priority + * --------------------------------- + * '00' | Normal + * '01' | Interactive + * '10' | Urgent + * '11' | Emergency + * ---------------------------------- + * Any Other values included Negative considered as Invalid Priority Indicator of the message. + * @param expectMore is a boolean to indicate the sending messages through same link or not. + * @param validityPeriod Validity Period of the message in mins. + * Refer specification 3GPP TS 23.040 V6.8.1 section 9.2.3.12.1. + * Validity Period(Minimum) -> 5 mins + * Validity Period(Maximum) -> 635040 mins(i.e.63 weeks). + * Any Other values included Negative considered as Invalid Validity Period of the message. + * @param skipShortCodeCheck Skip check for short code type destination address. + */ + public void sendText(String destAddr, String scAddr, String text, PendingIntent sentIntent, + PendingIntent deliveryIntent, Uri messageUri, String callingPkg, boolean persistMessage, + int priority, boolean expectMore, int validityPeriod, boolean isForVvm, + long messageId, boolean skipShortCodeCheck) { + if (TextUtils.isEmpty(scAddr)) { + scAddr = getSmscAddressFromUSIMWithPhoneIdentity(callingPkg); + } + + if (mDomainSelectionResolverProxy.isDomainSelectionSupported()) { + TelephonyManager tm = mContext.getSystemService(TelephonyManager.class); + boolean isEmergency = tm.isEmergencyNumber(destAddr); + DomainSelectionConnectionHolder holder = getDomainSelectionConnection(isEmergency); + + // If the DomainSelectionConnection is not available, + // fallback to the legacy implementation. + if (holder != null && holder.getConnection() != null) { + sendSmsUsingDomainSelection(holder, + new PendingRequest(PendingRequest.TYPE_TEXT, null, callingPkg, + destAddr, scAddr, asArrayList(sentIntent), + asArrayList(deliveryIntent), isForVvm, null, 0, asArrayList(text), + messageUri, persistMessage, priority, expectMore, validityPeriod, + messageId, skipShortCodeCheck), + "sendText"); + return; + } + } + if (mImsSmsDispatcher.isAvailable() || mImsSmsDispatcher.isEmergencySmsSupport(destAddr)) { mImsSmsDispatcher.sendText(destAddr, scAddr, text, sentIntent, deliveryIntent, messageUri, callingPkg, persistMessage, priority, false /*expectMore*/, - validityPeriod, isForVvm, messageId); + validityPeriod, isForVvm, messageId, skipShortCodeCheck); } else { if (isCdmaMo()) { mCdmaDispatcher.sendText(destAddr, scAddr, text, sentIntent, deliveryIntent, messageUri, callingPkg, persistMessage, priority, expectMore, - validityPeriod, isForVvm, messageId); + validityPeriod, isForVvm, messageId, skipShortCodeCheck); } else { mGsmDispatcher.sendText(destAddr, scAddr, text, sentIntent, deliveryIntent, messageUri, callingPkg, persistMessage, priority, expectMore, - validityPeriod, isForVvm, messageId); + validityPeriod, isForVvm, messageId, skipShortCodeCheck); } } } @@ -909,6 +1692,26 @@ ArrayList<PendingIntent> deliveryIntents, Uri messageUri, String callingPkg, boolean persistMessage, int priority, boolean expectMore, int validityPeriod, long messageId) { + if (TextUtils.isEmpty(scAddr)) { + scAddr = getSmscAddressFromUSIMWithPhoneIdentity(callingPkg); + } + + if (mDomainSelectionResolverProxy.isDomainSelectionSupported()) { + DomainSelectionConnectionHolder holder = getDomainSelectionConnection(false); + + // If the DomainSelectionConnection is not available, + // fallback to the legacy implementation. + if (holder != null && holder.getConnection() != null) { + sendSmsUsingDomainSelection(holder, + new PendingRequest(PendingRequest.TYPE_MULTIPART_TEXT, null, + callingPkg, destAddr, scAddr, sentIntents, deliveryIntents, false, + null, 0, parts, messageUri, persistMessage, priority, expectMore, + validityPeriod, messageId, false), + "sendMultipartText"); + return; + } + } + if (mImsSmsDispatcher.isAvailable()) { mImsSmsDispatcher.sendMultipartText(destAddr, scAddr, parts, sentIntents, deliveryIntents, messageUri, callingPkg, persistMessage, priority, @@ -1065,4 +1868,8 @@ private void logd(String msg) { Rlog.d(TAG, msg); } + + private void logi(String s) { + Rlog.i(TAG + " [" + mPhone.getPhoneId() + "]", s); + } }
diff --git a/src/java/com/android/internal/telephony/SmsPermissions.java b/src/java/com/android/internal/telephony/SmsPermissions.java index 44751ac..529eea0 100644 --- a/src/java/com/android/internal/telephony/SmsPermissions.java +++ b/src/java/com/android/internal/telephony/SmsPermissions.java
@@ -23,9 +23,11 @@ import android.content.Intent; import android.os.Binder; import android.os.Build; +import android.os.UserHandle; import android.service.carrier.CarrierMessagingService; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.telephony.util.TelephonyUtils; import com.android.telephony.Rlog; /** @@ -132,10 +134,19 @@ */ public boolean checkCallingOrSelfCanGetSmscAddress(String callingPackage, String message) { // Allow it to the default SMS app always. - if (!isCallerDefaultSmsPackage(callingPackage)) { - TelephonyPermissions - .enforceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege( - mContext, mPhone.getSubId(), message); + boolean isDefaultSmsPackage; + int callerUid = Binder.getCallingUid(); + final long identity = Binder.clearCallingIdentity(); + try { + isDefaultSmsPackage = isCallerDefaultSmsPackage(callingPackage, callerUid); + } finally { + Binder.restoreCallingIdentity(identity); + } + if (!isDefaultSmsPackage) { + Rlog.d(LOG_TAG, "Caller is not a default SMS application"); + TelephonyPermissions. + enforceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege( + mContext, mPhone.getSubId(), message); } return true; } @@ -151,33 +162,49 @@ */ public boolean checkCallingOrSelfCanSetSmscAddress(String callingPackage, String message) { // Allow it to the default SMS app always. - if (!isCallerDefaultSmsPackage(callingPackage)) { + boolean isDefaultSmsPackage; + int callerUid = Binder.getCallingUid(); + final long identity = Binder.clearCallingIdentity(); + try { + isDefaultSmsPackage = isCallerDefaultSmsPackage(callingPackage, callerUid); + } finally { + Binder.restoreCallingIdentity(identity); + } + if (!isDefaultSmsPackage) { + Rlog.d(LOG_TAG, "Caller is not a default SMS application"); // Allow it with MODIFY_PHONE_STATE or Carrier Privileges - TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege( - mContext, mPhone.getSubId(), message); + TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mContext, + mPhone.getSubId(), message); } return true; } /** Check if a package is default SMS app. */ @VisibleForTesting - public boolean isCallerDefaultSmsPackage(String packageName) { - if (packageNameMatchesCallingUid(packageName)) { - return SmsApplication.isDefaultSmsApplication(mContext, packageName); + public boolean isCallerDefaultSmsPackage(String packageName, int callerUid) { + if (packageNameMatchesCallingUid(packageName, callerUid)) { + UserHandle userHandle = TelephonyUtils.getSubscriptionUserHandle(mContext, + mPhone.getSubId()); + return SmsApplication.isDefaultSmsApplicationAsUser(mContext, packageName, userHandle); } return false; } + @VisibleForTesting + public boolean packageNameMatchesCallingUid(String packageName) { + return packageNameMatchesCallingUid(packageName, Binder.getCallingUid()); + } + /** * Check if the passed in packageName belongs to the calling uid. * @param packageName name of the package to check * @return true if package belongs to calling uid, false otherwise */ @VisibleForTesting - public boolean packageNameMatchesCallingUid(String packageName) { + public boolean packageNameMatchesCallingUid(String packageName, int callerUid) { try { ((AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE)) - .checkPackage(Binder.getCallingUid(), packageName); + .checkPackage(callerUid, packageName); // If checkPackage doesn't throw an exception then we are the given package return true; } catch (SecurityException e) {
diff --git a/src/java/com/android/internal/telephony/SmsStorageMonitor.java b/src/java/com/android/internal/telephony/SmsStorageMonitor.java old mode 100755 new mode 100644 index b890a4d..2736f7a --- a/src/java/com/android/internal/telephony/SmsStorageMonitor.java +++ b/src/java/com/android/internal/telephony/SmsStorageMonitor.java
@@ -18,17 +18,22 @@ import android.compat.annotation.UnsupportedAppUsage; import android.content.BroadcastReceiver; +import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.res.Resources; import android.os.AsyncResult; import android.os.Build; import android.os.Handler; import android.os.Message; import android.os.PowerManager; +import android.os.UserHandle; import android.provider.Telephony.Sms.Intents; import android.telephony.SubscriptionManager; +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.telephony.util.TelephonyUtils; import com.android.telephony.Rlog; /** @@ -39,16 +44,28 @@ * dual-mode devices that require support for both 3GPP and 3GPP2 format messages. */ public class SmsStorageMonitor extends Handler { - private static final String TAG = "SmsStorageMonitor"; + private static final String TAG = "SmsStorageMonitor1"; + + /** Maximum number of times to retry memory status reporting */ + private static final int MAX_RETRIES = 1; + + /** Delay before next attempt on a failed memory status reporting, in milliseconds. */ + private static final int RETRY_DELAY = 5000; // 5 seconds /** SIM/RUIM storage is full */ private static final int EVENT_ICC_FULL = 1; + /** Report memory status */ + private static final int EVENT_REPORT_MEMORY_STATUS = 2; + /** Memory status reporting is acknowledged by RIL */ - private static final int EVENT_REPORT_MEMORY_STATUS_DONE = 2; + private static final int EVENT_REPORT_MEMORY_STATUS_DONE = 3; + + /** Retry memory status reporting */ + private static final int EVENT_RETRY_MEMORY_STATUS_REPORTING = 4; /** Radio is ON */ - private static final int EVENT_RADIO_ON = 3; + private static final int EVENT_RADIO_ON = 5; /** Context from phone object passed to constructor. */ private final Context mContext; @@ -56,14 +73,23 @@ /** Wake lock to ensure device stays awake while dispatching the SMS intent. */ private PowerManager.WakeLock mWakeLock; - private boolean mReportMemoryStatusPending; + private int mMaxRetryCount = MAX_RETRIES; + private int mRetryDelay = RETRY_DELAY; + private int mRetryCount = 0; + private boolean mIsWaitingResponse = false; + private boolean mNeedNewReporting = false; + private boolean mIsMemoryStatusReportingFailed = false; /** it is use to put in to extra value for SIM_FULL_ACTION and SMS_REJECTED_ACTION */ Phone mPhone; @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - final CommandsInterface mCi; // accessed from inner class - boolean mStorageAvailable = true; // accessed from inner class + final CommandsInterface mCi; + boolean mStorageAvailable = true; + + boolean mInitialStorageAvailableStatus = true; + + private boolean mMemoryStatusOverrideFlag = false; /** * Hold the wake lock for 5 seconds, which should be enough time for @@ -93,6 +119,41 @@ mContext.registerReceiver(mResultReceiver, filter); } + /** + * Overriding of the Memory Status by the TestApi and send the event to Handler to test + * the RP-SMMA feature + * @param isStorageAvailable boolean value specifies the MemoryStatus to be + * sent to Handler + */ + public void sendMemoryStatusOverride(boolean isStorageAvailable) { + if (!mMemoryStatusOverrideFlag) { + mInitialStorageAvailableStatus = mStorageAvailable; + mMemoryStatusOverrideFlag = true; + } + mStorageAvailable = isStorageAvailable; + if (isStorageAvailable) { + sendMessage(obtainMessage(EVENT_REPORT_MEMORY_STATUS)); + } + } + + /** + * reset Memory Status change made by {@link #sendMemoryStatusOverride} + */ + public void clearMemoryStatusOverride() { + mStorageAvailable = mInitialStorageAvailableStatus; + mMemoryStatusOverrideFlag = false; + } + + @VisibleForTesting + public void setMaxRetries(int maxCount) { + mMaxRetryCount = maxCount; + } + + @VisibleForTesting + public void setRetryDelayInMillis(int delay) { + mRetryDelay = delay; + } + public void dispose() { mCi.unSetOnIccSmsFull(this); mCi.unregisterForOn(this); @@ -105,35 +166,97 @@ */ @Override public void handleMessage(Message msg) { - AsyncResult ar; + boolean isAvailable = mStorageAvailable; switch (msg.what) { case EVENT_ICC_FULL: handleIccFull(); break; - case EVENT_REPORT_MEMORY_STATUS_DONE: - ar = (AsyncResult) msg.obj; - if (ar.exception != null) { - mReportMemoryStatusPending = true; - Rlog.v(TAG, "Memory status report to modem pending : mStorageAvailable = " - + mStorageAvailable); + case EVENT_REPORT_MEMORY_STATUS: + if (mIsWaitingResponse) { + Rlog.v(TAG, "EVENT_REPORT_MEMORY_STATUS - deferred"); + // Previous reporting is on-going now. New reporting will be issued in + // EVENT_REPORT_MEMORY_STATUS_DONE. + mNeedNewReporting = true; } else { - mReportMemoryStatusPending = false; + Rlog.v(TAG, "EVENT_REPORT_MEMORY_STATUS - report sms memory status" + + (isAvailable ? "(not full)" : "(full)")); + // Clear a delayed EVENT_RETRY_MEMORY_STATUS_REPORTING + // and mIsMemoryStatusReportingFailed. + removeMessages(EVENT_RETRY_MEMORY_STATUS_REPORTING); + mIsMemoryStatusReportingFailed = false; + mRetryCount = 0; + sendMemoryStatusReport(isAvailable); } break; + case EVENT_REPORT_MEMORY_STATUS_DONE: + AsyncResult ar = (AsyncResult) msg.obj; + mIsWaitingResponse = false; + Rlog.v(TAG, "EVENT_REPORT_MEMORY_STATUS_DONE - " + + (ar.exception == null ? "succeeded" : "failed")); + if (mNeedNewReporting) { + Rlog.v(TAG, "EVENT_REPORT_MEMORY_STATUS_DONE - report again now" + + (isAvailable ? "(not full)" : "(full)")); + // New reportings have been requested, report last memory status here. + mNeedNewReporting = false; + mRetryCount = 0; + sendMemoryStatusReport(isAvailable); + } else { + if (ar.exception != null) { + if (mRetryCount++ < mMaxRetryCount) { + Rlog.v(TAG, "EVENT_REPORT_MEMORY_STATUS_DONE - retry in " + + mRetryDelay + "ms"); + sendMessageDelayed( + obtainMessage(EVENT_RETRY_MEMORY_STATUS_REPORTING), + mRetryDelay); + } else { + Rlog.v(TAG, "EVENT_REPORT_MEMORY_STATUS_DONE - " + + "no retry anymore(pended)"); + mRetryCount = 0; + mIsMemoryStatusReportingFailed = true; + } + } else { + mRetryCount = 0; + mIsMemoryStatusReportingFailed = false; + } + } + break; + + case EVENT_RETRY_MEMORY_STATUS_REPORTING: + Rlog.v(TAG, "EVENT_RETRY_MEMORY_STATUS_REPORTING - retry" + + (isAvailable ? "(not full)" : "(full)")); + sendMemoryStatusReport(isAvailable); + break; + case EVENT_RADIO_ON: - if (mReportMemoryStatusPending) { - Rlog.v(TAG, "Sending pending memory status report : mStorageAvailable = " - + mStorageAvailable); - mCi.reportSmsMemoryStatus(mStorageAvailable, - obtainMessage(EVENT_REPORT_MEMORY_STATUS_DONE)); + if (mIsMemoryStatusReportingFailed) { + Rlog.v(TAG, "EVENT_RADIO_ON - report failed sms memory status" + + (isAvailable ? "(not full)" : "(full)")); + sendMemoryStatusReport(isAvailable); } break; } } + private void sendMemoryStatusReport(boolean isAvailable) { + mIsWaitingResponse = true; + Resources r = mContext.getResources(); + if (r.getBoolean(com.android.internal.R.bool.config_smma_notification_supported_over_ims)) { + IccSmsInterfaceManager smsIfcMngr = mPhone.getIccSmsInterfaceManager(); + if (smsIfcMngr != null) { + Rlog.d(TAG, "sendMemoryStatusReport: smsIfcMngr is available"); + if (smsIfcMngr.mDispatchersController.isIms() && isAvailable) { + smsIfcMngr.mDispatchersController.reportSmsMemoryStatus( + obtainMessage(EVENT_REPORT_MEMORY_STATUS_DONE)); + return; + } + } + } + mCi.reportSmsMemoryStatus(isAvailable, obtainMessage(EVENT_REPORT_MEMORY_STATUS_DONE)); + } + private void createWakelock() { PowerManager pm = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE); mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "SmsStorageMonitor"); @@ -145,9 +268,14 @@ * that SIM storage for SMS messages is full. */ private void handleIccFull() { + UserHandle userHandle = TelephonyUtils.getSubscriptionUserHandle(mContext, + mPhone.getSubId()); + ComponentName componentName = SmsApplication.getDefaultSimFullApplicationAsUser(mContext, + false, userHandle); + // broadcast SIM_FULL intent Intent intent = new Intent(Intents.SIM_FULL_ACTION); - intent.setComponent(SmsApplication.getDefaultSimFullApplication(mContext, false)); + intent.setComponent(componentName); mWakeLock.acquire(WAKE_LOCK_TIMEOUT); SubscriptionManager.putPhoneIdAndSubIdExtra(intent, mPhone.getPhoneId()); mContext.sendBroadcast(intent, android.Manifest.permission.RECEIVE_SMS); @@ -161,12 +289,12 @@ private final BroadcastReceiver mResultReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { - if (intent.getAction().equals(Intent.ACTION_DEVICE_STORAGE_FULL)) { - mStorageAvailable = false; - mCi.reportSmsMemoryStatus(false, obtainMessage(EVENT_REPORT_MEMORY_STATUS_DONE)); - } else if (intent.getAction().equals(Intent.ACTION_DEVICE_STORAGE_NOT_FULL)) { - mStorageAvailable = true; - mCi.reportSmsMemoryStatus(true, obtainMessage(EVENT_REPORT_MEMORY_STATUS_DONE)); + final String action = intent.getAction(); + if (Intent.ACTION_DEVICE_STORAGE_FULL.equals(action) + || Intent.ACTION_DEVICE_STORAGE_NOT_FULL.equals(action)) { + mStorageAvailable = + Intent.ACTION_DEVICE_STORAGE_FULL.equals(action) ? false : true; + sendMessage(obtainMessage(EVENT_REPORT_MEMORY_STATUS)); } } };
diff --git a/src/java/com/android/internal/telephony/SmsUsageMonitor.java b/src/java/com/android/internal/telephony/SmsUsageMonitor.java index 8bcdc07..8e4ac60 100644 --- a/src/java/com/android/internal/telephony/SmsUsageMonitor.java +++ b/src/java/com/android/internal/telephony/SmsUsageMonitor.java
@@ -43,6 +43,7 @@ import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlSerializer; +import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; @@ -73,6 +74,8 @@ private static final String SHORT_CODE_PATH = "/data/misc/sms/codes"; + private static final String SHORT_CODE_VERSION_PATH = "/data/misc/sms/metadata/version"; + /** Default checking period for SMS sent without user permission. */ private static final int DEFAULT_SMS_CHECK_PERIOD = 60000; // 1 minute @@ -128,6 +131,8 @@ /** Last modified time for pattern file */ private long mPatternFileLastModified = 0; + private int mPatternFileVersion = -1; + private RoleManager mRoleManager; /** Directory for per-app SMS permission XML file. */ @@ -415,9 +420,11 @@ if (mPatternFile.exists()) { if (DBG) Rlog.d(TAG, "Loading SMS Short Code patterns from file"); mCurrentPatternMatcher = getPatternMatcherFromFile(countryIso); + mPatternFileVersion = getPatternFileVersionFromFile(); } else { if (DBG) Rlog.d(TAG, "Loading SMS Short Code patterns from resource"); mCurrentPatternMatcher = getPatternMatcherFromResource(countryIso); + mPatternFileVersion = -1; } mCurrentCountry = countryIso; } @@ -655,6 +662,37 @@ return false; } + private int getPatternFileVersionFromFile() { + File versionFile = new File(SHORT_CODE_VERSION_PATH); + if (versionFile.exists()) { + BufferedReader reader = null; + try { + reader = new BufferedReader(new FileReader(versionFile)); + String version = reader.readLine(); + if (version != null) { + return Integer.parseInt(version); + } + } catch (IOException e) { + Rlog.e(TAG, "File reader exception reading short code " + + "pattern file version", e); + } finally { + try { + if (reader != null) { + reader.close(); + } + } catch (IOException e) { + Rlog.e(TAG, "File reader exception closing short code " + + "pattern file version reader", e); + } + } + } + return -1; + } + + public int getShortCodeXmlFileVersion() { + return mPatternFileVersion; + } + private static void log(String msg) { Rlog.d(TAG, msg); }
diff --git a/src/java/com/android/internal/telephony/SrvccConnection.java b/src/java/com/android/internal/telephony/SrvccConnection.java new file mode 100644 index 0000000..f25a15c --- /dev/null +++ b/src/java/com/android/internal/telephony/SrvccConnection.java
@@ -0,0 +1,260 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.telephony; + +import static android.telephony.PreciseCallState.PRECISE_CALL_STATE_ACTIVE; +import static android.telephony.PreciseCallState.PRECISE_CALL_STATE_ALERTING; +import static android.telephony.PreciseCallState.PRECISE_CALL_STATE_DIALING; +import static android.telephony.PreciseCallState.PRECISE_CALL_STATE_HOLDING; +import static android.telephony.PreciseCallState.PRECISE_CALL_STATE_INCOMING; +import static android.telephony.PreciseCallState.PRECISE_CALL_STATE_INCOMING_SETUP; +import static android.telephony.PreciseCallState.PRECISE_CALL_STATE_WAITING; + +import android.net.Uri; +import android.telephony.Annotation.PreciseCallStates; +import android.telephony.ims.ImsCallProfile; +import android.telephony.ims.ImsStreamMediaProfile; +import android.text.TextUtils; + +import com.android.ims.internal.ConferenceParticipant; +import com.android.internal.telephony.imsphone.ImsPhoneConnection; +import com.android.telephony.Rlog; + +/** + * Connection information for SRVCC + */ +public class SrvccConnection { + private static final String TAG = "SrvccConnection"; + + public static final int CALL_TYPE_NORMAL = 0; + public static final int CALL_TYPE_EMERGENCY = 1; + + public static final int SUBSTATE_NONE = 0; + /** Pre-alerting state. Applicable for MT calls only */ + public static final int SUBSTATE_PREALERTING = 1; + + public static final int TONE_NONE = 0; + public static final int TONE_LOCAL = 1; + public static final int TONE_NETWORK = 2; + + /** Values are CALL_TYPE_ */ + private int mType = CALL_TYPE_NORMAL; + + /** Values are Call.State */ + private Call.State mState; + + /** Values are SUBSTATE_ */ + private int mSubstate = SUBSTATE_NONE; + + /** Values are TONE_ */ + private int mRingbackToneType = TONE_NONE; + + /** true if it is a multi-party call */ + private boolean mIsMpty = false; + + /** true if it is a mobile terminated call */ + private boolean mIsMT; + + /** Remote party nummber */ + private String mNumber; + + /** Values are PhoneConstants.PRESENTATION_ */ + private int mNumPresentation; + + /** Remote party name */ + private String mName; + + /** Values are PhoneConstants.PRESENTATION_ */ + private int mNamePresentation; + + public SrvccConnection(ImsCallProfile profile, + ImsPhoneConnection c, @PreciseCallStates int preciseCallState) { + mState = toCallState(preciseCallState); + if (mState == Call.State.ALERTING) { + mRingbackToneType = isLocalTone(profile) ? TONE_LOCAL : TONE_NETWORK; + } + if (preciseCallState == PRECISE_CALL_STATE_INCOMING_SETUP) { + mSubstate = SUBSTATE_PREALERTING; + } + + if (c == null) { + initialize(profile); + } else { + initialize(c); + } + } + + public SrvccConnection(ConferenceParticipant cp, @PreciseCallStates int preciseCallState) { + Rlog.d(TAG, "initialize with ConferenceParticipant"); + mState = toCallState(preciseCallState); + mIsMT = cp.getCallDirection() == android.telecom.Call.Details.DIRECTION_INCOMING; + mNumber = getParticipantAddress(cp.getHandle()); + mNumPresentation = cp.getParticipantPresentation(); + if (mNumPresentation == PhoneConstants.PRESENTATION_RESTRICTED) { + mNumber = ""; + } + mName = cp.getDisplayName(); + if (!TextUtils.isEmpty(mName)) { + mNamePresentation = PhoneConstants.PRESENTATION_ALLOWED; + } else { + mNamePresentation = PhoneConstants.PRESENTATION_UNKNOWN; + } + mIsMpty = true; + } + + private static String getParticipantAddress(Uri address) { + if (address == null) { + return null; + } + + String number = address.getSchemeSpecificPart(); + if (TextUtils.isEmpty(number)) { + return null; + } + + String[] numberParts = number.split("[@;:]"); + if (numberParts.length == 0) return null; + + return numberParts[0]; + } + + // MT call in alerting or prealerting state + private void initialize(ImsCallProfile profile) { + Rlog.d(TAG, "initialize with ImsCallProfile"); + mIsMT = true; + mNumber = profile.getCallExtra(ImsCallProfile.EXTRA_OI); + mName = profile.getCallExtra(ImsCallProfile.EXTRA_CNA); + mNumPresentation = ImsCallProfile.OIRToPresentation( + profile.getCallExtraInt(ImsCallProfile.EXTRA_OIR)); + mNamePresentation = ImsCallProfile.OIRToPresentation( + profile.getCallExtraInt(ImsCallProfile.EXTRA_CNAP)); + } + + private void initialize(ImsPhoneConnection c) { + Rlog.d(TAG, "initialize with ImsPhoneConnection"); + if (c.isEmergencyCall()) { + mType = CALL_TYPE_EMERGENCY; + } + mIsMT = c.isIncoming(); + mNumber = c.getAddress(); + mNumPresentation = c.getNumberPresentation(); + mName = c.getCnapName(); + mNamePresentation = c.getCnapNamePresentation(); + } + + private boolean isLocalTone(ImsCallProfile profile) { + if (profile == null) return false; + + ImsStreamMediaProfile mediaProfile = profile.getMediaProfile(); + if (mediaProfile == null) return false; + + boolean shouldPlayRingback = + (mediaProfile.getAudioDirection() == ImsStreamMediaProfile.DIRECTION_INACTIVE) + ? true : false; + return shouldPlayRingback; + } + + private static Call.State toCallState(int preciseCallState) { + switch (preciseCallState) { + case PRECISE_CALL_STATE_ACTIVE: return Call.State.ACTIVE; + case PRECISE_CALL_STATE_HOLDING: return Call.State.HOLDING; + case PRECISE_CALL_STATE_DIALING: return Call.State.DIALING; + case PRECISE_CALL_STATE_ALERTING: return Call.State.ALERTING; + case PRECISE_CALL_STATE_INCOMING: return Call.State.INCOMING; + case PRECISE_CALL_STATE_WAITING: return Call.State.WAITING; + case PRECISE_CALL_STATE_INCOMING_SETUP: return Call.State.INCOMING; + default: + } + return Call.State.DISCONNECTED; + } + + /** Returns the type of the call */ + public int getType() { + return mType; + } + + /** Returns the state */ + public Call.State getState() { + return mState; + } + + /** Updates the state */ + public void setState(Call.State state) { + mState = state; + } + + /** Returns the sub state */ + public int getSubState() { + return mSubstate; + } + + /** Returns the ringback tone type */ + public int getRingbackToneType() { + return mRingbackToneType; + } + + /** true if it is a multi-party call */ + public boolean isMultiParty() { + return mIsMpty; + } + + /** true if it is a mobile terminated call */ + public boolean isIncoming() { + return mIsMT; + } + + /** Returns the remote party nummber */ + public String getNumber() { + return mNumber; + } + + /** Returns the number presentation */ + public int getNumberPresentation() { + return mNumPresentation; + } + + /** Returns the remote party name */ + public String getName() { + return mName; + } + + /** Returns the name presentation */ + public int getNamePresentation() { + return mNamePresentation; + } + + /** + * Build a human representation of a connection instance, suitable for debugging. + * Don't log personal stuff unless in debug mode. + * @return a string representing the internal state of this connection. + */ + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(" type:").append(getType()); + sb.append(", state:").append(getState()); + sb.append(", subState:").append(getSubState()); + sb.append(", toneType:").append(getRingbackToneType()); + sb.append(", mpty:").append(isMultiParty()); + sb.append(", incoming:").append(isIncoming()); + sb.append(", numberPresentation:").append(getNumberPresentation()); + sb.append(", number:").append(Rlog.pii(TAG, getNumber())); + sb.append(", namePresentation:").append(getNamePresentation()); + sb.append(", name:").append(Rlog.pii(TAG, getName())); + return sb.toString(); + } +}
diff --git a/src/java/com/android/internal/telephony/SubscriptionController.java b/src/java/com/android/internal/telephony/SubscriptionController.java deleted file mode 100644 index 7e762d7..0000000 --- a/src/java/com/android/internal/telephony/SubscriptionController.java +++ /dev/null
@@ -1,4820 +0,0 @@ -/* -* Copyright (C) 2014 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - -package com.android.internal.telephony; - -import static android.Manifest.permission.READ_PHONE_NUMBERS; -import static android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE; -import static android.content.pm.PackageManager.PERMISSION_GRANTED; -import static android.telephony.TelephonyManager.MULTISIM_ALLOWED; -import static android.telephony.TelephonyManager.SET_OPPORTUNISTIC_SUB_REMOTE_SERVICE_EXCEPTION; -import static android.telephony.UiccSlotInfo.CARD_STATE_INFO_PRESENT; - -import android.Manifest; -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.app.AppOpsManager; -import android.app.PendingIntent; -import android.compat.annotation.UnsupportedAppUsage; -import android.content.ContentResolver; -import android.content.ContentValues; -import android.content.Context; -import android.content.Intent; -import android.content.pm.PackageManager; -import android.database.ContentObserver; -import android.database.Cursor; -import android.net.Uri; -import android.os.Binder; -import android.os.Build; -import android.os.Handler; -import android.os.ParcelUuid; -import android.os.PersistableBundle; -import android.os.RegistrantList; -import android.os.RemoteException; -import android.os.TelephonyServiceManager.ServiceRegisterer; -import android.os.UserHandle; -import android.provider.Settings; -import android.provider.Telephony.SimInfo; -import android.telecom.PhoneAccountHandle; -import android.telecom.TelecomManager; -import android.telephony.AnomalyReporter; -import android.telephony.CarrierConfigManager; -import android.telephony.RadioAccessFamily; -import android.telephony.SubscriptionInfo; -import android.telephony.SubscriptionManager; -import android.telephony.SubscriptionManager.SimDisplayNameSource; -import android.telephony.SubscriptionManager.UsageSetting; -import android.telephony.TelephonyFrameworkInitializer; -import android.telephony.TelephonyManager; -import android.telephony.TelephonyRegistryManager; -import android.telephony.UiccAccessRule; -import android.telephony.UiccPortInfo; -import android.telephony.UiccSlotInfo; -import android.telephony.UiccSlotMapping; -import android.telephony.euicc.EuiccManager; -import android.text.TextUtils; -import android.util.EventLog; -import android.util.LocalLog; -import android.util.Log; - -import com.android.ims.ImsManager; -import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.telephony.IccCardConstants.State; -import com.android.internal.telephony.data.DataEnabledOverride; -import com.android.internal.telephony.data.PhoneSwitcher; -import com.android.internal.telephony.metrics.TelephonyMetrics; -import com.android.internal.telephony.uicc.IccUtils; -import com.android.internal.telephony.uicc.UiccCard; -import com.android.internal.telephony.uicc.UiccController; -import com.android.internal.telephony.uicc.UiccProfile; -import com.android.internal.telephony.uicc.UiccSlot; -import com.android.internal.telephony.util.ArrayUtils; -import com.android.internal.telephony.util.TelephonyUtils; -import com.android.telephony.Rlog; - -import java.io.FileDescriptor; -import java.io.PrintWriter; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Objects; -import java.util.Set; -import java.util.UUID; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.stream.Collectors; - -/** - * Implementation of the ISub interface. - * - * Any setters which take subId, slotIndex or phoneId as a parameter will throw an exception if the - * parameter equals the corresponding INVALID_XXX_ID or DEFAULT_XXX_ID. - * - * All getters will lookup the corresponding default if the parameter is DEFAULT_XXX_ID. Ie calling - * getPhoneId(DEFAULT_SUB_ID) will return the same as getPhoneId(getDefaultSubId()). - * - * Finally, any getters which perform the mapping between subscriptions, slots and phones will - * return the corresponding INVALID_XXX_ID if the parameter is INVALID_XXX_ID. All other getters - * will fail and return the appropriate error value. Ie calling - * getSlotIndex(INVALID_SUBSCRIPTION_ID) will return INVALID_SIM_SLOT_INDEX and calling - * getSubInfoForSubscriber(INVALID_SUBSCRIPTION_ID) will return null. - * - */ -public class SubscriptionController extends ISub.Stub { - private static final String LOG_TAG = "SubscriptionController"; - private static final boolean DBG = false; - private static final boolean VDBG = Rlog.isLoggable(LOG_TAG, Log.VERBOSE); - private static final boolean DBG_CACHE = false; - private static final int DEPRECATED_SETTING = -1; - private static final ParcelUuid INVALID_GROUP_UUID = - ParcelUuid.fromString(CarrierConfigManager.REMOVE_GROUP_UUID_STRING); - private final LocalLog mLocalLog = new LocalLog(128); - private static final int SUB_ID_FOUND = 1; - private static final int NO_ENTRY_FOR_SLOT_INDEX = -1; - private static final int SUB_ID_NOT_IN_SLOT = -2; - - // Lock that both mCacheActiveSubInfoList and mCacheOpportunisticSubInfoList use. - private Object mSubInfoListLock = new Object(); - - /* The Cache of Active SubInfoRecord(s) list of currently in use SubInfoRecord(s) */ - private final List<SubscriptionInfo> mCacheActiveSubInfoList = new ArrayList<>(); - - /* Similar to mCacheActiveSubInfoList but only caching opportunistic subscriptions. */ - private List<SubscriptionInfo> mCacheOpportunisticSubInfoList = new ArrayList<>(); - private AtomicBoolean mOpptSubInfoListChangedDirtyBit = new AtomicBoolean(); - - private static final Comparator<SubscriptionInfo> SUBSCRIPTION_INFO_COMPARATOR = - (arg0, arg1) -> { - // Primary sort key on SimSlotIndex - int flag = arg0.getSimSlotIndex() - arg1.getSimSlotIndex(); - if (flag == 0) { - // Secondary sort on SubscriptionId - return arg0.getSubscriptionId() - arg1.getSubscriptionId(); - } - return flag; - }; - - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - protected final Object mLock = new Object(); - - /** The singleton instance. */ - protected static SubscriptionController sInstance = null; - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - protected Context mContext; - protected TelephonyManager mTelephonyManager; - protected UiccController mUiccController; - - private AppOpsManager mAppOps; - - // Allows test mocks to avoid SELinux failures on invalidate calls. - private static boolean sCachingEnabled = true; - - // Each slot can have multiple subs. - private static class WatchedSlotIndexToSubIds { - private Map<Integer, ArrayList<Integer>> mSlotIndexToSubIds = new ConcurrentHashMap<>(); - - WatchedSlotIndexToSubIds() { - } - - public void clear() { - mSlotIndexToSubIds.clear(); - invalidateDefaultSubIdCaches(); - invalidateSlotIndexCaches(); - } - - public Set<Entry<Integer, ArrayList<Integer>>> entrySet() { - return mSlotIndexToSubIds.entrySet(); - } - - // Force all updates to data structure through wrapper. - public ArrayList<Integer> getCopy(int slotIndex) { - ArrayList<Integer> subIdList = mSlotIndexToSubIds.get(slotIndex); - if (subIdList == null) { - return null; - } - - return new ArrayList<Integer>(subIdList); - } - - public void put(int slotIndex, ArrayList<Integer> value) { - mSlotIndexToSubIds.put(slotIndex, value); - invalidateDefaultSubIdCaches(); - invalidateSlotIndexCaches(); - } - - public void remove(int slotIndex) { - mSlotIndexToSubIds.remove(slotIndex); - invalidateDefaultSubIdCaches(); - invalidateSlotIndexCaches(); - } - - public int size() { - return mSlotIndexToSubIds.size(); - } - - @VisibleForTesting - public Map<Integer, ArrayList<Integer>> getMap() { - return mSlotIndexToSubIds; - } - - public int removeFromSubIdList(int slotIndex, int subId) { - ArrayList<Integer> subIdList = mSlotIndexToSubIds.get(slotIndex); - if (subIdList == null) { - return NO_ENTRY_FOR_SLOT_INDEX; - } else { - if (subIdList.contains(subId)) { - subIdList.remove(new Integer(subId)); - if (subIdList.isEmpty()) { - mSlotIndexToSubIds.remove(slotIndex); - } - invalidateDefaultSubIdCaches(); - invalidateSlotIndexCaches(); - return SUB_ID_FOUND; - } else { - return SUB_ID_NOT_IN_SLOT; - } - } - } - - public void addToSubIdList(int slotIndex, Integer value) { - ArrayList<Integer> subIdList = mSlotIndexToSubIds.get(slotIndex); - if (subIdList == null) { - subIdList = new ArrayList<Integer>(); - subIdList.add(value); - mSlotIndexToSubIds.put(slotIndex, subIdList); - } else { - subIdList.add(value); - } - invalidateDefaultSubIdCaches(); - invalidateSlotIndexCaches(); - } - - public void clearSubIdList(int slotIndex) { - ArrayList<Integer> subIdList = mSlotIndexToSubIds.get(slotIndex); - if (subIdList != null) { - subIdList.clear(); - invalidateDefaultSubIdCaches(); - invalidateSlotIndexCaches(); - } - } - } - - public static class WatchedInt { - private int mValue; - - public WatchedInt(int initialValue) { - mValue = initialValue; - } - - public int get() { - return mValue; - } - - public void set(int newValue) { - mValue = newValue; - } - } - - private static WatchedSlotIndexToSubIds sSlotIndexToSubIds = new WatchedSlotIndexToSubIds(); - - protected static WatchedInt sDefaultFallbackSubId = - new WatchedInt(SubscriptionManager.INVALID_SUBSCRIPTION_ID) { - @Override - public void set(int newValue) { - super.set(newValue); - invalidateDefaultSubIdCaches(); - invalidateSlotIndexCaches(); - } - }; - - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - private static int mDefaultPhoneId = SubscriptionManager.DEFAULT_PHONE_INDEX; - - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - private int[] colorArr; - private long mLastISubServiceRegTime; - private RegistrantList mUiccAppsEnableChangeRegList = new RegistrantList(); - - // The properties that should be shared and synced across grouped subscriptions. - private static final Set<String> GROUP_SHARING_PROPERTIES = new HashSet<>(Arrays.asList( - SubscriptionManager.ENHANCED_4G_MODE_ENABLED, - SubscriptionManager.VT_IMS_ENABLED, - SubscriptionManager.WFC_IMS_ENABLED, - SubscriptionManager.WFC_IMS_MODE, - SubscriptionManager.WFC_IMS_ROAMING_MODE, - SubscriptionManager.WFC_IMS_ROAMING_ENABLED, - SubscriptionManager.DATA_ROAMING, - SubscriptionManager.DISPLAY_NAME, - SubscriptionManager.DATA_ENABLED_OVERRIDE_RULES, - SubscriptionManager.UICC_APPLICATIONS_ENABLED, - SubscriptionManager.IMS_RCS_UCE_ENABLED, - SubscriptionManager.CROSS_SIM_CALLING_ENABLED, - SubscriptionManager.NR_ADVANCED_CALLING_ENABLED - )); - - public static SubscriptionController init(Context c) { - synchronized (SubscriptionController.class) { - if (sInstance == null) { - sInstance = new SubscriptionController(c); - } else { - Log.wtf(LOG_TAG, "init() called multiple times! sInstance = " + sInstance); - } - return sInstance; - } - } - - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - public static SubscriptionController getInstance() { - if (sInstance == null) { - Log.wtf(LOG_TAG, "getInstance null"); - } - - return sInstance; - } - - protected SubscriptionController(Context c) { - internalInit(c); - migrateImsSettings(); - } - - protected void internalInit(Context c) { - mContext = c; - mTelephonyManager = TelephonyManager.from(mContext); - - try { - mUiccController = UiccController.getInstance(); - } catch(RuntimeException ex) { - throw new RuntimeException( - "UiccController has to be initialised before SubscriptionController init"); - } - - mAppOps = (AppOpsManager)mContext.getSystemService(Context.APP_OPS_SERVICE); - - ServiceRegisterer subscriptionServiceRegisterer = TelephonyFrameworkInitializer - .getTelephonyServiceManager() - .getSubscriptionServiceRegisterer(); - if (subscriptionServiceRegisterer.get() == null) { - subscriptionServiceRegisterer.register(this); - mLastISubServiceRegTime = System.currentTimeMillis(); - } - - // clear SLOT_INDEX for all subs - clearSlotIndexForSubInfoRecords(); - - // Cache Setting values - cacheSettingValues(); - - // Initial invalidate activates caching. - invalidateDefaultSubIdCaches(); - invalidateDefaultDataSubIdCaches(); - invalidateDefaultSmsSubIdCaches(); - invalidateActiveDataSubIdCaches(); - invalidateSlotIndexCaches(); - - mContext.getContentResolver().registerContentObserver( - SubscriptionManager.SIM_INFO_SUW_RESTORE_CONTENT_URI, false, - new ContentObserver(new Handler()) { - @Override - public void onChange(boolean selfChange, Uri uri) { - if (uri.equals(SubscriptionManager.SIM_INFO_SUW_RESTORE_CONTENT_URI)) { - refreshCachedActiveSubscriptionInfoList(); - notifySubscriptionInfoChanged(); - - SubscriptionManager subManager = SubscriptionManager.from(mContext); - for (SubscriptionInfo subInfo : getActiveSubscriptionInfoList( - mContext.getOpPackageName(), mContext.getAttributionTag())) { - if (SubscriptionController.getInstance() - .isActiveSubId(subInfo.getSubscriptionId())) { - ImsManager imsManager = ImsManager.getInstance(mContext, - subInfo.getSimSlotIndex()); - imsManager.updateImsServiceConfig(); - } - } - } - } - }); - - if (DBG) logdl("[SubscriptionController] init by Context"); - } - - /** - * Should only be triggered once. - */ - public void notifySubInfoReady() { - // broadcast default subId. - sendDefaultChangedBroadcast(SubscriptionManager.getDefaultSubscriptionId()); - } - - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - private boolean isSubInfoReady() { - return SubscriptionInfoUpdater.isSubInfoInitialized(); - } - - /** - * This function marks SIM_SLOT_INDEX as INVALID for all subscriptions in the database. This - * should be done as part of initialization. - * - * TODO: SIM_SLOT_INDEX is based on current state and should not even be persisted in the - * database. - */ - private void clearSlotIndexForSubInfoRecords() { - if (mContext == null) { - logel("[clearSlotIndexForSubInfoRecords] TelephonyManager or mContext is null"); - return; - } - - // Update all subscriptions in simInfo db with invalid slot index - ContentValues value = new ContentValues(1); - value.put(SubscriptionManager.SIM_SLOT_INDEX, SubscriptionManager.INVALID_SIM_SLOT_INDEX); - mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI, value, null, null); - } - - /** - * Cache the Settings values by reading these values from Setting from disk to prevent disk I/O - * access during the API calling. This is based on an assumption that the Settings system will - * itself cache this value after the first read and thus only the first read after boot will - * access the disk. - */ - private void cacheSettingValues() { - Settings.Global.getInt(mContext.getContentResolver(), - Settings.Global.MULTI_SIM_SMS_SUBSCRIPTION, - SubscriptionManager.INVALID_SUBSCRIPTION_ID); - - Settings.Global.getInt(mContext.getContentResolver(), - Settings.Global.MULTI_SIM_VOICE_CALL_SUBSCRIPTION, - SubscriptionManager.INVALID_SUBSCRIPTION_ID); - - Settings.Global.getInt(mContext.getContentResolver(), - Settings.Global.MULTI_SIM_DATA_CALL_SUBSCRIPTION, - SubscriptionManager.INVALID_SUBSCRIPTION_ID); - } - - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - protected void enforceModifyPhoneState(String message) { - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.MODIFY_PHONE_STATE, message); - } - - private void enforceReadPrivilegedPhoneState(String message) { - mContext.enforceCallingOrSelfPermission( - Manifest.permission.READ_PRIVILEGED_PHONE_STATE, message); - } - - /** - * Returns whether the {@code callingPackage} has access to subscriber identifiers on the - * specified {@code subId} using the provided {@code message} in any resulting - * SecurityException. - */ - private boolean hasSubscriberIdentifierAccess(int subId, String callingPackage, - String callingFeatureId, String message, boolean reportFailure) { - try { - return TelephonyPermissions.checkCallingOrSelfReadSubscriberIdentifiers(mContext, subId, - callingPackage, callingFeatureId, message, reportFailure); - } catch (SecurityException e) { - // A SecurityException indicates that the calling package is targeting at least the - // minimum level that enforces identifier access restrictions and the new access - // requirements are not met. - return false; - } - } - - /** - * Returns whether the {@code callingPackage} has access to the phone number on the specified - * {@code subId} using the provided {@code message} in any resulting SecurityException. - */ - private boolean hasPhoneNumberAccess(int subId, String callingPackage, String callingFeatureId, - String message) { - try { - return TelephonyPermissions.checkCallingOrSelfReadPhoneNumber(mContext, subId, - callingPackage, callingFeatureId, message); - } catch (SecurityException e) { - return false; - } - } - - /** - * Broadcast when SubscriptionInfo has changed - * FIXME: Hopefully removed if the API council accepts SubscriptionInfoListener - */ - private void broadcastSimInfoContentChanged() { - Intent intent = new Intent(TelephonyIntents.ACTION_SUBINFO_CONTENT_CHANGE); - mContext.sendBroadcast(intent); - intent = new Intent(TelephonyIntents.ACTION_SUBINFO_RECORD_UPDATED); - mContext.sendBroadcast(intent); - } - - /** - * Notify the changed of subscription info. - */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - public void notifySubscriptionInfoChanged() { - TelephonyRegistryManager trm = - (TelephonyRegistryManager) - mContext.getSystemService(Context.TELEPHONY_REGISTRY_SERVICE); - if (DBG) logd("notifySubscriptionInfoChanged:"); - trm.notifySubscriptionInfoChanged(); - - // FIXME: Remove if listener technique accepted. - broadcastSimInfoContentChanged(); - - MultiSimSettingController.getInstance().notifySubscriptionInfoChanged(); - TelephonyMetrics metrics = TelephonyMetrics.getInstance(); - List<SubscriptionInfo> subInfos; - synchronized (mSubInfoListLock) { - subInfos = new ArrayList<>(mCacheActiveSubInfoList); - } - - if (mOpptSubInfoListChangedDirtyBit.getAndSet(false)) { - notifyOpportunisticSubscriptionInfoChanged(); - } - metrics.updateActiveSubscriptionInfoList(subInfos); - } - - /** - * New SubInfoRecord instance and fill in detail info - * @param cursor - * @return the query result of desired SubInfoRecord - */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - private SubscriptionInfo getSubInfoRecord(Cursor cursor) { - int id = cursor.getInt(cursor.getColumnIndexOrThrow( - SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID)); - String iccId = cursor.getString(cursor.getColumnIndexOrThrow( - SubscriptionManager.ICC_ID)); - int simSlotIndex = cursor.getInt(cursor.getColumnIndexOrThrow( - SubscriptionManager.SIM_SLOT_INDEX)); - String displayName = cursor.getString(cursor.getColumnIndexOrThrow( - SubscriptionManager.DISPLAY_NAME)); - String carrierName = cursor.getString(cursor.getColumnIndexOrThrow( - SubscriptionManager.CARRIER_NAME)); - int nameSource = cursor.getInt(cursor.getColumnIndexOrThrow( - SubscriptionManager.NAME_SOURCE)); - int iconTint = cursor.getInt(cursor.getColumnIndexOrThrow( - SubscriptionManager.HUE)); - String number = cursor.getString(cursor.getColumnIndexOrThrow( - SubscriptionManager.NUMBER)); - int dataRoaming = cursor.getInt(cursor.getColumnIndexOrThrow( - SubscriptionManager.DATA_ROAMING)); - String mcc = cursor.getString(cursor.getColumnIndexOrThrow( - SubscriptionManager.MCC_STRING)); - String mnc = cursor.getString(cursor.getColumnIndexOrThrow( - SubscriptionManager.MNC_STRING)); - String ehplmnsRaw = cursor.getString(cursor.getColumnIndexOrThrow( - SubscriptionManager.EHPLMNS)); - String hplmnsRaw = cursor.getString(cursor.getColumnIndexOrThrow( - SubscriptionManager.HPLMNS)); - String[] ehplmns = ehplmnsRaw == null ? null : ehplmnsRaw.split(","); - String[] hplmns = hplmnsRaw == null ? null : hplmnsRaw.split(","); - - // cardId is the private ICCID/EID string, also known as the card string - String cardId = cursor.getString(cursor.getColumnIndexOrThrow( - SubscriptionManager.CARD_ID)); - String countryIso = cursor.getString(cursor.getColumnIndexOrThrow( - SubscriptionManager.ISO_COUNTRY_CODE)); - // publicCardId is the publicly exposed int card ID - int publicCardId = mUiccController.convertToPublicCardId(cardId); - boolean isEmbedded = cursor.getInt(cursor.getColumnIndexOrThrow( - SubscriptionManager.IS_EMBEDDED)) == 1; - int carrierId = cursor.getInt(cursor.getColumnIndexOrThrow( - SubscriptionManager.CARRIER_ID)); - UiccAccessRule[] accessRules; - if (isEmbedded) { - accessRules = UiccAccessRule.decodeRules(cursor.getBlob( - cursor.getColumnIndexOrThrow(SubscriptionManager.ACCESS_RULES))); - } else { - accessRules = null; - } - UiccAccessRule[] carrierConfigAccessRules = UiccAccessRule.decodeRules(cursor.getBlob( - cursor.getColumnIndexOrThrow(SubscriptionManager.ACCESS_RULES_FROM_CARRIER_CONFIGS))); - boolean isOpportunistic = cursor.getInt(cursor.getColumnIndexOrThrow( - SubscriptionManager.IS_OPPORTUNISTIC)) == 1; - String groupUUID = cursor.getString(cursor.getColumnIndexOrThrow( - SubscriptionManager.GROUP_UUID)); - int profileClass = cursor.getInt(cursor.getColumnIndexOrThrow( - SubscriptionManager.PROFILE_CLASS)); - int portIndex = cursor.getInt(cursor.getColumnIndexOrThrow( - SubscriptionManager.PORT_INDEX)); - int subType = cursor.getInt(cursor.getColumnIndexOrThrow( - SubscriptionManager.SUBSCRIPTION_TYPE)); - String groupOwner = getOptionalStringFromCursor(cursor, SubscriptionManager.GROUP_OWNER, - /*defaultVal*/ null); - boolean areUiccApplicationsEnabled = cursor.getInt(cursor.getColumnIndexOrThrow( - SubscriptionManager.UICC_APPLICATIONS_ENABLED)) == 1; - - int usageSetting = cursor.getInt(cursor.getColumnIndexOrThrow( - SubscriptionManager.USAGE_SETTING)); - - if (VDBG) { - String iccIdToPrint = SubscriptionInfo.givePrintableIccid(iccId); - String cardIdToPrint = SubscriptionInfo.givePrintableIccid(cardId); - logd("[getSubInfoRecord] id:" + id + " iccid:" + iccIdToPrint + " simSlotIndex:" - + simSlotIndex + " carrierid:" + carrierId + " displayName:" + displayName - + " nameSource:" + nameSource + " iconTint:" + iconTint - + " dataRoaming:" + dataRoaming + " mcc:" + mcc + " mnc:" + mnc - + " countIso:" + countryIso + " isEmbedded:" - + isEmbedded + " accessRules:" + Arrays.toString(accessRules) - + " carrierConfigAccessRules: " + Arrays.toString(carrierConfigAccessRules) - + " cardId:" + cardIdToPrint + " portIndex:" + portIndex - + " publicCardId:" + publicCardId - + " isOpportunistic:" + isOpportunistic + " groupUUID:" + groupUUID - + " profileClass:" + profileClass + " subscriptionType: " + subType - + " carrierConfigAccessRules:" + carrierConfigAccessRules - + " areUiccApplicationsEnabled: " + areUiccApplicationsEnabled - + " usageSetting: " + usageSetting); - } - - // If line1number has been set to a different number, use it instead. - String line1Number = mTelephonyManager.getLine1Number(id); - if (!TextUtils.isEmpty(line1Number) && !line1Number.equals(number)) { - number = line1Number; - } - // FIXME(b/210771052): constructing a complete SubscriptionInfo requires a port index, - // but the port index isn't available here. Should it actually be part of SubscriptionInfo? - SubscriptionInfo info = new SubscriptionInfo(id, iccId, simSlotIndex, displayName, - carrierName, nameSource, iconTint, number, dataRoaming, /* icon= */ null, - mcc, mnc, countryIso, isEmbedded, accessRules, cardId, publicCardId, - isOpportunistic, groupUUID, /* isGroupDisabled= */ false , carrierId, profileClass, - subType, groupOwner, carrierConfigAccessRules, areUiccApplicationsEnabled, - portIndex, usageSetting); - info.setAssociatedPlmns(ehplmns, hplmns); - return info; - } - - private String getOptionalStringFromCursor(Cursor cursor, String column, String defaultVal) { - // Return defaultVal if the column doesn't exist. - int columnIndex = cursor.getColumnIndex(column); - return (columnIndex == -1) ? defaultVal : cursor.getString(columnIndex); - } - - /** - * Get a subscription that matches IccId. - * @return null if there isn't a match, or subscription info if there is one. - */ - public SubscriptionInfo getSubInfoForIccId(String iccId) { - List<SubscriptionInfo> info = getSubInfo( - SubscriptionManager.ICC_ID + "=\'" + iccId + "\'", null); - if (info == null || info.size() == 0) return null; - // Should be at most one subscription with the iccid. - return info.get(0); - } - - /** - * Query SubInfoRecord(s) from subinfo database - * @param selection A filter declaring which rows to return - * @param queryKey query key content - * @return Array list of queried result from database - */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - public List<SubscriptionInfo> getSubInfo(String selection, Object queryKey) { - if (VDBG) logd("selection:" + selection + ", querykey: " + queryKey); - String[] selectionArgs = null; - if (queryKey != null) { - selectionArgs = new String[] {queryKey.toString()}; - } - ArrayList<SubscriptionInfo> subList = null; - Cursor cursor = mContext.getContentResolver().query(SubscriptionManager.CONTENT_URI, - null, selection, selectionArgs, null); - try { - if (cursor != null) { - while (cursor.moveToNext()) { - SubscriptionInfo subInfo = getSubInfoRecord(cursor); - if (subInfo != null) { - if (subList == null) { - subList = new ArrayList<SubscriptionInfo>(); - } - subList.add(subInfo); - } - } - } else { - if (DBG) logd("Query fail"); - } - } finally { - if (cursor != null) { - cursor.close(); - } - } - - return subList; - } - - /** - * Find unused color to be set for new SubInfoRecord - * @param callingPackage The package making the IPC. - * @param callingFeatureId The feature in the package - * @return RGB integer value of color - */ - private int getUnusedColor(String callingPackage, String callingFeatureId) { - List<SubscriptionInfo> availableSubInfos = getActiveSubscriptionInfoList(callingPackage, - callingFeatureId); - colorArr = mContext.getResources().getIntArray(com.android.internal.R.array.sim_colors); - int colorIdx = 0; - - if (availableSubInfos != null) { - for (int i = 0; i < colorArr.length; i++) { - int j; - for (j = 0; j < availableSubInfos.size(); j++) { - if (colorArr[i] == availableSubInfos.get(j).getIconTint()) { - break; - } - } - if (j == availableSubInfos.size()) { - return colorArr[i]; - } - } - colorIdx = availableSubInfos.size() % colorArr.length; - } - return colorArr[colorIdx]; - } - - @Deprecated - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - public SubscriptionInfo getActiveSubscriptionInfo(int subId, String callingPackage) { - return getActiveSubscriptionInfo(subId, callingPackage, null); - } - - /** - * Get the active SubscriptionInfo with the subId key - * @param subId The unique SubscriptionInfo key in database - * @param callingPackage The package making the IPC. - * @param callingFeatureId The feature in the package - * @return SubscriptionInfo, maybe null if its not active - */ - @Override - public SubscriptionInfo getActiveSubscriptionInfo(int subId, String callingPackage, - String callingFeatureId) { - if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(mContext, subId, callingPackage, - callingFeatureId, "getActiveSubscriptionInfo")) { - return null; - } - - // Now that all security checks passes, perform the operation as ourselves. - final long identity = Binder.clearCallingIdentity(); - List<SubscriptionInfo> subList; - try { - subList = getActiveSubscriptionInfoList( - mContext.getOpPackageName(), mContext.getAttributionTag()); - } finally { - Binder.restoreCallingIdentity(identity); - } - if (subList != null) { - for (SubscriptionInfo si : subList) { - if (si.getSubscriptionId() == subId) { - if (VDBG) { - logd("[getActiveSubscriptionInfo]+ subId=" + subId + " subInfo=" + si); - } - return conditionallyRemoveIdentifiers(si, callingPackage, callingFeatureId, - "getActiveSubscriptionInfo"); - } - } - } - if (DBG) { - logd("[getActiveSubscriptionInfo]- subId=" + subId - + " subList=" + subList + " subInfo=null"); - } - - return null; - } - - /** - * Get a single subscription info record for a given subscription. - * - * @param subId the subId to query. - * - * @hide - */ - public SubscriptionInfo getSubscriptionInfo(int subId) { - synchronized (mSubInfoListLock) { - // check cache for active subscriptions first, before querying db - for (SubscriptionInfo subInfo : mCacheActiveSubInfoList) { - if (subInfo.getSubscriptionId() == subId) { - return subInfo; - } - } - - // check cache for opportunistic subscriptions too, before querying db - for (SubscriptionInfo subInfo : mCacheOpportunisticSubInfoList) { - if (subInfo.getSubscriptionId() == subId) { - return subInfo; - } - } - } - - List<SubscriptionInfo> subInfoList = getSubInfo( - SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=" + subId, null); - if (subInfoList == null || subInfoList.isEmpty()) return null; - return subInfoList.get(0); - } - - /** - * Get the active SubscriptionInfo associated with the iccId - * @param iccId the IccId of SIM card - * @param callingPackage The package making the IPC. - * @param callingFeatureId The feature in the package - * @return SubscriptionInfo, maybe null if its not active - */ - @Override - public SubscriptionInfo getActiveSubscriptionInfoForIccId(String iccId, String callingPackage, - String callingFeatureId) { - enforceReadPrivilegedPhoneState("getActiveSubscriptionInfoForIccId"); - return getActiveSubscriptionInfoForIccIdInternal(iccId); - } - - /** - * Get the active SubscriptionInfo associated with the given iccId. The caller *must* perform - * permission checks when using this method. - */ - private SubscriptionInfo getActiveSubscriptionInfoForIccIdInternal(String iccId) { - if (iccId == null) { - return null; - } - - final long identity = Binder.clearCallingIdentity(); - try { - List<SubscriptionInfo> subList = getActiveSubscriptionInfoList( - mContext.getOpPackageName(), mContext.getAttributionTag()); - if (subList != null) { - for (SubscriptionInfo si : subList) { - if (iccId.equals(si.getIccId())) { - if (DBG) - logd("[getActiveSubInfoUsingIccId]+ iccId=" + iccId + " subInfo=" + si); - return si; - } - } - } - if (DBG) { - logd("[getActiveSubInfoUsingIccId]+ iccId=" + iccId - + " subList=" + subList + " subInfo=null"); - } - } finally { - Binder.restoreCallingIdentity(identity); - } - - return null; - } - - /** - * Get the active SubscriptionInfo associated with the slotIndex. - * This API does not return details on Remote-SIM subscriptions. - * @param slotIndex the slot which the subscription is inserted - * @param callingPackage The package making the IPC. - * @param callingFeatureId The feature in the package - * @return SubscriptionInfo, null for Remote-SIMs or non-active slotIndex. - */ - @Override - public SubscriptionInfo getActiveSubscriptionInfoForSimSlotIndex(int slotIndex, - String callingPackage, String callingFeatureId) { - Phone phone = PhoneFactory.getPhone(slotIndex); - if (phone == null) { - if (DBG) { - loge("[getActiveSubscriptionInfoForSimSlotIndex] no phone, slotIndex=" + slotIndex); - } - return null; - } - if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState( - mContext, phone.getSubId(), callingPackage, callingFeatureId, - "getActiveSubscriptionInfoForSimSlotIndex")) { - return null; - } - - // Now that all security checks passes, perform the operation as ourselves. - final long identity = Binder.clearCallingIdentity(); - List<SubscriptionInfo> subList; - try { - subList = getActiveSubscriptionInfoList( - mContext.getOpPackageName(), mContext.getAttributionTag()); - } finally { - Binder.restoreCallingIdentity(identity); - } - if (subList != null) { - for (SubscriptionInfo si : subList) { - if (si.getSimSlotIndex() == slotIndex) { - if (DBG) { - logd("[getActiveSubscriptionInfoForSimSlotIndex]+ slotIndex=" - + slotIndex + " subId=" + si); - } else { - logd("[getActiveSubscriptionInfoForSimSlotIndex]+ slotIndex=" - + slotIndex + " subId=" + conditionallyRemoveIdentifiers(si, false, - false)); - } - return conditionallyRemoveIdentifiers(si, callingPackage, callingFeatureId, - "getActiveSubscriptionInfoForSimSlotIndex"); - } - } - if (DBG) { - logd("[getActiveSubscriptionInfoForSimSlotIndex]+ slotIndex=" + slotIndex - + " subId=null"); - } - } else { - if (DBG) { - logd("[getActiveSubscriptionInfoForSimSlotIndex]+ subList=null"); - } - } - - - return null; - } - - /** - * @param callingPackage The package making the IPC. - * @param callingFeatureId The feature in the package - * @return List of all SubscriptionInfo records in database, - * include those that were inserted before, maybe empty but not null. - * @hide - */ - @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 - // about carrier-privileged callers not having access. - if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState( - mContext, SubscriptionManager.INVALID_SUBSCRIPTION_ID, callingPackage, - callingFeatureId, "getAllSubInfoList")) { - return null; - } - - // Now that all security checks passes, perform the operation as ourselves. - final long identity = Binder.clearCallingIdentity(); - List<SubscriptionInfo> subList; - try { - subList = getSubInfo(null, null); - } 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) { - synchronized (mSubInfoListLock) { - return new ArrayList<>(cacheSubList); - } - } - - @Deprecated - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - public List<SubscriptionInfo> getActiveSubscriptionInfoList(String callingPackage) { - return getSubscriptionInfoListFromCacheHelper(callingPackage, null, - makeCacheListCopyWithLock(mCacheActiveSubInfoList)); - } - - /** - * Get the SubInfoRecord(s) of the currently active SIM(s) - which include both local - * and remote SIMs. - * @param callingPackage The package making the IPC. - * @param callingFeatureId The feature in the package - * @return Array list of currently inserted SubInfoRecord(s) - */ - @Override - public List<SubscriptionInfo> getActiveSubscriptionInfoList(String callingPackage, - String callingFeatureId) { - return getSubscriptionInfoListFromCacheHelper(callingPackage, callingFeatureId, - makeCacheListCopyWithLock(mCacheActiveSubInfoList)); - } - - /** - * Refresh the cache of SubInfoRecord(s) of the currently available SIM(s) - including - * local & remote SIMs. - */ - @VisibleForTesting // For mockito to mock this method - public void refreshCachedActiveSubscriptionInfoList() { - boolean opptSubListChanged; - - List<SubscriptionInfo> activeSubscriptionInfoList = getSubInfo( - SubscriptionManager.SIM_SLOT_INDEX + ">=0 OR " - + SubscriptionManager.SUBSCRIPTION_TYPE + "=" - + SubscriptionManager.SUBSCRIPTION_TYPE_REMOTE_SIM, - null); - - synchronized (mSubInfoListLock) { - if (activeSubscriptionInfoList != null) { - // Log when active sub info changes. - if (mCacheActiveSubInfoList.size() != activeSubscriptionInfoList.size() - || !mCacheActiveSubInfoList.containsAll(activeSubscriptionInfoList)) { - logdl("Active subscription info list changed. " + activeSubscriptionInfoList); - } - - mCacheActiveSubInfoList.clear(); - activeSubscriptionInfoList.sort(SUBSCRIPTION_INFO_COMPARATOR); - mCacheActiveSubInfoList.addAll(activeSubscriptionInfoList); - } else { - logd("activeSubscriptionInfoList is null."); - mCacheActiveSubInfoList.clear(); - } - if (DBG_CACHE) { - if (!mCacheActiveSubInfoList.isEmpty()) { - for (SubscriptionInfo si : mCacheActiveSubInfoList) { - logd("[refreshCachedActiveSubscriptionInfoList] Setting Cached info=" - + si); - } - } else { - logdl("[refreshCachedActiveSubscriptionInfoList]- no info return"); - } - } - } - - // Refresh cached opportunistic sub list and detect whether it's changed. - refreshCachedOpportunisticSubscriptionInfoList(); - } - - @Deprecated - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - public int getActiveSubInfoCount(String callingPackage) { - return getActiveSubInfoCount(callingPackage, null); - } - - /** - * Get the SUB count of active SUB(s) - * @param callingPackage The package making the IPC. - * @param callingFeatureId The feature in the package. - * @return active SIM count - */ - @Override - public int getActiveSubInfoCount(String callingPackage, String callingFeatureId) { - // Let getActiveSubscriptionInfoList perform permission checks / filtering. - List<SubscriptionInfo> records = getActiveSubscriptionInfoList(callingPackage, - callingFeatureId); - if (records == null) { - if (VDBG) logd("[getActiveSubInfoCount] records null"); - return 0; - } - if (VDBG) logd("[getActiveSubInfoCount]- count: " + records.size()); - return records.size(); - } - - /** - * Get the SUB count of all SUB(s) in SubscriptoinInfo database - * @param callingPackage The package making the IPC. - * @param callingFeatureId The feature in the package - * @return all SIM count in database, include what was inserted before - */ - @Override - public int getAllSubInfoCount(String callingPackage, String callingFeatureId) { - if (DBG) logd("[getAllSubInfoCount]+"); - - // This API isn't public, so no need to provide a valid subscription ID - we're not worried - // about carrier-privileged callers not having access. - if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState( - mContext, SubscriptionManager.INVALID_SUBSCRIPTION_ID, callingPackage, - callingFeatureId, "getAllSubInfoCount")) { - return 0; - } - - // Now that all security checks passes, perform the operation as ourselves. - final long identity = Binder.clearCallingIdentity(); - try { - Cursor cursor = mContext.getContentResolver().query(SubscriptionManager.CONTENT_URI, - null, null, null, null); - try { - if (cursor != null) { - int count = cursor.getCount(); - if (DBG) logd("[getAllSubInfoCount]- " + count + " SUB(s) in DB"); - return count; - } - } finally { - if (cursor != null) { - cursor.close(); - } - } - if (DBG) logd("[getAllSubInfoCount]- no SUB in DB"); - - return 0; - } finally { - Binder.restoreCallingIdentity(identity); - } - } - - /** - * @return the maximum number of local subscriptions this device will support at any one time. - */ - @Override - public int getActiveSubInfoCountMax() { - // FIXME: This valid now but change to use TelephonyDevController in the future - return mTelephonyManager.getSimCount(); - } - - @Override - public List<SubscriptionInfo> getAvailableSubscriptionInfoList(String callingPackage, - String callingFeatureId) { - try { - enforceReadPrivilegedPhoneState("getAvailableSubscriptionInfoList"); - } catch (SecurityException e) { - try { - mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_PHONE_STATE, null); - // If caller doesn't have READ_PRIVILEGED_PHONE_STATE permission but only - // has READ_PHONE_STATE permission, log this event. - EventLog.writeEvent(0x534e4554, "185235454", Binder.getCallingUid()); - } catch (SecurityException ex) { - // Ignore - } - throw new SecurityException("Need READ_PRIVILEGED_PHONE_STATE to call " - + " getAvailableSubscriptionInfoList"); - } - - // Now that all security checks pass, perform the operation as ourselves. - final long identity = Binder.clearCallingIdentity(); - try { - String selection = SubscriptionManager.SIM_SLOT_INDEX + ">=0 OR " - + SubscriptionManager.SUBSCRIPTION_TYPE + "=" - + SubscriptionManager.SUBSCRIPTION_TYPE_REMOTE_SIM; - - EuiccManager euiccManager = - (EuiccManager) mContext.getSystemService(Context.EUICC_SERVICE); - if (euiccManager.isEnabled()) { - selection += " OR " + SubscriptionManager.IS_EMBEDDED + "=1"; - } - - // Available eSIM profiles are reported by EuiccManager. However for physical SIMs if - // they are in inactive slot or programmatically disabled, they are still considered - // available. In this case we get their iccid from slot info and include their - // subscriptionInfos. - List<String> iccIds = getIccIdsOfInsertedPhysicalSims(); - - if (!iccIds.isEmpty()) { - selection += " OR (" + getSelectionForIccIdList(iccIds.toArray(new String[0])) - + ")"; - } - - List<SubscriptionInfo> subList = getSubInfo(selection, null /* queryKey */); - - if (subList != null) { - subList.sort(SUBSCRIPTION_INFO_COMPARATOR); - - if (VDBG) logdl("[getAvailableSubInfoList]- " + subList.size() + " infos return"); - } else { - if (DBG) logdl("[getAvailableSubInfoList]- no info return"); - } - - return subList; - } finally { - Binder.restoreCallingIdentity(identity); - } - } - - private List<String> getIccIdsOfInsertedPhysicalSims() { - List<String> ret = new ArrayList<>(); - UiccSlot[] uiccSlots = UiccController.getInstance().getUiccSlots(); - if (uiccSlots == null) return ret; - - for (UiccSlot uiccSlot : uiccSlots) { - if (uiccSlot != null && uiccSlot.getCardState() != null - && uiccSlot.getCardState().isCardPresent() - && !uiccSlot.isEuicc()) { - // Non euicc slots will have single port, so use default port index. - String iccId = uiccSlot.getIccId(TelephonyManager.DEFAULT_PORT_INDEX); - if (!TextUtils.isEmpty(iccId)) { - ret.add(IccUtils.stripTrailingFs(iccId)); - } - } - } - - return ret; - } - - @Override - public List<SubscriptionInfo> getAccessibleSubscriptionInfoList(String callingPackage) { - EuiccManager euiccManager = (EuiccManager) mContext.getSystemService(Context.EUICC_SERVICE); - if (!euiccManager.isEnabled()) { - if (DBG) { - logdl("[getAccessibleSubInfoList] Embedded subscriptions are disabled"); - } - return null; - } - - // Verify that the given package belongs to the calling UID. - mAppOps.checkPackage(Binder.getCallingUid(), callingPackage); - - // Perform the operation as ourselves. If the caller cannot read phone state, they may still - // have carrier privileges per the subscription metadata, so we always need to make the - // query and then filter the results. - final long identity = Binder.clearCallingIdentity(); - List<SubscriptionInfo> subList; - try { - subList = getSubInfo(SubscriptionManager.IS_EMBEDDED + "=1", null); - } finally { - Binder.restoreCallingIdentity(identity); - } - - if (subList == null) { - if (DBG) logdl("[getAccessibleSubInfoList] No info returned"); - return null; - } - - // Filter the list to only include subscriptions which the (restored) caller can manage. - List<SubscriptionInfo> filteredList = subList.stream() - .filter(subscriptionInfo -> - subscriptionInfo.canManageSubscription(mContext, callingPackage)) - .sorted(SUBSCRIPTION_INFO_COMPARATOR) - .collect(Collectors.toList()); - if (VDBG) { - logdl("[getAccessibleSubInfoList] " + filteredList.size() + " infos returned"); - } - return filteredList; - } - - /** - * Return the list of subscriptions in the database which are either: - * <ul> - * <li>Embedded (but see note about {@code includeNonRemovableSubscriptions}, or - * <li>In the given list of current embedded ICCIDs (which may not yet be in the database, or - * which may not currently be marked as embedded). - * </ul> - * - * <p>NOTE: This is not accessible to external processes, so it does not need a permission - * check. It is only intended for use by {@link SubscriptionInfoUpdater}. - * - * @param embeddedIccids all ICCIDs of available embedded subscriptions. This is used to surface - * entries for profiles which had been previously deleted. - * @param isEuiccRemovable whether the current ICCID is removable. Non-removable subscriptions - * will only be returned if the current ICCID is not removable; otherwise, they are left - * alone (not returned here unless in the embeddedIccids list) under the assumption that - * they will still be accessible when the eUICC containing them is activated. - */ - @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) - public List<SubscriptionInfo> getSubscriptionInfoListForEmbeddedSubscriptionUpdate( - String[] embeddedIccids, boolean isEuiccRemovable) { - StringBuilder whereClause = new StringBuilder(); - whereClause.append("(").append(SubscriptionManager.IS_EMBEDDED).append("=1"); - if (isEuiccRemovable) { - // Current eUICC is removable, so don't return non-removable subscriptions (which would - // be deleted), as these are expected to still be present on a different, non-removable - // eUICC. - whereClause.append(" AND ").append(SubscriptionManager.IS_REMOVABLE).append("=1"); - } - // Else, return both removable and non-removable subscriptions. This is expected to delete - // all removable subscriptions, which is desired as they may not be accessible. - - whereClause.append(") OR ").append(SubscriptionManager.ICC_ID).append(" IN ("); - // ICCIDs are validated to contain only numbers when passed in, and come from a trusted - // app, so no need to escape. - for (int i = 0; i < embeddedIccids.length; i++) { - if (i > 0) { - whereClause.append(","); - } - whereClause.append("'").append(embeddedIccids[i]).append("'"); - } - whereClause.append(")"); - - List<SubscriptionInfo> list = getSubInfo(whereClause.toString(), null); - if (list == null) { - return Collections.emptyList(); - } - return list; - } - - @Override - public void requestEmbeddedSubscriptionInfoListRefresh(int cardId) { - mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS, - "requestEmbeddedSubscriptionInfoListRefresh"); - long token = Binder.clearCallingIdentity(); - try { - PhoneFactory.requestEmbeddedSubscriptionInfoListRefresh(cardId, null /* callback */); - } finally { - Binder.restoreCallingIdentity(token); - } - } - - /** - * Asynchronously refresh the embedded subscription info list for the embedded card has the - * given card id {@code cardId}. - * - * @param callback Optional callback to execute after the refresh completes. Must terminate - * quickly as it will be called from SubscriptionInfoUpdater's handler thread. - */ - // No permission check needed as this is not exposed via AIDL. - public void requestEmbeddedSubscriptionInfoListRefresh( - int cardId, @Nullable Runnable callback) { - PhoneFactory.requestEmbeddedSubscriptionInfoListRefresh(cardId, callback); - } - - /** - * Asynchronously refresh the embedded subscription info list for the embedded card has the - * default card id return by {@link TelephonyManager#getCardIdForDefaultEuicc()}. - * - * @param callback Optional callback to execute after the refresh completes. Must terminate - * quickly as it will be called from SubscriptionInfoUpdater's handler thread. - */ - // No permission check needed as this is not exposed via AIDL. - public void requestEmbeddedSubscriptionInfoListRefresh(@Nullable Runnable callback) { - PhoneFactory.requestEmbeddedSubscriptionInfoListRefresh( - mTelephonyManager.getCardIdForDefaultEuicc(), callback); - } - - /** - * Add a new SubInfoRecord to subinfo database if needed - * @param iccId the IccId of the SIM card - * @param slotIndex the slot which the SIM is inserted - * @return 0 if success, < 0 on error. - */ - @Override - public int addSubInfoRecord(String iccId, int slotIndex) { - return addSubInfo(iccId, null, slotIndex, SubscriptionManager.SUBSCRIPTION_TYPE_LOCAL_SIM); - } - - /** - * Add a new subscription info record, if needed. - * @param uniqueId This is the unique identifier for the subscription within the specific - * subscription type. - * @param displayName human-readable name of the device the subscription corresponds to. - * @param slotIndex value for {@link SubscriptionManager#SIM_SLOT_INDEX} - * @param subscriptionType the type of subscription to be added. - * @return 0 if success, < 0 on error. - */ - @Override - public int addSubInfo(String uniqueId, String displayName, int slotIndex, - int subscriptionType) { - if (DBG) { - String iccIdStr = uniqueId; - if (!isSubscriptionForRemoteSim(subscriptionType)) { - iccIdStr = SubscriptionInfo.givePrintableIccid(uniqueId); - } - logdl("[addSubInfoRecord]+ iccid: " + iccIdStr - + ", slotIndex: " + slotIndex - + ", subscriptionType: " + subscriptionType); - } - - enforceModifyPhoneState("addSubInfo"); - - // Now that all security checks passes, perform the operation as ourselves. - final long identity = Binder.clearCallingIdentity(); - try { - if (uniqueId == null) { - if (DBG) logdl("[addSubInfo]- null iccId"); - return -1; - } - - ContentResolver resolver = mContext.getContentResolver(); - String selection = SubscriptionManager.ICC_ID + "=?"; - String[] args; - if (isSubscriptionForRemoteSim(subscriptionType)) { - PackageManager packageManager = mContext.getPackageManager(); - if (!packageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) { - logel("[addSubInfo] Remote SIM can only be added when FEATURE_AUTOMOTIVE" - + " is supported"); - return -1; - } - selection += " AND " + SubscriptionManager.SUBSCRIPTION_TYPE + "=?"; - args = new String[]{uniqueId, Integer.toString(subscriptionType)}; - } else { - selection += " OR " + SubscriptionManager.ICC_ID + "=?"; - args = new String[]{uniqueId, IccUtils.getDecimalSubstring(uniqueId)}; - } - Cursor cursor = resolver.query(SubscriptionManager.CONTENT_URI, - new String[]{SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID, - SubscriptionManager.SIM_SLOT_INDEX, SubscriptionManager.NAME_SOURCE, - SubscriptionManager.ICC_ID, SubscriptionManager.CARD_ID, - SubscriptionManager.PORT_INDEX}, - selection, args, null); - - boolean setDisplayName = false; - try { - boolean recordsDoNotExist = (cursor == null || !cursor.moveToFirst()); - if (isSubscriptionForRemoteSim(subscriptionType)) { - if (recordsDoNotExist) { - // create a Subscription record - slotIndex = SubscriptionManager.SLOT_INDEX_FOR_REMOTE_SIM_SUB; - Uri uri = insertEmptySubInfoRecord(uniqueId, displayName, - slotIndex, subscriptionType); - if (DBG) logd("[addSubInfoRecord] New record created: " + uri); - } else { - if (DBG) logdl("[addSubInfoRecord] Record already exists"); - } - } else { // Handle Local SIM devices - if (recordsDoNotExist) { - setDisplayName = true; - Uri uri = insertEmptySubInfoRecord(uniqueId, slotIndex); - if (DBG) logdl("[addSubInfoRecord] New record created: " + uri); - } else { // there are matching records in the database for the given ICC_ID - int subId = cursor.getInt(0); - int oldSimInfoId = cursor.getInt(1); - int nameSource = cursor.getInt(2); - String oldIccId = cursor.getString(3); - String oldCardId = cursor.getString(4); - int oldPortIndex = cursor.getInt(5); - ContentValues value = new ContentValues(); - - if (slotIndex != oldSimInfoId) { - value.put(SubscriptionManager.SIM_SLOT_INDEX, slotIndex); - } - - if (oldIccId != null && oldIccId.length() < uniqueId.length() - && (oldIccId.equals(IccUtils.getDecimalSubstring(uniqueId)))) { - value.put(SubscriptionManager.ICC_ID, uniqueId); - } - - UiccCard card = mUiccController.getUiccCardForPhone(slotIndex); - if (card != null) { - String cardId = card.getCardId(); - if (cardId != null && cardId != oldCardId) { - value.put(SubscriptionManager.CARD_ID, cardId); - } - } - - //update portIndex for pSim - UiccSlot slot = mUiccController.getUiccSlotForPhone(slotIndex); - if (slot != null && !slot.isEuicc()) { - int portIndex = slot.getPortIndexFromIccId(uniqueId); - if (portIndex != oldPortIndex) { - value.put(SubscriptionManager.PORT_INDEX, portIndex); - } - } - - if (value.size() > 0) { - resolver.update(SubscriptionManager.getUriForSubscriptionId(subId), - value, null, null); - } - - if (DBG) logdl("[addSubInfoRecord] Record already exists"); - } - } - } finally { - if (cursor != null) { - cursor.close(); - } - } - - selection = SubscriptionManager.SIM_SLOT_INDEX + "=?"; - args = new String[] {String.valueOf(slotIndex)}; - if (isSubscriptionForRemoteSim(subscriptionType)) { - selection = SubscriptionManager.ICC_ID + "=? AND " - + SubscriptionManager.SUBSCRIPTION_TYPE + "=?"; - args = new String[]{uniqueId, Integer.toString(subscriptionType)}; - } - cursor = resolver.query(SubscriptionManager.CONTENT_URI, null, - selection, args, null); - try { - if (cursor != null && cursor.moveToFirst()) { - do { - int subId = cursor.getInt(cursor.getColumnIndexOrThrow( - SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID)); - // If sSlotIndexToSubIds already has the same subId for a slotIndex/phoneId, - // do not add it. - if (addToSubIdList(slotIndex, subId, subscriptionType)) { - // TODO While two subs active, if user deactivats first - // one, need to update the default subId with second one. - - // FIXME: Currently we assume phoneId == slotIndex which in the future - // may not be true, for instance with multiple subs per slot. - // But is true at the moment. - int subIdCountMax = getActiveSubInfoCountMax(); - int defaultSubId = getDefaultSubId(); - if (DBG) { - logdl("[addSubInfoRecord]" - + " sSlotIndexToSubIds.size=" + sSlotIndexToSubIds.size() - + " slotIndex=" + slotIndex + " subId=" + subId - + " defaultSubId=" + defaultSubId - + " simCount=" + subIdCountMax); - } - - // Set the default sub if not set or if single sim device - if (!isSubscriptionForRemoteSim(subscriptionType)) { - if (!SubscriptionManager.isValidSubscriptionId(defaultSubId) - || subIdCountMax == 1) { - logdl("setting default fallback subid to " + subId); - setDefaultFallbackSubId(subId, subscriptionType); - } - // If single sim device, set this subscription as the default for - // everything - if (subIdCountMax == 1) { - if (DBG) { - logdl("[addSubInfoRecord] one sim set defaults to subId=" - + subId); - } - setDefaultDataSubId(subId); - setDefaultSmsSubId(subId); - setDefaultVoiceSubId(subId); - } - } else { - updateDefaultSubIdsIfNeeded(subId, subscriptionType); - } - } else { - if (DBG) { - logdl("[addSubInfoRecord] current SubId is already known, " - + "IGNORE"); - } - } - if (DBG) { - logdl("[addSubInfoRecord] hashmap(" + slotIndex + "," + subId + ")"); - } - } while (cursor.moveToNext()); - } - } finally { - if (cursor != null) { - cursor.close(); - } - } - - // Refresh the Cache of Active Subscription Info List. This should be done after - // updating sSlotIndexToSubIds which is done through addToSubIdList() above. - refreshCachedActiveSubscriptionInfoList(); - - if (isSubscriptionForRemoteSim(subscriptionType)) { - notifySubscriptionInfoChanged(); - } else { // Handle Local SIM devices - // Set Display name after sub id is set above so as to get valid simCarrierName - int subId = getSubIdUsingPhoneId(slotIndex); - if (!SubscriptionManager.isValidSubscriptionId(subId)) { - if (DBG) { - logdl("[addSubInfoRecord]- getSubId failed invalid subId = " + subId); - } - return -1; - } - if (setDisplayName) { - String simCarrierName = mTelephonyManager.getSimOperatorName(subId); - String nameToSet; - - if (!TextUtils.isEmpty(simCarrierName)) { - nameToSet = simCarrierName; - } else { - nameToSet = "CARD " + Integer.toString(slotIndex + 1); - } - - ContentValues value = new ContentValues(); - value.put(SubscriptionManager.DISPLAY_NAME, nameToSet); - resolver.update(SubscriptionManager.getUriForSubscriptionId(subId), value, - null, null); - - // Refresh the Cache of Active Subscription Info List - refreshCachedActiveSubscriptionInfoList(); - - if (DBG) logdl("[addSubInfoRecord] sim name = " + nameToSet); - } - - if (DBG) logdl("[addSubInfoRecord]- info size=" + sSlotIndexToSubIds.size()); - } - - } finally { - Binder.restoreCallingIdentity(identity); - } - return 0; - } - - private void updateDefaultSubIdsIfNeeded(int newDefault, int subscriptionType) { - if (DBG) { - logdl("[updateDefaultSubIdsIfNeeded] newDefault=" + newDefault - + ", subscriptionType=" + subscriptionType); - } - // Set the default ot new value only if the current default is invalid. - if (!isActiveSubscriptionId(getDefaultSubId())) { - // current default is not valid anylonger. set a new default - if (DBG) { - logdl("[updateDefaultSubIdsIfNeeded] set sDefaultFallbackSubId=" + newDefault); - } - setDefaultFallbackSubId(newDefault, subscriptionType); - } - - int value = getDefaultSmsSubId(); - if (!isActiveSubscriptionId(value)) { - // current default is not valid. set it to the given newDefault value - setDefaultSmsSubId(newDefault); - } - value = getDefaultDataSubId(); - if (!isActiveSubscriptionId(value)) { - setDefaultDataSubId(newDefault); - } - value = getDefaultVoiceSubId(); - if (!isActiveSubscriptionId(value)) { - setDefaultVoiceSubId(newDefault); - } - } - - /** - * This method returns true if the given subId is among the list of currently active - * subscriptions. - */ - private boolean isActiveSubscriptionId(int subId) { - if (!SubscriptionManager.isValidSubscriptionId(subId)) return false; - ArrayList<Integer> subIdList = getActiveSubIdArrayList(); - if (subIdList.isEmpty()) return false; - return subIdList.contains(new Integer(subId)); - } - - /* - * Delete subscription info record for the given device. - * @param uniqueId This is the unique identifier for the subscription within the specific - * subscription type. - * @param subscriptionType the type of subscription to be removed - * @return 0 if success, < 0 on error. - */ - @Override - public int removeSubInfo(String uniqueId, int subscriptionType) { - enforceModifyPhoneState("removeSubInfo"); - if (DBG) { - logd("[removeSubInfo] uniqueId: " + uniqueId - + ", subscriptionType: " + subscriptionType); - } - - // validate the given info - does it exist in the active subscription list - int subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; - int slotIndex = SubscriptionManager.INVALID_SIM_SLOT_INDEX; - synchronized (mSubInfoListLock) { - for (SubscriptionInfo info : mCacheActiveSubInfoList) { - if ((info.getSubscriptionType() == subscriptionType) - && info.getIccId().equalsIgnoreCase(uniqueId)) { - subId = info.getSubscriptionId(); - slotIndex = info.getSimSlotIndex(); - break; - } - } - } - if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) { - if (DBG) { - logd("Invalid subscription details: subscriptionType = " + subscriptionType - + ", uniqueId = " + uniqueId); - } - return -1; - } - - if (DBG) logd("removing the subid : " + subId); - - // Now that all security checks passes, perform the operation as ourselves. - int result = 0; - final long identity = Binder.clearCallingIdentity(); - try { - ContentResolver resolver = mContext.getContentResolver(); - result = resolver.delete(SubscriptionManager.CONTENT_URI, - SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=? AND " - + SubscriptionManager.SUBSCRIPTION_TYPE + "=?", - new String[]{Integer.toString(subId), Integer.toString(subscriptionType)}); - if (result != 1) { - if (DBG) { - logd("found NO subscription to remove with subscriptionType = " - + subscriptionType + ", uniqueId = " + uniqueId); - } - return -1; - } - refreshCachedActiveSubscriptionInfoList(); - result = sSlotIndexToSubIds.removeFromSubIdList(slotIndex, subId); - if (result == NO_ENTRY_FOR_SLOT_INDEX) { - loge("sSlotIndexToSubIds has no entry for slotIndex = " + slotIndex); - } else if (result == SUB_ID_NOT_IN_SLOT) { - loge("sSlotIndexToSubIds has no subid: " + subId + ", in index: " + slotIndex); - } - - // Since a subscription is removed, if this one is set as default for any setting, - // set some other subid as the default. - int newDefault = SubscriptionManager.INVALID_SUBSCRIPTION_ID; - SubscriptionInfo info = null; - final List<SubscriptionInfo> records = getActiveSubscriptionInfoList( - mContext.getOpPackageName(), mContext.getAttributionTag()); - if (!records.isEmpty()) { - // yes, we have more subscriptions. pick the first one. - // FIXME do we need a policy to figure out which one is to be next default - info = records.get(0); - } - updateDefaultSubIdsIfNeeded(info.getSubscriptionId(), info.getSubscriptionType()); - - notifySubscriptionInfoChanged(); - } finally { - Binder.restoreCallingIdentity(identity); - } - return result; - } - - /** - * Clear an subscriptionInfo to subinfo database if needed by updating slot index to invalid. - * @param slotIndex the slot which the SIM is removed - */ - public void clearSubInfoRecord(int slotIndex) { - if (DBG) logdl("[clearSubInfoRecord]+ iccId:" + " slotIndex:" + slotIndex); - - // update simInfo db with invalid slot index - ContentResolver resolver = mContext.getContentResolver(); - ContentValues value = new ContentValues(1); - value.put(SubscriptionManager.SIM_SLOT_INDEX, SubscriptionManager.INVALID_SIM_SLOT_INDEX); - String where = "(" + SubscriptionManager.SIM_SLOT_INDEX + "=" + slotIndex + ")"; - resolver.update(SubscriptionManager.CONTENT_URI, value, where, null); - - // Refresh the Cache of Active Subscription Info List - refreshCachedActiveSubscriptionInfoList(); - - sSlotIndexToSubIds.remove(slotIndex); - } - - /** - * Insert an empty SubInfo record into the database. - * - * <p>NOTE: This is not accessible to external processes, so it does not need a permission - * check. It is only intended for use by {@link SubscriptionInfoUpdater}. If there is a - * subscription record exist with the same ICCID, no new empty record will be created. - * - * @return the URL of the newly created row. Return <code>null</code> if no new empty record is - * created. - */ - @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) - @Nullable - public Uri insertEmptySubInfoRecord(String iccId, int slotIndex) { - if (getSubInfoForIccId(iccId) != null) { - loge("insertEmptySubInfoRecord: Found existing record by ICCID. Do not create a " - + "new empty entry."); - return null; - } - return insertEmptySubInfoRecord(iccId, null, slotIndex, - SubscriptionManager.SUBSCRIPTION_TYPE_LOCAL_SIM); - } - - Uri insertEmptySubInfoRecord(String uniqueId, String displayName, int slotIndex, - int subscriptionType) { - ContentResolver resolver = mContext.getContentResolver(); - ContentValues value = new ContentValues(); - value.put(SubscriptionManager.ICC_ID, uniqueId); - int color = getUnusedColor(mContext.getOpPackageName(), mContext.getAttributionTag()); - // default SIM color differs between slots - value.put(SubscriptionManager.HUE, color); - value.put(SubscriptionManager.SIM_SLOT_INDEX, slotIndex); - value.put(SubscriptionManager.CARRIER_NAME, ""); - value.put(SubscriptionManager.CARD_ID, uniqueId); - value.put(SubscriptionManager.SUBSCRIPTION_TYPE, subscriptionType); - if (!TextUtils.isEmpty(displayName)) { - value.put(SubscriptionManager.DISPLAY_NAME, displayName); - } - if (!isSubscriptionForRemoteSim(subscriptionType)) { - UiccCard card = mUiccController.getUiccCardForPhone(slotIndex); - if (card != null) { - String cardId = card.getCardId(); - if (cardId != null) { - value.put(SubscriptionManager.CARD_ID, cardId); - } - } - UiccSlot slot = mUiccController.getUiccSlotForPhone(slotIndex); - if (slot != null) { - value.put(SubscriptionManager.PORT_INDEX, slot.getPortIndexFromIccId(uniqueId)); - } - } - value.put(SubscriptionManager.ALLOWED_NETWORK_TYPES, - "user=" + RadioAccessFamily.getRafFromNetworkType( - RILConstants.PREFERRED_NETWORK_MODE)); - - value.put(SubscriptionManager.USAGE_SETTING, - SubscriptionManager.USAGE_SETTING_UNKNOWN); - - Uri uri = resolver.insert(SubscriptionManager.CONTENT_URI, value); - - // Refresh the Cache of Active Subscription Info List - refreshCachedActiveSubscriptionInfoList(); - - return uri; - } - - /** - * Generate and set carrier text based on input parameters - * @param showPlmn flag to indicate if plmn should be included in carrier text - * @param plmn plmn to be included in carrier text - * @param showSpn flag to indicate if spn should be included in carrier text - * @param spn spn to be included in carrier text - * @return true if carrier text is set, false otherwise - */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - public boolean setPlmnSpn(int slotIndex, boolean showPlmn, String plmn, boolean showSpn, - String spn) { - synchronized (mLock) { - int subId = getSubIdUsingPhoneId(slotIndex); - if (mContext.getPackageManager().resolveContentProvider( - SubscriptionManager.CONTENT_URI.getAuthority(), 0) == null || - !SubscriptionManager.isValidSubscriptionId(subId)) { - // No place to store this info. Notify registrants of the change anyway as they - // might retrieve the SPN/PLMN text from the SST sticky broadcast. - // TODO: This can be removed once SubscriptionController is not running on devices - // that don't need it, such as TVs. - if (DBG) logd("[setPlmnSpn] No valid subscription to store info"); - notifySubscriptionInfoChanged(); - return false; - } - String carrierText = ""; - if (showPlmn) { - carrierText = plmn; - if (showSpn) { - // Need to show both plmn and spn if both are not same. - if(!Objects.equals(spn, plmn)) { - String separator = mContext.getString( - com.android.internal.R.string.kg_text_message_separator).toString(); - carrierText = new StringBuilder().append(carrierText).append(separator) - .append(spn).toString(); - } - } - } else if (showSpn) { - carrierText = spn; - } - setCarrierText(carrierText, subId); - return true; - } - } - - /** - * Set carrier text by simInfo index - * @param text new carrier text - * @param subId the unique SubInfoRecord index in database - * @return the number of records updated - */ - private int setCarrierText(String text, int subId) { - if (DBG) logd("[setCarrierText]+ text:" + text + " subId:" + subId); - - enforceModifyPhoneState("setCarrierText"); - - // Now that all security checks passes, perform the operation as ourselves. - final long identity = Binder.clearCallingIdentity(); - try { - boolean update = true; - int result = 0; - SubscriptionInfo subInfo = getSubscriptionInfo(subId); - if (subInfo != null) { - update = !TextUtils.equals(text, subInfo.getCarrierName()); - } - if (update) { - ContentValues value = new ContentValues(1); - value.put(SubscriptionManager.CARRIER_NAME, text); - - result = mContext.getContentResolver().update( - SubscriptionManager.getUriForSubscriptionId(subId), value, null, null); - - // Refresh the Cache of Active Subscription Info List - refreshCachedActiveSubscriptionInfoList(); - - notifySubscriptionInfoChanged(); - } else { - if (DBG) logd("[setCarrierText]: no value update"); - } - return result; - } finally { - Binder.restoreCallingIdentity(identity); - } - } - - /** - * Set SIM color tint by simInfo index - * @param tint the tint color of the SIM - * @param subId the unique SubInfoRecord index in database - * @return the number of records updated - */ - @Override - public int setIconTint(int tint, int subId) { - if (DBG) logd("[setIconTint]+ tint:" + tint + " subId:" + subId); - - enforceModifyPhoneState("setIconTint"); - - // Now that all security checks passes, perform the operation as ourselves. - final long identity = Binder.clearCallingIdentity(); - try { - validateSubId(subId); - ContentValues value = new ContentValues(1); - value.put(SubscriptionManager.HUE, tint); - if (DBG) logd("[setIconTint]- tint:" + tint + " set"); - - int result = mContext.getContentResolver().update( - SubscriptionManager.getUriForSubscriptionId(subId), value, null, null); - - // Refresh the Cache of Active Subscription Info List - refreshCachedActiveSubscriptionInfoList(); - - notifySubscriptionInfoChanged(); - - return result; - } finally { - Binder.restoreCallingIdentity(identity); - } - } - - /** - * This is only for internal use and the returned priority is arbitrary. The idea is to give a - * higher value to name source that has higher priority to override other name sources. - * @param nameSource Source of display name - * @return int representing the priority. Higher value means higher priority. - */ - public static int getNameSourcePriority(@SimDisplayNameSource int nameSource) { - int index = Arrays.asList( - SubscriptionManager.NAME_SOURCE_CARRIER_ID, - SubscriptionManager.NAME_SOURCE_SIM_PNN, - SubscriptionManager.NAME_SOURCE_SIM_SPN, - SubscriptionManager.NAME_SOURCE_CARRIER, - SubscriptionManager.NAME_SOURCE_USER_INPUT // user has highest priority. - ).indexOf(nameSource); - return (index < 0) ? 0 : index; - } - - /** - * Validate whether the NAME_SOURCE_SIM_PNN, NAME_SOURCE_SIM_SPN and - * NAME_SOURCE_CARRIER exist or not. - */ - @VisibleForTesting - public boolean isExistingNameSourceStillValid(SubscriptionInfo subInfo) { - - int subId = subInfo.getSubscriptionId(); - int phoneId = getPhoneId(subInfo.getSubscriptionId()); - - Phone phone = PhoneFactory.getPhone(phoneId); - if (phone == null) { - return true; - } - - String spn; - - switch (subInfo.getNameSource()) { - case SubscriptionManager.NAME_SOURCE_SIM_PNN: - String pnn = phone.getPlmn(); - return !TextUtils.isEmpty(pnn); - case SubscriptionManager.NAME_SOURCE_SIM_SPN: - spn = getServiceProviderName(phoneId); - return !TextUtils.isEmpty(spn); - case SubscriptionManager.NAME_SOURCE_CARRIER: - // Can not validate eSIM since it should not override with a lower priority source - // if the name is actually coming from eSIM and not from carrier config. - if (subInfo.isEmbedded()) { - return true; - } - CarrierConfigManager configLoader = - mContext.getSystemService(CarrierConfigManager.class); - PersistableBundle config = - configLoader.getConfigForSubId(subId); - if (config == null) { - return true; - } - boolean isCarrierNameOverride = config.getBoolean( - CarrierConfigManager.KEY_CARRIER_NAME_OVERRIDE_BOOL, false); - String carrierName = config.getString( - CarrierConfigManager.KEY_CARRIER_NAME_STRING); - spn = getServiceProviderName(phoneId); - return isCarrierNameOverride - || (TextUtils.isEmpty(spn) && !TextUtils.isEmpty(carrierName)); - case SubscriptionManager.NAME_SOURCE_CARRIER_ID: - case SubscriptionManager.NAME_SOURCE_USER_INPUT: - return true; - } - return false; - } - - @VisibleForTesting - public String getServiceProviderName(int phoneId) { - UiccProfile profile = mUiccController.getUiccProfileForPhone(phoneId); - if (profile == null) { - return null; - } - return profile.getServiceProviderName(); - } - - /** - * Set display name by simInfo index with name source - * @param displayName the display name of SIM card - * @param subId the unique SubInfoRecord index in database - * @param nameSource SIM display name source - * @return the number of records updated - */ - @Override - public int setDisplayNameUsingSrc(String displayName, int subId, - @SimDisplayNameSource int nameSource) { - if (DBG) { - logd("[setDisplayName]+ displayName:" + displayName + " subId:" + subId - + " nameSource:" + nameSource); - } - - enforceModifyPhoneState("setDisplayNameUsingSrc"); - - // Now that all security checks passes, perform the operation as ourselves. - final long identity = Binder.clearCallingIdentity(); - try { - validateSubId(subId); - List<SubscriptionInfo> allSubInfo = getSubInfo(null, null); - // if there is no sub in the db, return 0 since subId does not exist in db - if (allSubInfo == null || allSubInfo.isEmpty()) return 0; - for (SubscriptionInfo subInfo : allSubInfo) { - int subInfoNameSource = subInfo.getNameSource(); - boolean isHigherPriority = (getNameSourcePriority(subInfoNameSource) - > getNameSourcePriority(nameSource)); - boolean isEqualPriorityAndName = (getNameSourcePriority(subInfoNameSource) - == getNameSourcePriority(nameSource)) - && (TextUtils.equals(displayName, subInfo.getDisplayName())); - if (subInfo.getSubscriptionId() == subId - && isExistingNameSourceStillValid(subInfo) - && (isHigherPriority || isEqualPriorityAndName)) { - logd("Name source " + subInfoNameSource + "'s priority " - + getNameSourcePriority(subInfoNameSource) + " is greater than " - + "name source " + nameSource + "'s priority " - + getNameSourcePriority(nameSource) + ", return now."); - return 0; - } - } - String nameToSet; - if (TextUtils.isEmpty(displayName) || displayName.trim().length() == 0) { - nameToSet = mTelephonyManager.getSimOperatorName(subId); - if (TextUtils.isEmpty(nameToSet)) { - if (nameSource == SubscriptionManager.NAME_SOURCE_USER_INPUT - && SubscriptionManager.isValidSlotIndex(getSlotIndex(subId))) { - nameToSet = "CARD " + (getSlotIndex(subId) + 1); - } else { - nameToSet = mContext.getString(SubscriptionManager.DEFAULT_NAME_RES); - } - } - } else { - nameToSet = displayName; - } - ContentValues value = new ContentValues(1); - value.put(SubscriptionManager.DISPLAY_NAME, nameToSet); - if (nameSource >= SubscriptionManager.NAME_SOURCE_CARRIER_ID) { - if (DBG) logd("Set nameSource=" + nameSource); - value.put(SubscriptionManager.NAME_SOURCE, nameSource); - } - if (DBG) logd("[setDisplayName]- mDisplayName:" + nameToSet + " set"); - - // Update the nickname on the eUICC chip if it's an embedded subscription. - SubscriptionInfo sub = getSubscriptionInfo(subId); - if (sub != null && sub.isEmbedded()) { - // Ignore the result. - int cardId = sub.getCardId(); - if (DBG) logd("Updating embedded sub nickname on cardId: " + cardId); - EuiccManager euiccManager = ((EuiccManager) - mContext.getSystemService(Context.EUICC_SERVICE)).createForCardId(cardId); - euiccManager.updateSubscriptionNickname(subId, displayName, - // This PendingIntent simply fulfills the requirement to pass in a callback; - // we don't care about the result (hence 0 requestCode and no action - // specified on the intent). - PendingIntent.getService( - mContext, 0 /* requestCode */, new Intent(), - PendingIntent.FLAG_IMMUTABLE /* flags */)); - } - - int result = updateDatabase(value, subId, true); - - // Refresh the Cache of Active Subscription Info List - refreshCachedActiveSubscriptionInfoList(); - - notifySubscriptionInfoChanged(); - - return result; - } finally { - Binder.restoreCallingIdentity(identity); - } - } - - /** - * Set phone number by subId - * @param number the phone number of the SIM - * @param subId the unique SubInfoRecord index in database - * @return the number of records updated - */ - @Override - public int setDisplayNumber(String number, int subId) { - if (DBG) logd("[setDisplayNumber]+ subId:" + subId); - - enforceModifyPhoneState("setDisplayNumber"); - - // Now that all security checks passes, perform the operation as ourselves. - final long identity = Binder.clearCallingIdentity(); - try { - validateSubId(subId); - int result = 0; - int phoneId = getPhoneId(subId); - - if (number == null || phoneId < 0 || - phoneId >= mTelephonyManager.getPhoneCount()) { - if (DBG) logd("[setDisplayNumber]- fail"); - return -1; - } - boolean update = true; - SubscriptionInfo subInfo = getSubscriptionInfo(subId); - if (subInfo != null) { - update = !TextUtils.equals(subInfo.getNumber(), number); - } - if (update) { - ContentValues value = new ContentValues(1); - value.put(SubscriptionManager.NUMBER, number); - - // This function had a call to update number on the SIM (Phone.setLine1Number()) but - // that was removed as there doesn't seem to be a reason for that. If it is added - // back, watch out for deadlocks. - result = mContext.getContentResolver().update( - SubscriptionManager.getUriForSubscriptionId(subId), value, null, null); - if (DBG) logd("[setDisplayNumber]- update result :" + result); - // Refresh the Cache of Active Subscription Info List - refreshCachedActiveSubscriptionInfoList(); - notifySubscriptionInfoChanged(); - } else { - if (DBG) logd("[setDisplayNumber]: no value update"); - } - return result; - } finally { - Binder.restoreCallingIdentity(identity); - } - } - - /** - * Set the EHPLMNs and HPLMNs associated with the subscription. - */ - public void setAssociatedPlmns(String[] ehplmns, String[] hplmns, int subId) { - if (DBG) logd("[setAssociatedPlmns]+ subId:" + subId); - - validateSubId(subId); - int phoneId = getPhoneId(subId); - - if (phoneId < 0 || phoneId >= mTelephonyManager.getPhoneCount()) { - if (DBG) logd("[setAssociatedPlmns]- fail"); - return; - } - - // remove trailing empty strings which will also get stripped from - // SubscriptionInfo.getEhplmns() and SubscriptionInfo.getHplmns() - String formattedEhplmns = ehplmns == null ? "" : - Arrays.stream(ehplmns).filter(s -> s != null && !s.isEmpty()) - .collect(Collectors.joining(",")); - String formattedHplmns = hplmns == null ? "" : - Arrays.stream(hplmns).filter(s -> s != null && !s.isEmpty()) - .collect(Collectors.joining(",")); - boolean noChange = false; - SubscriptionInfo subInfo = getSubscriptionInfo(subId); - if (subInfo != null) { - noChange = (ehplmns == null && subInfo.getEhplmns().isEmpty()) - || String.join(",", subInfo.getEhplmns()).equals(formattedEhplmns); - noChange = noChange && (hplmns == null && subInfo.getHplmns().isEmpty()) - || String.join(",", subInfo.getHplmns()).equals(formattedHplmns); - } - if (!noChange) { - ContentValues value = new ContentValues(2); - value.put(SubscriptionManager.EHPLMNS, formattedEhplmns); - value.put(SubscriptionManager.HPLMNS, formattedHplmns); - - int count = mContext.getContentResolver().update( - SubscriptionManager.getUriForSubscriptionId(subId), value, null, null); - if (DBG) logd("[setAssociatedPlmns]- update result :" + count); - // Refresh the Cache of Active Subscription Info List - refreshCachedActiveSubscriptionInfoList(); - notifySubscriptionInfoChanged(); - } else { - if (DBG) logd("[setAssociatedPlmns]+ subId:" + subId + "no value update"); - } - } - - /** - * Set data roaming by simInfo index - * @param roaming 0:Don't allow data when roaming, 1:Allow data when roaming - * @param subId the unique SubInfoRecord index in database - * @return the number of records updated - */ - @Override - public int setDataRoaming(int roaming, int subId) { - if (DBG) logd("[setDataRoaming]+ roaming:" + roaming + " subId:" + subId); - - enforceModifyPhoneState("setDataRoaming"); - - // Now that all security checks passes, perform the operation as ourselves. - final long identity = Binder.clearCallingIdentity(); - try { - validateSubId(subId); - if (roaming < 0) { - if (DBG) logd("[setDataRoaming]- fail"); - return -1; - } - ContentValues value = new ContentValues(1); - value.put(SubscriptionManager.DATA_ROAMING, roaming); - if (DBG) logd("[setDataRoaming]- roaming:" + roaming + " set"); - - int result = updateDatabase(value, subId, true); - - // Refresh the Cache of Active Subscription Info List - refreshCachedActiveSubscriptionInfoList(); - - notifySubscriptionInfoChanged(); - - return result; - } finally { - Binder.restoreCallingIdentity(identity); - } - } - - /** - * Set device to device status sharing preference - * @param sharing the sharing preference to set - * @param subId - * @return the number of records updated - */ - @Override - public int setDeviceToDeviceStatusSharing(int sharing, int subId) { - if (DBG) logd("[setDeviceToDeviceStatusSharing]- sharing:" + sharing + " subId:" + subId); - - enforceModifyPhoneState("setDeviceToDeviceStatusSharing"); - - // Now that all security checks passes, perform the operation as ourselves. - final long identity = Binder.clearCallingIdentity(); - try { - validateSubId(subId); - if (sharing < 0) { - if (DBG) logd("[setDeviceToDeviceStatusSharing]- fail"); - return -1; - } - ContentValues value = new ContentValues(1); - value.put(SubscriptionManager.D2D_STATUS_SHARING, sharing); - if (DBG) logd("[setDeviceToDeviceStatusSharing]- sharing:" + sharing + " set"); - - int result = updateDatabase(value, subId, true); - - // Refresh the Cache of Active Subscription Info List - refreshCachedActiveSubscriptionInfoList(); - - notifySubscriptionInfoChanged(); - - return result; - } finally { - Binder.restoreCallingIdentity(identity); - } - } - - /** - * Set contacts that allow device to device status sharing. - * @param contacts contacts to set - * @param subscriptionId - * @return the number of records updated - */ - @Override - public int setDeviceToDeviceStatusSharingContacts(String contacts, int subscriptionId) { - if (DBG) { - logd("[setDeviceToDeviceStatusSharingContacts]- contacts:" + contacts - + " subId:" + subscriptionId); - } - - enforceModifyPhoneState("setDeviceToDeviceStatusSharingContacts"); - - // Now that all security checks passes, perform the operation as ourselves. - final long identity = Binder.clearCallingIdentity(); - try { - validateSubId(subscriptionId); - ContentValues value = new ContentValues(1); - value.put(SubscriptionManager.D2D_STATUS_SHARING_SELECTED_CONTACTS, contacts); - if (DBG) { - logd("[setDeviceToDeviceStatusSharingContacts]- contacts:" + contacts - + " set"); - } - - int result = updateDatabase(value, subscriptionId, true); - - // Refresh the Cache of Active Subscription Info List - refreshCachedActiveSubscriptionInfoList(); - - notifySubscriptionInfoChanged(); - - return result; - } finally { - Binder.restoreCallingIdentity(identity); - } - } - - public void syncGroupedSetting(int refSubId) { - logd("syncGroupedSetting"); - try (Cursor cursor = mContext.getContentResolver().query( - SubscriptionManager.CONTENT_URI, null, - SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=?", - new String[] {String.valueOf(refSubId)}, null)) { - if (cursor == null || !cursor.moveToFirst()) { - logd("[syncGroupedSetting] failed. Can't find refSubId " + refSubId); - return; - } - - ContentValues values = new ContentValues(GROUP_SHARING_PROPERTIES.size()); - for (String propKey : GROUP_SHARING_PROPERTIES) { - copyDataFromCursorToContentValue(propKey, cursor, values); - } - updateDatabase(values, refSubId, true); - } - } - - private void copyDataFromCursorToContentValue(String propKey, Cursor cursor, - ContentValues values) { - int columnIndex = cursor.getColumnIndex(propKey); - if (columnIndex == -1) { - logd("[copyDataFromCursorToContentValue] can't find column " + propKey); - return; - } - - switch (propKey) { - case SubscriptionManager.ENHANCED_4G_MODE_ENABLED: - case SubscriptionManager.VT_IMS_ENABLED: - case SubscriptionManager.WFC_IMS_ENABLED: - case SubscriptionManager.WFC_IMS_MODE: - case SubscriptionManager.WFC_IMS_ROAMING_MODE: - case SubscriptionManager.WFC_IMS_ROAMING_ENABLED: - case SubscriptionManager.DATA_ROAMING: - case SubscriptionManager.IMS_RCS_UCE_ENABLED: - case SubscriptionManager.CROSS_SIM_CALLING_ENABLED: - case SubscriptionManager.NR_ADVANCED_CALLING_ENABLED: - values.put(propKey, cursor.getInt(columnIndex)); - break; - case SubscriptionManager.DISPLAY_NAME: - case SubscriptionManager.DATA_ENABLED_OVERRIDE_RULES: - values.put(propKey, cursor.getString(columnIndex)); - break; - default: - loge("[copyDataFromCursorToContentValue] invalid propKey " + propKey); - } - } - - // TODO: replace all updates with this helper method. - private int updateDatabase(ContentValues value, int subId, boolean updateEntireGroup) { - List<SubscriptionInfo> infoList = getSubscriptionsInGroup(getGroupUuid(subId), - mContext.getOpPackageName(), mContext.getAttributionTag()); - if (!updateEntireGroup || infoList == null || infoList.size() == 0) { - // Only update specified subscriptions. - return mContext.getContentResolver().update( - SubscriptionManager.getUriForSubscriptionId(subId), value, null, null); - } else { - // Update all subscriptions in the same group. - int[] subIdList = new int[infoList.size()]; - for (int i = 0; i < infoList.size(); i++) { - subIdList[i] = infoList.get(i).getSubscriptionId(); - } - return mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI, - value, getSelectionForSubIdList(subIdList), null); - } - } - - /** - * Set carrier id by subId - * @param carrierId the subscription carrier id. - * @param subId the unique SubInfoRecord index in database - * @return the number of records updated - * - * @see TelephonyManager#getSimCarrierId() - */ - public int setCarrierId(int carrierId, int subId) { - if (DBG) logd("[setCarrierId]+ carrierId:" + carrierId + " subId:" + subId); - - enforceModifyPhoneState("setCarrierId"); - - // Now that all security checks passes, perform the operation as ourselves. - final long identity = Binder.clearCallingIdentity(); - try { - validateSubId(subId); - int result = 0; - boolean update = true; - SubscriptionInfo subInfo = getSubscriptionInfo(subId); - if (subInfo != null) { - update = subInfo.getCarrierId() != carrierId; - } - if (update) { - ContentValues value = new ContentValues(1); - value.put(SubscriptionManager.CARRIER_ID, carrierId); - result = mContext.getContentResolver().update( - SubscriptionManager.getUriForSubscriptionId(subId), value, null, null); - - // Refresh the Cache of Active Subscription Info List - refreshCachedActiveSubscriptionInfoList(); - - notifySubscriptionInfoChanged(); - } else { - if (DBG) logd("[setCarrierId]: no value update"); - } - return result; - } finally { - Binder.restoreCallingIdentity(identity); - } - } - - /** - * Set MCC/MNC by subscription ID - * @param mccMnc MCC/MNC associated with the subscription - * @param subId the unique SubInfoRecord index in database - * @return the number of records updated - */ - public int setMccMnc(String mccMnc, int subId) { - String mccString = mccMnc.substring(0, 3); - String mncString = mccMnc.substring(3); - int mcc = 0; - int mnc = 0; - try { - mcc = Integer.parseInt(mccString); - mnc = Integer.parseInt(mncString); - } catch (NumberFormatException e) { - loge("[setMccMnc] - couldn't parse mcc/mnc: " + mccMnc); - } - SubscriptionInfo subInfo = getSubscriptionInfo(subId); - // check if there are any update - boolean update = true; - if (subInfo != null) { - update = (subInfo.getMcc() != mcc) || (subInfo.getMnc() != mnc) - || !mccString.equals(subInfo.getMccString()) - || !mncString.equals(subInfo.getMncString()); - } - int result = 0; - if (update) { - ContentValues value = new ContentValues(4); - value.put(SubscriptionManager.MCC, mcc); - value.put(SubscriptionManager.MNC, mnc); - value.put(SubscriptionManager.MCC_STRING, mccString); - value.put(SubscriptionManager.MNC_STRING, mncString); - - result = mContext.getContentResolver().update( - SubscriptionManager.getUriForSubscriptionId(subId), value, null, null); - if (DBG) logd("[setMccMnc]+ mcc/mnc:" + mcc + "/" + mnc + " subId:" + subId); - // Refresh the Cache of Active Subscription Info List - refreshCachedActiveSubscriptionInfoList(); - notifySubscriptionInfoChanged(); - } else { - if (DBG) logd("[setMccMnc] - no values update"); - } - return result; - } - - /** - * Scrub given IMSI on production builds. - */ - private String scrubImsi(String imsi) { - if (Build.IS_ENG) { - return imsi; - } else if (imsi != null) { - return imsi.substring(0, Math.min(6, imsi.length())) + "..."; - } else { - return "null"; - } - } - - /** - * Set IMSI by subscription ID - * @param imsi IMSI (International Mobile Subscriber Identity) - * @return the number of records updated - */ - public int setImsi(String imsi, int subId) { - if (DBG) logd("[setImsi]+ imsi:" + scrubImsi(imsi) + " subId:" + subId); - boolean update = true; - int result = 0; - SubscriptionInfo subInfo = getSubscriptionInfo(subId); - if (subInfo != null) { - update = !TextUtils.equals(getImsiPrivileged(subId),imsi); - } - - if (update) { - ContentValues value = new ContentValues(1); - value.put(SubscriptionManager.IMSI, imsi); - result = mContext.getContentResolver().update( - SubscriptionManager.getUriForSubscriptionId(subId), value, null, null); - // Refresh the Cache of Active Subscription Info List - refreshCachedActiveSubscriptionInfoList(); - - notifySubscriptionInfoChanged(); - } else { - if (DBG) logd("[setImsi]: no value update"); - } - return result; - } - - /** - * Set uicc applications being enabled or disabled. - * @param enabled whether uicc applications are enabled or disabled. - * @return the number of records updated - */ - public int setUiccApplicationsEnabled(boolean enabled, int subId) { - if (DBG) logd("[setUiccApplicationsEnabled]+ enabled:" + enabled + " subId:" + subId); - - enforceModifyPhoneState("setUiccApplicationsEnabled"); - - long identity = Binder.clearCallingIdentity(); - try { - ContentValues value = new ContentValues(1); - value.put(SubscriptionManager.UICC_APPLICATIONS_ENABLED, enabled); - - int result = mContext.getContentResolver().update( - SubscriptionManager.getUriForSubscriptionId(subId), value, null, null); - - // Refresh the Cache of Active Subscription Info List - refreshCachedActiveSubscriptionInfoList(); - - notifyUiccAppsEnableChanged(); - notifySubscriptionInfoChanged(); - - return result; - } finally { - Binder.restoreCallingIdentity(identity); - } - } - - /** - * Register to change of uicc applications enablement changes. - * @param notifyNow whether to notify target upon registration. - */ - public void registerForUiccAppsEnabled(Handler handler, int what, Object object, - boolean notifyNow) { - mUiccAppsEnableChangeRegList.addUnique(handler, what, object); - if (notifyNow) { - handler.obtainMessage(what, object).sendToTarget(); - } - } - - /** - * Unregister to change of uicc applications enablement changes. - */ - public void unregisterForUiccAppsEnabled(Handler handler) { - mUiccAppsEnableChangeRegList.remove(handler); - } - - private void notifyUiccAppsEnableChanged() { - mUiccAppsEnableChangeRegList.notifyRegistrants(); - } - - /** - * Get IMSI by subscription ID - * For active subIds, this will always return the corresponding imsi - * For inactive subIds, once they are activated once, even if they are deactivated at the time - * of calling this function, the corresponding imsi will be returned - * When calling this method, the permission check should have already been done to allow - * only privileged read - * - * @return imsi - */ - public String getImsiPrivileged(int subId) { - try (Cursor cursor = mContext.getContentResolver().query( - SubscriptionManager.CONTENT_URI, null, - SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=?", - new String[] {String.valueOf(subId)}, null)) { - String imsi = null; - if (cursor != null) { - if (cursor.moveToNext()) { - imsi = getOptionalStringFromCursor(cursor, SubscriptionManager.IMSI, - /*defaultVal*/ null); - } - } else { - logd("getImsiPrivileged: failed to retrieve imsi."); - } - - return imsi; - } - } - - /** - * Set ISO country code by subscription ID - * @param iso iso country code associated with the subscription - * @param subId the unique SubInfoRecord index in database - * @return the number of records updated - */ - public int setCountryIso(String iso, int subId) { - if (DBG) logd("[setCountryIso]+ iso:" + iso + " subId:" + subId); - boolean update = true; - int result = 0; - SubscriptionInfo subInfo = getSubscriptionInfo(subId); - if (subInfo != null) { - update = !TextUtils.equals(subInfo.getCountryIso(), iso); - } - if (update) { - ContentValues value = new ContentValues(); - value.put(SubscriptionManager.ISO_COUNTRY_CODE, iso); - - result = mContext.getContentResolver().update( - SubscriptionManager.getUriForSubscriptionId(subId), value, null, null); - // Refresh the Cache of Active Subscription Info List - refreshCachedActiveSubscriptionInfoList(); - - notifySubscriptionInfoChanged(); - } else { - if (DBG) logd("[setCountryIso]: no value update"); - } - return result; - } - - @Override - public int getSlotIndex(int subId) { - if (VDBG) printStackTrace("[getSlotIndex] subId=" + subId); - - if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) { - subId = getDefaultSubId(); - } - if (!SubscriptionManager.isValidSubscriptionId(subId)) { - if (DBG) logd("[getSlotIndex]- subId invalid"); - return SubscriptionManager.INVALID_SIM_SLOT_INDEX; - } - - int size = sSlotIndexToSubIds.size(); - - if (size == 0) { - if (DBG) logd("[getSlotIndex]- size == 0, return SIM_NOT_INSERTED instead"); - return SubscriptionManager.SIM_NOT_INSERTED; - } - - for (Entry<Integer, ArrayList<Integer>> entry : sSlotIndexToSubIds.entrySet()) { - int sim = entry.getKey(); - ArrayList<Integer> subs = entry.getValue(); - - if (subs != null && subs.contains(subId)) { - if (VDBG) logv("[getSlotIndex]- return = " + sim); - return sim; - } - } - - if (DBG) logd("[getSlotIndex]- return fail"); - return SubscriptionManager.INVALID_SIM_SLOT_INDEX; - } - - /** - * Return the subId for specified slot Id. - * @deprecated - */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - @Override - @Deprecated - public int[] getSubId(int slotIndex) { - if (VDBG) printStackTrace("[getSubId]+ slotIndex=" + slotIndex); - - // Map default slotIndex to the current default subId. - // TODO: Not used anywhere sp consider deleting as it's somewhat nebulous - // as a slot maybe used for multiple different type of "connections" - // such as: voice, data and sms. But we're doing the best we can and using - // getDefaultSubId which makes a best guess. - if (slotIndex == SubscriptionManager.DEFAULT_SIM_SLOT_INDEX) { - slotIndex = getSlotIndex(getDefaultSubId()); - if (VDBG) logd("[getSubId] map default slotIndex=" + slotIndex); - } - - // Check that we have a valid slotIndex or the slotIndex is for a remote SIM (remote SIM - // uses special slot index that may be invalid otherwise) - if (!SubscriptionManager.isValidSlotIndex(slotIndex) - && slotIndex != SubscriptionManager.SLOT_INDEX_FOR_REMOTE_SIM_SUB) { - if (DBG) logd("[getSubId]- invalid slotIndex=" + slotIndex); - return null; - } - - // Check if we've got any SubscriptionInfo records using slotIndexToSubId as a surrogate. - int size = sSlotIndexToSubIds.size(); - if (size == 0) { - if (VDBG) { - logd("[getSubId]- sSlotIndexToSubIds.size == 0, return null slotIndex=" - + slotIndex); - } - return null; - } - - // Convert ArrayList to array - ArrayList<Integer> subIds = sSlotIndexToSubIds.getCopy(slotIndex); - if (subIds != null && subIds.size() > 0) { - int[] subIdArr = new int[subIds.size()]; - for (int i = 0; i < subIds.size(); i++) { - subIdArr[i] = subIds.get(i); - } - if (VDBG) logd("[getSubId]- subIdArr=" + subIdArr); - return subIdArr; - } else { - if (DBG) logd("[getSubId]- numSubIds == 0, return null slotIndex=" + slotIndex); - return null; - } - } - - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - @Override - public int getPhoneId(int subId) { - if (VDBG) printStackTrace("[getPhoneId] subId=" + subId); - int phoneId; - - if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) { - subId = getDefaultSubId(); - if (DBG) logd("[getPhoneId] asked for default subId=" + subId); - } - - if (!SubscriptionManager.isValidSubscriptionId(subId)) { - if (VDBG) { - logdl("[getPhoneId]- invalid subId return=" - + SubscriptionManager.INVALID_PHONE_INDEX); - } - return SubscriptionManager.INVALID_PHONE_INDEX; - } - - int size = sSlotIndexToSubIds.size(); - if (size == 0) { - phoneId = mDefaultPhoneId; - if (VDBG) logdl("[getPhoneId]- no sims, returning default phoneId=" + phoneId); - return phoneId; - } - - // FIXME: Assumes phoneId == slotIndex - for (Entry<Integer, ArrayList<Integer>> entry: sSlotIndexToSubIds.entrySet()) { - int sim = entry.getKey(); - ArrayList<Integer> subs = entry.getValue(); - - if (subs != null && subs.contains(subId)) { - if (VDBG) logdl("[getPhoneId]- found subId=" + subId + " phoneId=" + sim); - return sim; - } - } - - phoneId = mDefaultPhoneId; - if (VDBG) { - logd("[getPhoneId]- subId=" + subId + " not found return default phoneId=" + phoneId); - } - return phoneId; - - } - - /** - * @return the number of records cleared - */ - @Override - public int clearSubInfo() { - enforceModifyPhoneState("clearSubInfo"); - - // Now that all security checks passes, perform the operation as ourselves. - final long identity = Binder.clearCallingIdentity(); - try { - int size = sSlotIndexToSubIds.size(); - - if (size == 0) { - if (DBG) logdl("[clearSubInfo]- no simInfo size=" + size); - return 0; - } - - sSlotIndexToSubIds.clear(); - if (DBG) logdl("[clearSubInfo]- clear size=" + size); - return size; - } finally { - Binder.restoreCallingIdentity(identity); - } - } - - private void logvl(String msg) { - logv(msg); - mLocalLog.log(msg); - } - - private void logv(String msg) { - Rlog.v(LOG_TAG, msg); - } - - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - protected void logdl(String msg) { - logd(msg); - mLocalLog.log(msg); - } - - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - private void logd(String msg) { - Rlog.d(LOG_TAG, msg); - } - - private void logel(String msg) { - loge(msg); - mLocalLog.log(msg); - } - - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - private void loge(String msg) { - Rlog.e(LOG_TAG, msg); - } - - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - @Override - public int getDefaultSubId() { - int subId; - boolean isVoiceCapable = mTelephonyManager.isVoiceCapable(); - if (isVoiceCapable) { - subId = getDefaultVoiceSubId(); - if (VDBG) logdl("[getDefaultSubId] isVoiceCapable subId=" + subId); - } else { - subId = getDefaultDataSubId(); - if (VDBG) logdl("[getDefaultSubId] NOT VoiceCapable subId=" + subId); - } - if (!isActiveSubId(subId)) { - subId = sDefaultFallbackSubId.get(); - if (VDBG) logdl("[getDefaultSubId] NOT active use fall back subId=" + subId); - } - if (VDBG) logv("[getDefaultSubId]- value = " + subId); - return subId; - } - - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - @Override - public void setDefaultSmsSubId(int subId) { - enforceModifyPhoneState("setDefaultSmsSubId"); - - if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) { - throw new RuntimeException("setDefaultSmsSubId called with DEFAULT_SUB_ID"); - } - if (DBG) logdl("[setDefaultSmsSubId] subId=" + subId); - setGlobalSetting(Settings.Global.MULTI_SIM_SMS_SUBSCRIPTION, subId); - broadcastDefaultSmsSubIdChanged(subId); - } - - private void broadcastDefaultSmsSubIdChanged(int subId) { - // Broadcast an Intent for default sms sub change - if (DBG) logdl("[broadcastDefaultSmsSubIdChanged] subId=" + subId); - Intent intent = new Intent(SubscriptionManager.ACTION_DEFAULT_SMS_SUBSCRIPTION_CHANGED); - intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); - SubscriptionManager.putSubscriptionIdExtra(intent, subId); - mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); - } - - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - @Override - public int getDefaultSmsSubId() { - int subId = Settings.Global.getInt(mContext.getContentResolver(), - Settings.Global.MULTI_SIM_SMS_SUBSCRIPTION, - SubscriptionManager.INVALID_SUBSCRIPTION_ID); - if (VDBG) logd("[getDefaultSmsSubId] subId=" + subId); - return subId; - } - - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - @Override - public void setDefaultVoiceSubId(int subId) { - enforceModifyPhoneState("setDefaultVoiceSubId"); - - if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) { - throw new RuntimeException("setDefaultVoiceSubId called with DEFAULT_SUB_ID"); - } - - logdl("[setDefaultVoiceSubId] subId=" + subId); - - int previousDefaultSub = getDefaultSubId(); - - setGlobalSetting(Settings.Global.MULTI_SIM_VOICE_CALL_SUBSCRIPTION, subId); - broadcastDefaultVoiceSubIdChanged(subId); - - PhoneAccountHandle newHandle = - subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID - ? null : mTelephonyManager.getPhoneAccountHandleForSubscriptionId( - subId); - - TelecomManager telecomManager = mContext.getSystemService(TelecomManager.class); - PhoneAccountHandle currentHandle = telecomManager.getUserSelectedOutgoingPhoneAccount(); - logd("[setDefaultVoiceSubId] current phoneAccountHandle=" + currentHandle); - - if (!Objects.equals(currentHandle, newHandle)) { - telecomManager.setUserSelectedOutgoingPhoneAccount(newHandle); - logd("[setDefaultVoiceSubId] change to phoneAccountHandle=" + newHandle); - } else { - logd("[setDefaultVoiceSubId] default phoneAccountHandle not changed."); - } - - if (previousDefaultSub != getDefaultSubId()) { - sendDefaultChangedBroadcast(getDefaultSubId()); - logd(String.format("[setDefaultVoiceSubId] change to subId=%d", getDefaultSubId())); - } else { - logd(String.format("[setDefaultVoiceSubId] default subId not changed. subId=%d", - previousDefaultSub)); - } - } - - /** - * Broadcast intent of ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED. - * @hide - */ - @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) - public void broadcastDefaultVoiceSubIdChanged(int subId) { - // Broadcast an Intent for default voice sub change - if (DBG) logdl("[broadcastDefaultVoiceSubIdChanged] subId=" + subId); - Intent intent = new Intent(TelephonyIntents.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED); - intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); - SubscriptionManager.putSubscriptionIdExtra(intent, subId); - mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); - } - - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - @Override - public int getDefaultVoiceSubId() { - int subId = Settings.Global.getInt(mContext.getContentResolver(), - Settings.Global.MULTI_SIM_VOICE_CALL_SUBSCRIPTION, - SubscriptionManager.INVALID_SUBSCRIPTION_ID); - if (VDBG) logd("[getDefaultVoiceSubId] subId=" + subId); - return subId; - } - - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - @Override - public int getDefaultDataSubId() { - int subId = Settings.Global.getInt(mContext.getContentResolver(), - Settings.Global.MULTI_SIM_DATA_CALL_SUBSCRIPTION, - SubscriptionManager.INVALID_SUBSCRIPTION_ID); - if (VDBG) logd("[getDefaultDataSubId] subId=" + subId); - return subId; - } - - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - @Override - public void setDefaultDataSubId(int subId) { - enforceModifyPhoneState("setDefaultDataSubId"); - - final long identity = Binder.clearCallingIdentity(); - try { - if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) { - throw new RuntimeException("setDefaultDataSubId called with DEFAULT_SUB_ID"); - } - - ProxyController proxyController = ProxyController.getInstance(); - int len = TelephonyManager.from(mContext).getActiveModemCount(); - logdl("[setDefaultDataSubId] num phones=" + len + ", subId=" + subId); - - if (SubscriptionManager.isValidSubscriptionId(subId)) { - // Only re-map modems if the new default data sub is valid - RadioAccessFamily[] rafs = new RadioAccessFamily[len]; - boolean atLeastOneMatch = false; - for (int phoneId = 0; phoneId < len; phoneId++) { - Phone phone = PhoneFactory.getPhone(phoneId); - int raf; - int id = phone.getSubId(); - if (id == subId) { - // TODO Handle the general case of N modems and M subscriptions. - raf = proxyController.getMaxRafSupported(); - atLeastOneMatch = true; - } else { - // TODO Handle the general case of N modems and M subscriptions. - raf = proxyController.getMinRafSupported(); - } - logdl("[setDefaultDataSubId] phoneId=" + phoneId + " subId=" + id + " RAF=" - + raf); - rafs[phoneId] = new RadioAccessFamily(phoneId, raf); - } - if (atLeastOneMatch) { - proxyController.setRadioCapability(rafs); - } else { - if (DBG) logdl("[setDefaultDataSubId] no valid subId's found - not updating."); - } - } - - int previousDefaultSub = getDefaultSubId(); - setGlobalSetting(Settings.Global.MULTI_SIM_DATA_CALL_SUBSCRIPTION, subId); - MultiSimSettingController.getInstance().notifyDefaultDataSubChanged(); - broadcastDefaultDataSubIdChanged(subId); - if (previousDefaultSub != getDefaultSubId()) { - sendDefaultChangedBroadcast(getDefaultSubId()); - } - } finally { - Binder.restoreCallingIdentity(identity); - } - } - - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - private void broadcastDefaultDataSubIdChanged(int subId) { - // Broadcast an Intent for default data sub change - if (DBG) logdl("[broadcastDefaultDataSubIdChanged] subId=" + subId); - Intent intent = new Intent(TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED); - intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); - SubscriptionManager.putSubscriptionIdExtra(intent, subId); - mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); - } - - /* Sets the default subscription. If only one sub is active that - * sub is set as default subId. If two or more sub's are active - * the first sub is set as default subscription - */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - protected void setDefaultFallbackSubId(int subId, int subscriptionType) { - if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) { - throw new RuntimeException("setDefaultSubId called with DEFAULT_SUB_ID"); - } - if (DBG) { - logdl("[setDefaultFallbackSubId] subId=" + subId + ", subscriptionType=" - + subscriptionType); - } - int previousDefaultSub = getDefaultSubId(); - if (isSubscriptionForRemoteSim(subscriptionType)) { - sDefaultFallbackSubId.set(subId); - return; - } - if (SubscriptionManager.isValidSubscriptionId(subId)) { - int phoneId = getPhoneId(subId); - if (phoneId >= 0 && (phoneId < mTelephonyManager.getPhoneCount() - || mTelephonyManager.getSimCount() == 1)) { - if (DBG) logdl("[setDefaultFallbackSubId] set sDefaultFallbackSubId=" + subId); - sDefaultFallbackSubId.set(subId); - // Update MCC MNC device configuration information - String defaultMccMnc = mTelephonyManager.getSimOperatorNumericForPhone(phoneId); - MccTable.updateMccMncConfiguration(mContext, defaultMccMnc); - } else { - if (DBG) { - logdl("[setDefaultFallbackSubId] not set invalid phoneId=" + phoneId - + " subId=" + subId); - } - } - } - if (previousDefaultSub != getDefaultSubId()) { - sendDefaultChangedBroadcast(getDefaultSubId()); - } - } - - public void sendDefaultChangedBroadcast(int subId) { - // Broadcast an Intent for default sub change - int phoneId = SubscriptionManager.getPhoneId(subId); - Intent intent = new Intent(SubscriptionManager.ACTION_DEFAULT_SUBSCRIPTION_CHANGED); - intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); - SubscriptionManager.putPhoneIdAndSubIdExtra(intent, phoneId, subId); - if (DBG) { - logdl("[sendDefaultChangedBroadcast] broadcast default subId changed phoneId=" - + phoneId + " subId=" + subId); - } - mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); - } - - /** - * Whether a subscription is opportunistic or not. - */ - public boolean isOpportunistic(int subId) { - SubscriptionInfo info = getActiveSubscriptionInfo(subId, mContext.getOpPackageName(), - mContext.getAttributionTag()); - return (info != null) && info.isOpportunistic(); - } - - // FIXME: We need we should not be assuming phoneId == slotIndex as it will not be true - // when there are multiple subscriptions per sim and probably for other reasons. - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - public int getSubIdUsingPhoneId(int phoneId) { - int[] subIds = getSubId(phoneId); - if (subIds == null || subIds.length == 0) { - return SubscriptionManager.INVALID_SUBSCRIPTION_ID; - } - return subIds[0]; - } - - /** Must be public for access from instrumentation tests. */ - @VisibleForTesting - public List<SubscriptionInfo> getSubInfoUsingSlotIndexPrivileged(int slotIndex) { - if (DBG) logd("[getSubInfoUsingSlotIndexPrivileged]+ slotIndex:" + slotIndex); - if (slotIndex == SubscriptionManager.DEFAULT_SIM_SLOT_INDEX) { - slotIndex = getSlotIndex(getDefaultSubId()); - } - if (!SubscriptionManager.isValidSlotIndex(slotIndex)) { - if (DBG) logd("[getSubInfoUsingSlotIndexPrivileged]- invalid slotIndex"); - return null; - } - - Cursor cursor = mContext.getContentResolver().query(SubscriptionManager.CONTENT_URI, - null, SubscriptionManager.SIM_SLOT_INDEX + "=?", - new String[]{String.valueOf(slotIndex)}, null); - ArrayList<SubscriptionInfo> subList = null; - try { - if (cursor != null) { - while (cursor.moveToNext()) { - SubscriptionInfo subInfo = getSubInfoRecord(cursor); - if (subInfo != null) { - if (subList == null) { - subList = new ArrayList<SubscriptionInfo>(); - } - subList.add(subInfo); - } - } - } - } finally { - if (cursor != null) { - cursor.close(); - } - } - if (DBG) logd("[getSubInfoUsingSlotIndex]- null info return"); - - return subList; - } - - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - private void validateSubId(int subId) { - if (DBG) logd("validateSubId subId: " + subId); - if (!SubscriptionManager.isValidSubscriptionId(subId)) { - throw new IllegalArgumentException("Invalid sub id passed as parameter"); - } else if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) { - throw new IllegalArgumentException("Default sub id passed as parameter"); - } - } - - private synchronized ArrayList<Integer> getActiveSubIdArrayList() { - // Clone the sub id list so it can't change out from under us while iterating - List<Entry<Integer, ArrayList<Integer>>> simInfoList = - new ArrayList<>(sSlotIndexToSubIds.entrySet()); - - // Put the set of sub ids in slot index order - Collections.sort(simInfoList, (x, y) -> x.getKey().compareTo(y.getKey())); - - // Collect the sub ids for each slot in turn - ArrayList<Integer> allSubs = new ArrayList<>(); - for (Entry<Integer, ArrayList<Integer>> slot : simInfoList) { - allSubs.addAll(slot.getValue()); - } - return allSubs; - } - - private boolean isSubscriptionVisible(int subId) { - synchronized (mSubInfoListLock) { - for (SubscriptionInfo info : mCacheOpportunisticSubInfoList) { - if (info.getSubscriptionId() == subId) { - // If group UUID is null, it's stand alone opportunistic profile. So it's - // visible. Otherwise, it's bundled opportunistic profile, and is not visible. - return info.getGroupUuid() == null; - } - } - } - - return true; - } - - /** - * @return the list of subId's that are active, is never null but the length maybe 0. - */ - @Override - public int[] getActiveSubIdList(boolean visibleOnly) { - enforceReadPrivilegedPhoneState("getActiveSubIdList"); - - final long token = Binder.clearCallingIdentity(); - try { - List<Integer> allSubs = getActiveSubIdArrayList(); - - if (visibleOnly) { - // Grouped opportunistic subscriptions should be hidden. - allSubs = allSubs.stream().filter(subId -> isSubscriptionVisible(subId)) - .collect(Collectors.toList()); - } - - int[] subIdArr = new int[allSubs.size()]; - int i = 0; - for (int sub : allSubs) { - subIdArr[i] = sub; - i++; - } - - if (VDBG) { - logdl("[getActiveSubIdList] allSubs=" + allSubs + " subIdArr.length=" - + subIdArr.length); - } - return subIdArr; - } finally { - Binder.restoreCallingIdentity(token); - } - } - - @Override - public boolean isActiveSubId(int subId, String callingPackage, String callingFeatureId) { - if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(mContext, subId, callingPackage, - callingFeatureId, "isActiveSubId")) { - throw new SecurityException("Requires READ_PHONE_STATE permission."); - } - final long identity = Binder.clearCallingIdentity(); - try { - return isActiveSubId(subId); - } finally { - Binder.restoreCallingIdentity(identity); - } - } - - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - @Deprecated // This should be moved into isActiveSubId(int, String) - public boolean isActiveSubId(int subId) { - boolean retVal = SubscriptionManager.isValidSubscriptionId(subId) - && getActiveSubIdArrayList().contains(subId); - - if (VDBG) logdl("[isActiveSubId]- " + retVal); - return retVal; - } - - /** - * Get the SIM state for the slot index. - * For Remote-SIMs, this method returns {@link #IccCardConstants.State.UNKNOWN} - * @return SIM state as the ordinal of {@See IccCardConstants.State} - */ - @Override - public int getSimStateForSlotIndex(int slotIndex) { - State simState; - String err; - if (slotIndex < 0) { - simState = IccCardConstants.State.UNKNOWN; - err = "invalid slotIndex"; - } else { - Phone phone = null; - try { - phone = PhoneFactory.getPhone(slotIndex); - } catch (IllegalStateException e) { - // ignore - } - if (phone == null) { - simState = IccCardConstants.State.UNKNOWN; - err = "phone == null"; - } else { - IccCard icc = phone.getIccCard(); - if (icc == null) { - simState = IccCardConstants.State.UNKNOWN; - err = "icc == null"; - } else { - simState = icc.getState(); - err = ""; - } - } - } - if (VDBG) { - logd("getSimStateForSlotIndex: " + err + " simState=" + simState - + " ordinal=" + simState.ordinal() + " slotIndex=" + slotIndex); - } - return simState.ordinal(); - } - - /** - * Store properties associated with SubscriptionInfo in database - * @param subId Subscription Id of Subscription - * @param propKey Column name in database associated with SubscriptionInfo - * @param propValue Value to store in DB for particular subId & column name - * - * @return number of rows updated. - * @hide - */ - @Override - public int setSubscriptionProperty(int subId, String propKey, String propValue) { - enforceModifyPhoneState("setSubscriptionProperty"); - final long token = Binder.clearCallingIdentity(); - - try { - validateSubId(subId); - ContentResolver resolver = mContext.getContentResolver(); - int result = setSubscriptionPropertyIntoContentResolver( - subId, propKey, propValue, resolver); - // Refresh the Cache of Active Subscription Info List - refreshCachedActiveSubscriptionInfoList(); - - return result; - } finally { - Binder.restoreCallingIdentity(token); - } - } - - private int setSubscriptionPropertyIntoContentResolver( - int subId, String propKey, String propValue, ContentResolver resolver) { - ContentValues value = new ContentValues(); - boolean updateEntireGroup = GROUP_SHARING_PROPERTIES.contains(propKey); - switch (propKey) { - case SubscriptionManager.CB_EXTREME_THREAT_ALERT: - case SubscriptionManager.CB_SEVERE_THREAT_ALERT: - case SubscriptionManager.CB_AMBER_ALERT: - case SubscriptionManager.CB_EMERGENCY_ALERT: - case SubscriptionManager.CB_ALERT_SOUND_DURATION: - case SubscriptionManager.CB_ALERT_REMINDER_INTERVAL: - case SubscriptionManager.CB_ALERT_VIBRATE: - case SubscriptionManager.CB_ALERT_SPEECH: - case SubscriptionManager.CB_ETWS_TEST_ALERT: - case SubscriptionManager.CB_CHANNEL_50_ALERT: - case SubscriptionManager.CB_CMAS_TEST_ALERT: - case SubscriptionManager.CB_OPT_OUT_DIALOG: - case SubscriptionManager.ENHANCED_4G_MODE_ENABLED: - case SubscriptionManager.IS_OPPORTUNISTIC: - case SubscriptionManager.VT_IMS_ENABLED: - case SubscriptionManager.WFC_IMS_ENABLED: - case SubscriptionManager.WFC_IMS_MODE: - case SubscriptionManager.WFC_IMS_ROAMING_MODE: - case SubscriptionManager.WFC_IMS_ROAMING_ENABLED: - case SubscriptionManager.IMS_RCS_UCE_ENABLED: - case SubscriptionManager.CROSS_SIM_CALLING_ENABLED: - case SubscriptionManager.VOIMS_OPT_IN_STATUS: - case SubscriptionManager.NR_ADVANCED_CALLING_ENABLED: - case SubscriptionManager.USAGE_SETTING: - value.put(propKey, Integer.parseInt(propValue)); - break; - case SubscriptionManager.ALLOWED_NETWORK_TYPES: - case SimInfo.COLUMN_PHONE_NUMBER_SOURCE_CARRIER: - case SimInfo.COLUMN_PHONE_NUMBER_SOURCE_IMS: - value.put(propKey, propValue); - break; - default: - if (DBG) logd("Invalid column name"); - break; - } - - return updateDatabase(value, subId, updateEntireGroup); - } - - /** - * Get properties associated with SubscriptionInfo from database - * - * @param subId Subscription Id of Subscription - * @param propKey Column name in SubscriptionInfo database - * @return Value associated with subId and propKey column in database - */ - @Override - public String getSubscriptionProperty(int subId, String propKey, String callingPackage, - String callingFeatureId) { - switch (propKey) { - case SubscriptionManager.GROUP_UUID: - if (mContext.checkCallingOrSelfPermission( - Manifest.permission.READ_PRIVILEGED_PHONE_STATE) != PERMISSION_GRANTED) { - EventLog.writeEvent(0x534e4554, "213457638", Binder.getCallingUid()); - return null; - } - break; - default: - if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(mContext, subId, - callingPackage, callingFeatureId, "getSubscriptionProperty")) { - return null; - } - } - - final long identity = Binder.clearCallingIdentity(); - try { - return getSubscriptionProperty(subId, propKey); - } finally { - Binder.restoreCallingIdentity(identity); - } - } - - /** - * Get properties associated with SubscriptionInfo from database. Note this is the version - * without permission check for telephony internal use only. - * - * @param subId Subscription Id of Subscription - * @param propKey Column name in SubscriptionInfo database - * @return Value associated with subId and propKey column in database - */ - public String getSubscriptionProperty(int subId, String propKey) { - String resultValue = null; - try (Cursor cursor = mContext.getContentResolver().query(SubscriptionManager.CONTENT_URI, - new String[]{propKey}, - SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=?", - new String[]{subId + ""}, null)) { - if (cursor != null) { - if (cursor.moveToFirst()) { - switch (propKey) { - case SubscriptionManager.CB_EXTREME_THREAT_ALERT: - case SubscriptionManager.CB_SEVERE_THREAT_ALERT: - case SubscriptionManager.CB_AMBER_ALERT: - case SubscriptionManager.CB_EMERGENCY_ALERT: - case SubscriptionManager.CB_ALERT_SOUND_DURATION: - case SubscriptionManager.CB_ALERT_REMINDER_INTERVAL: - case SubscriptionManager.CB_ALERT_VIBRATE: - case SubscriptionManager.CB_ALERT_SPEECH: - case SubscriptionManager.CB_ETWS_TEST_ALERT: - case SubscriptionManager.CB_CHANNEL_50_ALERT: - case SubscriptionManager.CB_CMAS_TEST_ALERT: - case SubscriptionManager.CB_OPT_OUT_DIALOG: - case SubscriptionManager.ENHANCED_4G_MODE_ENABLED: - case SubscriptionManager.VT_IMS_ENABLED: - case SubscriptionManager.WFC_IMS_ENABLED: - case SubscriptionManager.WFC_IMS_MODE: - case SubscriptionManager.WFC_IMS_ROAMING_MODE: - case SubscriptionManager.WFC_IMS_ROAMING_ENABLED: - case SubscriptionManager.IMS_RCS_UCE_ENABLED: - case SubscriptionManager.CROSS_SIM_CALLING_ENABLED: - case SubscriptionManager.IS_OPPORTUNISTIC: - case SubscriptionManager.GROUP_UUID: - case SubscriptionManager.DATA_ENABLED_OVERRIDE_RULES: - case SubscriptionManager.ALLOWED_NETWORK_TYPES: - case SubscriptionManager.D2D_STATUS_SHARING: - case SubscriptionManager.VOIMS_OPT_IN_STATUS: - case SubscriptionManager.D2D_STATUS_SHARING_SELECTED_CONTACTS: - case SubscriptionManager.NR_ADVANCED_CALLING_ENABLED: - case SimInfo.COLUMN_PHONE_NUMBER_SOURCE_CARRIER: - case SimInfo.COLUMN_PHONE_NUMBER_SOURCE_IMS: - case SubscriptionManager.USAGE_SETTING: - resultValue = cursor.getString(0); - break; - default: - if(DBG) logd("Invalid column name"); - break; - } - } else { - if(DBG) logd("Valid row not present in db"); - } - } else { - if(DBG) logd("Query failed"); - } - } - - if (DBG) logd("getSubscriptionProperty Query value = " + resultValue); - return resultValue; - } - - private void printStackTrace(String msg) { - RuntimeException re = new RuntimeException(); - logd("StackTrace - " + msg); - StackTraceElement[] st = re.getStackTrace(); - boolean first = true; - for (StackTraceElement ste : st) { - if (first) { - first = false; - } else { - logd(ste.toString()); - } - } - } - - @Override - public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, - "Requires DUMP"); - final long token = Binder.clearCallingIdentity(); - try { - pw.println("SubscriptionController:"); - pw.println(" mLastISubServiceRegTime=" + mLastISubServiceRegTime); - pw.println(" defaultSubId=" + getDefaultSubId()); - pw.println(" defaultDataSubId=" + getDefaultDataSubId()); - pw.println(" defaultVoiceSubId=" + getDefaultVoiceSubId()); - pw.println(" defaultSmsSubId=" + getDefaultSmsSubId()); - - pw.println(" defaultDataPhoneId=" + SubscriptionManager - .from(mContext).getDefaultDataPhoneId()); - pw.println(" defaultVoicePhoneId=" + SubscriptionManager.getDefaultVoicePhoneId()); - pw.println(" defaultSmsPhoneId=" + SubscriptionManager - .from(mContext).getDefaultSmsPhoneId()); - pw.flush(); - - for (Entry<Integer, ArrayList<Integer>> entry : sSlotIndexToSubIds.entrySet()) { - pw.println(" sSlotIndexToSubId[" + entry.getKey() + "]: subIds=" + entry); - } - pw.flush(); - pw.println("++++++++++++++++++++++++++++++++"); - - List<SubscriptionInfo> sirl = getActiveSubscriptionInfoList( - mContext.getOpPackageName(), mContext.getAttributionTag()); - if (sirl != null) { - pw.println(" ActiveSubInfoList:"); - for (SubscriptionInfo entry : sirl) { - pw.println(" " + entry.toString()); - } - } else { - pw.println(" ActiveSubInfoList: is null"); - } - pw.flush(); - pw.println("++++++++++++++++++++++++++++++++"); - - sirl = getAllSubInfoList(mContext.getOpPackageName(), mContext.getAttributionTag()); - if (sirl != null) { - pw.println(" AllSubInfoList:"); - for (SubscriptionInfo entry : sirl) { - pw.println(" " + entry.toString()); - } - } else { - pw.println(" AllSubInfoList: is null"); - } - pw.flush(); - pw.println("++++++++++++++++++++++++++++++++"); - - mLocalLog.dump(fd, pw, args); - pw.flush(); - pw.println("++++++++++++++++++++++++++++++++"); - pw.flush(); - } finally { - Binder.restoreCallingIdentity(token); - } - } - - /** - * Migrating Ims settings from global setting to subscription DB, if not already done. - */ - @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) - public void migrateImsSettings() { - migrateImsSettingHelper( - Settings.Global.ENHANCED_4G_MODE_ENABLED, - SubscriptionManager.ENHANCED_4G_MODE_ENABLED); - migrateImsSettingHelper( - Settings.Global.VT_IMS_ENABLED, - SubscriptionManager.VT_IMS_ENABLED); - migrateImsSettingHelper( - Settings.Global.WFC_IMS_ENABLED, - SubscriptionManager.WFC_IMS_ENABLED); - migrateImsSettingHelper( - Settings.Global.WFC_IMS_MODE, - SubscriptionManager.WFC_IMS_MODE); - migrateImsSettingHelper( - Settings.Global.WFC_IMS_ROAMING_MODE, - SubscriptionManager.WFC_IMS_ROAMING_MODE); - migrateImsSettingHelper( - Settings.Global.WFC_IMS_ROAMING_ENABLED, - SubscriptionManager.WFC_IMS_ROAMING_ENABLED); - } - - private void migrateImsSettingHelper(String settingGlobal, String subscriptionProperty) { - ContentResolver resolver = mContext.getContentResolver(); - int defaultSubId = getDefaultVoiceSubId(); - if (defaultSubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) { - return; - } - try { - int prevSetting = Settings.Global.getInt(resolver, settingGlobal); - - if (prevSetting != DEPRECATED_SETTING) { - // Write previous setting into Subscription DB. - setSubscriptionPropertyIntoContentResolver(defaultSubId, subscriptionProperty, - Integer.toString(prevSetting), resolver); - // Write global setting value with DEPRECATED_SETTING making sure - // migration only happen once. - Settings.Global.putInt(resolver, settingGlobal, DEPRECATED_SETTING); - } - } catch (Settings.SettingNotFoundException e) { - } - } - - /** - * Set whether a subscription is opportunistic. - * - * Throws SecurityException if doesn't have required permission. - * - * @param opportunistic whether it’s opportunistic subscription. - * @param subId the unique SubscriptionInfo index in database - * @param callingPackage The package making the IPC. - * @return the number of records updated - */ - @Override - public int setOpportunistic(boolean opportunistic, int subId, String callingPackage) { - try { - TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege( - mContext, subId, callingPackage); - } catch (SecurityException e) { - // The subscription may be inactive eSIM profile. If so, check the access rule in - // database. - enforceCarrierPrivilegeOnInactiveSub(subId, callingPackage, - "Caller requires permission on sub " + subId); - } - - long token = Binder.clearCallingIdentity(); - try { - int ret = setSubscriptionProperty(subId, SubscriptionManager.IS_OPPORTUNISTIC, - String.valueOf(opportunistic ? 1 : 0)); - - if (ret != 0) notifySubscriptionInfoChanged(); - - return ret; - } finally { - Binder.restoreCallingIdentity(token); - } - } - - /** - * Get subscription info from database, and check whether caller has carrier privilege - * permission with it. If checking fails, throws SecurityException. - */ - private void enforceCarrierPrivilegeOnInactiveSub(int subId, String callingPackage, - String message) { - mAppOps.checkPackage(Binder.getCallingUid(), callingPackage); - - SubscriptionManager subManager = (SubscriptionManager) - mContext.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE); - List<SubscriptionInfo> subInfo = getSubInfo( - SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=" + subId, null); - - try { - if (!isActiveSubId(subId) && subInfo != null && subInfo.size() == 1 - && subManager.canManageSubscription(subInfo.get(0), callingPackage)) { - return; - } - throw new SecurityException(message); - } catch (IllegalArgumentException e) { - // canManageSubscription will throw IllegalArgumentException if sub is not embedded - // or package name is unknown. In this case, we also see it as permission check failure - // and throw a SecurityException. - throw new SecurityException(message); - } - } - - @Override - public void setPreferredDataSubscriptionId(int subId, boolean needValidation, - ISetOpportunisticDataCallback callback) { - enforceModifyPhoneState("setPreferredDataSubscriptionId"); - final long token = Binder.clearCallingIdentity(); - - try { - PhoneSwitcher phoneSwitcher = PhoneSwitcher.getInstance(); - if (phoneSwitcher == null) { - logd("Set preferred data sub: phoneSwitcher is null."); - AnomalyReporter.reportAnomaly( - UUID.fromString("a3ab0b9d-f2aa-4baf-911d-7096c0d4645a"), - "Set preferred data sub: phoneSwitcher is null."); - if (callback != null) { - try { - callback.onComplete(SET_OPPORTUNISTIC_SUB_REMOTE_SERVICE_EXCEPTION); - } catch (RemoteException exception) { - logd("RemoteException " + exception); - } - } - return; - } - - phoneSwitcher.trySetOpportunisticDataSubscription(subId, needValidation, callback); - } finally { - Binder.restoreCallingIdentity(token); - } - } - - @Override - public int getPreferredDataSubscriptionId() { - enforceReadPrivilegedPhoneState("getPreferredDataSubscriptionId"); - final long token = Binder.clearCallingIdentity(); - - try { - PhoneSwitcher phoneSwitcher = PhoneSwitcher.getInstance(); - if (phoneSwitcher == null) { - AnomalyReporter.reportAnomaly( - UUID.fromString("a3ab0b9d-f2aa-4baf-911d-7096c0d4645a"), - "Get preferred data sub: phoneSwitcher is null."); - return SubscriptionManager.DEFAULT_SUBSCRIPTION_ID; - } - - return phoneSwitcher.getOpportunisticDataSubscriptionId(); - } finally { - Binder.restoreCallingIdentity(token); - } - } - - @Override - public List<SubscriptionInfo> getOpportunisticSubscriptions(String callingPackage, - String callingFeatureId) { - return getSubscriptionInfoListFromCacheHelper(callingPackage, callingFeatureId, - makeCacheListCopyWithLock(mCacheOpportunisticSubInfoList)); - } - - /** - * Inform SubscriptionManager that subscriptions in the list are bundled - * as a group. Typically it's a primary subscription and an opportunistic - * subscription. It should only affect multi-SIM scenarios where primary - * and opportunistic subscriptions can be activated together. - * Being in the same group means they might be activated or deactivated - * together, some of them may be invisible to the users, etc. - * - * Caller will either have {@link android.Manifest.permission#MODIFY_PHONE_STATE} - * permission or had carrier privilege permission on the subscriptions: - * {@link TelephonyManager#hasCarrierPrivileges(int)} or - * {@link SubscriptionManager#canManageSubscription(SubscriptionInfo)} - * - * @throws SecurityException if the caller doesn't meet the requirements - * outlined above. - * @throws IllegalArgumentException if the some subscriptions in the list doesn't exist. - * - * @param subIdList list of subId that will be in the same group - * @return groupUUID a UUID assigned to the subscription group. It returns - * null if fails. - * - */ - @Override - public ParcelUuid createSubscriptionGroup(int[] subIdList, String callingPackage) { - if (subIdList == null || subIdList.length == 0) { - throw new IllegalArgumentException("Invalid subIdList " + subIdList); - } - - // Makes sure calling package matches caller UID. - mAppOps.checkPackage(Binder.getCallingUid(), callingPackage); - // If it doesn't have modify phone state permission, or carrier privilege permission, - // a SecurityException will be thrown. - if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE) - != PERMISSION_GRANTED && !checkCarrierPrivilegeOnSubList( - subIdList, callingPackage)) { - throw new SecurityException("CreateSubscriptionGroup needs MODIFY_PHONE_STATE or" - + " carrier privilege permission on all specified subscriptions"); - } - - long identity = Binder.clearCallingIdentity(); - - try { - // Generate a UUID. - ParcelUuid groupUUID = new ParcelUuid(UUID.randomUUID()); - - ContentValues value = new ContentValues(); - value.put(SubscriptionManager.GROUP_UUID, groupUUID.toString()); - value.put(SubscriptionManager.GROUP_OWNER, callingPackage); - int result = mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI, - value, getSelectionForSubIdList(subIdList), null); - - if (DBG) logdl("createSubscriptionGroup update DB result: " + result); - - refreshCachedActiveSubscriptionInfoList(); - - notifySubscriptionInfoChanged(); - - MultiSimSettingController.getInstance().notifySubscriptionGroupChanged(groupUUID); - - return groupUUID; - } finally { - Binder.restoreCallingIdentity(identity); - } - } - - private String getOwnerPackageOfSubGroup(ParcelUuid groupUuid) { - if (groupUuid == null) return null; - - List<SubscriptionInfo> infoList = getSubInfo(SubscriptionManager.GROUP_UUID - + "=\'" + groupUuid.toString() + "\'", null); - - return ArrayUtils.isEmpty(infoList) ? null : infoList.get(0).getGroupOwner(); - } - - /** - * @param groupUuid a UUID assigned to the subscription group. - * @param callingPackage the package making the IPC. - * @return if callingPackage has carrier privilege on sublist. - * - */ - public boolean canPackageManageGroup(ParcelUuid groupUuid, String callingPackage) { - if (groupUuid == null) { - throw new IllegalArgumentException("Invalid groupUuid"); - } - - if (TextUtils.isEmpty(callingPackage)) { - throw new IllegalArgumentException("Empty callingPackage"); - } - - List<SubscriptionInfo> infoList; - - // Getting all subscriptions in the group. - long identity = Binder.clearCallingIdentity(); - try { - infoList = getSubInfo(SubscriptionManager.GROUP_UUID - + "=\'" + groupUuid.toString() + "\'", null); - } finally { - Binder.restoreCallingIdentity(identity); - } - - // If the group does not exist, then by default the UUID is up for grabs so no need to - // restrict management of a group (that someone may be attempting to create). - if (ArrayUtils.isEmpty(infoList)) { - return true; - } - - // If the calling package is the group owner, skip carrier permission check and return - // true as it was done before. - if (callingPackage.equals(infoList.get(0).getGroupOwner())) return true; - - // Check carrier privilege for all subscriptions in the group. - int[] subIdArray = infoList.stream().mapToInt(info -> info.getSubscriptionId()) - .toArray(); - return (checkCarrierPrivilegeOnSubList(subIdArray, callingPackage)); - } - - private int updateGroupOwner(ParcelUuid groupUuid, String groupOwner) { - // If the existing group owner is different from current caller, make caller the new - // owner of all subscriptions in group. - // This is for use-case of: - // 1) Both package1 and package2 has permission (MODIFY_PHONE_STATE or carrier - // privilege permission) of all related subscriptions. - // 2) Package 1 created a group. - // 3) Package 2 wants to add a subscription into it. - // Step 3 should be granted as all operations are permission based. Which means as - // long as the package passes the permission check, it can modify the subscription - // and the group. And package 2 becomes the new group owner as it's the last to pass - // permission checks on all members. - ContentValues value = new ContentValues(1); - value.put(SubscriptionManager.GROUP_OWNER, groupOwner); - return mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI, - value, SubscriptionManager.GROUP_UUID + "=\"" + groupUuid + "\"", null); - } - - @Override - public void addSubscriptionsIntoGroup(int[] subIdList, ParcelUuid groupUuid, - String callingPackage) { - if (subIdList == null || subIdList.length == 0) { - throw new IllegalArgumentException("Invalid subId list"); - } - - if (groupUuid == null || groupUuid.equals(INVALID_GROUP_UUID)) { - throw new IllegalArgumentException("Invalid groupUuid"); - } - - // Makes sure calling package matches caller UID. - mAppOps.checkPackage(Binder.getCallingUid(), callingPackage); - // If it doesn't have modify phone state permission, or carrier privilege permission, - // a SecurityException will be thrown. - if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE) - != PERMISSION_GRANTED && !(checkCarrierPrivilegeOnSubList(subIdList, callingPackage) - && canPackageManageGroup(groupUuid, callingPackage))) { - throw new SecurityException("Requires MODIFY_PHONE_STATE or carrier privilege" - + " permissions on subscriptions and the group."); - } - - long identity = Binder.clearCallingIdentity(); - - try { - if (DBG) { - logdl("addSubscriptionsIntoGroup sub list " - + Arrays.toString(subIdList) + " into group " + groupUuid); - } - - ContentValues value = new ContentValues(); - value.put(SubscriptionManager.GROUP_UUID, groupUuid.toString()); - int result = mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI, - value, getSelectionForSubIdList(subIdList), null); - - if (DBG) logdl("addSubscriptionsIntoGroup update DB result: " + result); - - if (result > 0) { - updateGroupOwner(groupUuid, callingPackage); - refreshCachedActiveSubscriptionInfoList(); - notifySubscriptionInfoChanged(); - MultiSimSettingController.getInstance().notifySubscriptionGroupChanged(groupUuid); - } - } finally { - Binder.restoreCallingIdentity(identity); - } - } - - /** - * Remove a list of subscriptions from their subscription group. - * See {@link SubscriptionManager#createSubscriptionGroup(List<Integer>)} for more details. - * - * Caller will either have {@link android.Manifest.permission#MODIFY_PHONE_STATE} - * permission or had carrier privilege permission on the subscriptions: - * {@link TelephonyManager#hasCarrierPrivileges()} or - * {@link SubscriptionManager#canManageSubscription(SubscriptionInfo)} - * - * @throws SecurityException if the caller doesn't meet the requirements - * outlined above. - * @throws IllegalArgumentException if the some subscriptions in the list doesn't belong - * the specified group. - * - * @param subIdList list of subId that need removing from their groups. - * - */ - public void removeSubscriptionsFromGroup(int[] subIdList, ParcelUuid groupUuid, - String callingPackage) { - if (subIdList == null || subIdList.length == 0) { - return; - } - - // Makes sure calling package matches caller UID. - mAppOps.checkPackage(Binder.getCallingUid(), callingPackage); - // If it doesn't have modify phone state permission, or carrier privilege permission, - // a SecurityException will be thrown. If it's due to invalid parameter or internal state, - // it will return null. - if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE) - != PERMISSION_GRANTED && !(checkCarrierPrivilegeOnSubList(subIdList, callingPackage) - && canPackageManageGroup(groupUuid, callingPackage))) { - throw new SecurityException("removeSubscriptionsFromGroup needs MODIFY_PHONE_STATE or" - + " carrier privilege permission on all specified subscriptions"); - } - - long identity = Binder.clearCallingIdentity(); - - try { - List<SubscriptionInfo> subInfoList = getSubInfo(getSelectionForSubIdList(subIdList), - null); - for (SubscriptionInfo info : subInfoList) { - if (!groupUuid.equals(info.getGroupUuid())) { - throw new IllegalArgumentException("Subscription " + info.getSubscriptionId() - + " doesn't belong to group " + groupUuid); - } - } - ContentValues value = new ContentValues(); - value.put(SubscriptionManager.GROUP_UUID, (String) null); - value.put(SubscriptionManager.GROUP_OWNER, (String) null); - int result = mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI, - value, getSelectionForSubIdList(subIdList), null); - - if (DBG) logdl("removeSubscriptionsFromGroup update DB result: " + result); - - if (result > 0) { - updateGroupOwner(groupUuid, callingPackage); - refreshCachedActiveSubscriptionInfoList(); - notifySubscriptionInfoChanged(); - } - } finally { - Binder.restoreCallingIdentity(identity); - } - } - - /** - * Helper function to check if the caller has carrier privilege permissions on a list of subId. - * The check can either be processed against access rules on currently active SIM cards, or - * the access rules we keep in our database for currently inactive eSIMs. - * - * @throws IllegalArgumentException if the some subId is invalid or doesn't exist. - * - * @return true if checking passes on all subId, false otherwise. - */ - private boolean checkCarrierPrivilegeOnSubList(int[] subIdList, String callingPackage) { - // Check carrier privilege permission on active subscriptions first. - // If it fails, they could be inactive. So keep them in a HashSet and later check - // access rules in our database. - Set<Integer> checkSubList = new HashSet<>(); - for (int subId : subIdList) { - if (isActiveSubId(subId)) { - if (!mTelephonyManager.hasCarrierPrivileges(subId)) { - return false; - } - } else { - checkSubList.add(subId); - } - } - - if (checkSubList.isEmpty()) { - return true; - } - - long identity = Binder.clearCallingIdentity(); - - try { - // Check access rules for each sub info. - SubscriptionManager subscriptionManager = (SubscriptionManager) - mContext.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE); - List<SubscriptionInfo> subInfoList = getSubInfo( - getSelectionForSubIdList(subIdList), null); - - // Didn't find all the subscriptions specified in subIdList. - if (subInfoList == null || subInfoList.size() != subIdList.length) { - throw new IllegalArgumentException("Invalid subInfoList."); - } - - for (SubscriptionInfo subInfo : subInfoList) { - if (checkSubList.contains(subInfo.getSubscriptionId())) { - if (subInfo.isEmbedded() && subscriptionManager.canManageSubscription( - subInfo, callingPackage)) { - checkSubList.remove(subInfo.getSubscriptionId()); - } else { - return false; - } - } - } - - return checkSubList.isEmpty(); - } finally { - Binder.restoreCallingIdentity(identity); - } - } - - /** - * Helper function to create selection argument of a list of subId. - * The result should be: "in (subId1, subId2, ...)". - */ - public static String getSelectionForSubIdList(int[] subId) { - StringBuilder selection = new StringBuilder(); - selection.append(SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID); - selection.append(" IN ("); - for (int i = 0; i < subId.length - 1; i++) { - selection.append(subId[i] + ", "); - } - selection.append(subId[subId.length - 1]); - selection.append(")"); - - return selection.toString(); - } - - /** - * Helper function to create selection argument of a list of subId. - * The result should be: "in (iccId1, iccId2, ...)". - */ - private String getSelectionForIccIdList(String[] iccIds) { - StringBuilder selection = new StringBuilder(); - selection.append(SubscriptionManager.ICC_ID); - selection.append(" IN ("); - for (int i = 0; i < iccIds.length - 1; i++) { - selection.append("'" + iccIds[i] + "', "); - } - selection.append("'" + iccIds[iccIds.length - 1] + "'"); - selection.append(")"); - - return selection.toString(); - } - - /** - * Get subscriptionInfo list of subscriptions that are in the same group of given subId. - * See {@link #createSubscriptionGroup(int[], String)} for more details. - * - * Caller will either have {@link android.Manifest.permission#READ_PHONE_STATE} - * permission or had carrier privilege permission on the subscription. - * {@link TelephonyManager#hasCarrierPrivileges(int)} - * - * @throws SecurityException if the caller doesn't meet the requirements - * outlined above. - * - * @param groupUuid of which list of subInfo will be returned. - * @return list of subscriptionInfo that belong to the same group, including the given - * subscription itself. It will return an empty list if no subscription belongs to the group. - * - */ - @Override - public List<SubscriptionInfo> getSubscriptionsInGroup(ParcelUuid groupUuid, - String callingPackage, String callingFeatureId) { - long identity = Binder.clearCallingIdentity(); - 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(), true); - if (groupUuid == null || subInfoList == null || subInfoList.isEmpty()) { - return new ArrayList<>(); - } - } finally { - Binder.restoreCallingIdentity(identity); - } - - return subInfoList.stream().filter(info -> { - if (!groupUuid.equals(info.getGroupUuid())) return false; - int subId = info.getSubscriptionId(); - return TelephonyPermissions.checkCallingOrSelfReadPhoneState(mContext, subId, - callingPackage, callingFeatureId, "getSubscriptionsInGroup") - || info.canManageSubscription(mContext, callingPackage); - }).map(subscriptionInfo -> conditionallyRemoveIdentifiers(subscriptionInfo, - callingPackage, callingFeatureId, "getSubscriptionsInGroup")) - .collect(Collectors.toList()); - - } - - /** - * Check if the passed in phoneId has a sub that belongs to the same group as the sub - * corresponding to the passed in iccid. - * @param phoneId phone id to check - * @param iccid ICCID to check - * @return true if sub/group is the same, false otherwise - */ - public boolean checkPhoneIdAndIccIdMatch(int phoneId, String iccid) { - int subId = getSubIdUsingPhoneId(phoneId); - if (!SubscriptionManager.isUsableSubIdValue(subId)) return false; - ParcelUuid groupUuid = getGroupUuid(subId); - List<SubscriptionInfo> subInfoList; - if (groupUuid != null) { - subInfoList = getSubInfo(SubscriptionManager.GROUP_UUID - + "=\'" + groupUuid.toString() + "\'", null); - } else { - subInfoList = getSubInfo(SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID - + "=" + subId, null); - } - return subInfoList != null && subInfoList.stream().anyMatch( - subInfo -> IccUtils.stripTrailingFs(subInfo.getIccId()).equals( - IccUtils.stripTrailingFs(iccid))); - } - - public ParcelUuid getGroupUuid(int subId) { - ParcelUuid groupUuid; - List<SubscriptionInfo> subInfo = getSubInfo(SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID - + "=" + subId, null); - if (subInfo == null || subInfo.size() == 0) { - groupUuid = null; - } else { - groupUuid = subInfo.get(0).getGroupUuid(); - } - - return groupUuid; - } - - - /** - * Enable/Disable a subscription - * @param enable true if enabling, false if disabling - * @param subId the unique SubInfoRecord index in database - * - * @return true if success, false if fails or the further action is - * needed hence it's redirected to Euicc. - */ - @Override - public boolean setSubscriptionEnabled(boolean enable, int subId) { - enforceModifyPhoneState("setSubscriptionEnabled"); - - final long identity = Binder.clearCallingIdentity(); - try { - logd("setSubscriptionEnabled" + (enable ? " enable " : " disable ") - + " subId " + subId); - - // Error checking. - if (!SubscriptionManager.isUsableSubscriptionId(subId)) { - throw new IllegalArgumentException( - "setSubscriptionEnabled not usable subId " + subId); - } - - // Nothing to do if it's already active or inactive. - if (enable == isActiveSubscriptionId(subId)) return true; - - SubscriptionInfo info = SubscriptionController.getInstance() - .getAllSubInfoList(mContext.getOpPackageName(), mContext.getAttributionTag()) - .stream() - .filter(subInfo -> subInfo.getSubscriptionId() == subId) - .findFirst() - .get(); - - if (info == null) { - logd("setSubscriptionEnabled subId " + subId + " doesn't exist."); - return false; - } - - // TODO: make sure after slot mapping, we enable the uicc applications for the - // subscription we are enabling. - if (info.isEmbedded()) { - return enableEmbeddedSubscription(info, enable); - } else { - return enablePhysicalSubscription(info, enable); - } - } finally { - Binder.restoreCallingIdentity(identity); - } - } - - private boolean enableEmbeddedSubscription(SubscriptionInfo info, boolean enable) { - // We need to send intents to Euicc for operations: - - // 1) In single SIM mode, turning on a eSIM subscription while pSIM is the active slot. - // Euicc will ask user to switch to DSDS if supported or to confirm SIM slot - // switching. - // 2) In DSDS mode, turning on / off an eSIM profile. Euicc can ask user whether - // to turn on DSDS, or whether to switch from current active eSIM profile to it, or - // to simply show a progress dialog. - // 3) In future, similar operations on triple SIM devices. - enableSubscriptionOverEuiccManager(info.getSubscriptionId(), enable, - SubscriptionManager.INVALID_SIM_SLOT_INDEX); - // returning false to indicate state is not changed. If changed, a subscriptionInfo - // change will be filed separately. - return false; - - // TODO: uncomment or clean up if we decide whether to support standalone CBRS for Q. - // subId = enable ? subId : SubscriptionManager.INVALID_SUBSCRIPTION_ID; - // updateEnabledSubscriptionGlobalSetting(subId, physicalSlotIndex); - } - - private boolean enablePhysicalSubscription(SubscriptionInfo info, boolean enable) { - if (info == null || !SubscriptionManager.isValidSubscriptionId(info.getSubscriptionId())) { - return false; - } - - int subId = info.getSubscriptionId(); - - UiccSlotInfo slotInfo = null; - int physicalSlotIndex = SubscriptionManager.INVALID_SIM_SLOT_INDEX; - UiccSlotInfo[] slotsInfo = mTelephonyManager.getUiccSlotsInfo(); - if (slotsInfo == null) return false; - for (int i = 0; i < slotsInfo.length; i++) { - UiccSlotInfo curSlotInfo = slotsInfo[i]; - if (curSlotInfo.getCardStateInfo() == CARD_STATE_INFO_PRESENT) { - if (TextUtils.equals(IccUtils.stripTrailingFs(curSlotInfo.getCardId()), - IccUtils.stripTrailingFs(info.getCardString()))) { - slotInfo = curSlotInfo; - physicalSlotIndex = i; - break; - } - } - } - - // Can't find the existing SIM. - if (slotInfo == null) return false; - - // this for physical slot which has only one port - if (enable && !slotInfo.getPorts().stream().findFirst().get().isActive()) { - // We need to send intents to Euicc if we are turning on an inactive slot. - // Euicc will decide whether to ask user to switch to DSDS, or change SIM - // slot mapping. - EuiccManager euiccManager = - (EuiccManager) mContext.getSystemService(Context.EUICC_SERVICE); - if (euiccManager != null && euiccManager.isEnabled()) { - enableSubscriptionOverEuiccManager(subId, enable, physicalSlotIndex); - } else { - // Enable / disable uicc applications. - if (!info.areUiccApplicationsEnabled()) setUiccApplicationsEnabled(enable, subId); - // If euiccManager is not enabled, we try to switch to DSDS if possible, - // or switch slot if not. - if (mTelephonyManager.isMultiSimSupported() == MULTISIM_ALLOWED) { - PhoneConfigurationManager.getInstance().switchMultiSimConfig( - mTelephonyManager.getSupportedModemCount()); - } else { - List<UiccSlotMapping> slotMapping = new ArrayList<>(); - // As this is single sim mode, set port index to 0 and logical slot index is 0 - slotMapping.add(new UiccSlotMapping(TelephonyManager.DEFAULT_PORT_INDEX, - physicalSlotIndex, 0)); - UiccController.getInstance().switchSlots(slotMapping, null); - } - } - return true; - } else { - // Enable / disable uicc applications. - setUiccApplicationsEnabled(enable, subId); - return true; - } - } - - private void enableSubscriptionOverEuiccManager(int subId, boolean enable, - int physicalSlotIndex) { - logdl("enableSubscriptionOverEuiccManager" + (enable ? " enable " : " disable ") - + "subId " + subId + " on slotIndex " + physicalSlotIndex); - Intent intent = new Intent(EuiccManager.ACTION_TOGGLE_SUBSCRIPTION_PRIVILEGED); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - intent.putExtra(EuiccManager.EXTRA_SUBSCRIPTION_ID, subId); - intent.putExtra(EuiccManager.EXTRA_ENABLE_SUBSCRIPTION, enable); - if (physicalSlotIndex != SubscriptionManager.INVALID_SIM_SLOT_INDEX) { - intent.putExtra(EuiccManager.EXTRA_PHYSICAL_SLOT_ID, physicalSlotIndex); - } - mContext.startActivity(intent); - } - - private void updateEnabledSubscriptionGlobalSetting(int subId, int physicalSlotIndex) { - // Write the value which subscription is enabled into global setting. - Settings.Global.putInt(mContext.getContentResolver(), - Settings.Global.ENABLED_SUBSCRIPTION_FOR_SLOT + physicalSlotIndex, subId); - } - - private void updateModemStackEnabledGlobalSetting(boolean enabled, int physicalSlotIndex) { - // Write the whether a modem stack is disabled into global setting. - Settings.Global.putInt(mContext.getContentResolver(), - Settings.Global.MODEM_STACK_ENABLED_FOR_SLOT - + physicalSlotIndex, enabled ? 1 : 0); - } - - private int getPhysicalSlotIndexFromLogicalSlotIndex(int logicalSlotIndex) { - int physicalSlotIndex = SubscriptionManager.INVALID_SIM_SLOT_INDEX; - UiccSlotInfo[] slotInfos = mTelephonyManager.getUiccSlotsInfo(); - for (int i = 0; i < slotInfos.length; i++) { - for (UiccPortInfo portInfo : slotInfos[i].getPorts()) { - if (portInfo.getLogicalSlotIndex() == logicalSlotIndex) { - physicalSlotIndex = i; - break; - } - } - } - - return physicalSlotIndex; - } - - @Override - public boolean isSubscriptionEnabled(int subId) { - // TODO: b/123314365 support multi-eSIM and removable eSIM. - enforceReadPrivilegedPhoneState("isSubscriptionEnabled"); - - long identity = Binder.clearCallingIdentity(); - try { - // Error checking. - if (!SubscriptionManager.isUsableSubscriptionId(subId)) { - throw new IllegalArgumentException( - "isSubscriptionEnabled not usable subId " + subId); - } - - List<SubscriptionInfo> infoList = getSubInfo( - SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=" + subId, null); - if (infoList == null || infoList.isEmpty()) { - // Subscription doesn't exist. - return false; - } - - boolean isEmbedded = infoList.get(0).isEmbedded(); - - if (isEmbedded) { - return isActiveSubId(subId); - } else { - // For pSIM, we also need to check if modem is disabled or not. - return isActiveSubId(subId) && PhoneConfigurationManager.getInstance() - .getPhoneStatus(PhoneFactory.getPhone(getPhoneId(subId))); - } - - } finally { - Binder.restoreCallingIdentity(identity); - } - } - - @Override - public int getEnabledSubscriptionId(int logicalSlotIndex) { - // TODO: b/123314365 support multi-eSIM and removable eSIM. - enforceReadPrivilegedPhoneState("getEnabledSubscriptionId"); - - long identity = Binder.clearCallingIdentity(); - try { - if (!SubscriptionManager.isValidPhoneId(logicalSlotIndex)) { - throw new IllegalArgumentException( - "getEnabledSubscriptionId with invalid logicalSlotIndex " - + logicalSlotIndex); - } - - // Getting and validating the physicalSlotIndex. - int physicalSlotIndex = getPhysicalSlotIndexFromLogicalSlotIndex(logicalSlotIndex); - if (physicalSlotIndex == SubscriptionManager.INVALID_SIM_SLOT_INDEX) { - return SubscriptionManager.INVALID_SUBSCRIPTION_ID; - } - - // if modem stack is disabled, return INVALID_SUBSCRIPTION_ID without reading - // Settings.Global.ENABLED_SUBSCRIPTION_FOR_SLOT. - int modemStackEnabled = Settings.Global.getInt(mContext.getContentResolver(), - Settings.Global.MODEM_STACK_ENABLED_FOR_SLOT + physicalSlotIndex, 1); - if (modemStackEnabled != 1) { - return SubscriptionManager.INVALID_SUBSCRIPTION_ID; - } - - int subId; - try { - subId = Settings.Global.getInt(mContext.getContentResolver(), - Settings.Global.ENABLED_SUBSCRIPTION_FOR_SLOT + physicalSlotIndex); - } catch (Settings.SettingNotFoundException e) { - // Value never set. Return whether it's currently active. - subId = getSubIdUsingPhoneId(logicalSlotIndex); - } - - return subId; - } finally { - Binder.restoreCallingIdentity(identity); - } - } - - /** - * Helper function of getOpportunisticSubscriptions and getActiveSubscriptionInfoList. - * They are doing similar things except operating on different cache. - * - * NOTE: the cacheSubList passed in is a *copy* of mCacheActiveSubInfoList or - * mCacheOpportunisticSubInfoList, so mSubInfoListLock is not required to access it. Also, this - * method may modify cacheSubList depending on the permissions the caller has. - */ - private List<SubscriptionInfo> getSubscriptionInfoListFromCacheHelper( - String callingPackage, String callingFeatureId, List<SubscriptionInfo> cacheSubList) { - boolean canReadPhoneState = false; - boolean canReadIdentifiers = false; - boolean canReadPhoneNumber = false; - try { - canReadPhoneState = TelephonyPermissions.checkReadPhoneState(mContext, - SubscriptionManager.INVALID_SUBSCRIPTION_ID, Binder.getCallingPid(), - Binder.getCallingUid(), callingPackage, callingFeatureId, - "getSubscriptionInfoList"); - // If the calling package has the READ_PHONE_STATE permission then check if the caller - // also has access to subscriber identifiers and the phone number to ensure that the ICC - // ID and any other unique identifiers are removed if the caller should not have access. - if (canReadPhoneState) { - canReadIdentifiers = hasSubscriberIdentifierAccess( - SubscriptionManager.INVALID_SUBSCRIPTION_ID, callingPackage, - callingFeatureId, "getSubscriptionInfoList", false); - canReadPhoneNumber = hasPhoneNumberAccess( - SubscriptionManager.INVALID_SUBSCRIPTION_ID, callingPackage, - callingFeatureId, "getSubscriptionInfoList"); - } - } catch (SecurityException e) { - // If a SecurityException is thrown during the READ_PHONE_STATE check then the only way - // to access a subscription is to have carrier privileges for its subId; an app with - // carrier privileges for a subscription is also granted access to all identifiers so - // the identifier and phone number access checks are not required. - } - - if (canReadIdentifiers && canReadPhoneNumber) { - return cacheSubList; - } - // Filter the list to only include subscriptions which the caller can manage. - for (int subIndex = cacheSubList.size() - 1; subIndex >= 0; subIndex--) { - SubscriptionInfo subscriptionInfo = cacheSubList.get(subIndex); - - int subId = subscriptionInfo.getSubscriptionId(); - boolean hasCarrierPrivileges = TelephonyPermissions.checkCarrierPrivilegeForSubId( - mContext, subId); - // If the caller has carrier privileges then they are granted access to all - // identifiers for their subscription. - if (hasCarrierPrivileges) continue; - - cacheSubList.remove(subIndex); - if (canReadPhoneState) { - // The caller does not have carrier privileges for this subId, filter the - // identifiers in the subscription based on the results of the initial - // permission checks. - cacheSubList.add(subIndex, conditionallyRemoveIdentifiers( - subscriptionInfo, canReadIdentifiers, canReadPhoneNumber)); - } - } - return cacheSubList; - } - - /** - * Conditionally removes identifiers from the provided {@code subInfo} if the {@code - * callingPackage} does not meet the access requirements for identifiers and returns the - * potentially modified object.. - * - * <p>If the caller does not meet the access requirements for identifiers a clone of the - * provided SubscriptionInfo is created and modified to avoid altering SubscriptionInfo objects - * in a cache. - */ - private SubscriptionInfo conditionallyRemoveIdentifiers(SubscriptionInfo subInfo, - String callingPackage, String callingFeatureId, String message) { - SubscriptionInfo result = subInfo; - int subId = subInfo.getSubscriptionId(); - boolean hasIdentifierAccess = hasSubscriberIdentifierAccess(subId, callingPackage, - callingFeatureId, message, true); - boolean hasPhoneNumberAccess = hasPhoneNumberAccess(subId, callingPackage, callingFeatureId, - message); - return conditionallyRemoveIdentifiers(subInfo, hasIdentifierAccess, hasPhoneNumberAccess); - } - - /** - * Conditionally removes identifiers from the provided {@code subInfo} based on if the calling - * package {@code hasIdentifierAccess} and {@code hasPhoneNumberAccess} and returns the - * potentially modified object. - * - * <p>If the caller specifies the package does not have identifier or phone number access - * a clone of the provided SubscriptionInfo is created and modified to avoid altering - * SubscriptionInfo objects in a cache. - */ - private SubscriptionInfo conditionallyRemoveIdentifiers(SubscriptionInfo subInfo, - boolean hasIdentifierAccess, boolean hasPhoneNumberAccess) { - if (hasIdentifierAccess && hasPhoneNumberAccess) { - return subInfo; - } - SubscriptionInfo result = new SubscriptionInfo(subInfo); - if (!hasIdentifierAccess) { - result.clearIccId(); - result.clearCardString(); - result.clearGroupUuid(); - } - if (!hasPhoneNumberAccess) { - result.clearNumber(); - } - return result; - } - - private synchronized boolean addToSubIdList(int slotIndex, int subId, int subscriptionType) { - ArrayList<Integer> subIdsList = sSlotIndexToSubIds.getCopy(slotIndex); - if (subIdsList == null) { - subIdsList = new ArrayList<>(); - sSlotIndexToSubIds.put(slotIndex, subIdsList); - } - - // add the given subId unless it already exists - if (subIdsList.contains(subId)) { - logdl("slotIndex, subId combo already exists in the map. Not adding it again."); - return false; - } - if (isSubscriptionForRemoteSim(subscriptionType)) { - // For Remote SIM subscriptions, a slot can have multiple subscriptions. - sSlotIndexToSubIds.addToSubIdList(slotIndex, subId); - } else { - // for all other types of subscriptions, a slot can have only one subscription at a time - sSlotIndexToSubIds.clearSubIdList(slotIndex); - sSlotIndexToSubIds.addToSubIdList(slotIndex, subId); - } - - - // Remove the slot from sSlotIndexToSubIds if it has the same sub id with the added slot - for (Entry<Integer, ArrayList<Integer>> entry : sSlotIndexToSubIds.entrySet()) { - if (entry.getKey() != slotIndex && entry.getValue() != null - && entry.getValue().contains(subId)) { - logdl("addToSubIdList - remove " + entry.getKey()); - sSlotIndexToSubIds.remove(entry.getKey()); - } - } - - if (DBG) logdl("slotIndex, subId combo is added to the map."); - return true; - } - - private boolean isSubscriptionForRemoteSim(int subscriptionType) { - return subscriptionType == SubscriptionManager.SUBSCRIPTION_TYPE_REMOTE_SIM; - } - - /** - * This is only for testing - * @hide - */ - @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) - public Map<Integer, ArrayList<Integer>> getSlotIndexToSubIdsMap() { - return sSlotIndexToSubIds.getMap(); - } - - /** - * This is only for testing - * @hide - */ - @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) - public void resetStaticMembers() { - sDefaultFallbackSubId.set(SubscriptionManager.INVALID_SUBSCRIPTION_ID); - mDefaultPhoneId = SubscriptionManager.DEFAULT_PHONE_INDEX; - } - - private void notifyOpportunisticSubscriptionInfoChanged() { - TelephonyRegistryManager trm = - (TelephonyRegistryManager) - mContext.getSystemService(Context.TELEPHONY_REGISTRY_SERVICE); - if (DBG) logd("notifyOpptSubscriptionInfoChanged:"); - trm.notifyOpportunisticSubscriptionInfoChanged(); - } - - private void refreshCachedOpportunisticSubscriptionInfoList() { - List<SubscriptionInfo> subList = getSubInfo( - SubscriptionManager.IS_OPPORTUNISTIC + "=1 AND (" - + SubscriptionManager.SIM_SLOT_INDEX + ">=0 OR " - + SubscriptionManager.IS_EMBEDDED + "=1)", null); - synchronized (mSubInfoListLock) { - List<SubscriptionInfo> oldOpptCachedList = mCacheOpportunisticSubInfoList; - - if (subList != null) { - subList.sort(SUBSCRIPTION_INFO_COMPARATOR); - } else { - subList = new ArrayList<>(); - } - - mCacheOpportunisticSubInfoList = subList; - - for (SubscriptionInfo info : mCacheOpportunisticSubInfoList) { - if (shouldDisableSubGroup(info.getGroupUuid())) { - info.setGroupDisabled(true); - } - } - - if (DBG_CACHE) { - if (!mCacheOpportunisticSubInfoList.isEmpty()) { - for (SubscriptionInfo si : mCacheOpportunisticSubInfoList) { - logd("[refreshCachedOpptSubscriptionInfoList] Setting Cached info=" - + si); - } - } else { - logdl("[refreshCachedOpptSubscriptionInfoList]- no info return"); - } - } - - if (!oldOpptCachedList.equals(mCacheOpportunisticSubInfoList)) { - mOpptSubInfoListChangedDirtyBit.set(true); - } - } - } - - private boolean shouldDisableSubGroup(ParcelUuid groupUuid) { - if (groupUuid == null) return false; - - synchronized (mSubInfoListLock) { - for (SubscriptionInfo activeInfo : mCacheActiveSubInfoList) { - if (!activeInfo.isOpportunistic() && groupUuid.equals(activeInfo.getGroupUuid())) { - return false; - } - } - } - - return true; - } - - /** - * Set allowing mobile data during voice call. - * - * @param subId Subscription index - * @param rules Data enabled override rules in string format. See {@link DataEnabledOverride} - * for details. - * @return {@code true} if settings changed, otherwise {@code false}. - */ - public boolean setDataEnabledOverrideRules(int subId, @NonNull String rules) { - if (DBG) logd("[setDataEnabledOverrideRules]+ rules:" + rules + " subId:" + subId); - - validateSubId(subId); - ContentValues value = new ContentValues(1); - value.put(SubscriptionManager.DATA_ENABLED_OVERRIDE_RULES, rules); - - boolean result = updateDatabase(value, subId, true) > 0; - - if (result) { - // Refresh the Cache of Active Subscription Info List - refreshCachedActiveSubscriptionInfoList(); - notifySubscriptionInfoChanged(); - } - - return result; - } - - /** - * Get data enabled override rules. - * - * @param subId Subscription index - * @return Data enabled override rules in string - */ - @NonNull - public String getDataEnabledOverrideRules(int subId) { - return TelephonyUtils.emptyIfNull(getSubscriptionProperty(subId, - SubscriptionManager.DATA_ENABLED_OVERRIDE_RULES)); - } - - /** - * Get active data subscription id. - * - * @return Active data subscription id - * - * @hide - */ - @Override - public int getActiveDataSubscriptionId() { - final long token = Binder.clearCallingIdentity(); - - try { - PhoneSwitcher phoneSwitcher = PhoneSwitcher.getInstance(); - if (phoneSwitcher != null) { - int activeDataSubId = phoneSwitcher.getActiveDataSubId(); - if (SubscriptionManager.isUsableSubscriptionId(activeDataSubId)) { - return activeDataSubId; - } - } - // If phone switcher isn't ready, or active data sub id is not available, use default - // sub id from settings. - return getDefaultDataSubId(); - } finally { - Binder.restoreCallingIdentity(token); - } - } - - /** - * Whether it's supported to disable / re-enable a subscription on a physical (non-euicc) SIM. - */ - @Override - public boolean canDisablePhysicalSubscription() { - enforceReadPrivilegedPhoneState("canToggleUiccApplicationsEnablement"); - - final long identity = Binder.clearCallingIdentity(); - try { - Phone phone = PhoneFactory.getDefaultPhone(); - return phone != null && phone.canDisablePhysicalSubscription(); - } finally { - Binder.restoreCallingIdentity(identity); - } - } - - /* - * Returns the phone number for the given {@code subId} and {@code source}, - * or an empty string if not available. - */ - @Override - public String getPhoneNumber(int subId, int source, - String callingPackage, String callingFeatureId) { - TelephonyPermissions.enforceAnyPermissionGrantedOrCarrierPrivileges( - mContext, subId, Binder.getCallingUid(), "getPhoneNumber", - READ_PHONE_NUMBERS, READ_PRIVILEGED_PHONE_STATE); - - final long identity = Binder.clearCallingIdentity(); - try { - String number = getPhoneNumber(subId, source); - return number == null ? "" : number; - } finally { - Binder.restoreCallingIdentity(identity); - } - } - - /* - * Returns the phone number for the given {@code subId} or an empty string if not available. - * - * <p>Built up on getPhoneNumber(int subId, int source) this API picks the 1st available - * source based on a priority order. - */ - @Override - public String getPhoneNumberFromFirstAvailableSource(int subId, - String callingPackage, String callingFeatureId) { - TelephonyPermissions.enforceAnyPermissionGrantedOrCarrierPrivileges( - mContext, subId, Binder.getCallingUid(), "getPhoneNumberFromFirstAvailableSource", - READ_PHONE_NUMBERS, READ_PRIVILEGED_PHONE_STATE); - - final long identity = Binder.clearCallingIdentity(); - try { - String numberFromCarrier = getPhoneNumber( - subId, SubscriptionManager.PHONE_NUMBER_SOURCE_CARRIER); - if (!TextUtils.isEmpty(numberFromCarrier)) { - return numberFromCarrier; - } - String numberFromUicc = getPhoneNumber( - subId, SubscriptionManager.PHONE_NUMBER_SOURCE_UICC); - if (!TextUtils.isEmpty(numberFromUicc)) { - return numberFromUicc; - } - String numberFromIms = getPhoneNumber( - subId, SubscriptionManager.PHONE_NUMBER_SOURCE_IMS); - if (!TextUtils.isEmpty(numberFromIms)) { - return numberFromIms; - } - return ""; - } finally { - Binder.restoreCallingIdentity(identity); - } - } - - // Internal helper method for implementing getPhoneNumber() API. - @Nullable - private String getPhoneNumber(int subId, int source) { - if (source == SubscriptionManager.PHONE_NUMBER_SOURCE_UICC) { - Phone phone = PhoneFactory.getPhone(getPhoneId(subId)); - return phone != null ? phone.getLine1Number() : null; - } - if (source == SubscriptionManager.PHONE_NUMBER_SOURCE_CARRIER) { - return getSubscriptionProperty(subId, SimInfo.COLUMN_PHONE_NUMBER_SOURCE_CARRIER); - } - if (source == SubscriptionManager.PHONE_NUMBER_SOURCE_IMS) { - return getSubscriptionProperty(subId, SimInfo.COLUMN_PHONE_NUMBER_SOURCE_IMS); - } - throw new IllegalArgumentException("setPhoneNumber doesn't accept source " + source); - } - - /** - * Sets the phone number for the given {@code subId}. - * - * <p>The only accepted {@code source} is {@link - * SubscriptionManager.PHONE_NUMBER_SOURCE_CARRIER}. - */ - @Override - public void setPhoneNumber(int subId, int source, String number, - String callingPackage, String callingFeatureId) { - if (source != SubscriptionManager.PHONE_NUMBER_SOURCE_CARRIER) { - throw new IllegalArgumentException("setPhoneNumber doesn't accept source " + source); - } - if (!TelephonyPermissions.checkCarrierPrivilegeForSubId(mContext, subId)) { - throw new SecurityException("setPhoneNumber for CARRIER needs carrier privilege"); - } - if (number == null) { - throw new NullPointerException("invalid number null"); - } - - final long identity = Binder.clearCallingIdentity(); - try { - setSubscriptionProperty(subId, SimInfo.COLUMN_PHONE_NUMBER_SOURCE_CARRIER, number); - } finally { - Binder.restoreCallingIdentity(identity); - } - } - - /** - * Set the Usage Setting for this subscription. - * - * @param usageSetting the cellular usage setting - * @param subId the unique SubscriptionInfo index in database - * @param callingPackage the package making the IPC - * @return the number of records updated - * - * @throws SecurityException if doesn't have required permission. - */ - @Override - public int setUsageSetting(@UsageSetting int usageSetting, int subId, String callingPackage) { - try { - TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege( - mContext, subId, callingPackage); - } catch (SecurityException e) { - enforceCarrierPrivilegeOnInactiveSub(subId, callingPackage, - "Caller requires permission on sub " + subId); - } - - if (usageSetting < SubscriptionManager.USAGE_SETTING_DEFAULT - || usageSetting > SubscriptionManager.USAGE_SETTING_DATA_CENTRIC) { - throw new IllegalArgumentException("setUsageSetting: Invalid usage setting: " - + usageSetting); - } - - final long token = Binder.clearCallingIdentity(); - int ret; - try { - ret = setSubscriptionProperty(subId, SubscriptionManager.USAGE_SETTING, - String.valueOf(usageSetting)); - - // ret is the number of records updated in the DB, which should always be 1. - // TODO(b/205027930): move this check prior to the database mutation request - if (ret != 1) throw new IllegalArgumentException( - "Invalid SubscriptionId for setUsageSetting"); - } finally { - Binder.restoreCallingIdentity(token); - // FIXME(b/205726099) return void - } - return ret; - } - - /** - * @hide - */ - private void setGlobalSetting(String name, int value) { - Settings.Global.putInt(mContext.getContentResolver(), name, value); - if (name == Settings.Global.MULTI_SIM_DATA_CALL_SUBSCRIPTION) { - invalidateDefaultDataSubIdCaches(); - invalidateActiveDataSubIdCaches(); - invalidateDefaultSubIdCaches(); - invalidateSlotIndexCaches(); - } else if (name == Settings.Global.MULTI_SIM_VOICE_CALL_SUBSCRIPTION) { - invalidateDefaultSubIdCaches(); - invalidateSlotIndexCaches(); - } else if (name == Settings.Global.MULTI_SIM_SMS_SUBSCRIPTION) { - invalidateDefaultSmsSubIdCaches(); - } - } - - /** - * @hide - */ - private static void invalidateDefaultSubIdCaches() { - if (sCachingEnabled) { - SubscriptionManager.invalidateDefaultSubIdCaches(); - } - } - - /** - * @hide - */ - private static void invalidateDefaultDataSubIdCaches() { - if (sCachingEnabled) { - SubscriptionManager.invalidateDefaultDataSubIdCaches(); - } - } - - /** - * @hide - */ - private static void invalidateDefaultSmsSubIdCaches() { - if (sCachingEnabled) { - SubscriptionManager.invalidateDefaultSmsSubIdCaches(); - } - } - - /** - * @hide - */ - public static void invalidateActiveDataSubIdCaches() { - if (sCachingEnabled) { - SubscriptionManager.invalidateActiveDataSubIdCaches(); - } - } - - /** - * @hide - */ - protected static void invalidateSlotIndexCaches() { - if (sCachingEnabled) { - SubscriptionManager.invalidateSlotIndexCaches(); - } - } - - /** - * @hide - */ - @VisibleForTesting - public static void disableCaching() { - sCachingEnabled = false; - } - - /** - * @hide - */ - @VisibleForTesting - public static void enableCaching() { - sCachingEnabled = true; - } -}
diff --git a/src/java/com/android/internal/telephony/SubscriptionInfoUpdater.java b/src/java/com/android/internal/telephony/SubscriptionInfoUpdater.java deleted file mode 100644 index 3ab229a..0000000 --- a/src/java/com/android/internal/telephony/SubscriptionInfoUpdater.java +++ /dev/null
@@ -1,1392 +0,0 @@ -/* -* Copyright (C) 2014 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - -package com.android.internal.telephony; - -import android.Manifest; -import android.annotation.Nullable; -import android.app.ActivityManager; -import android.compat.annotation.UnsupportedAppUsage; -import android.content.BroadcastReceiver; -import android.content.ContentResolver; -import android.content.ContentValues; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.SharedPreferences; -import android.content.res.Resources; -import android.os.AsyncResult; -import android.os.Build; -import android.os.Handler; -import android.os.Looper; -import android.os.Message; -import android.os.ParcelUuid; -import android.os.PersistableBundle; -import android.os.UserHandle; -import android.preference.PreferenceManager; -import android.service.carrier.CarrierIdentifier; -import android.service.euicc.EuiccProfileInfo; -import android.service.euicc.EuiccService; -import android.service.euicc.GetEuiccProfileInfoListResult; -import android.telephony.CarrierConfigManager; -import android.telephony.SubscriptionInfo; -import android.telephony.SubscriptionManager; -import android.telephony.SubscriptionManager.UsageSetting; -import android.telephony.TelephonyManager; -import android.telephony.TelephonyManager.SimState; -import android.telephony.UiccAccessRule; -import android.telephony.euicc.EuiccManager; -import android.text.TextUtils; -import android.util.Pair; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.telephony.euicc.EuiccController; -import com.android.internal.telephony.metrics.TelephonyMetrics; -import com.android.internal.telephony.uicc.IccRecords; -import com.android.internal.telephony.uicc.IccUtils; -import com.android.internal.telephony.uicc.UiccCard; -import com.android.internal.telephony.uicc.UiccController; -import com.android.internal.telephony.uicc.UiccPort; -import com.android.internal.telephony.uicc.UiccSlot; -import com.android.telephony.Rlog; - -import java.io.FileDescriptor; -import java.io.PrintWriter; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.CopyOnWriteArraySet; - -/** - *@hide - */ -public class SubscriptionInfoUpdater extends Handler { - private static final String LOG_TAG = "SubscriptionInfoUpdater"; - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - private static final int SUPPORTED_MODEM_COUNT = TelephonyManager.getDefault() - .getSupportedModemCount(); - - private static final boolean DBG = true; - - private static final int EVENT_INVALID = -1; - private static final int EVENT_GET_NETWORK_SELECTION_MODE_DONE = 2; - private static final int EVENT_SIM_LOADED = 3; - private static final int EVENT_SIM_ABSENT = 4; - private static final int EVENT_SIM_LOCKED = 5; - private static final int EVENT_SIM_IO_ERROR = 6; - private static final int EVENT_SIM_UNKNOWN = 7; - private static final int EVENT_SIM_RESTRICTED = 8; - private static final int EVENT_SIM_NOT_READY = 9; - private static final int EVENT_SIM_READY = 10; - private static final int EVENT_SIM_IMSI = 11; - private static final int EVENT_REFRESH_EMBEDDED_SUBSCRIPTIONS = 12; - private static final int EVENT_MULTI_SIM_CONFIG_CHANGED = 13; - private static final int EVENT_INACTIVE_SLOT_ICC_STATE_CHANGED = 14; - - private static final String ICCID_STRING_FOR_NO_SIM = ""; - - private static final ParcelUuid REMOVE_GROUP_UUID = - ParcelUuid.fromString(CarrierConfigManager.REMOVE_GROUP_UUID_STRING); - - // Key used to read/write the current IMSI. Updated on SIM_STATE_CHANGED - LOADED. - public static final String CURR_SUBID = "curr_subid"; - - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - private static Context sContext = null; - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - - protected static String[] sIccId = new String[SUPPORTED_MODEM_COUNT]; - protected SubscriptionController mSubscriptionController = null; - private static String[] sInactiveIccIds = new String[SUPPORTED_MODEM_COUNT]; - private static int[] sSimCardState = new int[SUPPORTED_MODEM_COUNT]; - private static int[] sSimApplicationState = new int[SUPPORTED_MODEM_COUNT]; - private static boolean sIsSubInfoInitialized = false; - private SubscriptionManager mSubscriptionManager = null; - private EuiccManager mEuiccManager; - private Handler mBackgroundHandler; - - // The current foreground user ID. - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - private int mCurrentlyActiveUserId; - private CarrierServiceBindHelper mCarrierServiceBindHelper; - - private volatile boolean shouldRetryUpdateEmbeddedSubscriptions = false; - private final CopyOnWriteArraySet<Integer> retryUpdateEmbeddedSubscriptionCards = - new CopyOnWriteArraySet<>(); - private final BroadcastReceiver mUserUnlockedReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - if (Intent.ACTION_USER_UNLOCKED.equals(intent.getAction())) { - // The LPA may not have been ready before user unlock, and so previous attempts - // to refresh the list of embedded subscriptions may have failed. This retries - // the refresh operation after user unlock. - if (shouldRetryUpdateEmbeddedSubscriptions) { - logd("Retrying refresh embedded subscriptions after user unlock."); - for (int cardId : retryUpdateEmbeddedSubscriptionCards){ - requestEmbeddedSubscriptionInfoListRefresh(cardId, null); - } - retryUpdateEmbeddedSubscriptionCards.clear(); - sContext.unregisterReceiver(mUserUnlockedReceiver); - } - } - } - }; - - /** - * Runnable with a boolean parameter. This is used in - * updateEmbeddedSubscriptions(List<Integer> cardIds, @Nullable UpdateEmbeddedSubsCallback). - */ - protected interface UpdateEmbeddedSubsCallback { - /** - * Callback of the Runnable. - * @param hasChanges Whether there is any subscription info change. If yes, we need to - * notify the listeners. - */ - void run(boolean hasChanges); - } - - @VisibleForTesting - public SubscriptionInfoUpdater(Looper looper, Context context, SubscriptionController sc) { - logd("Constructor invoked"); - mBackgroundHandler = new Handler(looper); - - sContext = context; - mSubscriptionController = sc; - mSubscriptionManager = SubscriptionManager.from(sContext); - mEuiccManager = (EuiccManager) sContext.getSystemService(Context.EUICC_SERVICE); - - mCarrierServiceBindHelper = new CarrierServiceBindHelper(sContext); - - sContext.registerReceiver( - mUserUnlockedReceiver, new IntentFilter(Intent.ACTION_USER_UNLOCKED)); - - initializeCarrierApps(); - - PhoneConfigurationManager.registerForMultiSimConfigChange( - this, EVENT_MULTI_SIM_CONFIG_CHANGED, null); - } - - private void initializeCarrierApps() { - // Initialize carrier apps: - // -Now (on system startup) - // -Whenever new carrier privilege rules might change (new SIM is loaded) - // -Whenever we switch to a new user - mCurrentlyActiveUserId = 0; - sContext.registerReceiverForAllUsers(new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - // Remove this line after testing - if (Intent.ACTION_USER_FOREGROUND.equals(intent.getAction())) { - UserHandle userHandle = intent.getParcelableExtra(Intent.EXTRA_USER); - // If couldn't get current user ID, guess it's 0. - mCurrentlyActiveUserId = userHandle != null ? userHandle.getIdentifier() : 0; - CarrierAppUtils.disableCarrierAppsUntilPrivileged(sContext.getOpPackageName(), - TelephonyManager.getDefault(), mCurrentlyActiveUserId, sContext); - } - } - }, new IntentFilter(Intent.ACTION_USER_FOREGROUND), null, null); - ActivityManager am = (ActivityManager) sContext.getSystemService(Context.ACTIVITY_SERVICE); - mCurrentlyActiveUserId = am.getCurrentUser(); - CarrierAppUtils.disableCarrierAppsUntilPrivileged(sContext.getOpPackageName(), - TelephonyManager.getDefault(), mCurrentlyActiveUserId, sContext); - } - - /** - * Update subscriptions when given a new ICC state. - */ - public void updateInternalIccState(String simStatus, String reason, int phoneId) { - logd("updateInternalIccState to simStatus " + simStatus + " reason " + reason - + " phoneId " + phoneId); - int message = internalIccStateToMessage(simStatus); - if (message != EVENT_INVALID) { - sendMessage(obtainMessage(message, phoneId, 0, reason)); - } - } - - /** - * Update subscriptions if needed when there's a change in inactive port. - * @param prevActivePhoneId is the corresponding phoneId of the port if port was previously - * active. It could be INVALID if it was already inactive. - * @param iccId iccId in that port, if any. - */ - public void updateInternalIccStateForInactivePort(int prevActivePhoneId, String iccId) { - sendMessage(obtainMessage(EVENT_INACTIVE_SLOT_ICC_STATE_CHANGED, prevActivePhoneId, - 0, iccId)); - } - - private int internalIccStateToMessage(String simStatus) { - switch(simStatus) { - case IccCardConstants.INTENT_VALUE_ICC_ABSENT: return EVENT_SIM_ABSENT; - case IccCardConstants.INTENT_VALUE_ICC_UNKNOWN: return EVENT_SIM_UNKNOWN; - case IccCardConstants.INTENT_VALUE_ICC_CARD_IO_ERROR: return EVENT_SIM_IO_ERROR; - case IccCardConstants.INTENT_VALUE_ICC_CARD_RESTRICTED: return EVENT_SIM_RESTRICTED; - case IccCardConstants.INTENT_VALUE_ICC_NOT_READY: return EVENT_SIM_NOT_READY; - case IccCardConstants.INTENT_VALUE_ICC_LOCKED: return EVENT_SIM_LOCKED; - case IccCardConstants.INTENT_VALUE_ICC_LOADED: return EVENT_SIM_LOADED; - case IccCardConstants.INTENT_VALUE_ICC_READY: return EVENT_SIM_READY; - case IccCardConstants.INTENT_VALUE_ICC_IMSI: return EVENT_SIM_IMSI; - default: - logd("Ignoring simStatus: " + simStatus); - return EVENT_INVALID; - } - } - - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - protected boolean isAllIccIdQueryDone() { - for (int i = 0; i < TelephonyManager.getDefault().getActiveModemCount(); i++) { - UiccSlot slot = UiccController.getInstance().getUiccSlotForPhone(i); - int slotId = UiccController.getInstance().getSlotIdFromPhoneId(i); - // When psim card is absent there is no port object even the port state is active. - // We should check the slot state for psim and port state for esim(MEP eUICC). - if (sIccId[i] == null || slot == null || !slot.isActive() - || (slot.isEuicc() && UiccController.getInstance().getUiccPort(i) == null)) { - if (sIccId[i] == null) { - logd("Wait for SIM " + i + " Iccid"); - } else { - logd(String.format("Wait for port corresponding to phone %d to be active, " - + "slotId is %d" + " , portIndex is %d", i, slotId, - slot.getPortIndexFromPhoneId(i))); - } - return false; - } - } - logd("All IccIds query complete"); - - return true; - } - - @Override - public void handleMessage(Message msg) { - List<Integer> cardIds = new ArrayList<>(); - switch (msg.what) { - case EVENT_GET_NETWORK_SELECTION_MODE_DONE: { - AsyncResult ar = (AsyncResult)msg.obj; - Integer slotId = (Integer)ar.userObj; - if (ar.exception == null && ar.result != null) { - int[] modes = (int[])ar.result; - if (modes[0] == 1) { // Manual mode. - PhoneFactory.getPhone(slotId).setNetworkSelectionModeAutomatic(null); - } - } else { - logd("EVENT_GET_NETWORK_SELECTION_MODE_DONE: error getting network mode."); - } - break; - } - - case EVENT_SIM_LOADED: - handleSimLoaded(msg.arg1); - break; - - case EVENT_SIM_ABSENT: - handleSimAbsent(msg.arg1); - break; - - case EVENT_INACTIVE_SLOT_ICC_STATE_CHANGED: - handleInactivePortIccStateChange(msg.arg1, (String) msg.obj); - break; - - case EVENT_SIM_LOCKED: - handleSimLocked(msg.arg1, (String) msg.obj); - break; - - case EVENT_SIM_UNKNOWN: - broadcastSimStateChanged(msg.arg1, IccCardConstants.INTENT_VALUE_ICC_UNKNOWN, null); - broadcastSimCardStateChanged(msg.arg1, TelephonyManager.SIM_STATE_UNKNOWN); - broadcastSimApplicationStateChanged(msg.arg1, TelephonyManager.SIM_STATE_UNKNOWN); - updateSubscriptionCarrierId(msg.arg1, IccCardConstants.INTENT_VALUE_ICC_UNKNOWN); - updateCarrierServices(msg.arg1, IccCardConstants.INTENT_VALUE_ICC_UNKNOWN); - break; - - case EVENT_SIM_IO_ERROR: - handleSimError(msg.arg1); - break; - - case EVENT_SIM_RESTRICTED: - broadcastSimStateChanged(msg.arg1, - IccCardConstants.INTENT_VALUE_ICC_CARD_RESTRICTED, - IccCardConstants.INTENT_VALUE_ICC_CARD_RESTRICTED); - broadcastSimCardStateChanged(msg.arg1, TelephonyManager.SIM_STATE_CARD_RESTRICTED); - broadcastSimApplicationStateChanged(msg.arg1, TelephonyManager.SIM_STATE_NOT_READY); - updateSubscriptionCarrierId(msg.arg1, - IccCardConstants.INTENT_VALUE_ICC_CARD_RESTRICTED); - updateCarrierServices(msg.arg1, IccCardConstants.INTENT_VALUE_ICC_CARD_RESTRICTED); - break; - - case EVENT_SIM_READY: - handleSimReady(msg.arg1); - break; - - case EVENT_SIM_IMSI: - broadcastSimStateChanged(msg.arg1, IccCardConstants.INTENT_VALUE_ICC_IMSI, null); - break; - - case EVENT_SIM_NOT_READY: - // an eUICC with no active subscriptions never becomes ready, so we need to trigger - // the embedded subscriptions update here - cardIds.add(getCardIdFromPhoneId(msg.arg1)); - updateEmbeddedSubscriptions(cardIds, (hasChanges) -> { - if (hasChanges) { - mSubscriptionController.notifySubscriptionInfoChanged(); - } - }); - handleSimNotReady(msg.arg1); - break; - - case EVENT_REFRESH_EMBEDDED_SUBSCRIPTIONS: - cardIds.add(msg.arg1); - Runnable r = (Runnable) msg.obj; - updateEmbeddedSubscriptions(cardIds, (hasChanges) -> { - if (hasChanges) { - mSubscriptionController.notifySubscriptionInfoChanged(); - } - if (r != null) { - r.run(); - } - }); - break; - - case EVENT_MULTI_SIM_CONFIG_CHANGED: - onMultiSimConfigChanged(); - break; - - default: - logd("Unknown msg:" + msg.what); - } - } - - private void onMultiSimConfigChanged() { - int activeModemCount = ((TelephonyManager) sContext.getSystemService( - Context.TELEPHONY_SERVICE)).getActiveModemCount(); - // For inactive modems, reset its states. - for (int phoneId = activeModemCount; phoneId < SUPPORTED_MODEM_COUNT; phoneId++) { - sIccId[phoneId] = null; - sSimCardState[phoneId] = TelephonyManager.SIM_STATE_UNKNOWN; - sSimApplicationState[phoneId] = TelephonyManager.SIM_STATE_UNKNOWN; - } - } - - protected int getCardIdFromPhoneId(int phoneId) { - UiccController uiccController = UiccController.getInstance(); - UiccCard card = uiccController.getUiccCardForPhone(phoneId); - if (card != null) { - return uiccController.convertToPublicCardId(card.getCardId()); - } - return TelephonyManager.UNINITIALIZED_CARD_ID; - } - - void requestEmbeddedSubscriptionInfoListRefresh(int cardId, @Nullable Runnable callback) { - sendMessage(obtainMessage( - EVENT_REFRESH_EMBEDDED_SUBSCRIPTIONS, cardId, 0 /* arg2 */, callback)); - } - - protected void handleSimLocked(int phoneId, String reason) { - if (sIccId[phoneId] != null && sIccId[phoneId].equals(ICCID_STRING_FOR_NO_SIM)) { - logd("SIM" + (phoneId + 1) + " hot plug in"); - sIccId[phoneId] = null; - } - - IccCard iccCard = PhoneFactory.getPhone(phoneId).getIccCard(); - if (iccCard == null) { - logd("handleSimLocked: IccCard null"); - return; - } - IccRecords records = iccCard.getIccRecords(); - if (records == null) { - logd("handleSimLocked: IccRecords null"); - return; - } - if (IccUtils.stripTrailingFs(records.getFullIccId()) == null) { - logd("handleSimLocked: IccID null"); - return; - } - sIccId[phoneId] = IccUtils.stripTrailingFs(records.getFullIccId()); - - updateSubscriptionInfoByIccId(phoneId, true /* updateEmbeddedSubs */); - - broadcastSimStateChanged(phoneId, IccCardConstants.INTENT_VALUE_ICC_LOCKED, reason); - broadcastSimCardStateChanged(phoneId, TelephonyManager.SIM_STATE_PRESENT); - broadcastSimApplicationStateChanged(phoneId, getSimStateFromLockedReason(reason)); - updateSubscriptionCarrierId(phoneId, IccCardConstants.INTENT_VALUE_ICC_LOCKED); - updateCarrierServices(phoneId, IccCardConstants.INTENT_VALUE_ICC_LOCKED); - } - - private static int getSimStateFromLockedReason(String lockedReason) { - switch (lockedReason) { - case IccCardConstants.INTENT_VALUE_LOCKED_ON_PIN: - return TelephonyManager.SIM_STATE_PIN_REQUIRED; - case IccCardConstants.INTENT_VALUE_LOCKED_ON_PUK: - return TelephonyManager.SIM_STATE_PUK_REQUIRED; - case IccCardConstants.INTENT_VALUE_LOCKED_NETWORK: - return TelephonyManager.SIM_STATE_NETWORK_LOCKED; - case IccCardConstants.INTENT_VALUE_ABSENT_ON_PERM_DISABLED: - return TelephonyManager.SIM_STATE_PERM_DISABLED; - default: - Rlog.e(LOG_TAG, "Unexpected SIM locked reason " + lockedReason); - return TelephonyManager.SIM_STATE_UNKNOWN; - } - } - - protected void handleSimReady(int phoneId) { - List<Integer> cardIds = new ArrayList<>(); - logd("handleSimReady: phoneId: " + phoneId); - - if (sIccId[phoneId] != null && sIccId[phoneId].equals(ICCID_STRING_FOR_NO_SIM)) { - logd(" SIM" + (phoneId + 1) + " hot plug in"); - sIccId[phoneId] = null; - } - - // ICCID is not available in IccRecords by the time SIM Ready event received - // hence get ICCID from UiccPort. - UiccPort port = UiccController.getInstance().getUiccPort(phoneId); - String iccId = (port == null) ? null : IccUtils.stripTrailingFs(port.getIccId()); - - if (!TextUtils.isEmpty(iccId)) { - sIccId[phoneId] = iccId; - updateSubscriptionInfoByIccId(phoneId, true /* updateEmbeddedSubs */); - } - - cardIds.add(getCardIdFromPhoneId(phoneId)); - updateEmbeddedSubscriptions(cardIds, (hasChanges) -> { - if (hasChanges) { - mSubscriptionController.notifySubscriptionInfoChanged(); - } - }); - broadcastSimStateChanged(phoneId, IccCardConstants.INTENT_VALUE_ICC_READY, null); - broadcastSimCardStateChanged(phoneId, TelephonyManager.SIM_STATE_PRESENT); - broadcastSimApplicationStateChanged(phoneId, TelephonyManager.SIM_STATE_NOT_READY); - } - - protected void handleSimNotReady(int phoneId) { - logd("handleSimNotReady: phoneId: " + phoneId); - boolean isFinalState = false; - - IccCard iccCard = PhoneFactory.getPhone(phoneId).getIccCard(); - boolean uiccAppsDisabled = areUiccAppsDisabledOnCard(phoneId); - if (iccCard.isEmptyProfile() || uiccAppsDisabled) { - if (uiccAppsDisabled) { - UiccPort port = UiccController.getInstance().getUiccPort(phoneId); - String iccId = (port == null) ? null : port.getIccId(); - sInactiveIccIds[phoneId] = IccUtils.stripTrailingFs(iccId); - } - isFinalState = true; - // ICC_NOT_READY is a terminal state for - // 1) It's an empty profile as there's no uicc applications. Or - // 2) Its uicc applications are set to be disabled. - // 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; - } - - broadcastSimStateChanged(phoneId, IccCardConstants.INTENT_VALUE_ICC_NOT_READY, - null); - broadcastSimCardStateChanged(phoneId, TelephonyManager.SIM_STATE_PRESENT); - broadcastSimApplicationStateChanged(phoneId, TelephonyManager.SIM_STATE_NOT_READY); - if (isFinalState) { - updateCarrierServices(phoneId, IccCardConstants.INTENT_VALUE_ICC_NOT_READY); - } - } - - private boolean areUiccAppsDisabledOnCard(int phoneId) { - // When uicc apps are disabled(supported in IRadio 1.5), we will still get IccId from - // cardStatus (since IRadio 1.2). Amd upon cardStatus change we'll receive another - // handleSimNotReady so this will be evaluated again. - UiccSlot slot = UiccController.getInstance().getUiccSlotForPhone(phoneId); - if (slot == null) return false; - UiccPort port = UiccController.getInstance().getUiccPort(phoneId); - String iccId = (port == null) ? null : port.getIccId(); - if (iccId == null) { - return false; - } - SubscriptionInfo info = - mSubscriptionController.getSubInfoForIccId( - IccUtils.stripTrailingFs(iccId)); - return info != null && !info.areUiccApplicationsEnabled(); - } - - protected void handleSimLoaded(int phoneId) { - logd("handleSimLoaded: phoneId: " + phoneId); - - // The SIM should be loaded at this state, but it is possible in cases such as SIM being - // removed or a refresh RESET that the IccRecords could be null. The right behavior is to - // not broadcast the SIM loaded. - IccCard iccCard = PhoneFactory.getPhone(phoneId).getIccCard(); - if (iccCard == null) { // Possibly a race condition. - logd("handleSimLoaded: IccCard null"); - return; - } - IccRecords records = iccCard.getIccRecords(); - if (records == null) { // Possibly a race condition. - logd("handleSimLoaded: IccRecords null"); - return; - } - if (IccUtils.stripTrailingFs(records.getFullIccId()) == null) { - logd("handleSimLoaded: IccID null"); - return; - } - - // Call updateSubscriptionInfoByIccId() only if was not done earlier from SIM READY event - if (sIccId[phoneId] == null) { - sIccId[phoneId] = IccUtils.stripTrailingFs(records.getFullIccId()); - - updateSubscriptionInfoByIccId(phoneId, true /* updateEmbeddedSubs */); - } - - List<SubscriptionInfo> subscriptionInfos = - mSubscriptionController.getSubInfoUsingSlotIndexPrivileged(phoneId); - if (subscriptionInfos == null || subscriptionInfos.isEmpty()) { - loge("empty subinfo for phoneId: " + phoneId + "could not update ContentResolver"); - } else { - for (SubscriptionInfo sub : subscriptionInfos) { - int subId = sub.getSubscriptionId(); - TelephonyManager tm = (TelephonyManager) - sContext.getSystemService(Context.TELEPHONY_SERVICE); - String operator = tm.getSimOperatorNumeric(subId); - - if (!TextUtils.isEmpty(operator)) { - if (subId == mSubscriptionController.getDefaultSubId()) { - MccTable.updateMccMncConfiguration(sContext, operator); - } - mSubscriptionController.setMccMnc(operator, subId); - } else { - logd("EVENT_RECORDS_LOADED Operator name is null"); - } - - String iso = tm.getSimCountryIsoForPhone(phoneId); - - if (!TextUtils.isEmpty(iso)) { - mSubscriptionController.setCountryIso(iso, subId); - } else { - logd("EVENT_RECORDS_LOADED sim country iso is null"); - } - - String msisdn = tm.getLine1Number(subId); - if (msisdn != null) { - mSubscriptionController.setDisplayNumber(msisdn, subId); - } - - String imsi = tm.createForSubscriptionId(subId).getSubscriberId(); - if (imsi != null) { - mSubscriptionController.setImsi(imsi, subId); - } - - String[] ehplmns = records.getEhplmns(); - String[] hplmns = records.getPlmnsFromHplmnActRecord(); - if (ehplmns != null || hplmns != null) { - mSubscriptionController.setAssociatedPlmns(ehplmns, hplmns, subId); - } - - /* Update preferred network type and network selection mode on SIM change. - * Storing last subId in SharedPreference for now to detect SIM change. - */ - SharedPreferences sp = - PreferenceManager.getDefaultSharedPreferences(sContext); - int storedSubId = sp.getInt(CURR_SUBID + phoneId, -1); - - if (storedSubId != subId) { - // Only support automatic selection mode on SIM change. - PhoneFactory.getPhone(phoneId).getNetworkSelectionMode( - obtainMessage(EVENT_GET_NETWORK_SELECTION_MODE_DONE, - new Integer(phoneId))); - // Update stored subId - SharedPreferences.Editor editor = sp.edit(); - editor.putInt(CURR_SUBID + phoneId, subId); - editor.apply(); - } - } - } - - /** - * The sim loading sequence will be - * 1. ACTION_SUBINFO_CONTENT_CHANGE happens through updateSubscriptionInfoByIccId() above. - * 2. ACTION_SIM_STATE_CHANGED/ACTION_SIM_CARD_STATE_CHANGED - * /ACTION_SIM_APPLICATION_STATE_CHANGED - * 3. ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED - * 4. restore sim-specific settings - * 5. ACTION_CARRIER_CONFIG_CHANGED - */ - broadcastSimStateChanged(phoneId, IccCardConstants.INTENT_VALUE_ICC_LOADED, null); - broadcastSimCardStateChanged(phoneId, TelephonyManager.SIM_STATE_PRESENT); - broadcastSimApplicationStateChanged(phoneId, TelephonyManager.SIM_STATE_LOADED); - updateSubscriptionCarrierId(phoneId, IccCardConstants.INTENT_VALUE_ICC_LOADED); - /* Sim-specific settings restore depends on knowing both the mccmnc and the carrierId of the - sim which is why it must be done after #updateSubscriptionCarrierId(). It is done before - carrier config update to avoid any race conditions with user settings that depend on - carrier config*/ - restoreSimSpecificSettingsForPhone(phoneId); - updateCarrierServices(phoneId, IccCardConstants.INTENT_VALUE_ICC_LOADED); - } - - /** - * Calculate the usage setting based on the carrier request. - * - * @param currentUsageSetting the current setting in the subscription DB - * @param preferredUsageSetting provided by the carrier config - * @return the calculated usage setting. - */ - @VisibleForTesting - @UsageSetting public int calculateUsageSetting( - @UsageSetting int currentUsageSetting, @UsageSetting int preferredUsageSetting) { - int defaultUsageSetting; - int[] supportedUsageSettings; - - // Load the resources to provide the device capability - try { - defaultUsageSetting = sContext.getResources().getInteger( - com.android.internal.R.integer.config_default_cellular_usage_setting); - supportedUsageSettings = sContext.getResources().getIntArray( - com.android.internal.R.array.config_supported_cellular_usage_settings); - // If usage settings are not supported, return the default setting, which is UNKNOWN. - if (supportedUsageSettings == null - || supportedUsageSettings.length < 1) return currentUsageSetting; - } catch (Resources.NotFoundException nfe) { - loge("Failed to load usage setting resources!"); - return currentUsageSetting; - } - - // If the current setting is invalid, including the first time the value is set, - // update it to default (this will trigger a change in the DB). - if (currentUsageSetting < SubscriptionManager.USAGE_SETTING_DEFAULT - || currentUsageSetting > SubscriptionManager.USAGE_SETTING_DATA_CENTRIC) { - logd("Updating usage setting for current subscription"); - currentUsageSetting = SubscriptionManager.USAGE_SETTING_DEFAULT; - } - - // Range check the inputs, and on failure, make no changes - if (preferredUsageSetting < SubscriptionManager.USAGE_SETTING_DEFAULT - || preferredUsageSetting > SubscriptionManager.USAGE_SETTING_DATA_CENTRIC) { - loge("Invalid usage setting!" + preferredUsageSetting); - return currentUsageSetting; - } - - // Default is always allowed - if (preferredUsageSetting == SubscriptionManager.USAGE_SETTING_DEFAULT) { - return preferredUsageSetting; - } - - // Forced setting must be explicitly supported - for (int i = 0; i < supportedUsageSettings.length; i++) { - if (preferredUsageSetting == supportedUsageSettings[i]) return preferredUsageSetting; - } - - // If the preferred setting is not possible, just keep the current setting. - return currentUsageSetting; - } - - private void restoreSimSpecificSettingsForPhone(int phoneId) { - SubscriptionManager subManager = SubscriptionManager.from(sContext); - subManager.restoreSimSpecificSettingsForIccIdFromBackup(sIccId[phoneId]); - } - - private void updateCarrierServices(int phoneId, String simState) { - if (!SubscriptionManager.isValidPhoneId(phoneId)) { - logd("Ignore updateCarrierServices request with invalid phoneId " + phoneId); - return; - } - CarrierConfigManager configManager = - (CarrierConfigManager) sContext.getSystemService(Context.CARRIER_CONFIG_SERVICE); - configManager.updateConfigForPhoneId(phoneId, simState); - mCarrierServiceBindHelper.updateForPhoneId(phoneId, simState); - } - - private void updateSubscriptionCarrierId(int phoneId, String simState) { - if (PhoneFactory.getPhone(phoneId) != null) { - PhoneFactory.getPhone(phoneId).resolveSubscriptionCarrierId(simState); - } - } - - /** - * PhoneId is the corresponding phoneId of the port if port was previously active. - * It could be INVALID if it was already inactive. - */ - private void handleInactivePortIccStateChange(int phoneId, String iccId) { - if (SubscriptionManager.isValidPhoneId(phoneId)) { - // If phoneId is valid, it means the physical slot was previously active in that - // phoneId. In this case, found the subId and set its phoneId to invalid. - if (sIccId[phoneId] != null && !sIccId[phoneId].equals(ICCID_STRING_FOR_NO_SIM)) { - logd("Slot of SIM" + (phoneId + 1) + " becomes inactive"); - } - cleanSubscriptionInPhone(phoneId, false); - } - if (!TextUtils.isEmpty(iccId)) { - // If iccId is new, add a subscription record in the db. - String strippedIccId = IccUtils.stripTrailingFs(iccId); - if (mSubscriptionController.getSubInfoForIccId(strippedIccId) == null) { - mSubscriptionController.insertEmptySubInfoRecord( - strippedIccId, "CARD", SubscriptionManager.INVALID_PHONE_INDEX, - SubscriptionManager.SUBSCRIPTION_TYPE_LOCAL_SIM); - } - } - } - - /** - * Clean subscription info when sim state becomes ABSENT. There are 2 scenarios for this: - * 1. SIM is actually removed - * 2. Slot becomes inactive, which results in SIM being treated as ABSENT, but SIM may not - * have been removed. - * @param phoneId phoneId for which the cleanup needs to be done - * @param isSimAbsent boolean to indicate if the SIM is actually ABSENT (case 1 above) - */ - private void cleanSubscriptionInPhone(int phoneId, boolean isSimAbsent) { - if (sInactiveIccIds[phoneId] != null || (isSimAbsent && sIccId[phoneId] != null - && !sIccId[phoneId].equals(ICCID_STRING_FOR_NO_SIM))) { - // When a SIM is unplugged, mark uicc applications enabled. This is to make sure when - // user unplugs and re-inserts the SIM card, we re-enable it. - // In certain cases this can happen before sInactiveIccIds is updated, which is why we - // check for sIccId as well (in case of isSimAbsent). The scenario is: after SIM - // deactivate request is sent to RIL, SIM is removed before SIM state is updated to - // NOT_READY. We do not need to check if this exact scenario is hit, because marking - // uicc applications enabled when SIM is removed should be okay to do regardless. - logd("cleanSubscriptionInPhone: " + phoneId + ", inactive iccid " - + sInactiveIccIds[phoneId]); - if (sInactiveIccIds[phoneId] == null) { - logd("cleanSubscriptionInPhone: " + phoneId + ", isSimAbsent=" + isSimAbsent - + ", iccid=" + sIccId[phoneId]); - } - String iccId = sInactiveIccIds[phoneId] != null - ? sInactiveIccIds[phoneId] : sIccId[phoneId]; - ContentValues value = new ContentValues(); - value.put(SubscriptionManager.UICC_APPLICATIONS_ENABLED, true); - if (isSimAbsent) { - // When sim is absent, set the port index to invalid port index -1; - value.put(SubscriptionManager.PORT_INDEX, TelephonyManager.INVALID_PORT_INDEX); - } - sContext.getContentResolver().update(SubscriptionManager.CONTENT_URI, value, - SubscriptionManager.ICC_ID + "=\'" + iccId + "\'", null); - sInactiveIccIds[phoneId] = null; - } - sIccId[phoneId] = ICCID_STRING_FOR_NO_SIM; - updateSubscriptionInfoByIccId(phoneId, true /* updateEmbeddedSubs */); - } - - protected void handleSimAbsent(int phoneId) { - if (!SubscriptionManager.isValidPhoneId(phoneId)) { - logd("handleSimAbsent on invalid phoneId"); - return; - } - if (sIccId[phoneId] != null && !sIccId[phoneId].equals(ICCID_STRING_FOR_NO_SIM)) { - logd("SIM" + (phoneId + 1) + " hot plug out"); - } - cleanSubscriptionInPhone(phoneId, true); - - broadcastSimStateChanged(phoneId, IccCardConstants.INTENT_VALUE_ICC_ABSENT, null); - broadcastSimCardStateChanged(phoneId, TelephonyManager.SIM_STATE_ABSENT); - broadcastSimApplicationStateChanged(phoneId, TelephonyManager.SIM_STATE_UNKNOWN); - updateSubscriptionCarrierId(phoneId, IccCardConstants.INTENT_VALUE_ICC_ABSENT); - updateCarrierServices(phoneId, IccCardConstants.INTENT_VALUE_ICC_ABSENT); - } - - protected void handleSimError(int phoneId) { - if (sIccId[phoneId] != null && !sIccId[phoneId].equals(ICCID_STRING_FOR_NO_SIM)) { - logd("SIM" + (phoneId + 1) + " Error "); - } - sIccId[phoneId] = ICCID_STRING_FOR_NO_SIM; - updateSubscriptionInfoByIccId(phoneId, true /* updateEmbeddedSubs */); - broadcastSimStateChanged(phoneId, IccCardConstants.INTENT_VALUE_ICC_CARD_IO_ERROR, - IccCardConstants.INTENT_VALUE_ICC_CARD_IO_ERROR); - broadcastSimCardStateChanged(phoneId, TelephonyManager.SIM_STATE_CARD_IO_ERROR); - broadcastSimApplicationStateChanged(phoneId, TelephonyManager.SIM_STATE_NOT_READY); - updateSubscriptionCarrierId(phoneId, IccCardConstants.INTENT_VALUE_ICC_CARD_IO_ERROR); - updateCarrierServices(phoneId, IccCardConstants.INTENT_VALUE_ICC_CARD_IO_ERROR); - } - - protected synchronized void updateSubscriptionInfoByIccId(int phoneId, - boolean updateEmbeddedSubs) { - logd("updateSubscriptionInfoByIccId:+ Start - phoneId: " + phoneId); - if (!SubscriptionManager.isValidPhoneId(phoneId)) { - loge("[updateSubscriptionInfoByIccId]- invalid phoneId=" + phoneId); - return; - } - logd("updateSubscriptionInfoByIccId: removing subscription info record: phoneId " - + phoneId); - // Clear phoneId only when sim absent is not enough. It's possible to switch SIM profile - // within the same slot. Need to clear the slot index of the previous sub. Thus always clear - // for the changing slot first. - mSubscriptionController.clearSubInfoRecord(phoneId); - - // If SIM is not absent, insert new record or update existing record. - if (!ICCID_STRING_FOR_NO_SIM.equals(sIccId[phoneId]) && sIccId[phoneId] != null) { - logd("updateSubscriptionInfoByIccId: adding subscription info record: iccid: " - + sIccId[phoneId] + ", phoneId:" + phoneId); - mSubscriptionManager.addSubscriptionInfoRecord(sIccId[phoneId], phoneId); - } - - List<SubscriptionInfo> subInfos = - mSubscriptionController.getSubInfoUsingSlotIndexPrivileged(phoneId); - if (subInfos != null) { - boolean changed = false; - for (int i = 0; i < subInfos.size(); i++) { - SubscriptionInfo temp = subInfos.get(i); - ContentValues value = new ContentValues(1); - - String msisdn = TelephonyManager.getDefault().getLine1Number( - temp.getSubscriptionId()); - - if (!TextUtils.equals(msisdn, temp.getNumber())) { - value.put(SubscriptionManager.NUMBER, msisdn); - sContext.getContentResolver().update(SubscriptionManager - .getUriForSubscriptionId(temp.getSubscriptionId()), value, null, null); - changed = true; - } - } - if (changed) { - // refresh Cached Active Subscription Info List - mSubscriptionController.refreshCachedActiveSubscriptionInfoList(); - } - } - - // TODO investigate if we can update for each slot separately. - if (isAllIccIdQueryDone()) { - // Ensure the modems are mapped correctly - if (mSubscriptionManager.isActiveSubId( - mSubscriptionManager.getDefaultDataSubscriptionId())) { - mSubscriptionManager.setDefaultDataSubId( - mSubscriptionManager.getDefaultDataSubscriptionId()); - } else { - logd("bypass reset default data sub if inactive"); - } - setSubInfoInitialized(); - } - - UiccController uiccController = UiccController.getInstance(); - UiccSlot[] uiccSlots = uiccController.getUiccSlots(); - if (uiccSlots != null && updateEmbeddedSubs) { - List<Integer> cardIds = new ArrayList<>(); - for (UiccSlot uiccSlot : uiccSlots) { - if (uiccSlot != null && uiccSlot.getUiccCard() != null) { - int cardId = uiccController.convertToPublicCardId( - uiccSlot.getUiccCard().getCardId()); - cardIds.add(cardId); - } - } - updateEmbeddedSubscriptions(cardIds, (hasChanges) -> { - if (hasChanges) { - mSubscriptionController.notifySubscriptionInfoChanged(); - } - if (DBG) logd("updateSubscriptionInfoByIccId: SubscriptionInfo update complete"); - }); - } - - mSubscriptionController.notifySubscriptionInfoChanged(); - if (DBG) logd("updateSubscriptionInfoByIccId: SubscriptionInfo update complete"); - } - - private void setSubInfoInitialized() { - // Should only be triggered once. - if (!sIsSubInfoInitialized) { - if (DBG) logd("SubInfo Initialized"); - sIsSubInfoInitialized = true; - mSubscriptionController.notifySubInfoReady(); - } - MultiSimSettingController.getInstance().notifyAllSubscriptionLoaded(); - } - - /** - * Whether subscriptions of all SIMs are initialized. - */ - public static boolean isSubInfoInitialized() { - return sIsSubInfoInitialized; - } - - /** - * Updates the cached list of embedded subscription for the eUICC with the given list of card - * IDs {@code cardIds}. The step of reading the embedded subscription list from eUICC card is - * executed in background thread. The callback {@code callback} is executed after the cache is - * refreshed. The callback is executed in main thread. - */ - @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) - public void updateEmbeddedSubscriptions(List<Integer> cardIds, - @Nullable UpdateEmbeddedSubsCallback callback) { - // Do nothing if eUICCs are disabled. (Previous entries may remain in the cache, but they - // are filtered out of list calls as long as EuiccManager.isEnabled returns false). - if (!mEuiccManager.isEnabled()) { - if (DBG) logd("updateEmbeddedSubscriptions: eUICC not enabled"); - callback.run(false /* hasChanges */); - return; - } - - mBackgroundHandler.post(() -> { - List<Pair<Integer, GetEuiccProfileInfoListResult>> results = new ArrayList<>(); - for (int cardId : cardIds) { - GetEuiccProfileInfoListResult result = - EuiccController.get().blockingGetEuiccProfileInfoList(cardId); - if (DBG) logd("blockingGetEuiccProfileInfoList cardId " + cardId); - results.add(Pair.create(cardId, result)); - } - - // The runnable will be executed in the main thread. - this.post(() -> { - boolean hasChanges = false; - for (Pair<Integer, GetEuiccProfileInfoListResult> cardIdAndResult : results) { - if (updateEmbeddedSubscriptionsCache(cardIdAndResult.first, - cardIdAndResult.second)) { - hasChanges = true; - } - } - // The latest state in the main thread may be changed when the callback is - // triggered. - if (callback != null) { - callback.run(hasChanges); - } - }); - }); - } - - /** - * Update the cached list of embedded subscription based on the passed in - * GetEuiccProfileInfoListResult {@code result}. - * - * @return true if changes may have been made. This is not a guarantee that changes were made, - * but notifications about subscription changes may be skipped if this returns false as an - * optimization to avoid spurious notifications. - */ - private boolean updateEmbeddedSubscriptionsCache(int cardId, - GetEuiccProfileInfoListResult result) { - if (DBG) logd("updateEmbeddedSubscriptionsCache"); - - if (result == null) { - if (DBG) logd("updateEmbeddedSubscriptionsCache: IPC to the eUICC controller failed"); - retryUpdateEmbeddedSubscriptionCards.add(cardId); - shouldRetryUpdateEmbeddedSubscriptions = true; - return false; - } - - // If the returned result is not RESULT_OK or the profile list is null, don't update cache. - // Otherwise, update the cache. - final EuiccProfileInfo[] embeddedProfiles; - List<EuiccProfileInfo> list = result.getProfiles(); - if (result.getResult() == EuiccService.RESULT_OK && list != null) { - embeddedProfiles = list.toArray(new EuiccProfileInfo[list.size()]); - if (DBG) { - logd("blockingGetEuiccProfileInfoList: got " + result.getProfiles().size() - + " profiles"); - } - } else { - if (DBG) { - logd("blockingGetEuiccProfileInfoList returns an error. " - + "Result code=" + result.getResult() - + ". Null profile list=" + (result.getProfiles() == null)); - } - return false; - } - - final boolean isRemovable = result.getIsRemovable(); - - final String[] embeddedIccids = new String[embeddedProfiles.length]; - for (int i = 0; i < embeddedProfiles.length; i++) { - embeddedIccids[i] = embeddedProfiles[i].getIccid(); - } - - if (DBG) logd("Get eUICC profile list of size " + embeddedProfiles.length); - - // Note that this only tracks whether we make any writes to the DB. It's possible this will - // be set to true for an update even when the row contents remain exactly unchanged from - // before, since we don't compare against the previous value. Since this is only intended to - // avoid some spurious broadcasts (particularly for users who don't use eSIM at all), this - // is fine. - boolean hasChanges = false; - - // Update or insert records for all embedded subscriptions (except non-removable ones if the - // current eUICC is non-removable, since we assume these are still accessible though not - // returned by the eUICC controller). - List<SubscriptionInfo> existingSubscriptions = - mSubscriptionController.getSubscriptionInfoListForEmbeddedSubscriptionUpdate( - embeddedIccids, isRemovable); - ContentResolver contentResolver = sContext.getContentResolver(); - for (EuiccProfileInfo embeddedProfile : embeddedProfiles) { - int index = - findSubscriptionInfoForIccid(existingSubscriptions, embeddedProfile.getIccid()); - int prevCarrierId = TelephonyManager.UNKNOWN_CARRIER_ID; - int nameSource = SubscriptionManager.NAME_SOURCE_CARRIER_ID; - if (index < 0) { - // No existing entry for this ICCID; create an empty one. - mSubscriptionController.insertEmptySubInfoRecord( - embeddedProfile.getIccid(), SubscriptionManager.SIM_NOT_INSERTED); - } else { - nameSource = existingSubscriptions.get(index).getNameSource(); - prevCarrierId = existingSubscriptions.get(index).getCarrierId(); - existingSubscriptions.remove(index); - } - - if (DBG) { - logd("embeddedProfile " + embeddedProfile + " existing record " - + (index < 0 ? "not found" : "found")); - } - - ContentValues values = new ContentValues(); - values.put(SubscriptionManager.IS_EMBEDDED, 1); - List<UiccAccessRule> ruleList = embeddedProfile.getUiccAccessRules(); - boolean isRuleListEmpty = false; - if (ruleList == null || ruleList.size() == 0) { - isRuleListEmpty = true; - } - values.put(SubscriptionManager.ACCESS_RULES, - isRuleListEmpty ? null : UiccAccessRule.encodeRules( - ruleList.toArray(new UiccAccessRule[ruleList.size()]))); - values.put(SubscriptionManager.IS_REMOVABLE, isRemovable); - // override DISPLAY_NAME if the priority of existing nameSource is <= carrier - if (SubscriptionController.getNameSourcePriority(nameSource) - <= SubscriptionController.getNameSourcePriority( - SubscriptionManager.NAME_SOURCE_CARRIER)) { - values.put(SubscriptionManager.DISPLAY_NAME, embeddedProfile.getNickname()); - values.put(SubscriptionManager.NAME_SOURCE, - SubscriptionManager.NAME_SOURCE_CARRIER); - } - values.put(SubscriptionManager.PROFILE_CLASS, embeddedProfile.getProfileClass()); - values.put(SubscriptionManager.PORT_INDEX, - getEmbeddedProfilePortIndex(embeddedProfile.getIccid())); - CarrierIdentifier cid = embeddedProfile.getCarrierIdentifier(); - if (cid != null) { - // Due to the limited subscription information, carrier id identified here might - // not be accurate compared with CarrierResolver. Only update carrier id if there - // is no valid carrier id present. - if (prevCarrierId == TelephonyManager.UNKNOWN_CARRIER_ID) { - values.put(SubscriptionManager.CARRIER_ID, - CarrierResolver.getCarrierIdFromIdentifier(sContext, cid)); - } - String mcc = cid.getMcc(); - String mnc = cid.getMnc(); - values.put(SubscriptionManager.MCC_STRING, mcc); - values.put(SubscriptionManager.MCC, mcc); - values.put(SubscriptionManager.MNC_STRING, mnc); - values.put(SubscriptionManager.MNC, mnc); - } - // If cardId = unsupported or unitialized, we have no reason to update DB. - // Additionally, if the device does not support cardId for default eUICC, the CARD_ID - // field should not contain the EID - UiccController uiccController = UiccController.getInstance(); - if (cardId >= 0 && uiccController.getCardIdForDefaultEuicc() - != TelephonyManager.UNSUPPORTED_CARD_ID) { - values.put(SubscriptionManager.CARD_ID, uiccController.convertToCardString(cardId)); - } - hasChanges = true; - contentResolver.update(SubscriptionManager.CONTENT_URI, values, - SubscriptionManager.ICC_ID + "='" + embeddedProfile.getIccid() + "'", null); - - // refresh Cached Active Subscription Info List - mSubscriptionController.refreshCachedActiveSubscriptionInfoList(); - } - - // Remove all remaining subscriptions which have embedded = true. We set embedded to false - // to ensure they are not returned in the list of embedded subscriptions (but keep them - // around in case the subscription is added back later, which is equivalent to a removable - // SIM being removed and reinserted). - if (!existingSubscriptions.isEmpty()) { - if (DBG) { - logd("Removing existing embedded subscriptions of size" - + existingSubscriptions.size()); - } - List<String> iccidsToRemove = new ArrayList<>(); - for (int i = 0; i < existingSubscriptions.size(); i++) { - SubscriptionInfo info = existingSubscriptions.get(i); - if (info.isEmbedded()) { - if (DBG) logd("Removing embedded subscription of IccId " + info.getIccId()); - iccidsToRemove.add("'" + info.getIccId() + "'"); - } - } - String whereClause = SubscriptionManager.ICC_ID + " IN (" - + TextUtils.join(",", iccidsToRemove) + ")"; - ContentValues values = new ContentValues(); - values.put(SubscriptionManager.IS_EMBEDDED, 0); - hasChanges = true; - contentResolver.update(SubscriptionManager.CONTENT_URI, values, whereClause, null); - - // refresh Cached Active Subscription Info List - mSubscriptionController.refreshCachedActiveSubscriptionInfoList(); - } - - if (DBG) logd("updateEmbeddedSubscriptions done hasChanges=" + hasChanges); - return hasChanges; - } - - private int getEmbeddedProfilePortIndex(String iccId) { - UiccSlot[] slots = UiccController.getInstance().getUiccSlots(); - for (UiccSlot slot : slots) { - if (slot != null && slot.isEuicc() - && slot.getPortIndexFromIccId(iccId) != TelephonyManager.INVALID_PORT_INDEX) { - return slot.getPortIndexFromIccId(iccId); - } - } - return TelephonyManager.INVALID_PORT_INDEX; - } - /** - * Called by CarrierConfigLoader to update the subscription before sending a broadcast. - */ - public void updateSubscriptionByCarrierConfigAndNotifyComplete(int phoneId, - String configPackageName, PersistableBundle config, Message onComplete) { - post(() -> { - updateSubscriptionByCarrierConfig(phoneId, configPackageName, config); - onComplete.sendToTarget(); - }); - } - - private String getDefaultCarrierServicePackageName() { - CarrierConfigManager configManager = - (CarrierConfigManager) sContext.getSystemService(Context.CARRIER_CONFIG_SERVICE); - return configManager.getDefaultCarrierServicePackageName(); - } - - private boolean isCarrierServicePackage(int phoneId, String pkgName) { - if (pkgName.equals(getDefaultCarrierServicePackageName())) return false; - - String carrierPackageName = TelephonyManager.from(sContext) - .getCarrierServicePackageNameForLogicalSlot(phoneId); - if (DBG) logd("Carrier service package for subscription = " + carrierPackageName); - return pkgName.equals(carrierPackageName); - } - - /** - * Update the currently active Subscription based on information from CarrierConfig - */ - @VisibleForTesting - public void updateSubscriptionByCarrierConfig( - int phoneId, String configPackageName, PersistableBundle config) { - if (!SubscriptionManager.isValidPhoneId(phoneId) - || TextUtils.isEmpty(configPackageName) || config == null) { - if (DBG) { - logd("In updateSubscriptionByCarrierConfig(): phoneId=" + phoneId - + " configPackageName=" + configPackageName + " config=" - + ((config == null) ? "null" : config.hashCode())); - } - return; - } - - int currentSubId = mSubscriptionController.getSubIdUsingPhoneId(phoneId); - if (!SubscriptionManager.isValidSubscriptionId(currentSubId) - || currentSubId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) { - if (DBG) logd("No subscription is active for phone being updated"); - return; - } - - SubscriptionInfo currentSubInfo = mSubscriptionController.getSubscriptionInfo(currentSubId); - if (currentSubInfo == null) { - loge("Couldn't retrieve subscription info for current subscription"); - return; - } - - ContentValues cv = new ContentValues(); - ParcelUuid groupUuid = null; - - // carrier certificates are not subscription-specific, so we want to load them even if - // this current package is not a CarrierServicePackage - String[] certs = config.getStringArray( - CarrierConfigManager.KEY_CARRIER_CERTIFICATE_STRING_ARRAY); - UiccAccessRule[] carrierConfigAccessRules = UiccAccessRule.decodeRulesFromCarrierConfig( - certs); - cv.put(SubscriptionManager.ACCESS_RULES_FROM_CARRIER_CONFIGS, - UiccAccessRule.encodeRules(carrierConfigAccessRules)); - - if (!isCarrierServicePackage(phoneId, configPackageName)) { - loge("Cannot manage subId=" + currentSubId + ", carrierPackage=" + configPackageName); - } else { - boolean isOpportunistic = config.getBoolean( - CarrierConfigManager.KEY_IS_OPPORTUNISTIC_SUBSCRIPTION_BOOL, false); - if (currentSubInfo.isOpportunistic() != isOpportunistic) { - if (DBG) logd("Set SubId=" + currentSubId + " isOpportunistic=" + isOpportunistic); - cv.put(SubscriptionManager.IS_OPPORTUNISTIC, isOpportunistic ? "1" : "0"); - } - - String groupUuidString = - config.getString(CarrierConfigManager.KEY_SUBSCRIPTION_GROUP_UUID_STRING, ""); - if (!TextUtils.isEmpty(groupUuidString)) { - try { - // Update via a UUID Structure to ensure consistent formatting - groupUuid = ParcelUuid.fromString(groupUuidString); - if (groupUuid.equals(REMOVE_GROUP_UUID) - && currentSubInfo.getGroupUuid() != null) { - cv.put(SubscriptionManager.GROUP_UUID, (String) null); - if (DBG) logd("Group Removed for" + currentSubId); - } else if (mSubscriptionController.canPackageManageGroup( - groupUuid, configPackageName)) { - cv.put(SubscriptionManager.GROUP_UUID, groupUuid.toString()); - cv.put(SubscriptionManager.GROUP_OWNER, configPackageName); - if (DBG) logd("Group Added for" + currentSubId); - } else { - loge("configPackageName " + configPackageName + " doesn't own grouUuid " - + groupUuid); - } - } catch (IllegalArgumentException e) { - loge("Invalid Group UUID=" + groupUuidString); - } - } - } - - final int preferredUsageSetting = - config.getInt( - CarrierConfigManager.KEY_CELLULAR_USAGE_SETTING_INT, - SubscriptionManager.USAGE_SETTING_UNKNOWN); - - @UsageSetting int newUsageSetting = calculateUsageSetting( - currentSubInfo.getUsageSetting(), - preferredUsageSetting); - - if (newUsageSetting != currentSubInfo.getUsageSetting()) { - cv.put(SubscriptionManager.USAGE_SETTING, newUsageSetting); - if (DBG) { - logd("UsageSetting changed," - + " oldSetting=" + currentSubInfo.getUsageSetting() - + " preferredSetting=" + preferredUsageSetting - + " newSetting=" + newUsageSetting); - } - } else { - if (DBG) { - logd("UsageSetting unchanged," - + " oldSetting=" + currentSubInfo.getUsageSetting() - + " preferredSetting=" + preferredUsageSetting - + " newSetting=" + newUsageSetting); - } - } - - if (cv.size() > 0 && sContext.getContentResolver().update(SubscriptionManager - .getUriForSubscriptionId(currentSubId), cv, null, null) > 0) { - mSubscriptionController.refreshCachedActiveSubscriptionInfoList(); - mSubscriptionController.notifySubscriptionInfoChanged(); - MultiSimSettingController.getInstance().notifySubscriptionGroupChanged(groupUuid); - } - } - - private static int findSubscriptionInfoForIccid(List<SubscriptionInfo> list, String iccid) { - for (int i = 0; i < list.size(); i++) { - if (TextUtils.equals(iccid, list.get(i).getIccId())) { - return i; - } - } - return -1; - } - - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - protected void broadcastSimStateChanged(int phoneId, String state, String reason) { - // Note: This intent is way deprecated and is only being kept around because there's no - // graceful way to deprecate a sticky broadcast that has a lot of listeners. - // DO NOT add any new extras to this broadcast -- it is not protected by any permissions. - Intent i = new Intent(TelephonyIntents.ACTION_SIM_STATE_CHANGED); - i.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); - i.putExtra(PhoneConstants.PHONE_NAME_KEY, "Phone"); - i.putExtra(IccCardConstants.INTENT_KEY_ICC_STATE, state); - i.putExtra(IccCardConstants.INTENT_KEY_LOCKED_REASON, reason); - SubscriptionManager.putPhoneIdAndSubIdExtra(i, phoneId); - logd("Broadcasting intent ACTION_SIM_STATE_CHANGED " + state + " reason " + reason + - " for phone: " + phoneId); - IntentBroadcaster.getInstance().broadcastStickyIntent(sContext, i, phoneId); - } - - protected void broadcastSimCardStateChanged(int phoneId, int state) { - if (state != sSimCardState[phoneId]) { - sSimCardState[phoneId] = state; - Intent i = new Intent(TelephonyManager.ACTION_SIM_CARD_STATE_CHANGED); - i.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); - i.putExtra(TelephonyManager.EXTRA_SIM_STATE, state); - SubscriptionManager.putPhoneIdAndSubIdExtra(i, phoneId); - // TODO(b/130664115) we manually populate this intent with the slotId. In the future we - // should do a review of whether to make this public - UiccSlot slot = UiccController.getInstance().getUiccSlotForPhone(phoneId); - int slotId = UiccController.getInstance().getSlotIdFromPhoneId(phoneId); - i.putExtra(PhoneConstants.SLOT_KEY, slotId); - if (slot != null) { - i.putExtra(PhoneConstants.PORT_KEY, slot.getPortIndexFromPhoneId(phoneId)); - } - logd("Broadcasting intent ACTION_SIM_CARD_STATE_CHANGED " + simStateString(state) - + " for phone: " + phoneId + " slot: " + slotId + " port: " - + slot.getPortIndexFromPhoneId(phoneId)); - sContext.sendBroadcast(i, Manifest.permission.READ_PRIVILEGED_PHONE_STATE); - TelephonyMetrics.getInstance().updateSimState(phoneId, state); - } - } - - protected void broadcastSimApplicationStateChanged(int phoneId, int state) { - // Broadcast if the state has changed, except if old state was UNKNOWN and new is NOT_READY, - // because that's the initial state and a broadcast should be sent only on a transition - // after SIM is PRESENT. The only exception is eSIM boot profile, where NOT_READY is the - // terminal state. - boolean isUnknownToNotReady = - (sSimApplicationState[phoneId] == TelephonyManager.SIM_STATE_UNKNOWN - && state == TelephonyManager.SIM_STATE_NOT_READY); - IccCard iccCard = PhoneFactory.getPhone(phoneId).getIccCard(); - boolean emptyProfile = iccCard != null && iccCard.isEmptyProfile(); - if (state != sSimApplicationState[phoneId] && (!isUnknownToNotReady || emptyProfile)) { - sSimApplicationState[phoneId] = state; - Intent i = new Intent(TelephonyManager.ACTION_SIM_APPLICATION_STATE_CHANGED); - i.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); - i.putExtra(TelephonyManager.EXTRA_SIM_STATE, state); - SubscriptionManager.putPhoneIdAndSubIdExtra(i, phoneId); - // TODO(b/130664115) we populate this intent with the actual slotId. In the future we - // should do a review of whether to make this public - UiccSlot slot = UiccController.getInstance().getUiccSlotForPhone(phoneId); - int slotId = UiccController.getInstance().getSlotIdFromPhoneId(phoneId); - i.putExtra(PhoneConstants.SLOT_KEY, slotId); - if (slot != null) { - i.putExtra(PhoneConstants.PORT_KEY, slot.getPortIndexFromPhoneId(phoneId)); - } - logd("Broadcasting intent ACTION_SIM_APPLICATION_STATE_CHANGED " + simStateString(state) - + " for phone: " + phoneId + " slot: " + slotId + "port: " - + slot.getPortIndexFromPhoneId(phoneId)); - sContext.sendBroadcast(i, Manifest.permission.READ_PRIVILEGED_PHONE_STATE); - TelephonyMetrics.getInstance().updateSimState(phoneId, state); - } - } - - /** - * Convert SIM state into string - * - * @param state SIM state - * @return SIM state in string format - */ - public static String simStateString(@SimState int state) { - switch (state) { - case TelephonyManager.SIM_STATE_UNKNOWN: - return "UNKNOWN"; - case TelephonyManager.SIM_STATE_ABSENT: - return "ABSENT"; - case TelephonyManager.SIM_STATE_PIN_REQUIRED: - return "PIN_REQUIRED"; - case TelephonyManager.SIM_STATE_PUK_REQUIRED: - return "PUK_REQUIRED"; - case TelephonyManager.SIM_STATE_NETWORK_LOCKED: - return "NETWORK_LOCKED"; - case TelephonyManager.SIM_STATE_READY: - return "READY"; - case TelephonyManager.SIM_STATE_NOT_READY: - return "NOT_READY"; - case TelephonyManager.SIM_STATE_PERM_DISABLED: - return "PERM_DISABLED"; - case TelephonyManager.SIM_STATE_CARD_IO_ERROR: - return "CARD_IO_ERROR"; - case TelephonyManager.SIM_STATE_CARD_RESTRICTED: - return "CARD_RESTRICTED"; - case TelephonyManager.SIM_STATE_LOADED: - return "LOADED"; - case TelephonyManager.SIM_STATE_PRESENT: - return "PRESENT"; - default: - return "INVALID"; - } - } - - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - private static void logd(String message) { - Rlog.d(LOG_TAG, message); - } - - private static void loge(String message) { - Rlog.e(LOG_TAG, message); - } - - public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - pw.println("SubscriptionInfoUpdater:"); - mCarrierServiceBindHelper.dump(fd, pw, args); - } -}
diff --git a/src/java/com/android/internal/telephony/TelephonyAdminReceiver.java b/src/java/com/android/internal/telephony/TelephonyAdminReceiver.java new file mode 100644 index 0000000..994405b --- /dev/null +++ b/src/java/com/android/internal/telephony/TelephonyAdminReceiver.java
@@ -0,0 +1,100 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.telephony; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.UserManager; +import android.telephony.Rlog; + +/** + * A BroadcastReceiver for device administration events. + */ +public class TelephonyAdminReceiver extends BroadcastReceiver { + private static final String TAG = "TelephonyAdminReceiver"; + private final Phone mPhone; + // We keep track of the last value to avoid updating when unrelated user restrictions change + private boolean mDisallowCellular2gRestriction = false; + private final Context mContext; + private UserManager mUserManager; + + public TelephonyAdminReceiver(Context context, Phone phone) { + mContext = context; + mPhone = phone; + mUserManager = null; + if (ensureUserManagerExists()) { + mDisallowCellular2gRestriction = mUserManager.hasUserRestriction( + UserManager.DISALLOW_CELLULAR_2G); + } + IntentFilter filter = new IntentFilter(); + filter.addAction(UserManager.ACTION_USER_RESTRICTIONS_CHANGED); + context.registerReceiver(this, filter); + } + + @Override + public void onReceive(Context context, Intent intent) { + Rlog.d(TAG, "Processing onReceive"); + if (context == null || intent == null) return; + if (!intent.getAction().equals(UserManager.ACTION_USER_RESTRICTIONS_CHANGED)) { + Rlog.d(TAG, "Ignoring unexpected action: " + intent.getAction()); + return; + } + if (!ensureUserManagerExists()) { + return; + } + boolean shouldDisallow2g = mUserManager.hasUserRestriction( + UserManager.DISALLOW_CELLULAR_2G); + + if (shouldDisallow2g != mDisallowCellular2gRestriction) { + Rlog.i(TAG, + "Updating allowed network types with new admin 2g restriction. no_cellular_2g: " + + shouldDisallow2g); + mDisallowCellular2gRestriction = shouldDisallow2g; + mPhone.sendSubscriptionSettings(false); + } else { + Rlog.i(TAG, "Skipping update of allowed network types. Restriction no_cellular_2g " + + "unchanged: " + mDisallowCellular2gRestriction); + } + } + + /** + * Returns the current state of the {@link UserManager#DISALLOW_CELLULAR_2G} user restriction. + */ + public boolean isCellular2gDisabled() { + return mDisallowCellular2gRestriction; + } + + /** + * Tries to resolve the user manager system service. Returns true if successful, false + * otherwise. + */ + private boolean ensureUserManagerExists() { + if (mUserManager == null) { + Rlog.d(TAG, "No user manager. Attempting to resolve one."); + mUserManager = mContext.getSystemService(UserManager.class); + } + if (mUserManager == null) { + Rlog.e(TAG, + "Could not get a user manager instance. All operations will be no-ops until " + + "one is resolved"); + return false; + } + return true; + } +}
diff --git a/src/java/com/android/internal/telephony/TelephonyComponentFactory.java b/src/java/com/android/internal/telephony/TelephonyComponentFactory.java index dcbd2d5..f7f24a2 100644 --- a/src/java/com/android/internal/telephony/TelephonyComponentFactory.java +++ b/src/java/com/android/internal/telephony/TelephonyComponentFactory.java
@@ -27,7 +27,6 @@ import android.system.Os; import android.system.OsConstants; import android.system.StructStatVfs; -import android.telephony.AccessNetworkConstants.TransportType; import android.text.TextUtils; import com.android.ims.ImsManager; @@ -40,11 +39,9 @@ import com.android.internal.telephony.data.DataSettingsManager; import com.android.internal.telephony.data.LinkBandwidthEstimator; import com.android.internal.telephony.data.PhoneSwitcher; -import com.android.internal.telephony.dataconnection.DataEnabledSettings; -import com.android.internal.telephony.dataconnection.DcTracker; -import com.android.internal.telephony.dataconnection.TransportManager; import com.android.internal.telephony.emergency.EmergencyNumberTracker; import com.android.internal.telephony.imsphone.ImsExternalCallTracker; +import com.android.internal.telephony.imsphone.ImsNrSaModeHandler; import com.android.internal.telephony.imsphone.ImsPhone; import com.android.internal.telephony.imsphone.ImsPhoneCallTracker; import com.android.internal.telephony.nitz.NitzStateMachineImpl; @@ -313,10 +310,6 @@ return new SimActivationTracker(phone); } - public DcTracker makeDcTracker(Phone phone, @TransportType int transportType) { - return new DcTracker(phone, transportType); - } - public CarrierSignalAgent makeCarrierSignalAgent(Phone phone) { return new CarrierSignalAgent(phone); } @@ -394,6 +387,14 @@ } /** + * Create an ImsNrSaModeHandler. + */ + public ImsNrSaModeHandler makeImsNrSaModeHandler(ImsPhone imsPhone) { + + return new ImsNrSaModeHandler(imsPhone, imsPhone.getLooper()); + } + + /** * Create an AppSmsManager for per-app SMS message. */ public AppSmsManager makeAppSmsManager(Context context) { @@ -404,10 +405,6 @@ return new DeviceStateMonitor(phone); } - public TransportManager makeTransportManager(Phone phone) { - return new TransportManager(phone); - } - /** * Make access networks manager * @@ -430,10 +427,6 @@ return new LocaleTracker(phone, nitzStateMachine, looper); } - public DataEnabledSettings makeDataEnabledSettings(Phone phone) { - return new DataEnabledSettings(phone); - } - public Phone makePhone(Context context, CommandsInterface ci, PhoneNotifier notifier, int phoneId, int precisePhoneType, TelephonyComponentFactory telephonyComponentFactory) { @@ -441,10 +434,6 @@ telephonyComponentFactory); } - public SubscriptionController initSubscriptionController(Context c) { - return SubscriptionController.init(c); - } - public PhoneSwitcher makePhoneSwitcher(int maxDataAttachModemCount, Context context, Looper looper) { return PhoneSwitcher.make(maxDataAttachModemCount, context, looper); @@ -457,9 +446,14 @@ return new DisplayInfoController(phone); } - public MultiSimSettingController initMultiSimSettingController(Context c, - SubscriptionController sc) { - return MultiSimSettingController.init(c, sc); + /** + * Initialize multi sim settings controller. + * + * @param c The context. + * @return The multi sim settings controller instance. + */ + public MultiSimSettingController initMultiSimSettingController(Context c) { + return MultiSimSettingController.init(c); } /** @@ -469,11 +463,6 @@ return new SignalStrengthController(phone); } - public SubscriptionInfoUpdater makeSubscriptionInfoUpdater(Looper looper, Context context, - SubscriptionController sc) { - return new SubscriptionInfoUpdater(looper, context, sc); - } - /** * Create a new LinkBandwidthEstimator. */
diff --git a/src/java/com/android/internal/telephony/TelephonyTester.java b/src/java/com/android/internal/telephony/TelephonyTester.java index 40bb212..b9e04c8 100644 --- a/src/java/com/android/internal/telephony/TelephonyTester.java +++ b/src/java/com/android/internal/telephony/TelephonyTester.java
@@ -16,6 +16,7 @@ package com.android.internal.telephony; +import android.annotation.NonNull; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -62,7 +63,6 @@ * */ public class TelephonyTester { - private static final String LOG_TAG = "TelephonyTester"; private static final boolean DBG = true; /** @@ -144,6 +144,7 @@ "com.android.internal.telephony.TestServiceState"; private static final String EXTRA_ACTION = "action"; + private static final String EXTRA_PHONE_ID = "phone_id"; private static final String EXTRA_VOICE_RAT = "voice_rat"; private static final String EXTRA_DATA_RAT = "data_rat"; private static final String EXTRA_VOICE_REG_STATE = "voice_reg_state"; @@ -157,6 +158,8 @@ private static final String ACTION_RESET = "reset"; + private String mLogTag; + private static List<ImsExternalCallState> mImsExternalCallStates = null; private Intent mServiceStateTestIntent; @@ -198,11 +201,7 @@ sendTestSuppServiceNotification(intent); } else if (action.equals(ACTION_TEST_SERVICE_STATE)) { log("handle test service state changed intent"); - // Trigger the service state update. The replacement will be done in - // overrideServiceState(). - mServiceStateTestIntent = intent; - mPhone.getServiceStateTracker().sendEmptyMessage( - ServiceStateTracker.EVENT_NETWORK_STATE_CHANGED); + setServiceStateTestIntent(intent); } else if (action.equals(ACTION_TEST_IMS_E_CALL)) { log("handle test IMS ecall intent"); testImsECall(); @@ -216,7 +215,7 @@ if (DBG) log("onReceive: unknown action=" + action); } } catch (BadParcelableException e) { - Rlog.w(LOG_TAG, e); + Rlog.w(mLogTag, e); } } }; @@ -225,6 +224,7 @@ mPhone = phone; if (TelephonyUtils.IS_DEBUGGABLE) { + mLogTag = "TelephonyTester-" + mPhone.getPhoneId(); IntentFilter filter = new IntentFilter(); filter.addAction(mPhone.getActionDetached()); @@ -261,8 +261,8 @@ } } - private static void log(String s) { - Rlog.d(LOG_TAG, s); + private void log(String s) { + Rlog.d(mLogTag, s); } private void handleSuppServiceFailedIntent(Intent intent) { @@ -385,8 +385,25 @@ } } + /** + * Set the service state test intent. + * + * @param intent The service state test intent. + */ + public void setServiceStateTestIntent(@NonNull Intent intent) { + mServiceStateTestIntent = intent; + // Trigger the service state update. The replacement will be done in + // overrideServiceState(). + mPhone.getServiceStateTracker().sendEmptyMessage( + ServiceStateTracker.EVENT_NETWORK_STATE_CHANGED); + } + void overrideServiceState(ServiceState ss) { if (mServiceStateTestIntent == null || ss == null) return; + if (mPhone.getPhoneId() != mServiceStateTestIntent.getIntExtra( + EXTRA_PHONE_ID, mPhone.getPhoneId())) { + return; + } if (mServiceStateTestIntent.hasExtra(EXTRA_ACTION) && ACTION_RESET.equals(mServiceStateTestIntent.getStringExtra(EXTRA_ACTION))) { log("Service state override reset"); @@ -394,13 +411,36 @@ } if (mServiceStateTestIntent.hasExtra(EXTRA_VOICE_REG_STATE)) { + int state = mServiceStateTestIntent.getIntExtra(EXTRA_DATA_REG_STATE, + ServiceState.STATE_OUT_OF_SERVICE); ss.setVoiceRegState(mServiceStateTestIntent.getIntExtra(EXTRA_VOICE_REG_STATE, ServiceState.STATE_OUT_OF_SERVICE)); + NetworkRegistrationInfo nri = ss.getNetworkRegistrationInfo( + NetworkRegistrationInfo.DOMAIN_CS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN); + NetworkRegistrationInfo.Builder builder = new NetworkRegistrationInfo.Builder(nri); + if (state == ServiceState.STATE_IN_SERVICE) { + builder.setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_HOME); + } else { + builder.setRegistrationState( + NetworkRegistrationInfo.REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING); + } + ss.addNetworkRegistrationInfo(builder.build()); log("Override voice service state with " + ss.getState()); } if (mServiceStateTestIntent.hasExtra(EXTRA_DATA_REG_STATE)) { - ss.setDataRegState(mServiceStateTestIntent.getIntExtra(EXTRA_DATA_REG_STATE, - ServiceState.STATE_OUT_OF_SERVICE)); + int state = mServiceStateTestIntent.getIntExtra(EXTRA_DATA_REG_STATE, + ServiceState.STATE_OUT_OF_SERVICE); + ss.setDataRegState(state); + NetworkRegistrationInfo nri = ss.getNetworkRegistrationInfo( + NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN); + NetworkRegistrationInfo.Builder builder = new NetworkRegistrationInfo.Builder(nri); + if (state == ServiceState.STATE_IN_SERVICE) { + builder.setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_HOME); + } else { + builder.setRegistrationState( + NetworkRegistrationInfo.REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING); + } + ss.addNetworkRegistrationInfo(builder.build()); log("Override data service state with " + ss.getDataRegistrationState()); } if (mServiceStateTestIntent.hasExtra(EXTRA_OPERATOR)) {
diff --git a/src/java/com/android/internal/telephony/UiccPhoneBookController.java b/src/java/com/android/internal/telephony/UiccPhoneBookController.java index 5b55c35..96fd208 100644 --- a/src/java/com/android/internal/telephony/UiccPhoneBookController.java +++ b/src/java/com/android/internal/telephony/UiccPhoneBookController.java
@@ -19,11 +19,12 @@ package com.android.internal.telephony; import android.compat.annotation.UnsupportedAppUsage; +import android.content.ContentValues; import android.os.Build; import android.os.TelephonyServiceManager.ServiceRegisterer; import android.telephony.TelephonyFrameworkInitializer; -import android.content.ContentValues; +import com.android.internal.telephony.subscription.SubscriptionManagerService; import com.android.internal.telephony.uicc.AdnCapacity; import com.android.internal.telephony.uicc.AdnRecord; import com.android.telephony.Rlog; @@ -146,7 +147,7 @@ private IccPhoneBookInterfaceManager getIccPhoneBookInterfaceManager(int subId) { - int phoneId = SubscriptionController.getInstance().getPhoneId(subId); + int phoneId = SubscriptionManagerService.getInstance().getPhoneId(subId); try { return PhoneFactory.getPhone(phoneId).getIccPhoneBookInterfaceManager(); } catch (NullPointerException e) {
diff --git a/src/java/com/android/internal/telephony/VisualVoicemailSmsFilter.java b/src/java/com/android/internal/telephony/VisualVoicemailSmsFilter.java index 5bbe8b4..e374811 100644 --- a/src/java/com/android/internal/telephony/VisualVoicemailSmsFilter.java +++ b/src/java/com/android/internal/telephony/VisualVoicemailSmsFilter.java
@@ -15,10 +15,13 @@ */ package com.android.internal.telephony; +import static com.android.internal.telephony.SmsConstants.ENCODING_8BIT; + import android.annotation.Nullable; import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.os.UserHandle; import android.provider.VoicemailContract; import android.telecom.PhoneAccountHandle; import android.telephony.PhoneNumberUtils; @@ -58,7 +61,7 @@ /** * Convert the subId to a {@link PhoneAccountHandle} */ - PhoneAccountHandle fromSubId(int subId); + PhoneAccountHandle fromSubId(int subId, Context context); } private static final String TAG = "VvmSmsFilter"; @@ -75,7 +78,7 @@ new PhoneAccountHandleConverter() { @Override - public PhoneAccountHandle fromSubId(int subId) { + public PhoneAccountHandle fromSubId(int subId, Context context) { if (!SubscriptionManager.isValidSubscriptionId(subId)) { return null; } @@ -83,6 +86,15 @@ if (phoneId == SubscriptionManager.INVALID_PHONE_INDEX) { return null; } + SubscriptionManager subscriptionManager = + (SubscriptionManager) context.getSystemService( + Context.TELEPHONY_SUBSCRIPTION_SERVICE); + UserHandle userHandle = subscriptionManager.getSubscriptionUserHandle(subId); + if (userHandle != null) { + return new PhoneAccountHandle(PSTN_CONNECTION_SERVICE_COMPONENT, + Integer.toString(PhoneFactory.getPhone(phoneId).getSubId()), + userHandle); + } return new PhoneAccountHandle(PSTN_CONNECTION_SERVICE_COMPONENT, Integer.toString(PhoneFactory.getPhone(phoneId).getSubId())); } @@ -136,7 +148,8 @@ return false; } - PhoneAccountHandle phoneAccountHandle = sPhoneAccountHandleConverter.fromSubId(subId); + PhoneAccountHandle phoneAccountHandle = sPhoneAccountHandleConverter.fromSubId(subId, + context); if (phoneAccountHandle == null) { Log.e(TAG, "Unable to convert subId " + subId + " to PhoneAccountHandle"); @@ -294,7 +307,19 @@ result.firstMessage = message; } String body = message.getMessageBody(); - if (body == null && message.getUserData() != null) { + + /* + * For visual voice mail SMS message, UTF-8 is used by default + * {@link com.android.internal.telephony.SmsController#sendVisualVoicemailSmsForSubscriber} + * + * If config_sms_decode_gsm_8bit_data is enabled, GSM-8bit will be used to decode the + * received message. However, the message is most likely encoded with UTF-8. Therefore, + * we need to retry decoding the received message with UTF-8. + */ + if ((body == null || (message.is3gpp() + && message.getReceivedEncodingType() == ENCODING_8BIT)) + && message.getUserData() != null) { + Log.d(TAG, "getFullMessage decode using UTF-8"); // Attempt to interpret the user data as UTF-8. UTF-8 string over data SMS using // 8BIT data coding scheme is our recommended way to send VVM SMS and is used in CTS // Tests. The OMTP visual voicemail specification does not specify the SMS type and @@ -303,7 +328,8 @@ try { body = decoder.decode(byteBuffer).toString(); } catch (CharacterCodingException e) { - // User data is not decode-able as UTF-8. Ignoring. + Log.e(TAG, "getFullMessage: got CharacterCodingException" + + " when decoding with UTF-8, e = " + e); return null; } }
diff --git a/src/java/com/android/internal/telephony/VoiceIndication.java b/src/java/com/android/internal/telephony/VoiceIndication.java index 4ed82a6..9720bb7 100644 --- a/src/java/com/android/internal/telephony/VoiceIndication.java +++ b/src/java/com/android/internal/telephony/VoiceIndication.java
@@ -16,6 +16,8 @@ package com.android.internal.telephony; +import static android.telephony.TelephonyManager.HAL_SERVICE_VOICE; + import static com.android.internal.telephony.RILConstants.RIL_UNSOL_CALL_RING; import static com.android.internal.telephony.RILConstants.RIL_UNSOL_CDMA_CALL_WAITING; import static com.android.internal.telephony.RILConstants.RIL_UNSOL_CDMA_INFO_REC; @@ -63,7 +65,7 @@ */ public void callRing(int indicationType, boolean isGsm, android.hardware.radio.voice.CdmaSignalInfoRecord record) { - mRil.processIndication(RIL.VOICE_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_VOICE, indicationType); char[] response = null; @@ -78,7 +80,7 @@ mRil.writeMetricsCallRing(response); } - if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_CALL_RING, response); + if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_CALL_RING, response); if (mRil.mRingRegistrant != null) { mRil.mRingRegistrant.notifyRegistrant(new AsyncResult(null, response, null)); @@ -90,9 +92,9 @@ * @param indicationType Type of radio indication */ public void callStateChanged(int indicationType) { - mRil.processIndication(RIL.VOICE_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_VOICE, indicationType); - if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED); + if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED); mRil.mCallStateRegistrants.notifyRegistrants(); } @@ -104,7 +106,7 @@ */ public void cdmaCallWaiting(int indicationType, android.hardware.radio.voice.CdmaCallWaiting callWaitingRecord) { - mRil.processIndication(RIL.VOICE_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_VOICE, indicationType); // TODO: create a CdmaCallWaitingNotification constructor that takes in these fields to make // sure no fields are missing @@ -121,7 +123,7 @@ notification.numberType = callWaitingRecord.numberType; notification.numberPlan = callWaitingRecord.numberPlan; - if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_CDMA_CALL_WAITING, notification); + if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_CDMA_CALL_WAITING, notification); mRil.mCallWaitingInfoRegistrants.notifyRegistrants( new AsyncResult(null, notification, null)); @@ -134,7 +136,7 @@ */ public void cdmaInfoRec(int indicationType, android.hardware.radio.voice.CdmaInformationRecord[] records) { - mRil.processIndication(RIL.VOICE_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_VOICE, indicationType); for (int i = 0; i < records.length; i++) { android.hardware.radio.voice.CdmaInformationRecord record = records[i]; @@ -220,7 +222,9 @@ + CdmaInformationRecords.idToString(id) + " "); } - if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_CDMA_INFO_REC, cdmaInformationRecords); + if (mRil.isLogOrTrace()) { + mRil.unsljLogRet(RIL_UNSOL_CDMA_INFO_REC, cdmaInformationRecords); + } mRil.notifyRegistrantsCdmaInfoRec(cdmaInformationRecords); } } @@ -231,11 +235,13 @@ * @param status CDMA OTA provision status */ public void cdmaOtaProvisionStatus(int indicationType, int status) { - mRil.processIndication(RIL.VOICE_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_VOICE, indicationType); int[] response = new int[] {status}; - if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_CDMA_OTA_PROVISION_STATUS, response); + if (mRil.isLogOrTrace()) { + mRil.unsljLogRet(RIL_UNSOL_CDMA_OTA_PROVISION_STATUS, response); + } mRil.mOtaProvisionRegistrants.notifyRegistrants(new AsyncResult(null, response, null)); } @@ -248,7 +254,7 @@ */ public void currentEmergencyNumberList(int indicationType, android.hardware.radio.voice.EmergencyNumber[] emergencyNumberList) { - mRil.processIndication(RIL.VOICE_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_VOICE, indicationType); List<EmergencyNumber> response = new ArrayList<>(emergencyNumberList.length); for (android.hardware.radio.voice.EmergencyNumber enHal : emergencyNumberList) { @@ -259,7 +265,7 @@ response.add(emergencyNumber); } - if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_EMERGENCY_NUMBER_LIST, response); + if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_EMERGENCY_NUMBER_LIST, response); // Cache emergency number list from last indication. mRil.cacheEmergencyNumberListIndication(response); @@ -275,9 +281,9 @@ * @param indicationType Type of radio indication */ public void enterEmergencyCallbackMode(int indicationType) { - mRil.processIndication(RIL.VOICE_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_VOICE, indicationType); - if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_ENTER_EMERGENCY_CALLBACK_MODE); + if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_ENTER_EMERGENCY_CALLBACK_MODE); if (mRil.mEmergencyCallbackModeRegistrant != null) { mRil.mEmergencyCallbackModeRegistrant.notifyRegistrant(); @@ -290,9 +296,9 @@ * @param indicationType Type of radio indication */ public void exitEmergencyCallbackMode(int indicationType) { - mRil.processIndication(RIL.VOICE_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_VOICE, indicationType); - if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_EXIT_EMERGENCY_CALLBACK_MODE); + if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_EXIT_EMERGENCY_CALLBACK_MODE); mRil.mExitEmergencyCallbackModeRegistrants.notifyRegistrants(); } @@ -303,9 +309,9 @@ * @param start true = start play ringback tone, false = stop playing ringback tone */ public void indicateRingbackTone(int indicationType, boolean start) { - mRil.processIndication(RIL.VOICE_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_VOICE, indicationType); - if (RIL.RILJ_LOGD) mRil.unsljLogvRet(RIL_UNSOL_RINGBACK_TONE, start); + if (mRil.isLogOrTrace()) mRil.unsljLogvRet(RIL_UNSOL_RINGBACK_TONE, start); mRil.mRingbackToneRegistrants.notifyRegistrants(new AsyncResult(null, start, null)); } @@ -318,7 +324,7 @@ */ public void onSupplementaryServiceIndication(int indicationType, android.hardware.radio.voice.StkCcUnsolSsResult ss) { - mRil.processIndication(RIL.VOICE_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_VOICE, indicationType); int num; SsData ssData = new SsData(); @@ -355,7 +361,7 @@ } } - if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_ON_SS, ssData); + if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_ON_SS, ssData); if (mRil.mSsRegistrant != null) { mRil.mSsRegistrant.notifyRegistrant(new AsyncResult(null, ssData, null)); @@ -370,9 +376,9 @@ * @param msg Message string in UTF-8, if applicable */ public void onUssd(int indicationType, int ussdModeType, String msg) { - mRil.processIndication(RIL.VOICE_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_VOICE, indicationType); - if (RIL.RILJ_LOGD) mRil.unsljLogMore(RIL_UNSOL_ON_USSD, "" + ussdModeType); + if (mRil.isLogOrTrace()) mRil.unsljLogMore(RIL_UNSOL_ON_USSD, "" + ussdModeType); // TODO: Clean this up with a parcelable class for better self-documentation String[] resp = new String[]{"" + ussdModeType, msg}; @@ -386,9 +392,9 @@ * @param indicationType Type of radio indication */ public void resendIncallMute(int indicationType) { - mRil.processIndication(RIL.VOICE_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_VOICE, indicationType); - if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_RESEND_INCALL_MUTE); + if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_RESEND_INCALL_MUTE); mRil.mResendIncallMuteRegistrants.notifyRegistrants(); } @@ -399,11 +405,11 @@ * @param state New SRVCC State */ public void srvccStateNotify(int indicationType, int state) { - mRil.processIndication(RIL.VOICE_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_VOICE, indicationType); int[] response = new int[] {state}; - if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_SRVCC_STATE_NOTIFY, response); + if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_SRVCC_STATE_NOTIFY, response); mRil.writeMetricsSrvcc(state); mRil.mSrvccStateRegistrants.notifyRegistrants(new AsyncResult(null, response, null)); @@ -415,9 +421,9 @@ * @param alpha ALPHA string from UICC in UTF-8 format */ public void stkCallControlAlphaNotify(int indicationType, String alpha) { - mRil.processIndication(RIL.VOICE_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_VOICE, indicationType); - if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_STK_CC_ALPHA_NOTIFY, alpha); + if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_STK_CC_ALPHA_NOTIFY, alpha); if (mRil.mCatCcAlphaRegistrant != null) { mRil.mCatCcAlphaRegistrant.notifyRegistrant(new AsyncResult(null, alpha, null)); @@ -430,9 +436,9 @@ * @param timeout Timeout value in milliseconds for setting up voice call */ public void stkCallSetup(int indicationType, long timeout) { - mRil.processIndication(RIL.VOICE_SERVICE, indicationType); + mRil.processIndication(HAL_SERVICE_VOICE, indicationType); - if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_STK_CALL_SETUP, timeout); + if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_STK_CALL_SETUP, timeout); if (mRil.mCatCallSetUpRegistrant != null) { mRil.mCatCallSetUpRegistrant.notifyRegistrant(new AsyncResult(null, timeout, null));
diff --git a/src/java/com/android/internal/telephony/VoiceResponse.java b/src/java/com/android/internal/telephony/VoiceResponse.java index b1ba9d9..d1e060e 100644 --- a/src/java/com/android/internal/telephony/VoiceResponse.java +++ b/src/java/com/android/internal/telephony/VoiceResponse.java
@@ -16,6 +16,8 @@ package com.android.internal.telephony; +import static android.telephony.TelephonyManager.HAL_SERVICE_VOICE; + import android.hardware.radio.RadioError; import android.hardware.radio.RadioResponseInfo; import android.hardware.radio.voice.IRadioVoiceResponse; @@ -47,49 +49,49 @@ * @param responseInfo Response info struct containing response type, serial no. and error */ public void acceptCallResponse(RadioResponseInfo responseInfo) { - RadioResponse.responseVoid(RIL.VOICE_SERVICE, mRil, responseInfo); + RadioResponse.responseVoid(HAL_SERVICE_VOICE, mRil, responseInfo); } /** * @param responseInfo Response info struct containing response type, serial no. and error */ public void cancelPendingUssdResponse(RadioResponseInfo responseInfo) { - RadioResponse.responseVoid(RIL.VOICE_SERVICE, mRil, responseInfo); + RadioResponse.responseVoid(HAL_SERVICE_VOICE, mRil, responseInfo); } /** * @param responseInfo Response info struct containing response type, serial no. and error */ public void conferenceResponse(RadioResponseInfo responseInfo) { - RadioResponse.responseVoid(RIL.VOICE_SERVICE, mRil, responseInfo); + RadioResponse.responseVoid(HAL_SERVICE_VOICE, mRil, responseInfo); } /** * @param responseInfo Response info struct containing response type, serial no. and error */ public void dialResponse(RadioResponseInfo responseInfo) { - RadioResponse.responseVoid(RIL.VOICE_SERVICE, mRil, responseInfo); + RadioResponse.responseVoid(HAL_SERVICE_VOICE, mRil, responseInfo); } /** * @param responseInfo Response info struct containing response type, serial no. and error */ public void emergencyDialResponse(RadioResponseInfo responseInfo) { - RadioResponse.responseVoid(RIL.VOICE_SERVICE, mRil, responseInfo); + RadioResponse.responseVoid(HAL_SERVICE_VOICE, mRil, responseInfo); } /** * @param responseInfo Response info struct containing response type, serial no. and error */ public void exitEmergencyCallbackModeResponse(RadioResponseInfo responseInfo) { - RadioResponse.responseVoid(RIL.VOICE_SERVICE, mRil, responseInfo); + RadioResponse.responseVoid(HAL_SERVICE_VOICE, mRil, responseInfo); } /** * @param responseInfo Response info struct containing response type, serial no. and error */ public void explicitCallTransferResponse(RadioResponseInfo responseInfo) { - RadioResponse.responseVoid(RIL.VOICE_SERVICE, mRil, responseInfo); + RadioResponse.responseVoid(HAL_SERVICE_VOICE, mRil, responseInfo); } /** @@ -99,7 +101,7 @@ */ public void getCallForwardStatusResponse(RadioResponseInfo responseInfo, android.hardware.radio.voice.CallForwardInfo[] callForwardInfos) { - RILRequest rr = mRil.processResponse(RIL.VOICE_SERVICE, responseInfo); + RILRequest rr = mRil.processResponse(HAL_SERVICE_VOICE, responseInfo); if (rr != null) { CallForwardInfo[] ret = new CallForwardInfo[callForwardInfos.length]; for (int i = 0; i < callForwardInfos.length; i++) { @@ -129,7 +131,7 @@ public void getCallWaitingResponse(RadioResponseInfo responseInfo, boolean enable, int serviceClass) { RadioResponse.responseInts( - RIL.VOICE_SERVICE, mRil, responseInfo, enable ? 1 : 0, serviceClass); + HAL_SERVICE_VOICE, mRil, responseInfo, enable ? 1 : 0, serviceClass); } /** @@ -137,7 +139,7 @@ * @param status indicates CLIP status */ public void getClipResponse(RadioResponseInfo responseInfo, int status) { - RadioResponse.responseInts(RIL.VOICE_SERVICE, mRil, responseInfo, status); + RadioResponse.responseInts(HAL_SERVICE_VOICE, mRil, responseInfo, status); } /** @@ -146,7 +148,7 @@ * @param m is "m" parameter from TS 27.007 7.7 */ public void getClirResponse(RadioResponseInfo responseInfo, int n, int m) { - RadioResponse.responseInts(RIL.VOICE_SERVICE, mRil, responseInfo, n, m); + RadioResponse.responseInts(HAL_SERVICE_VOICE, mRil, responseInfo, n, m); } /** @@ -155,7 +157,7 @@ */ public void getCurrentCallsResponse(RadioResponseInfo responseInfo, android.hardware.radio.voice.Call[] calls) { - RILRequest rr = mRil.processResponse(RIL.VOICE_SERVICE, responseInfo); + RILRequest rr = mRil.processResponse(HAL_SERVICE_VOICE, responseInfo); if (rr != null) { int num = calls.length; @@ -198,7 +200,7 @@ */ public void getLastCallFailCauseResponse(RadioResponseInfo responseInfo, android.hardware.radio.voice.LastCallFailCauseInfo fcInfo) { - RILRequest rr = mRil.processResponse(RIL.VOICE_SERVICE, responseInfo); + RILRequest rr = mRil.processResponse(HAL_SERVICE_VOICE, responseInfo); if (rr != null) { LastCallFailCause ret = new LastCallFailCause(); @@ -216,7 +218,7 @@ * @param enable true for "mute enabled" and false for "mute disabled" */ public void getMuteResponse(RadioResponseInfo responseInfo, boolean enable) { - RadioResponse.responseInts(RIL.VOICE_SERVICE, mRil, responseInfo, enable ? 1 : 0); + RadioResponse.responseInts(HAL_SERVICE_VOICE, mRil, responseInfo, enable ? 1 : 0); } /** @@ -225,7 +227,7 @@ * true for Enhanced Privacy Mode (Private Long Code Mask) */ public void getPreferredVoicePrivacyResponse(RadioResponseInfo responseInfo, boolean enable) { - RadioResponse.responseInts(RIL.VOICE_SERVICE, mRil, responseInfo, enable ? 1 : 0); + RadioResponse.responseInts(HAL_SERVICE_VOICE, mRil, responseInfo, enable ? 1 : 0); } /** @@ -233,35 +235,35 @@ * @param mode TTY mode */ public void getTtyModeResponse(RadioResponseInfo responseInfo, int mode) { - RadioResponse.responseInts(RIL.VOICE_SERVICE, mRil, responseInfo, mode); + RadioResponse.responseInts(HAL_SERVICE_VOICE, mRil, responseInfo, mode); } /** * @param responseInfo Response info struct containing response type, serial no. and error */ public void handleStkCallSetupRequestFromSimResponse(RadioResponseInfo responseInfo) { - RadioResponse.responseVoid(RIL.VOICE_SERVICE, mRil, responseInfo); + RadioResponse.responseVoid(HAL_SERVICE_VOICE, mRil, responseInfo); } /** * @param responseInfo Response info struct containing response type, serial no. and error */ public void hangupConnectionResponse(RadioResponseInfo responseInfo) { - RadioResponse.responseVoid(RIL.VOICE_SERVICE, mRil, responseInfo); + RadioResponse.responseVoid(HAL_SERVICE_VOICE, mRil, responseInfo); } /** * @param responseInfo Response info struct containing response type, serial no. and error */ public void hangupForegroundResumeBackgroundResponse(RadioResponseInfo responseInfo) { - RadioResponse.responseVoid(RIL.VOICE_SERVICE, mRil, responseInfo); + RadioResponse.responseVoid(HAL_SERVICE_VOICE, mRil, responseInfo); } /** * @param responseInfo Response info struct containing response type, serial no. and error */ public void hangupWaitingOrBackgroundResponse(RadioResponseInfo responseInfo) { - RadioResponse.responseVoid(RIL.VOICE_SERVICE, mRil, responseInfo); + RadioResponse.responseVoid(HAL_SERVICE_VOICE, mRil, responseInfo); } /** @@ -269,7 +271,7 @@ * @param enable true for "vonr enabled" and false for "vonr disabled" */ public void isVoNrEnabledResponse(RadioResponseInfo responseInfo, boolean enable) { - RILRequest rr = mRil.processResponse(RIL.VOICE_SERVICE, responseInfo); + RILRequest rr = mRil.processResponse(HAL_SERVICE_VOICE, responseInfo); if (rr != null) { if (responseInfo.error == RadioError.NONE) { @@ -283,112 +285,112 @@ * @param responseInfo Response info struct containing response type, serial no. and error */ public void rejectCallResponse(RadioResponseInfo responseInfo) { - RadioResponse.responseVoid(RIL.VOICE_SERVICE, mRil, responseInfo); + RadioResponse.responseVoid(HAL_SERVICE_VOICE, mRil, responseInfo); } /** * @param responseInfo Response info struct containing response type, serial no. and error */ public void sendBurstDtmfResponse(RadioResponseInfo responseInfo) { - RadioResponse.responseVoid(RIL.VOICE_SERVICE, mRil, responseInfo); + RadioResponse.responseVoid(HAL_SERVICE_VOICE, mRil, responseInfo); } /** * @param responseInfo Response info struct containing response type, serial no. and error */ public void sendCdmaFeatureCodeResponse(RadioResponseInfo responseInfo) { - RadioResponse.responseVoid(RIL.VOICE_SERVICE, mRil, responseInfo); + RadioResponse.responseVoid(HAL_SERVICE_VOICE, mRil, responseInfo); } /** * @param responseInfo Response info struct containing response type, serial no. and error */ public void sendDtmfResponse(RadioResponseInfo responseInfo) { - RadioResponse.responseVoid(RIL.VOICE_SERVICE, mRil, responseInfo); + RadioResponse.responseVoid(HAL_SERVICE_VOICE, mRil, responseInfo); } /** * @param responseInfo Response info struct containing response type, serial no. and error */ public void sendUssdResponse(RadioResponseInfo responseInfo) { - RadioResponse.responseVoid(RIL.VOICE_SERVICE, mRil, responseInfo); + RadioResponse.responseVoid(HAL_SERVICE_VOICE, mRil, responseInfo); } /** * @param responseInfo Response info struct containing response type, serial no. and error */ public void separateConnectionResponse(RadioResponseInfo responseInfo) { - RadioResponse.responseVoid(RIL.VOICE_SERVICE, mRil, responseInfo); + RadioResponse.responseVoid(HAL_SERVICE_VOICE, mRil, responseInfo); } /** * @param responseInfo Response info struct containing response type, serial no. and error */ public void setCallForwardResponse(RadioResponseInfo responseInfo) { - RadioResponse.responseVoid(RIL.VOICE_SERVICE, mRil, responseInfo); + RadioResponse.responseVoid(HAL_SERVICE_VOICE, mRil, responseInfo); } /** * @param responseInfo Response info struct containing response type, serial no. and error */ public void setCallWaitingResponse(RadioResponseInfo responseInfo) { - RadioResponse.responseVoid(RIL.VOICE_SERVICE, mRil, responseInfo); + RadioResponse.responseVoid(HAL_SERVICE_VOICE, mRil, responseInfo); } /** * @param responseInfo Response info struct containing response type, serial no. and error */ public void setClirResponse(RadioResponseInfo responseInfo) { - RadioResponse.responseVoid(RIL.VOICE_SERVICE, mRil, responseInfo); + RadioResponse.responseVoid(HAL_SERVICE_VOICE, mRil, responseInfo); } /** * @param responseInfo Response info struct containing response type, serial no. and error */ public void setMuteResponse(RadioResponseInfo responseInfo) { - RadioResponse.responseVoid(RIL.VOICE_SERVICE, mRil, responseInfo); + RadioResponse.responseVoid(HAL_SERVICE_VOICE, mRil, responseInfo); } /** * @param responseInfo Response info struct containing response type, serial no. and error */ public void setPreferredVoicePrivacyResponse(RadioResponseInfo responseInfo) { - RadioResponse.responseVoid(RIL.VOICE_SERVICE, mRil, responseInfo); + RadioResponse.responseVoid(HAL_SERVICE_VOICE, mRil, responseInfo); } /** * @param responseInfo Response info struct containing response type, serial no. and error */ public void setTtyModeResponse(RadioResponseInfo responseInfo) { - RadioResponse.responseVoid(RIL.VOICE_SERVICE, mRil, responseInfo); + RadioResponse.responseVoid(HAL_SERVICE_VOICE, mRil, responseInfo); } /** * @param responseInfo Response info struct containing response type, serial no. and error */ public void setVoNrEnabledResponse(RadioResponseInfo responseInfo) { - RadioResponse.responseVoid(RIL.VOICE_SERVICE, mRil, responseInfo); + RadioResponse.responseVoid(HAL_SERVICE_VOICE, mRil, responseInfo); } /** * @param responseInfo Response info struct containing response type, serial no. and error */ public void startDtmfResponse(RadioResponseInfo responseInfo) { - RadioResponse.responseVoid(RIL.VOICE_SERVICE, mRil, responseInfo); + RadioResponse.responseVoid(HAL_SERVICE_VOICE, mRil, responseInfo); } /** * @param responseInfo Response info struct containing response type, serial no. and error */ public void stopDtmfResponse(RadioResponseInfo responseInfo) { - RadioResponse.responseVoid(RIL.VOICE_SERVICE, mRil, responseInfo); + RadioResponse.responseVoid(HAL_SERVICE_VOICE, mRil, responseInfo); } /** * @param responseInfo Response info struct containing response type, serial no. and error */ public void switchWaitingOrHoldingAndActiveResponse(RadioResponseInfo responseInfo) { - RadioResponse.responseVoid(RIL.VOICE_SERVICE, mRil, responseInfo); + RadioResponse.responseVoid(HAL_SERVICE_VOICE, mRil, responseInfo); } @Override
diff --git a/src/java/com/android/internal/telephony/WapPushOverSms.java b/src/java/com/android/internal/telephony/WapPushOverSms.java old mode 100755 new mode 100644 index d6f69e2..d6ea4ad --- a/src/java/com/android/internal/telephony/WapPushOverSms.java +++ b/src/java/com/android/internal/telephony/WapPushOverSms.java
@@ -46,6 +46,7 @@ import android.text.TextUtils; import com.android.internal.telephony.uicc.IccUtils; +import com.android.internal.telephony.util.TelephonyUtils; import com.android.telephony.Rlog; import com.google.android.mms.pdu.GenericPdu; @@ -245,9 +246,10 @@ System.arraycopy(pdu, dataIndex, intentData, 0, intentData.length); } - int[] subIds = SubscriptionManager.getSubId(phoneId); - int subId = (subIds != null) && (subIds.length > 0) ? subIds[0] - : SmsManager.getDefaultSmsSubscriptionId(); + int subId = SubscriptionManager.getSubscriptionId(phoneId); + if (!SubscriptionManager.isValidSubscriptionId(subId)) { + subId = SmsManager.getDefaultSmsSubscriptionId(); + } // Continue if PDU parsing fails: the default messaging app may successfully parse the // same PDU. @@ -391,7 +393,10 @@ // Direct the intent to only the default MMS app. If we can't find a default MMS app // then sent it to all broadcast receivers. - ComponentName componentName = SmsApplication.getDefaultMmsApplication(mContext, true); + UserHandle userHandle = TelephonyUtils.getSubscriptionUserHandle(mContext, subId); + ComponentName componentName = SmsApplication.getDefaultMmsApplicationAsUser(mContext, + true, userHandle); + Bundle options = null; if (componentName != null) { // Deliver MMS message only to this receiver @@ -409,9 +414,12 @@ options = bopts.toBundle(); } + if (userHandle == null) { + userHandle = UserHandle.SYSTEM; + } handler.dispatchIntent(intent, getPermissionForType(result.mimeType), getAppOpsStringPermissionForIntent(result.mimeType), options, receiver, - UserHandle.SYSTEM, subId); + userHandle, subId); return Activity.RESULT_OK; }
diff --git a/src/java/com/android/internal/telephony/WspTypeDecoder.java b/src/java/com/android/internal/telephony/WspTypeDecoder.java old mode 100755 new mode 100644
diff --git a/src/java/com/android/internal/telephony/cat/AppInterface.java b/src/java/com/android/internal/telephony/cat/AppInterface.java old mode 100755 new mode 100644
diff --git a/src/java/com/android/internal/telephony/cat/CatCmdMessage.java b/src/java/com/android/internal/telephony/cat/CatCmdMessage.java index 3d21270..4447c07 100644 --- a/src/java/com/android/internal/telephony/cat/CatCmdMessage.java +++ b/src/java/com/android/internal/telephony/cat/CatCmdMessage.java
@@ -40,6 +40,7 @@ private ToneSettings mToneSettings = null; @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) private CallSettings mCallSettings = null; + private SMSSettings mSMSSettings = null; private SetupEventListSettings mSetupEventListSettings = null; private boolean mLoadIconFailed = false; @@ -61,6 +62,14 @@ public TextMessage callMsg; } + /** + * Container for SEND SMS command settings. + */ + public class SMSSettings { + public TextMessage smsText; + public TextMessage destAddr; + } + public class SetupEventListSettings { @UnsupportedAppUsage public int[] eventList; @@ -84,57 +93,69 @@ mCmdDet = cmdParams.mCmdDet; mLoadIconFailed = cmdParams.mLoadIconFailed; switch(getCmdType()) { - case SET_UP_MENU: - case SELECT_ITEM: - mMenu = ((SelectItemParams) cmdParams).mMenu; - break; - case DISPLAY_TEXT: - case SET_UP_IDLE_MODE_TEXT: - case SEND_DTMF: - case SEND_SMS: - case REFRESH: - case RUN_AT: - case SEND_SS: - case SEND_USSD: - mTextMsg = ((DisplayTextParams) cmdParams).mTextMsg; - break; - case GET_INPUT: - case GET_INKEY: - mInput = ((GetInputParams) cmdParams).mInput; - break; - case LAUNCH_BROWSER: - mTextMsg = ((LaunchBrowserParams) cmdParams).mConfirmMsg; - mBrowserSettings = new BrowserSettings(); - mBrowserSettings.url = ((LaunchBrowserParams) cmdParams).mUrl; - mBrowserSettings.mode = ((LaunchBrowserParams) cmdParams).mMode; - break; - case PLAY_TONE: - PlayToneParams params = (PlayToneParams) cmdParams; - mToneSettings = params.mSettings; - mTextMsg = params.mTextMsg; - break; - case GET_CHANNEL_STATUS: - mTextMsg = ((CallSetupParams) cmdParams).mConfirmMsg; - break; - case SET_UP_CALL: - mCallSettings = new CallSettings(); - mCallSettings.confirmMsg = ((CallSetupParams) cmdParams).mConfirmMsg; - mCallSettings.callMsg = ((CallSetupParams) cmdParams).mCallMsg; - break; - case OPEN_CHANNEL: - case CLOSE_CHANNEL: - case RECEIVE_DATA: - case SEND_DATA: - BIPClientParams param = (BIPClientParams) cmdParams; - mTextMsg = param.mTextMsg; - break; - case SET_UP_EVENT_LIST: - mSetupEventListSettings = new SetupEventListSettings(); - mSetupEventListSettings.eventList = ((SetEventListParams) cmdParams).mEventInfo; - break; - case PROVIDE_LOCAL_INFORMATION: - default: - break; + case SET_UP_MENU: + case SELECT_ITEM: + mMenu = ((SelectItemParams) cmdParams).mMenu; + break; + case SEND_SMS: + /* If cmdParams is an instanceof SendSMSParams , then it means config value + * config_stk_sms_send_support is true and the SMS should be sent by framework + */ + if (cmdParams instanceof SendSMSParams) { + mSMSSettings = new SMSSettings(); + mSMSSettings.smsText = ((SendSMSParams) cmdParams).mTextSmsMsg; + mSMSSettings.destAddr = ((SendSMSParams) cmdParams).mDestAddress; + mTextMsg = ((SendSMSParams) cmdParams).mDisplayText.mTextMsg; + } else { + mTextMsg = ((DisplayTextParams) cmdParams).mTextMsg; + } + break; + case DISPLAY_TEXT: + case SET_UP_IDLE_MODE_TEXT: + case SEND_DTMF: + case REFRESH: + case RUN_AT: + case SEND_SS: + case SEND_USSD: + mTextMsg = ((DisplayTextParams) cmdParams).mTextMsg; + break; + case GET_INPUT: + case GET_INKEY: + mInput = ((GetInputParams) cmdParams).mInput; + break; + case LAUNCH_BROWSER: + mTextMsg = ((LaunchBrowserParams) cmdParams).mConfirmMsg; + mBrowserSettings = new BrowserSettings(); + mBrowserSettings.url = ((LaunchBrowserParams) cmdParams).mUrl; + mBrowserSettings.mode = ((LaunchBrowserParams) cmdParams).mMode; + break; + case PLAY_TONE: + PlayToneParams params = (PlayToneParams) cmdParams; + mToneSettings = params.mSettings; + mTextMsg = params.mTextMsg; + break; + case GET_CHANNEL_STATUS: + mTextMsg = ((CallSetupParams) cmdParams).mConfirmMsg; + break; + case SET_UP_CALL: + mCallSettings = new CallSettings(); + mCallSettings.confirmMsg = ((CallSetupParams) cmdParams).mConfirmMsg; + mCallSettings.callMsg = ((CallSetupParams) cmdParams).mCallMsg; + break; + case OPEN_CHANNEL: + case CLOSE_CHANNEL: + case RECEIVE_DATA: + case SEND_DATA: + BIPClientParams param = (BIPClientParams) cmdParams; + mTextMsg = param.mTextMsg; + break; + case SET_UP_EVENT_LIST: + mSetupEventListSettings = new SetupEventListSettings(); + mSetupEventListSettings.eventList = ((SetEventListParams) cmdParams).mEventInfo; + break; + case PROVIDE_LOCAL_INFORMATION: + default: + break; } } @@ -145,29 +166,34 @@ mInput = in.readParcelable(Input.class.getClassLoader()); mLoadIconFailed = (in.readByte() == 1); switch (getCmdType()) { - case LAUNCH_BROWSER: - mBrowserSettings = new BrowserSettings(); - mBrowserSettings.url = in.readString(); - mBrowserSettings.mode = LaunchBrowserMode.values()[in.readInt()]; - break; - case PLAY_TONE: - mToneSettings = in.readParcelable(ToneSettings.class.getClassLoader()); - break; - case SET_UP_CALL: - mCallSettings = new CallSettings(); - mCallSettings.confirmMsg = in.readParcelable(TextMessage.class.getClassLoader()); - mCallSettings.callMsg = in.readParcelable(TextMessage.class.getClassLoader()); - break; - case SET_UP_EVENT_LIST: - mSetupEventListSettings = new SetupEventListSettings(); - int length = in.readInt(); - mSetupEventListSettings.eventList = new int[length]; - for (int i = 0; i < length; i++) { - mSetupEventListSettings.eventList[i] = in.readInt(); - } - break; - default: - break; + case LAUNCH_BROWSER: + mBrowserSettings = new BrowserSettings(); + mBrowserSettings.url = in.readString(); + mBrowserSettings.mode = LaunchBrowserMode.values()[in.readInt()]; + break; + case PLAY_TONE: + mToneSettings = in.readParcelable(ToneSettings.class.getClassLoader()); + break; + case SET_UP_CALL: + mCallSettings = new CallSettings(); + mCallSettings.confirmMsg = in.readParcelable(TextMessage.class.getClassLoader()); + mCallSettings.callMsg = in.readParcelable(TextMessage.class.getClassLoader()); + break; + case SET_UP_EVENT_LIST: + mSetupEventListSettings = new SetupEventListSettings(); + int length = in.readInt(); + mSetupEventListSettings.eventList = new int[length]; + for (int i = 0; i < length; i++) { + mSetupEventListSettings.eventList[i] = in.readInt(); + } + break; + case SEND_SMS: + mSMSSettings = new SMSSettings(); + mSMSSettings.smsText = in.readParcelable(SendSMSParams.class.getClassLoader()); + mSMSSettings.destAddr = in.readParcelable(SendSMSParams.class.getClassLoader()); + break; + default: + break; } } @@ -178,23 +204,29 @@ dest.writeParcelable(mMenu, 0); dest.writeParcelable(mInput, 0); dest.writeByte((byte) (mLoadIconFailed ? 1 : 0)); - switch(getCmdType()) { - case LAUNCH_BROWSER: - dest.writeString(mBrowserSettings.url); - dest.writeInt(mBrowserSettings.mode.ordinal()); - break; - case PLAY_TONE: - dest.writeParcelable(mToneSettings, 0); - break; - case SET_UP_CALL: - dest.writeParcelable(mCallSettings.confirmMsg, 0); - dest.writeParcelable(mCallSettings.callMsg, 0); - break; - case SET_UP_EVENT_LIST: - dest.writeIntArray(mSetupEventListSettings.eventList); - break; - default: - break; + switch (getCmdType()) { + case LAUNCH_BROWSER: + dest.writeString(mBrowserSettings.url); + dest.writeInt(mBrowserSettings.mode.ordinal()); + break; + case PLAY_TONE: + dest.writeParcelable(mToneSettings, 0); + break; + case SET_UP_CALL: + dest.writeParcelable(mCallSettings.confirmMsg, 0); + dest.writeParcelable(mCallSettings.callMsg, 0); + break; + case SET_UP_EVENT_LIST: + dest.writeIntArray(mSetupEventListSettings.eventList); + break; + case SEND_SMS: + if (mSMSSettings != null) { + dest.writeParcelable(mSMSSettings.smsText, 0); + dest.writeParcelable(mSMSSettings.destAddr, 0); + } + break; + default: + break; } }
diff --git a/src/java/com/android/internal/telephony/cat/CatService.java b/src/java/com/android/internal/telephony/cat/CatService.java index 9ed5eb9..fa2b19b 100644 --- a/src/java/com/android/internal/telephony/cat/CatService.java +++ b/src/java/com/android/internal/telephony/cat/CatService.java
@@ -20,25 +20,36 @@ import static com.android.internal.telephony.cat.CatCmdMessage.SetupEventListConstants.LANGUAGE_SELECTION_EVENT; import static com.android.internal.telephony.cat.CatCmdMessage.SetupEventListConstants.USER_ACTIVITY_EVENT; +import android.app.Activity; import android.app.ActivityManager; +import android.app.PendingIntent; import android.app.backup.BackupManager; import android.compat.annotation.UnsupportedAppUsage; +import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; +import android.content.IntentFilter; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.res.Resources.NotFoundException; import android.os.AsyncResult; import android.os.Build; import android.os.Handler; +import android.os.HandlerThread; import android.os.LocaleList; +import android.os.Looper; import android.os.Message; import android.os.RemoteException; +import android.telephony.SubscriptionInfo; +import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.telephony.CommandsInterface; import com.android.internal.telephony.PhoneConstants; -import com.android.internal.telephony.SubscriptionController; +import com.android.internal.telephony.ProxyController; +import com.android.internal.telephony.SmsController; +import com.android.internal.telephony.subscription.SubscriptionManagerService; import com.android.internal.telephony.uicc.IccCardStatus.CardState; import com.android.internal.telephony.uicc.IccFileHandler; import com.android.internal.telephony.uicc.IccRecords; @@ -138,12 +149,21 @@ static final String STK_DEFAULT = "Default Message"; + private static final String SMS_DELIVERY_ACTION = + "com.android.internal.telephony.cat.SMS_DELIVERY_ACTION"; + private static final String SMS_SENT_ACTION = + "com.android.internal.telephony.cat.SMS_SENT_ACTION"; + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) private int mSlotId; + private static HandlerThread sCatServiceThread; /* For multisim catservice should not be singleton */ private CatService(CommandsInterface ci, UiccCardApplication ca, IccRecords ir, - Context context, IccFileHandler fh, UiccProfile uiccProfile, int slotId) { + Context context, IccFileHandler fh, UiccProfile uiccProfile, int slotId, + Looper looper) { + //creating new thread to avoid deadlock conditions with the framework thread. + super(looper); if (ci == null || ca == null || ir == null || context == null || fh == null || uiccProfile == null) { throw new NullPointerException( @@ -187,6 +207,9 @@ CatLog.d(this, "Running CAT service on Slotid: " + mSlotId + ". STK app installed:" + mStkAppInstalled); + + mContext.registerReceiver(mSmsBroadcastReceiver, new IntentFilter(SMS_DELIVERY_ACTION)); + mContext.registerReceiver(mSmsBroadcastReceiver, new IntentFilter(SMS_SENT_ACTION)); } /** @@ -200,6 +223,10 @@ */ public static CatService getInstance(CommandsInterface ci, Context context, UiccProfile uiccProfile, int slotId) { + if (sCatServiceThread == null) { + sCatServiceThread = new HandlerThread("CatServiceThread"); + sCatServiceThread.start(); + } UiccCardApplication ca = null; IccFileHandler fh = null; IccRecords ir = null; @@ -227,8 +254,8 @@ || uiccProfile == null) { return null; } - - sInstance[slotId] = new CatService(ci, ca, ir, context, fh, uiccProfile, slotId); + sInstance[slotId] = new CatService(ci, ca, ir, context, fh, uiccProfile, slotId, + sCatServiceThread.getLooper()); } else if ((ir != null) && (mIccRecords != ir)) { if (mIccRecords != null) { mIccRecords.unregisterForRecordsLoaded(sInstance[slotId]); @@ -447,8 +474,49 @@ ((DisplayTextParams)cmdParams).mTextMsg.text = null; } break; - case SEND_DTMF: case SEND_SMS: + /* If cmdParams is an instanceof SendSMSParams , then it means config value + * config_stk_sms_send_support is true and the SMS should be sent by framework + */ + if (cmdParams instanceof SendSMSParams) { + String text = null, destAddr = null; + if (((SendSMSParams) cmdParams).mTextSmsMsg != null) { + text = ((SendSMSParams) cmdParams).mTextSmsMsg.text; + } + if (((SendSMSParams) cmdParams).mDestAddress != null) { + destAddr = ((SendSMSParams) cmdParams).mDestAddress.text; + } + if (text != null && destAddr != null) { + ProxyController proxyController = ProxyController.getInstance(mContext); + SubscriptionManager subscriptionManager = (SubscriptionManager) + mContext.getSystemService( + Context.TELEPHONY_SUBSCRIPTION_SERVICE); + SubscriptionInfo subInfo = + subscriptionManager.getActiveSubscriptionInfoForSimSlotIndex( + mSlotId); + if (subInfo != null) { + sendStkSms(text, destAddr, subInfo.getSubscriptionId(), cmdParams, + proxyController); + } else { + sendTerminalResponse(cmdParams.mCmdDet, + ResultCode.CMD_DATA_NOT_UNDERSTOOD, false, 0x00, null); + CatLog.d(this, "Subscription info is null"); + } + } else { + sendTerminalResponse(cmdParams.mCmdDet, ResultCode.CMD_DATA_NOT_UNDERSTOOD, + false, 0x00, null); + CatLog.d(this, "Sms text or Destination Address is null"); + } + } else { + if ((((DisplayTextParams) cmdParams).mTextMsg.text != null) + && (((DisplayTextParams) cmdParams).mTextMsg.text.equals( + STK_DEFAULT))) { + message = mContext.getText(com.android.internal.R.string.sending); + ((DisplayTextParams) cmdParams).mTextMsg.text = message.toString(); + } + } + break; + case SEND_DTMF: case SEND_SS: case SEND_USSD: if ((((DisplayTextParams)cmdParams).mTextMsg.text != null) @@ -536,6 +604,94 @@ broadcastCatCmdIntent(cmdMsg); } + /** + * Used to send STK based sms via CATService + * @param text The message body + * @param destAddr The destination Address + * @param subId Subscription Id + * @param cmdParams Send SMS Command Params + * @param proxyController ProxyController + * @hide + */ + public void sendStkSms(String text, String destAddr, int subId, CommandParams cmdParams, + ProxyController proxyController) { + PendingIntent sentPendingIntent = PendingIntent.getBroadcast(mContext, 0, + new Intent(SMS_SENT_ACTION) + .putExtra("cmdDetails", cmdParams.mCmdDet) + .setPackage(mContext.getPackageName()), + PendingIntent.FLAG_MUTABLE); + PendingIntent deliveryPendingIntent = PendingIntent.getBroadcast(mContext, 0, + new Intent(SMS_DELIVERY_ACTION) + .putExtra("cmdDetails", cmdParams.mCmdDet) + .setPackage(mContext.getPackageName()), + PendingIntent.FLAG_MUTABLE); + SmsController smsController = proxyController.getSmsController(); + smsController.sendTextForSubscriber(subId, mContext.getOpPackageName(), + mContext.getAttributionTag(), destAddr, null, text, sentPendingIntent, + deliveryPendingIntent, false, 0L, true, true); + } + + /** + * BroadcastReceiver class to handle error and success cases of + * SEND and DELIVERY pending intents used for sending of STK SMS + */ + @VisibleForTesting + public final BroadcastReceiver mSmsBroadcastReceiver = new BroadcastReceiver() { + + @Override + public void onReceive(Context context, Intent intent) { + CommandDetails commandDetails = (CommandDetails) intent.getExtra("cmdDetails"); + if (intent.getAction().equals(SMS_SENT_ACTION)) { + int resultCode = getResultCode(); + ResultCode terminalResponseResultCode = ResultCode.NETWORK_CRNTLY_UNABLE_TO_PROCESS; + CatLog.d(this, "STK SMS errorCode : " + resultCode); + int additionalInfo = 0; + if (resultCode != Activity.RESULT_OK) { + /** + * The Terminal Response Result code is assigned as per Section 12.12.3 + * and 12.12.5 TS 101.267. The Result code SMS_RP_ERROR is added in Ims Case + * and additional information is added as per RP-Cause Values in TS 124.011. + * The Result code NETWORK_CRNTLY_UNABLE_TO_PROCESS is added in non-Ims Case + * and additional information added as per cause values in TS 04.08. + */ + if (intent.hasExtra("ims") && intent.getBooleanExtra("ims", false)) { + terminalResponseResultCode = ResultCode.SMS_RP_ERROR; + //Additional information's 8th bit is 0 as per section 12.12.5 of TS 101.267 + if (intent.hasExtra("errorCode")) { + additionalInfo = (int) intent.getExtra("errorCode"); + if ((additionalInfo & 0x80) != 0) additionalInfo = 0; + } + } else { + //Additional information's 8th bit is 1 as per section 12.12.3 of TS 101.267 + if (intent.hasExtra("errorCode")) { + additionalInfo = (int) intent.getExtra("errorCode"); + additionalInfo |= 0x80; + } + } + CatLog.d(this, "Error delivering STK SMS errorCode : " + additionalInfo + + " terminalResponseResultCode = " + terminalResponseResultCode); + sendTerminalResponse(commandDetails, terminalResponseResultCode, + true, additionalInfo, null); + } else { + CatLog.d(this, " STK SMS sent successfully "); + } + } + if (intent.getAction().equals(SMS_DELIVERY_ACTION)) { + int resultCode = getResultCode(); + switch (resultCode) { + case Activity.RESULT_OK: + sendTerminalResponse(commandDetails, ResultCode.OK, false, 0, null); + CatLog.d(this, " STK SMS delivered successfully "); + break; + default: + CatLog.d(this, "Error delivering STK SMS : " + resultCode); + sendTerminalResponse(commandDetails, + ResultCode.TERMINAL_CRNTLY_UNABLE_TO_PROCESS, false, + 0, null); + } + } + } + }; private void broadcastCatCmdIntent(CatCmdMessage cmdMsg) { Intent intent = new Intent(AppInterface.CAT_CMD_ACTION); @@ -808,9 +964,9 @@ //TODO Need to take care for MSIM public static AppInterface getInstance() { int slotId = PhoneConstants.DEFAULT_SLOT_INDEX; - SubscriptionController sControl = SubscriptionController.getInstance(); - if (sControl != null) { - slotId = sControl.getSlotIndex(sControl.getDefaultSubId()); + if (SubscriptionManagerService.getInstance() != null) { + slotId = SubscriptionManagerService.getInstance().getSlotIndex( + SubscriptionManagerService.getInstance().getDefaultSubId()); } return getInstance(null, null, null, slotId); } @@ -845,7 +1001,11 @@ } } } - mMsgDecoder.sendStartDecodingMessageParams(new RilMessage(msg.what, data)); + if (mMsgDecoder != null) { + mMsgDecoder.sendStartDecodingMessageParams(new RilMessage(msg.what, data)); + } else { + CatLog.e(this, "Error in handleMessage (" + msg.what + ") mMsgDecoder is NULL"); + } break; case MSG_ID_CALL_SETUP: mMsgDecoder.sendStartDecodingMessageParams(new RilMessage(msg.what, null));
diff --git a/src/java/com/android/internal/telephony/cat/CommandParams.java b/src/java/com/android/internal/telephony/cat/CommandParams.java old mode 100755 new mode 100644 index b9de4d1..8530ee2 --- a/src/java/com/android/internal/telephony/cat/CommandParams.java +++ b/src/java/com/android/internal/telephony/cat/CommandParams.java
@@ -22,16 +22,16 @@ /** * Container class for proactive command parameters. - * + * @hide */ -class CommandParams { +public class CommandParams { @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) CommandDetails mCmdDet; // Variable to track if an optional icon load has failed. boolean mLoadIconFailed = false; @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - CommandParams(CommandDetails cmdDet) { + public CommandParams(CommandDetails cmdDet) { mCmdDet = cmdDet; }
diff --git a/src/java/com/android/internal/telephony/cat/CommandParamsFactory.java b/src/java/com/android/internal/telephony/cat/CommandParamsFactory.java index 7fbebfa..65f3c4a 100644 --- a/src/java/com/android/internal/telephony/cat/CommandParamsFactory.java +++ b/src/java/com/android/internal/telephony/cat/CommandParamsFactory.java
@@ -29,6 +29,7 @@ import android.os.Build; import android.os.Handler; import android.os.Message; +import android.telephony.SmsMessage; import android.text.TextUtils; import com.android.internal.telephony.GsmAlphabet; @@ -40,9 +41,9 @@ /** * Factory class, used for decoding raw byte arrays, received from baseband, * into a CommandParams object. - * + * @hide */ -class CommandParamsFactory extends Handler { +public class CommandParamsFactory extends Handler { private static CommandParamsFactory sInstance = null; @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) private IconLoader mIconLoader; @@ -53,6 +54,7 @@ private String mSavedLanguage; private String mRequestedLanguage; private boolean mNoAlphaUsrCnf = false; + private boolean mStkSmsSendViaTelephony = false; // constants static final int MSG_ID_LOAD_ICON_DONE = 1; @@ -86,7 +88,15 @@ private static final int MAX_GSM7_DEFAULT_CHARS = 239; private static final int MAX_UCS2_CHARS = 118; - static synchronized CommandParamsFactory getInstance(RilMessageDecoder caller, + /** + * Returns a singleton instance of CommandParamsFactory + * @param caller Class used for queuing raw ril messages, decoding them into + * CommandParams objects and sending the result back to the CAT Service. + * @param fh IccFileHandler Object + * @param context The Context + * @return CommandParamsFactory instance + */ + public static synchronized CommandParamsFactory getInstance(RilMessageDecoder caller, IccFileHandler fh, Context context) { if (sInstance != null) { return sInstance; @@ -106,6 +116,12 @@ } catch (NotFoundException e) { mNoAlphaUsrCnf = false; } + try { + mStkSmsSendViaTelephony = context.getResources().getBoolean( + com.android.internal.R.bool.config_stk_sms_send_support); + } catch (NotFoundException e) { + mStkSmsSendViaTelephony = false; + } } private CommandDetails processCommandDetails(List<ComprehensionTlv> ctlvs) { @@ -187,8 +203,14 @@ case GET_INPUT: cmdPending = processGetInput(cmdDet, ctlvs); break; - case SEND_DTMF: case SEND_SMS: + if (mStkSmsSendViaTelephony) { + cmdPending = processSMSEventNotify(cmdDet, ctlvs); + } else { + cmdPending = processEventNotify(cmdDet, ctlvs); + } + break; + case SEND_DTMF: case REFRESH: case RUN_AT: case SEND_SS: @@ -735,6 +757,62 @@ return false; } + + /** + * Processes SMS_EVENT_NOTIFY message from baseband. + * + * Method extracts values such as Alpha Id,Icon Id,Sms Tpdu etc from the ComprehensionTlv, + * in order to create the CommandParams i.e. SendSMSParams. + * + * @param cmdDet Command Details container object. + * @param ctlvs List of ComprehensionTlv objects following Command Details + * object and Device Identities object within the proactive command + * @return true if the command is processing is pending and additional + * asynchronous processing is required. + * @hide + */ + public boolean processSMSEventNotify(CommandDetails cmdDet, + List<ComprehensionTlv> ctlvs) throws ResultException { + CatLog.d(this, "processSMSEventNotify"); + + TextMessage textMsg = new TextMessage(); + IconId iconId = null; + + ComprehensionTlv ctlv = searchForTag(ComprehensionTlvTag.ALPHA_ID, + ctlvs); + /* Retrieves alpha identifier from an Alpha Identifier COMPREHENSION-TLV object. + * + * String corresponding to the alpha identifier is obtained and saved as part of + * the DisplayTextParams. + */ + textMsg.text = ValueParser.retrieveAlphaId(ctlv, mNoAlphaUsrCnf); + + ctlv = searchForTag(ComprehensionTlvTag.ICON_ID, ctlvs); + if (ctlv != null) { + // Retrieves icon id from the Icon Identifier COMPREHENSION-TLV object + iconId = ValueParser.retrieveIconId(ctlv); + textMsg.iconSelfExplanatory = iconId.selfExplanatory; + } + + textMsg.responseNeeded = false; + DisplayTextParams displayTextParams = new DisplayTextParams(cmdDet, textMsg); + ComprehensionTlv ctlvTpdu = searchForTag(ComprehensionTlvTag.SMS_TPDU, + ctlvs); + // Retrieves smsMessage from the SMS TPDU COMPREHENSION-TLV object + SmsMessage smsMessage = ValueParser.retrieveTpduAsSmsMessage(ctlvTpdu); + if (smsMessage != null) { + TextMessage smsText = new TextMessage(); + // Obtains the sms message content. + smsText.text = smsMessage.getMessageBody(); + TextMessage destAddr = new TextMessage(); + // Obtains the destination Address. + destAddr.text = smsMessage.getRecipientAddress(); + mCmdParams = new SendSMSParams(cmdDet, smsText, destAddr, displayTextParams); + return false; + } + return true; + } + /** * Processes SET_UP_EVENT_LIST proactive command from the SIM card. *
diff --git a/src/java/com/android/internal/telephony/cat/ComprehensionTlv.java b/src/java/com/android/internal/telephony/cat/ComprehensionTlv.java index 5542b65..416c669 100644 --- a/src/java/com/android/internal/telephony/cat/ComprehensionTlv.java +++ b/src/java/com/android/internal/telephony/cat/ComprehensionTlv.java
@@ -50,7 +50,7 @@ * @param data Byte array containing the value * @param valueIndex Index in data at which the value starts */ - protected ComprehensionTlv(int tag, boolean cr, int length, byte[] data, + public ComprehensionTlv(int tag, boolean cr, int length, byte[] data, int valueIndex) { mTag = tag; mCr = cr;
diff --git a/src/java/com/android/internal/telephony/cat/RilMessageDecoder.java b/src/java/com/android/internal/telephony/cat/RilMessageDecoder.java old mode 100755 new mode 100644 index c25b59e..4b10cae --- a/src/java/com/android/internal/telephony/cat/RilMessageDecoder.java +++ b/src/java/com/android/internal/telephony/cat/RilMessageDecoder.java
@@ -32,8 +32,9 @@ /** * Class used for queuing raw ril messages, decoding them into CommanParams * objects and sending the result back to the CAT Service. + * @hide */ -class RilMessageDecoder extends StateMachine { +public class RilMessageDecoder extends StateMachine { // constants private static final int CMD_START = 1;
diff --git a/src/java/com/android/internal/telephony/cat/SendSMSParams.java b/src/java/com/android/internal/telephony/cat/SendSMSParams.java new file mode 100644 index 0000000..f5108f0 --- /dev/null +++ b/src/java/com/android/internal/telephony/cat/SendSMSParams.java
@@ -0,0 +1,33 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.telephony.cat; + +class SendSMSParams extends CommandParams { + + TextMessage mTextSmsMsg; + TextMessage mDestAddress; + DisplayTextParams mDisplayText; + + SendSMSParams(CommandDetails cmdDet, TextMessage textMsg, TextMessage destAddress, + DisplayTextParams displayText) { + super(cmdDet); + mTextSmsMsg = textMsg; + mDestAddress = destAddress; + mDisplayText = displayText; + } + +}
diff --git a/src/java/com/android/internal/telephony/cat/ValueParser.java b/src/java/com/android/internal/telephony/cat/ValueParser.java index 7c09136..bd17f48 100644 --- a/src/java/com/android/internal/telephony/cat/ValueParser.java +++ b/src/java/com/android/internal/telephony/cat/ValueParser.java
@@ -18,16 +18,24 @@ import android.compat.annotation.UnsupportedAppUsage; import android.os.Build; +import android.telephony.SmsMessage; import com.android.internal.telephony.GsmAlphabet; import com.android.internal.telephony.cat.Duration.TimeUnit; import com.android.internal.telephony.uicc.IccUtils; +import java.io.ByteArrayOutputStream; import java.io.UnsupportedEncodingException; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; -abstract class ValueParser { + +/** + * Util class that parses different entities from the ctlvs ComprehensionTlv List + * @hide + */ +public abstract class ValueParser { /** * Search for a Command Details object from a list. @@ -352,4 +360,42 @@ throw new ResultException(ResultCode.CMD_DATA_NOT_UNDERSTOOD); } } + + /** + * Retrieve's the tpdu from the ctlv and creates the SmsMessage from pdu. + * @param ctlv ComprehensionTlv value + * @return message SmsMessage to retrieve the destAddress and Text + * @throws ResultException + * @hide + */ + public static SmsMessage retrieveTpduAsSmsMessage(ComprehensionTlv ctlv) + throws ResultException { + if (ctlv != null) { + byte[] rawValue = ctlv.getRawValue(); + int valueIndex = ctlv.getValueIndex(); + int length = ctlv.getLength(); + if (length != 0) { + try { + byte[] pdu = Arrays.copyOfRange(rawValue, valueIndex, (valueIndex + length)); + ByteArrayOutputStream bo = new ByteArrayOutputStream(pdu.length + 1); + /* Framework's TPdu Parser expects the TPdu be prepended with SC-Address. + * else the parser will throw an exception. So prepending TPdu with 0, + * which indicates that there is no SC address and its length is 0. + * This way Parser will skip parsing for SC-Address + */ + bo.write(0x00); + bo.write(pdu, 0, pdu.length); + byte[] frameworkPdu = bo.toByteArray(); + //ToDO handle for 3GPP2 format bug: b/243123533 + SmsMessage message = SmsMessage.createFromPdu(frameworkPdu, + SmsMessage.FORMAT_3GPP); + return message; + } catch (IndexOutOfBoundsException e) { + throw new ResultException(ResultCode.CMD_DATA_NOT_UNDERSTOOD); + } + } + } + return null; + } + }
diff --git a/src/java/com/android/internal/telephony/cdma/CdmaInboundSmsHandler.java b/src/java/com/android/internal/telephony/cdma/CdmaInboundSmsHandler.java index eb5f866..784c974 100644 --- a/src/java/com/android/internal/telephony/cdma/CdmaInboundSmsHandler.java +++ b/src/java/com/android/internal/telephony/cdma/CdmaInboundSmsHandler.java
@@ -21,6 +21,7 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.res.Resources; +import android.os.Looper; import android.os.Message; import android.os.RemoteCallback; import android.os.SystemProperties; @@ -77,8 +78,8 @@ * Create a new inbound SMS handler for CDMA. */ private CdmaInboundSmsHandler(Context context, SmsStorageMonitor storageMonitor, - Phone phone, CdmaSMSDispatcher smsDispatcher) { - super("CdmaInboundSmsHandler", context, storageMonitor, phone); + Phone phone, CdmaSMSDispatcher smsDispatcher, Looper looper) { + super("CdmaInboundSmsHandler", context, storageMonitor, phone, looper); mSmsDispatcher = smsDispatcher; phone.mCi.setOnNewCdmaSms(getHandler(), EVENT_NEW_SMS, null); @@ -169,9 +170,10 @@ * Wait for state machine to enter startup state. We can't send any messages until then. */ public static CdmaInboundSmsHandler makeInboundSmsHandler(Context context, - SmsStorageMonitor storageMonitor, Phone phone, CdmaSMSDispatcher smsDispatcher) { + SmsStorageMonitor storageMonitor, Phone phone, CdmaSMSDispatcher smsDispatcher, + Looper looper) { CdmaInboundSmsHandler handler = new CdmaInboundSmsHandler(context, storageMonitor, - phone, smsDispatcher); + phone, smsDispatcher, looper); handler.start(); return handler; } @@ -194,7 +196,8 @@ * @return true if the message was handled here; false to continue processing */ @Override - protected int dispatchMessageRadioSpecific(SmsMessageBase smsb, @SmsSource int smsSource) { + protected int dispatchMessageRadioSpecific(SmsMessageBase smsb, @SmsSource int smsSource, + int token) { SmsMessage sms = (SmsMessage) smsb; boolean isBroadcastType = (SmsEnvelope.MESSAGE_TYPE_BROADCAST == sms.getMessageType());
diff --git a/src/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java b/src/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java index 5a830a8..ebc6342 100644 --- a/src/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java +++ b/src/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
@@ -93,6 +93,21 @@ } @Override + protected SmsMessageBase.SubmitPduBase getSubmitPdu(String scAddr, String destAddr, + String message, boolean statusReportRequested, SmsHeader smsHeader, int priority, + int validityPeriod, int messageRef) { + return SMSDispatcherUtil.getSubmitPduCdma(scAddr, destAddr, message, + statusReportRequested, smsHeader, priority); + } + + @Override + protected SmsMessageBase.SubmitPduBase getSubmitPdu(String scAddr, String destAddr, + int destPort, byte[] message, boolean statusReportRequested, int messageRef) { + return SMSDispatcherUtil.getSubmitPduCdma(scAddr, destAddr, destPort, message, + statusReportRequested); + } + + @Override protected TextEncodingDetails calculateLength(CharSequence messageBody, boolean use7bitOnly) { return SMSDispatcherUtil.calculateLengthCdma(messageBody, use7bitOnly); }
diff --git a/src/java/com/android/internal/telephony/cdma/CdmaSmsBroadcastConfigInfo.java b/src/java/com/android/internal/telephony/cdma/CdmaSmsBroadcastConfigInfo.java old mode 100755 new mode 100644 index b31df59..24ee56d --- a/src/java/com/android/internal/telephony/cdma/CdmaSmsBroadcastConfigInfo.java +++ b/src/java/com/android/internal/telephony/cdma/CdmaSmsBroadcastConfigInfo.java
@@ -17,6 +17,8 @@ package com.android.internal.telephony.cdma; +import java.util.Objects; + /** * CdmaSmsBroadcastConfigInfo defines one configuration of Cdma Broadcast * Message to be received by the ME @@ -84,4 +86,22 @@ mFromServiceCategory + ", " + mToServiceCategory + "] " + (isSelected() ? "ENABLED" : "DISABLED"); } + + @Override + public int hashCode() { + return Objects.hash(mFromServiceCategory, mToServiceCategory, mLanguage, mSelected); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof CdmaSmsBroadcastConfigInfo)) { + return false; + } + + CdmaSmsBroadcastConfigInfo other = (CdmaSmsBroadcastConfigInfo) obj; + + return mFromServiceCategory == other.mFromServiceCategory + && mToServiceCategory == other.mToServiceCategory + && mLanguage == other.mLanguage && mSelected == other.mSelected; + } }
diff --git a/src/java/com/android/internal/telephony/cdnr/CarrierDisplayNameResolver.java b/src/java/com/android/internal/telephony/cdnr/CarrierDisplayNameResolver.java index 6299dac..0ae1b5c 100644 --- a/src/java/com/android/internal/telephony/cdnr/CarrierDisplayNameResolver.java +++ b/src/java/com/android/internal/telephony/cdnr/CarrierDisplayNameResolver.java
@@ -482,7 +482,7 @@ } } else if (mPhone.getServiceStateTracker().getCombinedRegState(getServiceState()) == ServiceState.STATE_IN_SERVICE) { - if (mPhone.isWifiCallingEnabled()) { + if (mPhone.isWifiCallingEnabled() && mPhone.isImsRegistered()) { data = getCarrierDisplayNameFromWifiCallingOverride(data); if (DBG) { Rlog.d(TAG, "CarrierName override by wifi-calling " + data);
diff --git a/src/java/com/android/internal/telephony/data/AccessNetworksManager.java b/src/java/com/android/internal/telephony/data/AccessNetworksManager.java index 2a12ecc..267f70b 100644 --- a/src/java/com/android/internal/telephony/data/AccessNetworksManager.java +++ b/src/java/com/android/internal/telephony/data/AccessNetworksManager.java
@@ -18,23 +18,20 @@ import android.annotation.CallbackExecutor; import android.annotation.NonNull; -import android.annotation.StringDef; -import android.content.BroadcastReceiver; +import android.annotation.Nullable; import android.content.ComponentName; import android.content.Context; import android.content.Intent; -import android.content.IntentFilter; import android.content.ServiceConnection; -import android.content.pm.PackageManager; +import android.os.AsyncResult; import android.os.Handler; import android.os.IBinder; import android.os.Looper; +import android.os.Message; import android.os.PersistableBundle; import android.os.Registrant; import android.os.RegistrantList; import android.os.RemoteException; -import android.os.SystemProperties; -import android.os.UserHandle; import android.telephony.AccessNetworkConstants; import android.telephony.AccessNetworkConstants.AccessNetworkType; import android.telephony.AccessNetworkConstants.RadioAccessNetworkType; @@ -55,17 +52,13 @@ import android.util.SparseArray; import com.android.internal.telephony.Phone; -import com.android.internal.telephony.RIL; -import com.android.internal.telephony.dataconnection.DataThrottler; +import com.android.internal.telephony.SlidingWindowEventCounter; import com.android.telephony.Rlog; import java.io.FileDescriptor; import java.io.PrintWriter; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Arrays; -import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -81,35 +74,14 @@ */ public class AccessNetworksManager extends Handler { private static final boolean DBG = false; - public static final String SYSTEM_PROPERTIES_IWLAN_OPERATION_MODE = - "ro.telephony.iwlan_operation_mode"; - @Retention(RetentionPolicy.SOURCE) - @StringDef(prefix = {"IWLAN_OPERATION_MODE_"}, - value = { - IWLAN_OPERATION_MODE_DEFAULT, - IWLAN_OPERATION_MODE_LEGACY, - IWLAN_OPERATION_MODE_AP_ASSISTED}) - public @interface IwlanOperationMode {} + /** Event to guide a transport type for initial data connection of emergency data network. */ + private static final int EVENT_GUIDE_TRANSPORT_TYPE_FOR_EMERGENCY = 1; /** - * IWLAN default mode. On device that has IRadio 1.4 or above, it means - * {@link #IWLAN_OPERATION_MODE_AP_ASSISTED}. On device that has IRadio 1.3 or below, it means - * {@link #IWLAN_OPERATION_MODE_LEGACY}. + * The counters to detect frequent QNS attempt to change preferred network transport by ApnType. */ - public static final String IWLAN_OPERATION_MODE_DEFAULT = "default"; - - /** - * IWLAN legacy mode. IWLAN is completely handled by the modem, and when the device is on - * IWLAN, modem reports IWLAN as a RAT. - */ - public static final String IWLAN_OPERATION_MODE_LEGACY = "legacy"; - - /** - * IWLAN application processor assisted mode. IWLAN is handled by the bound IWLAN data service - * and network service separately. - */ - public static final String IWLAN_OPERATION_MODE_AP_ASSISTED = "AP-assisted"; + private final @NonNull SparseArray<SlidingWindowEventCounter> mApnTypeToQnsChangeNetworkCounter; private final String mLogTag; private final LocalLog mLocalLog = new LocalLog(64); @@ -124,13 +96,16 @@ ApnSetting.TYPE_CBS, ApnSetting.TYPE_SUPL, ApnSetting.TYPE_EMERGENCY, - ApnSetting.TYPE_XCAP + ApnSetting.TYPE_XCAP, + ApnSetting.TYPE_DUN }; private final Phone mPhone; private final CarrierConfigManager mCarrierConfigManager; + private @Nullable DataConfigManager mDataConfigManager; + private IQualifiedNetworksService mIQualifiedNetworksService; private AccessNetworksManagerDeathRecipient mDeathRecipient; @@ -146,38 +121,10 @@ private final RegistrantList mQualifiedNetworksChangedRegistrants = new RegistrantList(); - private final Set<DataThrottler> mDataThrottlers = new HashSet<>(); - - private final BroadcastReceiver mConfigChangedReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - final String action = intent.getAction(); - if (CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED.equals(action) - && mPhone.getPhoneId() == intent.getIntExtra( - CarrierConfigManager.EXTRA_SLOT_INDEX, 0)) { - // We should wait for carrier config changed event because the target binding - // package name can come from the carrier config. Note that we still get this event - // even when SIM is absent. - if (DBG) log("Carrier config changed. Try to bind qualified network service."); - bindQualifiedNetworksService(); - } - } - }; - - /** - * The current transport of the APN type. The key is the APN type, and the value is the - * transport. - */ - private final Map<Integer, Integer> mCurrentTransports = new ConcurrentHashMap<>(); - /** * The preferred transport of the APN type. The key is the APN type, and the value is the - * transport. The preferred transports are updated as soon as QNS changes the preference, while - * the current transports are updated after handover complete. + * transport. The preferred transports are updated as soon as QNS changes the preference. */ - // TODO: Deprecate mPreferredTransports. Should expose mAvailableNetworks to - // DataNetworkController after we support multi preferred access networks (i.e. - // DataNetworkController might select 2nd preferred access network in some scenarios.) private final Map<Integer, Integer> mPreferredTransports = new ConcurrentHashMap<>(); /** @@ -187,21 +134,6 @@ new ArraySet<>(); /** - * Registers the data throttler in order to receive APN status changes. - * - * @param dataThrottler the data throttler to register - */ - public void registerDataThrottler(DataThrottler dataThrottler) { - this.post(() -> { - QualifiedNetworksServiceConnection serviceConnection = mServiceConnection; - this.mDataThrottlers.add(dataThrottler); - if (serviceConnection != null) { - serviceConnection.registerDataThrottler(dataThrottler); - } - }); - } - - /** * Represents qualified network types list on a specific APN type. */ public static class QualifiedNetworks { @@ -229,29 +161,32 @@ } } + @Override + public void handleMessage(@NonNull Message msg) { + switch (msg.what) { + case EVENT_GUIDE_TRANSPORT_TYPE_FOR_EMERGENCY: + AsyncResult ar = (AsyncResult) msg.obj; + int transport = (int) ar.result; + onEmergencyDataNetworkPreferredTransportChanged(transport); + break; + default: + loge("Unexpected event " + msg.what); + } + } + private class AccessNetworksManagerDeathRecipient implements IBinder.DeathRecipient { @Override public void binderDied() { // TODO: try to rebind the service. String message = "Qualified network service " + mLastBoundPackageName + " died."; + // clear the anomaly report counters when QNS crash + mApnTypeToQnsChangeNetworkCounter.clear(); loge(message); AnomalyReporter.reportAnomaly(mAnomalyUUID, message, mPhone.getCarrierId()); } } private final class QualifiedNetworksServiceConnection implements ServiceConnection { - - /** - * The APN throttle status callback is attached to the service connection so that they have - * the same life cycle. - */ - @NonNull - private final ThrottleStatusChangedCallback mThrottleStatusCallback; - - QualifiedNetworksServiceConnection() { - mThrottleStatusCallback = new ThrottleStatusChangedCallback(); - } - @Override public void onServiceConnected(ComponentName name, IBinder service) { if (DBG) log("onServiceConnected " + name); @@ -263,9 +198,6 @@ service.linkToDeath(mDeathRecipient, 0 /* flags */); mIQualifiedNetworksService.createNetworkAvailabilityProvider(mPhone.getPhoneId(), new QualifiedNetworksServiceCallback()); - - registerDataThrottlersFirstTime(); - } catch (RemoteException e) { loge("Remote exception. " + e); } @@ -274,55 +206,9 @@ @Override public void onServiceDisconnected(ComponentName name) { if (DBG) log("onServiceDisconnected " + name); - unregisterForThrottleCallbacks(); mTargetBindingPackageName = null; } - /** - * Runs on all of the data throttlers when the service is connected - */ - private void registerDataThrottlersFirstTime() { - post(() -> { - for (DataThrottler dataThrottler : mDataThrottlers) { - dataThrottler.registerForThrottleStatusChanges(mThrottleStatusCallback); - } - }); - } - - private void registerDataThrottler(DataThrottler dataThrottler) { - post(() -> { - dataThrottler.registerForThrottleStatusChanges(mThrottleStatusCallback); - }); - } - - private void unregisterForThrottleCallbacks() { - post(() -> { - for (DataThrottler dataThrottler : mDataThrottlers) { - dataThrottler.unregisterForThrottleStatusChanges(mThrottleStatusCallback); - } - }); - } - } - - private class ThrottleStatusChangedCallback implements DataThrottler.Callback { - @Override - public void onThrottleStatusChanged(List<ThrottleStatus> throttleStatuses) { - post(() -> { - try { - List<ThrottleStatus> throttleStatusesBySlot = - throttleStatuses - .stream() - .filter(x -> x.getSlotIndex() == mPhone.getPhoneId()) - .collect(Collectors.toList()); - if (mIQualifiedNetworksService != null) { - mIQualifiedNetworksService.reportThrottleStatusChanged(mPhone.getPhoneId(), - throttleStatusesBySlot); - } - } catch (Exception ex) { - loge("onThrottleStatusChanged", ex); - } - }); - } } private final class QualifiedNetworksServiceCallback extends @@ -344,12 +230,23 @@ if (Arrays.stream(qualifiedNetworkTypes).anyMatch(accessNetwork -> !DataUtils.isValidAccessNetwork(accessNetwork))) { loge("Invalid access networks " + Arrays.toString(qualifiedNetworkTypes)); + if (mDataConfigManager != null + && mDataConfigManager.isInvalidQnsParamAnomalyReportEnabled()) { + reportAnomaly("QNS requested invalid Network Type", + "3e89a3df-3524-45fa-b5f2-0fb0e4c77ec4"); + } return; } List<QualifiedNetworks> qualifiedNetworksList = new ArrayList<>(); + int satisfiedApnTypes = 0; for (int apnType : SUPPORTED_APN_TYPES) { if ((apnTypes & apnType) == apnType) { + // skip the APN anomaly detection if not using the T data stack + if (mDataConfigManager != null) { + satisfiedApnTypes |= apnType; + } + if (mAvailableNetworks.get(apnType) != null) { if (Arrays.equals(mAvailableNetworks.get(apnType), qualifiedNetworkTypes)) { @@ -382,6 +279,16 @@ } } + // Report anomaly if any requested APN types are unsatisfied + if (satisfiedApnTypes != apnTypes + && mDataConfigManager != null + && mDataConfigManager.isInvalidQnsParamAnomalyReportEnabled()) { + int unsatisfied = satisfiedApnTypes ^ apnTypes; + reportAnomaly("QNS requested unsupported APN Types:" + + Integer.toBinaryString(unsatisfied), + "3e89a3df-3524-45fa-b5f2-0fb0e4c77ec5"); + } + if (!qualifiedNetworksList.isEmpty()) { setPreferredTransports(qualifiedNetworksList); mQualifiedNetworksChangedRegistrants.notifyResult(qualifiedNetworksList); @@ -389,6 +296,20 @@ } } + private void onEmergencyDataNetworkPreferredTransportChanged( + @AccessNetworkConstants.TransportType int transportType) { + try { + logl("onEmergencyDataNetworkPreferredTransportChanged: " + + AccessNetworkConstants.transportTypeToString(transportType)); + if (mIQualifiedNetworksService != null) { + mIQualifiedNetworksService.reportEmergencyDataNetworkPreferredTransportChanged( + mPhone.getPhoneId(), transportType); + } + } catch (Exception ex) { + loge("onEmergencyDataNetworkPreferredTransportChanged: ", ex); + } + } + /** * Access networks manager callback. This should be only used by {@link DataNetworkController}. */ @@ -422,33 +343,27 @@ mCarrierConfigManager = (CarrierConfigManager) phone.getContext().getSystemService( Context.CARRIER_CONFIG_SERVICE); mLogTag = "ANM-" + mPhone.getPhoneId(); + mApnTypeToQnsChangeNetworkCounter = new SparseArray<>(); + mAvailableTransports = new int[]{AccessNetworkConstants.TRANSPORT_TYPE_WWAN, + AccessNetworkConstants.TRANSPORT_TYPE_WLAN}; - if (isInLegacyMode()) { - log("operates in legacy mode."); - // For legacy mode, WWAN is the only transport to handle all data connections, even - // the IWLAN ones. - mAvailableTransports = new int[]{AccessNetworkConstants.TRANSPORT_TYPE_WWAN}; - } else { - log("operates in AP-assisted mode."); - mAvailableTransports = new int[]{AccessNetworkConstants.TRANSPORT_TYPE_WWAN, - AccessNetworkConstants.TRANSPORT_TYPE_WLAN}; - IntentFilter intentFilter = new IntentFilter(); - intentFilter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED); - try { - Context contextAsUser = phone.getContext().createPackageContextAsUser( - phone.getContext().getPackageName(), 0, UserHandle.ALL); - contextAsUser.registerReceiver(mConfigChangedReceiver, intentFilter, - null /* broadcastPermission */, null); - } catch (PackageManager.NameNotFoundException e) { - loge("Package name not found: ", e); - } - bindQualifiedNetworksService(); - } + // bindQualifiedNetworksService posts real work to handler thread. So here we can + // let the callback execute in binder thread to avoid post twice. + mCarrierConfigManager.registerCarrierConfigChangeListener(Runnable::run, + (slotIndex, subId, carrierId, specificCarrierId) -> { + if (slotIndex != mPhone.getPhoneId()) return; + // We should wait for carrier config changed event because the target binding + // package name can come from the carrier config. Note that we still get this + // event even when SIM is absent. + if (DBG) log("Carrier config changed. Try to bind qualified network service."); + bindQualifiedNetworksService(); + }); + bindQualifiedNetworksService(); - if (phone.isUsingNewDataStack()) { - // Using post to delay the registering because data retry manager instance is created - // later than access networks manager. - post(() -> mPhone.getDataNetworkController().getDataRetryManager().registerCallback( + // Using post to delay the registering because data retry manager and data config + // manager instances are created later than access networks manager. + post(() -> { + mPhone.getDataNetworkController().getDataRetryManager().registerCallback( new DataRetryManager.DataRetryManagerCallback(this::post) { @Override public void onThrottleStatusChanged(List<ThrottleStatus> throttleStatuses) { @@ -462,8 +377,29 @@ loge("onThrottleStatusChanged: ", ex); } } - })); - } + }); + mDataConfigManager = mPhone.getDataNetworkController().getDataConfigManager(); + mDataConfigManager.registerCallback( + new DataConfigManager.DataConfigManagerCallback(this::post) { + @Override + public void onDeviceConfigChanged() { + mApnTypeToQnsChangeNetworkCounter.clear(); + } + }); + mPhone.registerForEmergencyDomainSelected( + this, EVENT_GUIDE_TRANSPORT_TYPE_FOR_EMERGENCY, null); + }); + } + + /** + * Trigger the anomaly report with the specified UUID. + * + * @param anomalyMsg Description of the event + * @param uuid UUID associated with that event + */ + private void reportAnomaly(@NonNull String anomalyMsg, @NonNull String uuid) { + logl(anomalyMsg); + AnomalyReporter.reportAnomaly(UUID.fromString(uuid), anomalyMsg, mPhone.getCarrierId()); } /** @@ -527,24 +463,29 @@ /** * Get the qualified network service package. * - * @return package name of the qualified networks service package. Return empty string when in - * legacy mode (i.e. Dedicated IWLAN data/network service is not supported). + * @return package name of the qualified networks service package. */ private String getQualifiedNetworksServicePackageName() { // Read package name from the resource String packageName = mPhone.getContext().getResources().getString( com.android.internal.R.string.config_qualified_networks_service_package); - PersistableBundle b = mCarrierConfigManager.getConfigForSubId(mPhone.getSubId()); - - if (b != null) { - // If carrier config overrides it, use the one from carrier config - String carrierConfigPackageName = b.getString(CarrierConfigManager - .KEY_CARRIER_QUALIFIED_NETWORKS_SERVICE_PACKAGE_OVERRIDE_STRING); - if (!TextUtils.isEmpty(carrierConfigPackageName)) { - if (DBG) log("Found carrier config override " + carrierConfigPackageName); - packageName = carrierConfigPackageName; + PersistableBundle b; + try { + b = mCarrierConfigManager.getConfigForSubId(mPhone.getSubId(), + CarrierConfigManager + .KEY_CARRIER_QUALIFIED_NETWORKS_SERVICE_PACKAGE_OVERRIDE_STRING); + if (b != null && !b.isEmpty()) { + // If carrier config overrides it, use the one from carrier config + String carrierConfigPackageName = b.getString(CarrierConfigManager + .KEY_CARRIER_QUALIFIED_NETWORKS_SERVICE_PACKAGE_OVERRIDE_STRING); + if (!TextUtils.isEmpty(carrierConfigPackageName)) { + if (DBG) log("Found carrier config override " + carrierConfigPackageName); + packageName = carrierConfigPackageName; + } } + } catch (RuntimeException e) { + loge("Carrier config loader is not available."); } return packageName; @@ -560,16 +501,22 @@ String className = mPhone.getContext().getResources().getString( com.android.internal.R.string.config_qualified_networks_service_class); - PersistableBundle b = mCarrierConfigManager.getConfigForSubId(mPhone.getSubId()); - - if (b != null) { - // If carrier config overrides it, use the one from carrier config - String carrierConfigClassName = b.getString(CarrierConfigManager - .KEY_CARRIER_QUALIFIED_NETWORKS_SERVICE_CLASS_OVERRIDE_STRING); - if (!TextUtils.isEmpty(carrierConfigClassName)) { - if (DBG) log("Found carrier config override " + carrierConfigClassName); - className = carrierConfigClassName; + PersistableBundle b; + try { + b = mCarrierConfigManager.getConfigForSubId(mPhone.getSubId(), + CarrierConfigManager + .KEY_CARRIER_QUALIFIED_NETWORKS_SERVICE_CLASS_OVERRIDE_STRING); + if (b != null && !b.isEmpty()) { + // If carrier config overrides it, use the one from carrier config + String carrierConfigClassName = b.getString(CarrierConfigManager + .KEY_CARRIER_QUALIFIED_NETWORKS_SERVICE_CLASS_OVERRIDE_STRING); + if (!TextUtils.isEmpty(carrierConfigClassName)) { + if (DBG) log("Found carrier config override " + carrierConfigClassName); + className = carrierConfigClassName; + } } + } catch (RuntimeException e) { + loge("Carrier config loader is not available."); } return className; @@ -605,87 +552,12 @@ } /** - * @return {@code true} if the device operates in legacy mode, otherwise {@code false}. + * @return The available transports. */ - public boolean isInLegacyMode() { - // Get IWLAN operation mode from the system property. If the system property is configured - // to default or not configured, the mode is tied to IRadio version. For 1.4 or above, it's - // AP-assisted mode, for 1.3 or below, it's legacy mode. - String mode = SystemProperties.get(SYSTEM_PROPERTIES_IWLAN_OPERATION_MODE); - - if (mode.equals(IWLAN_OPERATION_MODE_AP_ASSISTED)) { - return false; - } else if (mode.equals(IWLAN_OPERATION_MODE_LEGACY)) { - return true; - } - - return mPhone.getHalVersion().less(RIL.RADIO_HAL_VERSION_1_4); - } - - /** - * @return The available transports. Note that on legacy devices, the only available transport - * would be WWAN only. If the device is configured as AP-assisted mode, the available transport - * will always be WWAN and WLAN (even if the device is not camped on IWLAN). - * See {@link #isInLegacyMode()} for mode details. - */ - public synchronized @NonNull int[] getAvailableTransports() { + public @NonNull int[] getAvailableTransports() { return mAvailableTransports; } - /** - * Get the transport based on the network capability. - * - * @param netCap The network capability. - * @return The transport type. - */ - public @TransportType int getCurrentTransportByNetworkCapability(@NetCapability int netCap) { - return getCurrentTransport(DataUtils.networkCapabilityToApnType(netCap)); - } - - /** - * Get the transport based on the APN type. - * - * @param apnType APN type - * @return The transport type - */ - // TODO: Remove this after TransportManager is removed. - public @TransportType int getCurrentTransport(@ApnType int apnType) { - // In legacy mode, always route to cellular. - if (isInLegacyMode()) { - return AccessNetworkConstants.TRANSPORT_TYPE_WWAN; - } - - // If we can't find the corresponding transport, always route to cellular. - return mCurrentTransports.get(apnType) == null - ? AccessNetworkConstants.TRANSPORT_TYPE_WWAN : mCurrentTransports.get(apnType); - } - - /** - * Set the current transport of a network capability. - * - * @param netCap The network capability. - * @param transport The transport. - */ - public void setCurrentTransportByNetworkCapability(@NetCapability int netCap, - @TransportType int transport) { - setCurrentTransport(DataUtils.networkCapabilityToApnType(netCap), transport); - } - - /** - * Set the current transport of apn type. - * - * @param apnType The APN type - * @param transport The transport. - */ - // TODO: Remove this after TransportManager is removed. - public void setCurrentTransport(@ApnType int apnType, @TransportType int transport) { - Integer previousTransport = mCurrentTransports.put(apnType, transport); - if (previousTransport == null || previousTransport != transport) { - logl("setCurrentTransport: apnType=" + ApnSetting.getApnTypeString(apnType) - + ", transport=" + AccessNetworkConstants.transportTypeToString(transport)); - } - } - private static @TransportType int getTransportFromAccessNetwork(int accessNetwork) { return accessNetwork == AccessNetworkType.IWLAN ? AccessNetworkConstants.TRANSPORT_TYPE_WLAN @@ -717,11 +589,6 @@ * @return The preferred transport. */ public @TransportType int getPreferredTransport(@ApnType int apnType) { - // In legacy mode, always preferred on cellular. - if (isInLegacyMode()) { - return AccessNetworkConstants.TRANSPORT_TYPE_WWAN; - } - return mPreferredTransports.get(apnType) == null ? AccessNetworkConstants.TRANSPORT_TYPE_WWAN : mPreferredTransports.get(apnType); } @@ -744,20 +611,14 @@ } /** - * Check if there is any APN type's current transport is on IWLAN. + * Check if there is any APN type preferred on IWLAN. * * @return {@code true} if there is any APN is on IWLAN, otherwise {@code false}. */ public boolean isAnyApnOnIwlan() { for (int apnType : AccessNetworksManager.SUPPORTED_APN_TYPES) { - if (mPhone.isUsingNewDataStack()) { - if (getPreferredTransport(apnType) == AccessNetworkConstants.TRANSPORT_TYPE_WLAN) { - return true; - } - } else { - if (getCurrentTransport(apnType) == AccessNetworkConstants.TRANSPORT_TYPE_WLAN) { - return true; - } + if (getPreferredTransport(apnType) == AccessNetworkConstants.TRANSPORT_TYPE_WLAN) { + return true; } } return false; @@ -821,14 +682,6 @@ IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, " "); pw.println(AccessNetworksManager.class.getSimpleName() + "-" + mPhone.getPhoneId() + ":"); pw.increaseIndent(); - pw.println("current transports="); - pw.increaseIndent(); - for (int apnType : AccessNetworksManager.SUPPORTED_APN_TYPES) { - pw.println(ApnSetting.getApnTypeString(apnType) - + ": " + AccessNetworkConstants.transportTypeToString( - getCurrentTransport(apnType))); - } - pw.decreaseIndent(); pw.println("preferred transports="); pw.increaseIndent(); for (int apnType : AccessNetworksManager.SUPPORTED_APN_TYPES) { @@ -838,9 +691,6 @@ } pw.decreaseIndent(); - pw.println("isInLegacy=" + isInLegacyMode()); - pw.println("IWLAN operation mode=" - + SystemProperties.get(SYSTEM_PROPERTIES_IWLAN_OPERATION_MODE)); pw.println("Local logs="); pw.increaseIndent(); mLocalLog.dump(fd, pw, args);
diff --git a/src/java/com/android/internal/telephony/data/CellularNetworkValidator.java b/src/java/com/android/internal/telephony/data/CellularNetworkValidator.java index c63676f..c1d1203 100644 --- a/src/java/com/android/internal/telephony/data/CellularNetworkValidator.java +++ b/src/java/com/android/internal/telephony/data/CellularNetworkValidator.java
@@ -17,7 +17,7 @@ package com.android.internal.telephony.data; import static android.telephony.AccessNetworkConstants.TRANSPORT_TYPE_WWAN; -import static android.telephony.CarrierConfigManager.KEY_DATA_SWITCH_VALIDATION_MIN_GAP_LONG; +import static android.telephony.CarrierConfigManager.KEY_DATA_SWITCH_VALIDATION_MIN_INTERVAL_MILLIS_LONG; import static android.telephony.NetworkRegistrationInfo.DOMAIN_PS; import android.content.Context; @@ -40,9 +40,10 @@ import com.android.internal.telephony.Phone; import com.android.internal.telephony.PhoneConfigurationManager; import com.android.internal.telephony.PhoneFactory; -import com.android.internal.telephony.SubscriptionController; import com.android.internal.telephony.metrics.TelephonyMetrics; import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent; +import com.android.internal.telephony.subscription.SubscriptionInfoInternal; +import com.android.internal.telephony.subscription.SubscriptionManagerService; import java.util.Comparator; import java.util.HashMap; @@ -164,9 +165,9 @@ private String getValidationNetworkIdentity(int subId) { if (!SubscriptionManager.isUsableSubscriptionId(subId)) return null; - SubscriptionController subController = SubscriptionController.getInstance(); - if (subController == null) return null; - Phone phone = PhoneFactory.getPhone(subController.getPhoneId(subId)); + if (SubscriptionManagerService.getInstance() == null) return null; + Phone phone = PhoneFactory.getPhone(SubscriptionManagerService.getInstance() + .getPhoneId(subId)); if (phone == null || phone.getServiceState() == null) return null; NetworkRegistrationInfo regInfo = phone.getServiceState().getNetworkRegistrationInfo( @@ -192,7 +193,7 @@ if (configManager != null) { PersistableBundle b = configManager.getConfigForSubId(subId); if (b != null) { - ttl = b.getLong(KEY_DATA_SWITCH_VALIDATION_MIN_GAP_LONG); + ttl = b.getLong(KEY_DATA_SWITCH_VALIDATION_MIN_INTERVAL_MILLIS_LONG); } } // Ttl can't be bigger than one day for now. @@ -257,7 +258,9 @@ // If it's already validating the same subscription, do nothing. if (subId == mSubId) return; - if (!SubscriptionController.getInstance().isActiveSubId(subId)) { + SubscriptionInfoInternal subInfo = SubscriptionManagerService.getInstance() + .getSubscriptionInfoInternal(subId); + if (subInfo == null || !subInfo.isActive()) { logd("Failed to start validation. Inactive subId " + subId); callback.onValidationDone(false, subId); return;
diff --git a/src/java/com/android/internal/telephony/data/DataConfigManager.java b/src/java/com/android/internal/telephony/data/DataConfigManager.java index 82a4ccd..78450a8 100644 --- a/src/java/com/android/internal/telephony/data/DataConfigManager.java +++ b/src/java/com/android/internal/telephony/data/DataConfigManager.java
@@ -16,12 +16,9 @@ package com.android.internal.telephony.data; +import android.annotation.CallbackExecutor; import android.annotation.NonNull; import android.annotation.StringDef; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; import android.content.res.Resources; import android.net.LinkProperties; import android.net.NetworkCapabilities; @@ -29,7 +26,6 @@ import android.os.Looper; import android.os.Message; import android.os.PersistableBundle; -import android.os.RegistrantList; import android.provider.DeviceConfig; import android.telephony.Annotation.ApnType; import android.telephony.Annotation.NetCapability; @@ -41,6 +37,7 @@ import android.telephony.TelephonyManager; import android.telephony.data.ApnSetting; import android.text.TextUtils; +import android.util.ArraySet; import android.util.IndentingPrintWriter; import com.android.internal.annotations.VisibleForTesting; @@ -59,9 +56,11 @@ import java.util.Collections; import java.util.HashSet; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.Executor; import java.util.stream.Collectors; /** @@ -132,8 +131,7 @@ private static final String DATA_CONFIG_NETWORK_TYPE_IDEN = "iDEN"; /** Network type LTE. Should not be used outside of DataConfigManager. */ - // TODO: Public only for use by DcTracker. This should be private once DcTracker is removed. - public static final String DATA_CONFIG_NETWORK_TYPE_LTE = "LTE"; + private static final String DATA_CONFIG_NETWORK_TYPE_LTE = "LTE"; /** Network type HSPA+. Should not be used outside of DataConfigManager. */ private static final String DATA_CONFIG_NETWORK_TYPE_HSPAP = "HSPA+"; @@ -151,12 +149,10 @@ private static final String DATA_CONFIG_NETWORK_TYPE_LTE_CA = "LTE_CA"; /** Network type NR_NSA. Should not be used outside of DataConfigManager. */ - // TODO: Public only for use by DcTracker. This should be private once DcTracker is removed. - public static final String DATA_CONFIG_NETWORK_TYPE_NR_NSA = "NR_NSA"; + private static final String DATA_CONFIG_NETWORK_TYPE_NR_NSA = "NR_NSA"; /** Network type NR_NSA_MMWAVE. Should not be used outside of DataConfigManager. */ - // TODO: Public only for use by DcTracker. This should be private once DcTracker is removed. - public static final String DATA_CONFIG_NETWORK_TYPE_NR_NSA_MMWAVE = "NR_NSA_MMWAVE"; + private static final String DATA_CONFIG_NETWORK_TYPE_NR_NSA_MMWAVE = "NR_NSA_MMWAVE"; /** Network type NR_SA. Should not be used outside of DataConfigManager. */ private static final String DATA_CONFIG_NETWORK_TYPE_NR_SA = "NR_SA"; @@ -192,6 +188,10 @@ @Retention(RetentionPolicy.SOURCE) private @interface DataConfigNetworkType {} + /** Data config update callbacks. */ + private final @NonNull Set<DataConfigManagerCallback> mDataConfigManagerCallbacks = + new ArraySet<>(); + /** DeviceConfig key of anomaly report threshold for back to back ims release-request. */ private static final String KEY_ANOMALY_IMS_RELEASE_REQUEST = "anomaly_ims_release_request"; /** DeviceConfig key of anomaly report threshold for frequent setup data failure. */ @@ -199,6 +199,10 @@ "anomaly_setup_data_call_failure"; /** DeviceConfig key of anomaly report threshold for frequent network-unwanted call. */ private static final String KEY_ANOMALY_NETWORK_UNWANTED = "anomaly_network_unwanted"; + /** DeviceConfig key of anomaly report threshold for frequent change of preferred network. */ + private static final String KEY_ANOMALY_QNS_CHANGE_NETWORK = "anomaly_qns_change_network"; + /** DeviceConfig key of anomaly report threshold for invalid QNS params. */ + private static final String KEY_ANOMALY_QNS_PARAM = "anomaly_qns_param"; /** DeviceConfig key of anomaly report threshold for DataNetwork stuck in connecting state. */ private static final String KEY_ANOMALY_NETWORK_CONNECTING_TIMEOUT = "anomaly_network_connecting_timeout"; @@ -208,6 +212,14 @@ /** DeviceConfig key of anomaly report threshold for DataNetwork stuck in handover state. */ private static final String KEY_ANOMALY_NETWORK_HANDOVER_TIMEOUT = "anomaly_network_handover_timeout"; + /** DeviceConfig key of anomaly report: True for enabling APN config invalidity detection */ + private static final String KEY_ANOMALY_APN_CONFIG_ENABLED = "anomaly_apn_config_enabled"; + /** DeviceConfig key of the time threshold in ms for defining a network status to be stable. **/ + private static final String KEY_AUTO_DATA_SWITCH_AVAILABILITY_STABILITY_TIME_THRESHOLD = + "auto_data_switch_availability_stability_time_threshold"; + /** DeviceConfig key of the maximum number of retries when a validation for switching failed.**/ + private static final String KEY_AUTO_DATA_SWITCH_VALIDATION_MAX_RETRY = + "auto_data_switch_validation_max_retry"; /** Anomaly report thresholds for frequent setup data call failure. */ private EventFrequency mSetupDataCallAnomalyReportThreshold; @@ -222,6 +234,12 @@ private EventFrequency mNetworkUnwantedAnomalyReportThreshold; /** + * {@code true} if enabled anomaly detection for param when QNS wants to change preferred + * network at {@link AccessNetworksManager}. + */ + private boolean mIsInvalidQnsParamAnomalyReportEnabled; + + /** * Timeout in ms before creating an anomaly report for a DataNetwork stuck in * {@link DataNetwork.ConnectingState}. */ @@ -239,12 +257,14 @@ */ private int mNetworkHandoverTimeout; + /** + * True if enabled anomaly detection for APN config that's read from {@link DataProfileManager} + */ + private boolean mIsApnConfigAnomalyReportEnabled; + private @NonNull final Phone mPhone; private @NonNull final String mLogTag; - /** The registrants list for config update event. */ - private @NonNull final RegistrantList mConfigUpdateRegistrants = new RegistrantList(); - private @NonNull final CarrierConfigManager mCarrierConfigManager; private @NonNull PersistableBundle mCarrierConfig = null; private @NonNull Resources mResources = null; @@ -263,6 +283,8 @@ /** The network types that only support single data networks */ private @NonNull final @NetworkType List<Integer> mSingleDataNetworkTypeList = new ArrayList<>(); + private @NonNull final @NetCapability Set<Integer> mCapabilitiesExemptFromSingleDataList = + new HashSet<>(); /** The network types that support temporarily not metered */ private @NonNull final @DataConfigNetworkType Set<String> mUnmeteredNetworkTypes = new HashSet<>(); @@ -277,6 +299,8 @@ new ConcurrentHashMap<>(); /** Rules for handover between IWLAN and cellular network. */ private @NonNull final List<HandoverRule> mHandoverRuleList = new ArrayList<>(); + /** {@code True} keep IMS network in case of moving to non VOPS area; {@code false} otherwise.*/ + private boolean mShouldKeepNetworkUpInNonVops = false; /** * Constructor @@ -292,22 +316,13 @@ log("DataConfigManager created."); mCarrierConfigManager = mPhone.getContext().getSystemService(CarrierConfigManager.class); - - // Register for carrier configs update - IntentFilter filter = new IntentFilter(); - filter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED); - mPhone.getContext().registerReceiver(new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - if (intent.getAction().equals(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)) { - if (mPhone.getPhoneId() == intent.getIntExtra( - CarrierConfigManager.EXTRA_SLOT_INDEX, - SubscriptionManager.INVALID_SIM_SLOT_INDEX)) { + // Callback send msg to handler thread, so callback itself can be executed in binder thread. + mCarrierConfigManager.registerCarrierConfigChangeListener(Runnable::run, + (slotIndex, subId, carrierId, specificCarrierId) -> { + if (slotIndex == mPhone.getPhoneId()) { sendEmptyMessage(EVENT_CARRIER_CONFIG_CHANGED); } - } - } - }, filter, null, mPhone); + }); // Register for device config update DeviceConfig.addOnPropertiesChangedListener( @@ -321,8 +336,46 @@ // Must be called to set mCarrierConfig and mResources to non-null values updateCarrierConfig(); + // Must be called to set anomaly report threshold to non-null values updateDeviceConfig(); - mConfigUpdateRegistrants.notifyRegistrants(); + } + + /** + * The data config callback. + */ + public static class DataConfigManagerCallback extends DataCallback { + /** + * Constructor + * + * @param executor The executor of the callback. + */ + public DataConfigManagerCallback(@NonNull @CallbackExecutor Executor executor) { + super(executor); + } + + /** Callback on carrier config update.*/ + public void onCarrierConfigChanged() {} + + /** Callback on device config update.*/ + public void onDeviceConfigChanged() {} + } + + /** + * Register the callback for receiving information from {@link DataConfigManager}. + * + * @param callback The callback. + */ + public void registerCallback(@NonNull DataConfigManagerCallback callback) { + mDataConfigManagerCallbacks.add(callback); + } + + /** + * Unregister the callback. + * + * @param callback The previously registered callback. + */ + public void unregisterCallback(@NonNull DataConfigManagerCallback callback) { + mDataConfigManagerCallbacks.remove(callback); } @Override @@ -331,12 +384,14 @@ case EVENT_CARRIER_CONFIG_CHANGED: log("EVENT_CARRIER_CONFIG_CHANGED"); updateCarrierConfig(); - mConfigUpdateRegistrants.notifyRegistrants(); + mDataConfigManagerCallbacks.forEach(callback -> callback.invokeFromExecutor( + callback::onCarrierConfigChanged)); break; case EVENT_DEVICE_CONFIG_CHANGED: log("EVENT_DEVICE_CONFIG_CHANGED"); updateDeviceConfig(); - mConfigUpdateRegistrants.notifyRegistrants(); + mDataConfigManagerCallbacks.forEach(callback -> callback.invokeFromExecutor( + callback::onDeviceConfigChanged)); break; default: loge("Unexpected message " + msg.what); @@ -349,17 +404,13 @@ DeviceConfig.getProperties(DeviceConfig.NAMESPACE_TELEPHONY); mImsReleaseRequestAnomalyReportThreshold = parseSlidingWindowCounterThreshold( - properties.getString(KEY_ANOMALY_IMS_RELEASE_REQUEST, null), - 0, - 12); + properties.getString(KEY_ANOMALY_IMS_RELEASE_REQUEST, null), 0, 2); mNetworkUnwantedAnomalyReportThreshold = parseSlidingWindowCounterThreshold( - properties.getString(KEY_ANOMALY_NETWORK_UNWANTED, null), - 0, - 12); + properties.getString(KEY_ANOMALY_NETWORK_UNWANTED, null), 0, 12); mSetupDataCallAnomalyReportThreshold = parseSlidingWindowCounterThreshold( - properties.getString(KEY_ANOMALY_SETUP_DATA_CALL_FAILURE, null), - 0, - 2); + properties.getString(KEY_ANOMALY_SETUP_DATA_CALL_FAILURE, null), 0, 12); + mIsInvalidQnsParamAnomalyReportEnabled = properties.getBoolean( + KEY_ANOMALY_QNS_PARAM, false); mNetworkConnectingTimeout = properties.getInt( KEY_ANOMALY_NETWORK_CONNECTING_TIMEOUT, DEFAULT_NETWORK_TRANSIT_STATE_TIMEOUT_MS); mNetworkDisconnectingTimeout = properties.getInt( @@ -367,6 +418,8 @@ DEFAULT_NETWORK_TRANSIT_STATE_TIMEOUT_MS); mNetworkHandoverTimeout = properties.getInt( KEY_ANOMALY_NETWORK_HANDOVER_TIMEOUT, DEFAULT_NETWORK_TRANSIT_STATE_TIMEOUT_MS); + mIsApnConfigAnomalyReportEnabled = properties.getBoolean( + KEY_ANOMALY_APN_CONFIG_ENABLED, false); } /** @@ -393,13 +446,14 @@ updateNetworkCapabilityPriority(); updateDataRetryRules(); updateMeteredApnTypes(); - updateSingleDataNetworkTypeList(); + updateSingleDataNetworkTypeAndCapabilityExemption(); + updateVopsConfig(); updateUnmeteredNetworkTypes(); updateBandwidths(); updateTcpBuffers(); updateHandoverRules(); - log("Data config updated. Config is " + (isConfigCarrierSpecific() ? "" : "not ") + log("Carrier config updated. Config is " + (isConfigCarrierSpecific() ? "" : "not ") + "carrier specific."); } @@ -413,7 +467,8 @@ CarrierConfigManager.KEY_TELEPHONY_NETWORK_CAPABILITY_PRIORITIES_STRING_ARRAY); if (capabilityPriorityStrings != null) { for (String capabilityPriorityString : capabilityPriorityStrings) { - capabilityPriorityString = capabilityPriorityString.trim().toUpperCase(); + capabilityPriorityString = + capabilityPriorityString.trim().toUpperCase(Locale.ROOT); String[] tokens = capabilityPriorityString.split(":"); if (tokens.length != 2) { loge("Invalid config \"" + capabilityPriorityString + "\""); @@ -584,15 +639,32 @@ /** * Update the network types for only single data networks from the carrier config. */ - private void updateSingleDataNetworkTypeList() { + private void updateSingleDataNetworkTypeAndCapabilityExemption() { synchronized (this) { mSingleDataNetworkTypeList.clear(); + mCapabilitiesExemptFromSingleDataList.clear(); int[] singleDataNetworkTypeList = mCarrierConfig.getIntArray( CarrierConfigManager.KEY_ONLY_SINGLE_DC_ALLOWED_INT_ARRAY); if (singleDataNetworkTypeList != null) { - Arrays.stream(singleDataNetworkTypeList) - .forEach(mSingleDataNetworkTypeList::add); + Arrays.stream(singleDataNetworkTypeList).forEach(mSingleDataNetworkTypeList::add); } + + int[] singleDataCapabilitiesExemptList = mCarrierConfig.getIntArray( + CarrierConfigManager.KEY_CAPABILITIES_EXEMPT_FROM_SINGLE_DC_CHECK_INT_ARRAY); + if (singleDataCapabilitiesExemptList != null) { + Arrays.stream(singleDataCapabilitiesExemptList) + .forEach(mCapabilitiesExemptFromSingleDataList::add); + } + } + } + + /** + * Update the voice over PS related config from the carrier config. + */ + private void updateVopsConfig() { + synchronized (this) { + mShouldKeepNetworkUpInNonVops = mCarrierConfig.getBoolean(CarrierConfigManager + .Ims.KEY_KEEP_PDN_UP_IN_NO_VOPS_BOOL); } } @@ -604,6 +676,25 @@ } /** + * @return The list of {@link android.net.NetworkCapabilities.NetCapability} that every of which + * is exempt from the single PDN check. + */ + public @NonNull @NetCapability Set<Integer> getCapabilitiesExemptFromSingleDataNetwork() { + return Collections.unmodifiableSet(mCapabilitiesExemptFromSingleDataList); + } + + /** {@code True} keep IMS network in case of moving to non VOPS area; {@code false} otherwise.*/ + public boolean shouldKeepNetworkUpInNonVops() { + return mShouldKeepNetworkUpInNonVops; + } + + /** {@code True} requires ping test to pass on the target slot before switching to it.*/ + public boolean isPingTestBeforeAutoDataSwitchRequired() { + return mResources.getBoolean(com.android.internal.R.bool + .auto_data_switch_ping_test_before_switch); + } + + /** * @return Whether {@link NetworkCapabilities#NET_CAPABILITY_TEMPORARILY_NOT_METERED} * is supported by the carrier. */ @@ -787,6 +878,14 @@ } /** + * @return {@code true} if enabled anomaly report for invalid param when QNS wants to change + * preferred network at {@link AccessNetworksManager}. + */ + public boolean isInvalidQnsParamAnomalyReportEnabled() { + return mIsInvalidQnsParamAnomalyReportEnabled; + } + + /** * @return Timeout in ms before creating an anomaly report for a DataNetwork stuck in * {@link DataNetwork.ConnectingState}. */ @@ -811,6 +910,32 @@ } /** + * @return {@code true} if enabled anomaly report for invalid APN config + * at {@link DataProfileManager} + */ + public boolean isApnConfigAnomalyReportEnabled() { + return mIsApnConfigAnomalyReportEnabled; + } + + /** + * @return The maximum number of retries when a validation for switching failed. + */ + public int getAutoDataSwitchValidationMaxRetry() { + return mResources.getInteger(com.android.internal.R.integer + .auto_data_switch_validation_max_retry); + } + + /** + * @return Time threshold in ms to define a internet connection status to be stable + * (e.g. out of service, in service, wifi is the default active network.etc), while -1 indicates + * auto switch feature disabled. + */ + public long getAutoDataSwitchAvailabilityStabilityTimeThreshold() { + return mResources.getInteger(com.android.internal.R.integer + .auto_data_switch_availability_stability_time_threshold_millis); + } + + /** * Get the TCP config string, used by {@link LinkProperties#setTcpBufferSizes(String)}. * The config string will have the following form, with values in bytes: * "read_min,read_default,read_max,write_min,write_default,write_max" @@ -865,7 +990,7 @@ * @return {@code true} if tearing down IMS data network should be delayed until the voice call * ends. */ - public boolean isImsDelayTearDownEnabled() { + public boolean isImsDelayTearDownUntilVoiceCallEndEnabled() { return mCarrierConfig.getBoolean( CarrierConfigManager.KEY_DELAY_IMS_TEAR_DOWN_UNTIL_CALL_END_BOOL); } @@ -895,9 +1020,8 @@ * @param displayInfo The {@link TelephonyDisplayInfo} used to determine the type. * @return The equivalent {@link DataConfigNetworkType}. */ - public static @NonNull @DataConfigNetworkType String getDataConfigNetworkType( + private static @NonNull @DataConfigNetworkType String getDataConfigNetworkType( @NonNull TelephonyDisplayInfo displayInfo) { - // TODO: Make method private once DataConnection is removed int networkType = displayInfo.getNetworkType(); switch (displayInfo.getOverrideNetworkType()) { case TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED: @@ -1140,24 +1264,6 @@ } /** - * Registration point for subscription info ready. - * - * @param h handler to notify. - * @param what what code of message when delivered. - */ - public void registerForConfigUpdate(Handler h, int what) { - mConfigUpdateRegistrants.addUnique(h, what, null); - } - - /** - * - * @param h The original handler passed in {@link #registerForConfigUpdate(Handler, int)}. - */ - public void unregisterForConfigUpdate(Handler h) { - mConfigUpdateRegistrants.remove(h); - } - - /** * Log debug messages. * @param s debug messages */ @@ -1203,15 +1309,27 @@ pw.println("mSetupDataCallAnomalyReport=" + mSetupDataCallAnomalyReportThreshold); pw.println("mNetworkUnwantedAnomalyReport=" + mNetworkUnwantedAnomalyReportThreshold); pw.println("mImsReleaseRequestAnomalyReport=" + mImsReleaseRequestAnomalyReportThreshold); + pw.println("mIsInvalidQnsParamAnomalyReportEnabled=" + + mIsInvalidQnsParamAnomalyReportEnabled); pw.println("mNetworkConnectingTimeout=" + mNetworkConnectingTimeout); pw.println("mNetworkDisconnectingTimeout=" + mNetworkDisconnectingTimeout); pw.println("mNetworkHandoverTimeout=" + mNetworkHandoverTimeout); + pw.println("mIsApnConfigAnomalyReportEnabled=" + mIsApnConfigAnomalyReportEnabled); + pw.println("getAutoDataSwitchAvailabilityStabilityTimeThreshold=" + + getAutoDataSwitchAvailabilityStabilityTimeThreshold()); + pw.println("getAutoDataSwitchValidationMaxRetry=" + getAutoDataSwitchValidationMaxRetry()); pw.println("Metered APN types=" + mMeteredApnTypes.stream() .map(ApnSetting::getApnTypeString).collect(Collectors.joining(","))); pw.println("Roaming metered APN types=" + mRoamingMeteredApnTypes.stream() .map(ApnSetting::getApnTypeString).collect(Collectors.joining(","))); pw.println("Single data network types=" + mSingleDataNetworkTypeList.stream() .map(TelephonyManager::getNetworkTypeName).collect(Collectors.joining(","))); + pw.println("Capabilities exempt from single PDN=" + mCapabilitiesExemptFromSingleDataList + .stream().map(DataUtils::networkCapabilityToString) + .collect(Collectors.joining(","))); + pw.println("mShouldKeepNetworkUpInNoVops=" + mShouldKeepNetworkUpInNonVops); + pw.println("isPingTestBeforeAutoDataSwitchRequired=" + + isPingTestBeforeAutoDataSwitchRequired()); pw.println("Unmetered network types=" + String.join(",", mUnmeteredNetworkTypes)); pw.println("Roaming unmetered network types=" + String.join(",", mRoamingUnmeteredNetworkTypes)); @@ -1236,7 +1354,8 @@ + shouldPersistIwlanDataNetworksWhenDataServiceRestarted()); pw.println("Bandwidth estimation source=" + mResources.getString( com.android.internal.R.string.config_bandwidthEstimateSource)); - pw.println("isDelayTearDownImsEnabled=" + isImsDelayTearDownEnabled()); + pw.println("isImsDelayTearDownUntilVoiceCallEndEnabled=" + + isImsDelayTearDownUntilVoiceCallEndEnabled()); pw.println("isEnhancedIwlanHandoverCheckEnabled=" + isEnhancedIwlanHandoverCheckEnabled()); pw.println("isTetheringProfileDisabledForRoaming=" + isTetheringProfileDisabledForRoaming());
diff --git a/src/java/com/android/internal/telephony/data/DataEnabledOverride.java b/src/java/com/android/internal/telephony/data/DataEnabledOverride.java deleted file mode 100644 index e639ba6..0000000 --- a/src/java/com/android/internal/telephony/data/DataEnabledOverride.java +++ /dev/null
@@ -1,460 +0,0 @@ -/* - * 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 com.android.internal.telephony.data; - -import android.annotation.IntDef; -import android.annotation.NonNull; -import android.telephony.Annotation.ApnType; -import android.telephony.SubscriptionManager; -import android.telephony.TelephonyManager; -import android.telephony.data.ApnSetting; -import android.text.TextUtils; -import android.util.ArrayMap; -import android.util.Log; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.telephony.Phone; -import com.android.internal.telephony.PhoneConstants; -import com.android.internal.telephony.PhoneFactory; -import com.android.internal.telephony.SubscriptionController; -import com.android.internal.telephony.data.DataEnabledOverride.OverrideConditions.Condition; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Set; - -/** - * This class represents the rules for overriding data enabled settings in different conditions. - * When data is disabled by the user, data can still be turned on temporarily when conditions - * satisfy any rule here. - */ -public class DataEnabledOverride { - - private final Set<OverrideRule> mRules = new HashSet<>(); - - /** - * The rule for allowing data during voice call. - */ - private static final OverrideRule OVERRIDE_RULE_ALLOW_DATA_DURING_VOICE_CALL = - new OverrideRule(ApnSetting.TYPE_ALL, OverrideConditions.CONDITION_IN_VOICE_CALL - | OverrideConditions.CONDITION_NON_DEFAULT - | OverrideConditions.CONDITION_DEFAULT_DATA_ENABLED - | OverrideConditions.CONDITION_DSDS_ENABLED); - - /** - * The rule for always allowing mms. Without adding any condition to the rule, any condition can - * satisfy this rule for mms. - */ - private static final OverrideRule OVERRIDE_RULE_ALWAYS_ALLOW_MMS = - new OverrideRule(ApnSetting.TYPE_MMS, OverrideConditions.CONDITION_UNCONDITIONALLY); - - /** - * Data enabled override rule - */ - private static class OverrideRule { - /** - * APN type of the rule. The rule is APN type specific. The override is applicable to the - * specified APN type as well. For now we only support one APN type per rule. Can be - * expanded to multiple APN types in the future. - */ - private final @ApnType int mApnType; - - /** The required conditions for overriding */ - private final OverrideConditions mRequiredConditions; - - /** - * Constructor - * - * @param rule The override rule string. For example, {@code mms=nonDefault} or - * {@code default=voiceCall & nonDefault} - */ - OverrideRule(@NonNull String rule) { - String[] tokens = rule.trim().split("\\s*=\\s*"); - if (tokens.length != 2) { - throw new IllegalArgumentException("Invalid data enabled override rule format: " - + rule); - } - - if (TextUtils.isEmpty(tokens[0])) { - throw new IllegalArgumentException("APN type can't be empty"); - } - - mApnType = ApnSetting.getApnTypesBitmaskFromString(tokens[0]); - if (mApnType == ApnSetting.TYPE_NONE) { - throw new IllegalArgumentException("Invalid APN type. Rule=" + rule); - } - - mRequiredConditions = new OverrideConditions(tokens[1]); - } - - /** - * Constructor - * - * @param apnType APN type of the rule - * @param requiredConditions The required conditions for the rule - */ - private OverrideRule(int apnType, int requiredConditions) { - mApnType = apnType; - mRequiredConditions = new OverrideConditions(requiredConditions); - } - - /** - * Check if this rule can be satisfied by the given APN type and provided conditions. - * - * @param apnType APN type to check - * @param providedConditions The provided conditions to check - * @return {@code true} if satisfied - */ - boolean isSatisfiedByConditions(@ApnType int apnType, @Condition int providedConditions) { - return (mApnType == apnType || mApnType == ApnSetting.TYPE_ALL) - && mRequiredConditions.allMet(providedConditions); - } - - @Override - public String toString() { - return ApnSetting.getApnTypeString(mApnType) + "=" + mRequiredConditions; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - OverrideRule that = (OverrideRule) o; - return mApnType == that.mApnType - && Objects.equals(mRequiredConditions, that.mRequiredConditions); - } - - @Override - public int hashCode() { - return Objects.hash(mApnType, mRequiredConditions); - } - } - - /** - * Represent the conditions for overriding data enabled settings - */ - static class OverrideConditions { - // Possible values for data enabled override condition. Note these flags are bitmasks. - /** Unconditionally override enabled settings */ - static final int CONDITION_UNCONDITIONALLY = 0; - - /** Enable data only on subscription that is not user selected default data subscription */ - static final int CONDITION_NON_DEFAULT = 1 << 0; - - /** Enable data only when device has ongoing voice call */ - static final int CONDITION_IN_VOICE_CALL = 1 << 1; - - /** Enable data only when default data is on */ - static final int CONDITION_DEFAULT_DATA_ENABLED = 1 << 2; - - /** Enable data only when device is in DSDS mode */ - static final int CONDITION_DSDS_ENABLED = 1 << 3; - - /** Enable data unconditionally in string format */ - static final String CONDITION_UNCONDITIONALLY_STRING = "unconditionally"; - - /** Enable data only on subscription that is not default in string format */ - static final String CONDITION_NON_DEFAULT_STRING = "nonDefault"; - - /** Enable data only when device has ongoing voice call in string format */ - static final String CONDITION_VOICE_CALL_STRING = "inVoiceCall"; - - /** Enable data only when default data is on in string format */ - static final String CONDITION_DEFAULT_DATA_ENABLED_STRING = "DefaultDataOn"; - - /** Enable data only when device is in DSDS mode in string format */ - static final String CONDITION_DSDS_ENABLED_STRING = "dsdsEnabled"; - - /** @hide */ - @IntDef(flag = true, prefix = { "OVERRIDE_CONDITION_" }, value = { - CONDITION_NON_DEFAULT, - CONDITION_IN_VOICE_CALL, - CONDITION_DEFAULT_DATA_ENABLED, - CONDITION_DSDS_ENABLED - }) - @Retention(RetentionPolicy.SOURCE) - public @interface Condition {} - - private static final Map<Integer, String> OVERRIDE_CONDITION_INT_MAP = new ArrayMap<>(); - private static final Map<String, Integer> OVERRIDE_CONDITION_STRING_MAP = new ArrayMap<>(); - - static { - OVERRIDE_CONDITION_INT_MAP.put(CONDITION_NON_DEFAULT, - CONDITION_NON_DEFAULT_STRING); - OVERRIDE_CONDITION_INT_MAP.put(CONDITION_IN_VOICE_CALL, - CONDITION_VOICE_CALL_STRING); - OVERRIDE_CONDITION_INT_MAP.put(CONDITION_DEFAULT_DATA_ENABLED, - CONDITION_DEFAULT_DATA_ENABLED_STRING); - OVERRIDE_CONDITION_INT_MAP.put(CONDITION_DSDS_ENABLED, - CONDITION_DSDS_ENABLED_STRING); - - OVERRIDE_CONDITION_STRING_MAP.put(CONDITION_UNCONDITIONALLY_STRING, - CONDITION_UNCONDITIONALLY); - OVERRIDE_CONDITION_STRING_MAP.put(CONDITION_NON_DEFAULT_STRING, - CONDITION_NON_DEFAULT); - OVERRIDE_CONDITION_STRING_MAP.put(CONDITION_VOICE_CALL_STRING, - CONDITION_IN_VOICE_CALL); - OVERRIDE_CONDITION_STRING_MAP.put(CONDITION_DEFAULT_DATA_ENABLED_STRING, - CONDITION_DEFAULT_DATA_ENABLED); - OVERRIDE_CONDITION_STRING_MAP.put(CONDITION_DSDS_ENABLED_STRING, - CONDITION_DSDS_ENABLED); - } - - private final @Condition int mConditions; - - /** - * Conditions for overriding data enabled setting - * - * @param conditions Conditions in string format - */ - OverrideConditions(@NonNull String conditions) { - mConditions = getBitmaskFromString(conditions); - } - - /** - * Conditions for overriding data enabled setting - * - * @param conditions Conditions in bitmask - */ - OverrideConditions(@Condition int conditions) { - mConditions = conditions; - } - - private static String getStringFromBitmask(@Condition int conditions) { - if (conditions == CONDITION_UNCONDITIONALLY) { - return CONDITION_UNCONDITIONALLY_STRING; - } - List<String> conditionsStrings = new ArrayList<>(); - for (Integer condition : OVERRIDE_CONDITION_INT_MAP.keySet()) { - if ((conditions & condition) == condition) { - conditionsStrings.add(OVERRIDE_CONDITION_INT_MAP.get(condition)); - } - } - return TextUtils.join("&", conditionsStrings); - } - - private static @Condition int getBitmaskFromString(@NonNull String str) { - if (TextUtils.isEmpty(str)) { - throw new IllegalArgumentException("Empty rule string"); - } - - String[] conditionStrings = str.trim().split("\\s*&\\s*"); - int bitmask = 0; - - for (String conditionStr : conditionStrings) { - if (!TextUtils.isEmpty(conditionStr)) { - if (!OVERRIDE_CONDITION_STRING_MAP.containsKey(conditionStr)) { - throw new IllegalArgumentException("Invalid conditions: " + str); - } - bitmask |= OVERRIDE_CONDITION_STRING_MAP.get(conditionStr); - } - } - - return bitmask; - } - - /** - * Check if provided conditions can meet all conditions in the rule. - * - * @param providedConditions The provided conditions - * @return {@code true} if all conditions are met. - */ - boolean allMet(@Condition int providedConditions) { - return (providedConditions & mConditions) == mConditions; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - OverrideConditions that = (OverrideConditions) o; - return mConditions == that.mConditions; - } - - @Override - public int hashCode() { - return Objects.hash(mConditions); - } - - @Override - public String toString() { - return getStringFromBitmask(mConditions); - } - } - - /** - * Constructor - * - * @param rules Data enabled override rules - */ - public DataEnabledOverride(@NonNull String rules) { - updateRules(rules); - } - - /** - * Update the data enabled override rules. - * - * @param newRules New override rules - */ - @VisibleForTesting - public void updateRules(@NonNull String newRules) { - mRules.clear(); - String[] rulesString = newRules.trim().split("\\s*,\\s*"); - for (String rule : rulesString) { - if (!TextUtils.isEmpty(rule)) { - mRules.add(new OverrideRule(rule)); - } - } - } - - /** - * Set always allowing MMS - * - * @param allow {@code true} if always allowing, otherwise {@code false}. - */ - public void setAlwaysAllowMms(boolean allow) { - if (allow) { - mRules.add(OVERRIDE_RULE_ALWAYS_ALLOW_MMS); - } else { - mRules.remove(OVERRIDE_RULE_ALWAYS_ALLOW_MMS); - } - } - - /** - * Set allowing mobile data during voice call. This is used for allowing data on the non-default - * data SIM. When a voice call is placed on the non-default data SIM on DSDS devices, users will - * not be able to use mobile data. By calling this API, data will be temporarily enabled on the - * non-default data SIM during the life cycle of the voice call. - * - * @param allow {@code true} if allowing using data during voice call, {@code false} if - * disallowed. - */ - public void setDataAllowedInVoiceCall(boolean allow) { - if (allow) { - mRules.add(OVERRIDE_RULE_ALLOW_DATA_DURING_VOICE_CALL); - } else { - mRules.remove(OVERRIDE_RULE_ALLOW_DATA_DURING_VOICE_CALL); - } - } - - /** - * Check if data is allowed during voice call. - * - * @return {@code true} if data is allowed during voice call. - */ - public boolean isDataAllowedInVoiceCall() { - return mRules.contains(OVERRIDE_RULE_ALLOW_DATA_DURING_VOICE_CALL); - } - - public boolean isMmsAlwaysAllowed() { - return mRules.contains(OVERRIDE_RULE_ALWAYS_ALLOW_MMS); - } - - private boolean canSatisfyAnyRule(@ApnType int apnType, - @Condition int providedConditions) { - for (OverrideRule rule : mRules) { - if (rule.isSatisfiedByConditions(apnType, providedConditions)) { - return true; - } - } - return false; - } - - private @Condition int getCurrentConditions(Phone phone) { - int conditions = 0; - - if (phone != null) { - // Check if the device is on voice call - if (phone.getState() != PhoneConstants.State.IDLE) { - conditions |= OverrideConditions.CONDITION_IN_VOICE_CALL; - } - - int defaultDataSubId = SubscriptionController.getInstance().getDefaultDataSubId(); - - if (phone.getSubId() != defaultDataSubId) { - conditions |= OverrideConditions.CONDITION_NON_DEFAULT; - } - - if (defaultDataSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) { - int phoneId = SubscriptionController.getInstance().getPhoneId(defaultDataSubId); - try { - Phone defaultDataPhone = PhoneFactory.getPhone(phoneId); - if (defaultDataPhone != null && defaultDataPhone.isUserDataEnabled()) { - conditions |= OverrideConditions.CONDITION_DEFAULT_DATA_ENABLED; - } - } catch (IllegalStateException e) { - //ignore the exception and do not add the condition - Log.d("DataEnabledOverride", e.getMessage()); - } - } - - if (TelephonyManager.from(phone.getContext()).isMultiSimEnabled()) { - conditions |= OverrideConditions.CONDITION_DSDS_ENABLED; - } - } - - return conditions; - } - - /** - * Check for given APN type if we should enable data. - * - * @param phone Phone object - * @param apnType APN type - * @return {@code true} if data should be enabled for the current condition. - */ - public boolean shouldOverrideDataEnabledSettings(Phone phone, @ApnType int apnType) { - return canSatisfyAnyRule(apnType, getCurrentConditions(phone)); - } - - /** - * Get data enabled override rules. - * - * @return Get data enabled override rules in string format - */ - @NonNull - public String getRules() { - List<String> ruleStrings = new ArrayList<>(); - for (OverrideRule rule : mRules) { - ruleStrings.add(rule.toString()); - } - return TextUtils.join(",", ruleStrings); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - DataEnabledOverride that = (DataEnabledOverride) o; - return mRules.equals(that.mRules); - } - - @Override - public int hashCode() { - return Objects.hash(mRules); - } - - @Override - public String toString() { - return "DataEnabledOverride: [rules=\"" + getRules() + "\"]"; - } -}
diff --git a/src/java/com/android/internal/telephony/data/DataEvaluation.java b/src/java/com/android/internal/telephony/data/DataEvaluation.java index c061e16..2ba3fe4 100644 --- a/src/java/com/android/internal/telephony/data/DataEvaluation.java +++ b/src/java/com/android/internal/telephony/data/DataEvaluation.java
@@ -195,50 +195,78 @@ @VisibleForTesting public enum DataEvaluationReason { /** New request from the apps. */ - NEW_REQUEST, + NEW_REQUEST(false), /** Data config changed. */ - DATA_CONFIG_CHANGED, + DATA_CONFIG_CHANGED(true), /** SIM is loaded. */ - SIM_LOADED, + SIM_LOADED(true), /** SIM is removed. */ - SIM_REMOVAL, + SIM_REMOVAL(true), /** Data profiles changed. */ - DATA_PROFILES_CHANGED, + DATA_PROFILES_CHANGED(true), /** When service state changes.(For now only considering data RAT and data registration). */ - DATA_SERVICE_STATE_CHANGED, + DATA_SERVICE_STATE_CHANGED(true), /** When data is enabled or disabled (by user, carrier, thermal, etc...) */ - DATA_ENABLED_CHANGED, + DATA_ENABLED_CHANGED(true), /** When data enabled overrides are changed (MMS always allowed, data on non-DDS sub). */ - DATA_ENABLED_OVERRIDE_CHANGED, + DATA_ENABLED_OVERRIDE_CHANGED(true), /** When data roaming is enabled or disabled. */ - ROAMING_ENABLED_CHANGED, + ROAMING_ENABLED_CHANGED(true), /** When voice call ended (for concurrent voice/data not supported RAT). */ - VOICE_CALL_ENDED, + VOICE_CALL_ENDED(true), /** When network restricts or no longer restricts mobile data. */ - DATA_RESTRICTED_CHANGED, + DATA_RESTRICTED_CHANGED(true), /** Network capabilities changed. The unsatisfied requests might have chances to attach. */ - DATA_NETWORK_CAPABILITIES_CHANGED, + DATA_NETWORK_CAPABILITIES_CHANGED(true), /** When emergency call started or ended. */ - EMERGENCY_CALL_CHANGED, + EMERGENCY_CALL_CHANGED(true), /** When data disconnected, re-evaluate later to see if data could be brought up again. */ - RETRY_AFTER_DISCONNECTED, + RETRY_AFTER_DISCONNECTED(true), /** Data setup retry. */ - DATA_RETRY, + DATA_RETRY(false), /** For handover evaluation, or for network tearing down after handover succeeds/fails. */ - DATA_HANDOVER, + DATA_HANDOVER(true), /** Preferred transport changed. */ - PREFERRED_TRANSPORT_CHANGED, + PREFERRED_TRANSPORT_CHANGED(true), /** Slice config changed. */ - SLICE_CONFIG_CHANGED, + SLICE_CONFIG_CHANGED(true), + /** SRVCC state changed. */ + SRVCC_STATE_CHANGED(true), /** * Single data network arbitration. On certain RATs, only one data network is allowed at the * same time. */ - SINGLE_DATA_NETWORK_ARBITRATION, + SINGLE_DATA_NETWORK_ARBITRATION(true), /** Query from {@link TelephonyManager#isDataConnectivityPossible()}. */ - EXTERNAL_QUERY, + EXTERNAL_QUERY(false), /** Tracking area code changed. */ - TAC_CHANGED, + TAC_CHANGED(true), + /** Unsatisfied network request detached. */ + UNSATISFIED_REQUEST_DETACHED(true); + + /** + * {@code true} if the evaluation is due to environmental changes (i.e. SIM removal, + * registration state changes, etc.... + */ + private final boolean mIsConditionBased; + + /** + * @return {@code true} if the evaluation is due to environmental changes (i.e. SIM removal, + * registration state changes, etc.... + */ + public boolean isConditionBased() { + return mIsConditionBased; + } + + /** + * Constructor + * + * @param isConditionBased {@code true} if the evaluation is due to environmental changes + * (i.e. SIM removal, registration state changes, etc....) + */ + DataEvaluationReason(boolean isConditionBased) { + mIsConditionBased = isConditionBased; + } } /** Disallowed reasons. There could be multiple reasons if it is not allowed. */ @@ -295,7 +323,9 @@ /** Only one data network is allowed at one time. */ ONLY_ALLOWED_SINGLE_NETWORK(true), /** Data enabled settings are not ready. */ - DATA_SETTINGS_NOT_READY(true); + DATA_SETTINGS_NOT_READY(true), + /** Handover max retry stopped but network is not on the preferred transport. */ + HANDOVER_RETRY_STOPPED(true); private final boolean mIsHardReason; @@ -334,6 +364,10 @@ */ NORMAL, /** + * Data is allowed because an ongoing VoPS call depends on this network + */ + IN_VOICE_CALL, + /** * The network brought up by this network request is unmetered. Should allowed no matter * the user enables or disables data. */
diff --git a/src/java/com/android/internal/telephony/data/DataNetwork.java b/src/java/com/android/internal/telephony/data/DataNetwork.java index e483d2b..d533933 100644 --- a/src/java/com/android/internal/telephony/data/DataNetwork.java +++ b/src/java/com/android/internal/telephony/data/DataNetwork.java
@@ -16,6 +16,8 @@ package com.android.internal.telephony.data; +import static android.telephony.TelephonyManager.HAL_SERVICE_DATA; + import android.annotation.CallbackExecutor; import android.annotation.IntDef; import android.annotation.NonNull; @@ -52,6 +54,7 @@ import android.telephony.Annotation.NetworkType; import android.telephony.Annotation.ValidationStatus; import android.telephony.AnomalyReporter; +import android.telephony.CarrierConfigManager; import android.telephony.DataFailCause; import android.telephony.DataSpecificRegistrationInfo; import android.telephony.LinkCapacityEstimate; @@ -87,6 +90,7 @@ import com.android.internal.telephony.PhoneConstants; import com.android.internal.telephony.PhoneFactory; import com.android.internal.telephony.RIL; +import com.android.internal.telephony.data.DataConfigManager.DataConfigManagerCallback; import com.android.internal.telephony.data.DataEvaluation.DataAllowedReason; import com.android.internal.telephony.data.DataNetworkController.NetworkRequestList; import com.android.internal.telephony.data.DataRetryManager.DataHandoverRetryEntry; @@ -165,6 +169,9 @@ /** Event for detaching a network request. */ private static final int EVENT_DETACH_NETWORK_REQUEST = 3; + /** Event when detect radio not available. */ + private static final int EVENT_RADIO_NOT_AVAILABLE = 4; + /** Event for allocating PDU session id response. */ private static final int EVENT_ALLOCATE_PDU_SESSION_ID_RESPONSE = 5; @@ -189,9 +196,6 @@ /** Event for display info changed. This is for getting 5G NSA or mmwave information. */ private static final int EVENT_DISPLAY_INFO_CHANGED = 13; - /** Event for initiating an handover between cellular and IWLAN. */ - private static final int EVENT_START_HANDOVER = 14; - /** Event for setup data call (for handover) response from the data service. */ private static final int EVENT_HANDOVER_RESPONSE = 15; @@ -231,6 +235,24 @@ /** Event for CSS indicator changed. */ private static final int EVENT_CSS_INDICATOR_CHANGED = 24; + /** + * Event for notifying source transport that handover is about to be initiated on target + * transport. + */ + private static final int EVENT_NOTIFY_HANDOVER_STARTED = 25; + + /** + * Event for the response of notifying source transport that handover is about to be initiated + * on target transport. + */ + private static final int EVENT_NOTIFY_HANDOVER_STARTED_RESPONSE = 26; + + /** + * Event for the response of notifying source transport that handover is cancelled/failed on the + * target transport. + */ + private static final int EVENT_NOTIFY_HANDOVER_CANCELLED_RESPONSE = 27; + /** Invalid context id. */ private static final int INVALID_CID = -1; @@ -245,8 +267,9 @@ private static final int DEFAULT_INTERNET_NETWORK_SCORE = 50; private static final int OTHER_NETWORK_SCORE = 45; - @IntDef(prefix = {"DEACTIVATION_REASON_"}, + @IntDef(prefix = {"TEAR_DOWN_REASON_"}, value = { + TEAR_DOWN_REASON_NONE, TEAR_DOWN_REASON_CONNECTIVITY_SERVICE_UNWANTED, TEAR_DOWN_REASON_SIM_REMOVAL, TEAR_DOWN_REASON_AIRPLANE_MODE_ON, @@ -279,6 +302,9 @@ }) public @interface TearDownReason {} + /** Data network was not torn down. */ + public static final int TEAR_DOWN_REASON_NONE = 0; + /** Data network tear down requested by connectivity service. */ public static final int TEAR_DOWN_REASON_CONNECTIVITY_SERVICE_UNWANTED = 1; @@ -356,7 +382,7 @@ /** Data network tear down due to data profile not preferred. */ public static final int TEAR_DOWN_REASON_DATA_PROFILE_NOT_PREFERRED = 26; - /** Data network tear down due to not allowed by policy. */ + /** Data network tear down due to handover not allowed by policy. */ public static final int TEAR_DOWN_REASON_NOT_ALLOWED_BY_POLICY = 27; /** Data network tear down due to illegal state. */ @@ -526,6 +552,10 @@ /** Data network controller. */ private final @NonNull DataNetworkController mDataNetworkController; + /** Data network controller callback. */ + private final @NonNull DataNetworkController.DataNetworkControllerCallback + mDataNetworkControllerCallback; + /** Data config manager. */ private final @NonNull DataConfigManager mDataConfigManager; @@ -605,6 +635,11 @@ private @DataFailureCause int mFailCause = DataFailCause.NONE; /** + * The tear down reason if the data call is voluntarily deactivated, not due to failure. + */ + private @TearDownReason int mTearDownReason = TEAR_DOWN_REASON_NONE; + + /** * The retry delay in milliseconds from setup data failure. */ private long mRetryDelayMillis = DataCallResponse.RETRY_DURATION_UNDEFINED; @@ -624,6 +659,11 @@ */ private @TransportType int mTransport; + /** + * The last known data network type. + */ + private @NetworkType int mLastKnownDataNetworkType; + /** The reason that why setting up this data network is allowed. */ private @NonNull DataAllowedReason mDataAllowedReason; @@ -641,6 +681,9 @@ */ private @NonNull int[] mAdministratorUids = new int[0]; + /** Carrier privileges callback to monitor administrator UID change. */ + private @Nullable TelephonyManager.CarrierPrivilegesCallback mCarrierPrivilegesCallback; + /** * Carrier service package uid. This UID will not change through the life cycle of data network. */ @@ -652,6 +695,11 @@ private @Nullable LinkBandwidthEstimatorCallback mLinkBandwidthEstimatorCallback; /** + * Data config callback for carrier config update. + */ + private @Nullable DataConfigManagerCallback mDataConfigManagerCallback; + + /** * The network bandwidth. */ public static class NetworkBandwidth { @@ -754,9 +802,10 @@ * * @param dataNetwork The data network. * @param cause The disconnect cause. + * @param tearDownReason The reason the network was torn down */ public abstract void onDisconnected(@NonNull DataNetwork dataNetwork, - @DataFailureCause int cause); + @DataFailureCause int cause, @TearDownReason int tearDownReason); /** * Called when handover between IWLAN and cellular network succeeded. @@ -810,6 +859,14 @@ * @param dataNetwork The data network. */ public abstract void onTrackNetworkUnwanted(@NonNull DataNetwork dataNetwork); + + /** + * Called when a network request is detached after no longer satisfied. + * + * @param networkRequest The detached network request. + */ + public abstract void onRetryUnsatisfiedNetworkRequest( + @NonNull TelephonyNetworkRequest networkRequest); } /** @@ -846,12 +903,14 @@ mAccessNetworksManager = phone.getAccessNetworksManager(); mVcnManager = mPhone.getContext().getSystemService(VcnManager.class); mDataNetworkController = phone.getDataNetworkController(); + mDataNetworkControllerCallback = new DataNetworkController.DataNetworkControllerCallback( + getHandler()::post) { + @Override + public void onSubscriptionPlanOverride() { + sendMessage(EVENT_SUBSCRIPTION_PLAN_OVERRIDE); + }}; mDataNetworkController.registerDataNetworkControllerCallback( - new DataNetworkController.DataNetworkControllerCallback(getHandler()::post) { - @Override - public void onSubscriptionPlanOverride() { - sendMessage(EVENT_SUBSCRIPTION_PLAN_OVERRIDE); - }}); + mDataNetworkControllerCallback); mDataConfigManager = mDataNetworkController.getDataConfigManager(); mDataCallSessionStats = new DataCallSessionStats(mPhone); mDataNetworkCallback = callback; @@ -863,13 +922,15 @@ mTrafficDescriptors.add(dataProfile.getTrafficDescriptor()); } mTransport = transport; + mLastKnownDataNetworkType = getDataNetworkType(); mDataAllowedReason = dataAllowedReason; dataProfile.setLastSetupTimestamp(SystemClock.elapsedRealtime()); mAttachedNetworkRequestList.addAll(networkRequestList); - mCid.put(AccessNetworkConstants.TRANSPORT_TYPE_WWAN, INVALID_CID); - mCid.put(AccessNetworkConstants.TRANSPORT_TYPE_WLAN, INVALID_CID); - mTcpBufferSizes = mDataConfigManager.getDefaultTcpConfigString(); + for (int transportType : mAccessNetworksManager.getAvailableTransports()) { + mCid.put(transportType, INVALID_CID); + } mTelephonyDisplayInfo = mPhone.getDisplayInfoController().getTelephonyDisplayInfo(); + mTcpBufferSizes = mDataConfigManager.getTcpConfigString(mTelephonyDisplayInfo); for (TelephonyNetworkRequest networkRequest : networkRequestList) { networkRequest.setAttachedNetwork(DataNetwork.this); @@ -982,18 +1043,39 @@ @Override public void enter() { logv("Registering all events."); - mDataConfigManager.registerForConfigUpdate(getHandler(), EVENT_DATA_CONFIG_UPDATED); + mDataConfigManagerCallback = new DataConfigManagerCallback(getHandler()::post) { + @Override + public void onCarrierConfigChanged() { + sendMessage(EVENT_DATA_CONFIG_UPDATED); + } + }; mRil.registerForPcoData(getHandler(), EVENT_PCO_DATA_RECEIVED, null); + + mDataConfigManager.registerCallback(mDataConfigManagerCallback); mPhone.getDisplayInfoController().registerForTelephonyDisplayInfoChanged( getHandler(), EVENT_DISPLAY_INFO_CHANGED, null); mPhone.getServiceStateTracker().registerForServiceStateChanged(getHandler(), - EVENT_SERVICE_STATE_CHANGED); + EVENT_SERVICE_STATE_CHANGED, null); for (int transport : mAccessNetworksManager.getAvailableTransports()) { mDataServiceManagers.get(transport) .registerForDataCallListChanged(getHandler(), EVENT_DATA_STATE_CHANGED); } - mPhone.getCarrierPrivilegesTracker().registerCarrierPrivilegesListener(getHandler(), - EVENT_CARRIER_PRIVILEGED_UIDS_CHANGED, null); + + mCarrierPrivilegesCallback = + (Set<String> privilegedPackageNames, Set<Integer> privilegedUids) -> { + log("onCarrierPrivilegesChanged, Uids=" + privilegedUids.toString()); + Message message = obtainMessage(EVENT_CARRIER_PRIVILEGED_UIDS_CHANGED); + AsyncResult.forMessage( + message, + privilegedUids.stream().mapToInt(i -> i).toArray(), + null /* ex */); + sendMessage(message); + }; + TelephonyManager tm = mPhone.getContext().getSystemService(TelephonyManager.class); + if (tm != null) { + tm.registerCarrierPrivilegesCallback( + mPhone.getPhoneId(), getHandler()::post, mCarrierPrivilegesCallback); + } mPhone.getServiceStateTracker().registerForCssIndicatorChanged( getHandler(), EVENT_CSS_INDICATOR_CHANGED, null); @@ -1027,7 +1109,10 @@ mPhone.getCallTracker().unregisterForVoiceCallEnded(getHandler()); mPhone.getServiceStateTracker().unregisterForCssIndicatorChanged(getHandler()); - mPhone.getCarrierPrivilegesTracker().unregisterCarrierPrivilegesListener(getHandler()); + TelephonyManager tm = mPhone.getContext().getSystemService(TelephonyManager.class); + if (tm != null && mCarrierPrivilegesCallback != null) { + tm.unregisterCarrierPrivilegesCallback(mCarrierPrivilegesCallback); + } for (int transport : mAccessNetworksManager.getAvailableTransports()) { mDataServiceManagers.get(transport) .unregisterForDataCallListChanged(getHandler()); @@ -1035,18 +1120,22 @@ mPhone.getServiceStateTracker().unregisterForServiceStateChanged(getHandler()); mPhone.getDisplayInfoController().unregisterForTelephonyDisplayInfoChanged( getHandler()); - mDataConfigManager.unregisterForConfigUpdate(getHandler()); mRil.unregisterForPcoData(getHandler()); + mDataConfigManager.unregisterCallback(mDataConfigManagerCallback); } @Override public boolean processMessage(Message msg) { switch (msg.what) { case EVENT_DATA_CONFIG_UPDATED: - onDataConfigUpdated(); + onCarrierConfigUpdated(); break; case EVENT_SERVICE_STATE_CHANGED: { - mDataCallSessionStats.onDrsOrRatChanged(getDataNetworkType()); + int networkType = getDataNetworkType(); + mDataCallSessionStats.onDrsOrRatChanged(networkType); + if (networkType != TelephonyManager.NETWORK_TYPE_UNKNOWN) { + mLastKnownDataNetworkType = networkType; + } updateSuspendState(); updateNetworkCapabilities(); break; @@ -1057,7 +1146,8 @@ break; } case EVENT_DETACH_NETWORK_REQUEST: { - onDetachNetworkRequest((TelephonyNetworkRequest) msg.obj); + onDetachNetworkRequest((TelephonyNetworkRequest) msg.obj, + msg.arg1 != 0 /* shouldRetry */); updateNetworkScore(); break; } @@ -1088,6 +1178,9 @@ onPcoDataReceived((PcoData) ar.result); break; } + case EVENT_NOTIFY_HANDOVER_CANCELLED_RESPONSE: + log("Notified handover cancelled."); + break; case EVENT_BANDWIDTH_ESTIMATE_FROM_MODEM_CHANGED: case EVENT_TEAR_DOWN_NETWORK: case EVENT_STUCK_IN_TRANSIENT_STATE: @@ -1099,10 +1192,23 @@ // Ignore the events when not in the correct state. log("Ignored " + eventToString(msg.what)); break; - case EVENT_START_HANDOVER: - log("Ignore the handover to " + AccessNetworkConstants + case EVENT_NOTIFY_HANDOVER_STARTED_RESPONSE: + case EVENT_NOTIFY_HANDOVER_STARTED: + // We reach here if network is not in the right state. + if (msg.obj != null) { + // Cancel it because it's either HO in progress or will soon disconnect. + // Either case we want to clean up obsolete retry attempts. + DataHandoverRetryEntry retryEntry = (DataHandoverRetryEntry) msg.obj; + retryEntry.setState(DataRetryEntry.RETRY_STATE_CANCELLED); + } + log("Ignore handover to " + AccessNetworkConstants .transportTypeToString(msg.arg1) + " request."); break; + case EVENT_RADIO_NOT_AVAILABLE: + mFailCause = DataFailCause.RADIO_NOT_AVAILABLE; + loge(eventToString(msg.what) + ": transition to disconnected state"); + transitionTo(mDisconnectedState); + break; default: loge("Unhandled event " + eventToString(msg.what)); break; @@ -1166,7 +1272,7 @@ msg.getData().getParcelable(DataServiceManager.DATA_CALL_RESPONSE); onSetupResponse(resultCode, dataCallResponse); break; - case EVENT_START_HANDOVER: + case EVENT_NOTIFY_HANDOVER_STARTED: case EVENT_TEAR_DOWN_NETWORK: case EVENT_WAITING_FOR_TEARING_DOWN_CONDITION_MET: // Defer the request until connected or disconnected. @@ -1234,13 +1340,14 @@ } } - // If we've ever received PCO data before connected, now it's the time to - // process it. + // If we've ever received PCO data before connected, now it's the time to process it. mPcoData.getOrDefault(mCid.get(mTransport), Collections.emptyMap()) .forEach((pcoId, pcoData) -> { onPcoDataChanged(pcoData); }); + mDataNetworkCallback.invokeFromExecutor( + () -> mDataNetworkCallback.onLinkStatusChanged(DataNetwork.this, mLinkStatus)); notifyPreciseDataConnectionState(); updateSuspendState(); } @@ -1256,17 +1363,6 @@ } int tearDownReason = msg.arg1; - // If the tear down request is from upper layer, for example, IMS service - // releases network request, we don't need to delay. The purpose of the delay - // is to have IMS service have time to perform IMS de-registration, so if this - // request is from IMS service itself, that means IMS service is already aware - // of the tear down. So there is no need to delay in this case. - if (tearDownReason != TEAR_DOWN_REASON_CONNECTIVITY_SERVICE_UNWANTED - && shouldDelayImsTearDown()) { - logl("Delay IMS tear down until call ends. reason=" - + tearDownReasonToString(tearDownReason)); - break; - } removeMessages(EVENT_TEAR_DOWN_NETWORK); removeDeferredMessages(EVENT_TEAR_DOWN_NETWORK); @@ -1285,8 +1381,21 @@ case EVENT_DISPLAY_INFO_CHANGED: onDisplayInfoChanged(); break; - case EVENT_START_HANDOVER: - onStartHandover(msg.arg1, (DataHandoverRetryEntry) msg.obj); + case EVENT_NOTIFY_HANDOVER_STARTED: + // Notify source transport that handover is about to start. Note this will not + // initiate the handover process on target transport, but more for notifying + // the source transport so that PDU session id can be preserved if network + // notifies PDN lost during handover. The real handover process will kick off + // after receiving EVENT_NOTIFY_HANDOVER_STARTED_RESPONSE. + log("Notifying source transport " + + AccessNetworkConstants.transportTypeToString(mTransport) + + " that handover is about to start."); + mDataServiceManagers.get(mTransport).startHandover(mCid.get(mTransport), + obtainMessage(EVENT_NOTIFY_HANDOVER_STARTED_RESPONSE, 0, msg.arg2, + msg.obj)); + // We enter handover state here because this is the first action we do for + // handover. + transitionTo(mHandoverState); break; case EVENT_SUBSCRIPTION_PLAN_OVERRIDE: updateMeteredAndCongested(); @@ -1339,8 +1448,15 @@ // Otherwise the deferred message might be incorrectly treated as "disconnected" // signal. So we only defer the related data call list changed event, and drop // the unrelated. - if (shouldDeferDataStateChangedEvent(msg)) { - log("Defer message " + eventToString(msg.what)); + AsyncResult ar = (AsyncResult) msg.obj; + int transport = (int) ar.userObj; + List<DataCallResponse> responseList = (List<DataCallResponse>) ar.result; + if (transport != mTransport) { + log("Dropped unrelated " + + AccessNetworkConstants.transportTypeToString(transport) + + " data call list changed event. " + responseList); + } else { + log("Defer message " + eventToString(msg.what) + ":" + responseList); deferMessage(msg); } break; @@ -1354,6 +1470,9 @@ log("Defer message " + eventToString(msg.what)); deferMessage(msg); break; + case EVENT_NOTIFY_HANDOVER_STARTED_RESPONSE: + onStartHandover(msg.arg2, (DataHandoverRetryEntry) msg.obj); + break; case EVENT_HANDOVER_RESPONSE: int resultCode = msg.arg1; DataCallResponse dataCallResponse = @@ -1362,11 +1481,11 @@ (DataHandoverRetryEntry) msg.obj); break; case EVENT_STUCK_IN_TRANSIENT_STATE: + // enable detection only for valid timeout range reportAnomaly("Data service did not respond the handover request within " - + TimeUnit.MILLISECONDS.toSeconds( + + TimeUnit.MILLISECONDS.toSeconds( mDataConfigManager.getNetworkHandoverTimeoutMs()) + " seconds.", "1afe68cb-8b41-4964-a737-4f34372429ea"); - // Handover failed. Use the retry logic defined in // CarrierConfigManager.KEY_TELEPHONY_DATA_HANDOVER_RETRY_RULES_STRING_ARRAY. long retry = DataCallResponse.RETRY_DURATION_UNDEFINED; @@ -1384,39 +1503,6 @@ } return HANDLED; } - - /** - * Check if the data call list changed event should be deferred or dropped when handover - * is in progress. - * - * @param msg The data call list changed message. - * - * @return {@code true} if the message should be deferred. - */ - private boolean shouldDeferDataStateChangedEvent(@NonNull Message msg) { - // The data call list changed event should be conditionally deferred. - // Otherwise the deferred message might be incorrectly treated as "disconnected" - // signal. So we only defer the related data call list changed event, and drop - // the unrelated. - AsyncResult ar = (AsyncResult) msg.obj; - int transport = (int) ar.userObj; - List<DataCallResponse> responseList = (List<DataCallResponse>) ar.result; - if (transport != mTransport) { - log("Dropped unrelated " + AccessNetworkConstants.transportTypeToString(transport) - + " data call list changed event. " + responseList); - return false; - } - - // Check if the data call list changed event are related to the current data network. - boolean related = responseList.stream().anyMatch( - r -> mCid.get(mTransport) == r.getId()); - if (related) { - log("Deferred the related data call list changed event." + responseList); - } else { - log("Dropped unrelated data call list changed event. " + responseList); - } - return related; - } } /** @@ -1460,6 +1546,7 @@ case EVENT_STUCK_IN_TRANSIENT_STATE: // After frameworks issues deactivate data call request, RIL should report // data disconnected through data call list changed event subsequently. + reportAnomaly("RIL did not send data call list changed event after " + "deactivate data call request within " + TimeUnit.MILLISECONDS.toSeconds( @@ -1512,8 +1599,11 @@ //************************************************************// if (mEverConnected) { + mLinkStatus = DataCallResponse.LINK_STATUS_INACTIVE; mDataNetworkCallback.invokeFromExecutor(() -> mDataNetworkCallback - .onDisconnected(DataNetwork.this, mFailCause)); + .onLinkStatusChanged(DataNetwork.this, mLinkStatus)); + mDataNetworkCallback.invokeFromExecutor(() -> mDataNetworkCallback + .onDisconnected(DataNetwork.this, mFailCause, mTearDownReason)); if (mTransport == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) { unregisterForWwanEvents(); } @@ -1524,6 +1614,8 @@ } notifyPreciseDataConnectionState(); mNetworkAgent.unregister(); + mDataNetworkController.unregisterDataNetworkControllerCallback( + mDataNetworkControllerCallback); mDataCallSessionStats.onDataCallDisconnected(mFailCause); if (mTransport == AccessNetworkConstants.TRANSPORT_TYPE_WLAN @@ -1549,6 +1641,7 @@ private void registerForWwanEvents() { registerForBandwidthUpdate(); mKeepaliveTracker.registerForKeepaliveStatus(); + mRil.registerForNotAvailable(this.getHandler(), EVENT_RADIO_NOT_AVAILABLE, null); } /** @@ -1557,6 +1650,7 @@ private void unregisterForWwanEvents() { unregisterForBandwidthUpdate(); mKeepaliveTracker.unregisterForKeepaliveStatus(); + mRil.unregisterForNotAvailable(this.getHandler()); } @Override @@ -1623,12 +1717,21 @@ * Called when detaching the network request from this data network. * * @param networkRequest Network request to detach. + * @param shouldRetry {@code true} if the detached network request should be retried. */ - private void onDetachNetworkRequest(@NonNull TelephonyNetworkRequest networkRequest) { + private void onDetachNetworkRequest(@NonNull TelephonyNetworkRequest networkRequest, + boolean shouldRetry) { mAttachedNetworkRequestList.remove(networkRequest); networkRequest.setState(TelephonyNetworkRequest.REQUEST_STATE_UNSATISFIED); networkRequest.setAttachedNetwork(null); + if (shouldRetry) { + // Inform DataNetworkController that a network request was detached and should be + // scheduled to retry. + mDataNetworkCallback.invokeFromExecutor( + () -> mDataNetworkCallback.onRetryUnsatisfiedNetworkRequest(networkRequest)); + } + if (mAttachedNetworkRequestList.isEmpty()) { log("All network requests are detached."); @@ -1649,12 +1752,15 @@ * network. * * @param networkRequest Network request to detach. + * @param shouldRetry {@code true} if the detached network request should be retried. */ - public void detachNetworkRequest(@NonNull TelephonyNetworkRequest networkRequest) { + public void detachNetworkRequest(@NonNull TelephonyNetworkRequest networkRequest, + boolean shouldRetry) { if (getCurrentState() == null || isDisconnected()) { return; } - sendMessage(obtainMessage(EVENT_DETACH_NETWORK_REQUEST, networkRequest)); + sendMessage(obtainMessage(EVENT_DETACH_NETWORK_REQUEST, shouldRetry ? 1 : 0, 0, + networkRequest)); } /** @@ -1705,13 +1811,15 @@ /** * Remove network requests that can't be satisfied anymore. + * + * @param shouldRetry {@code true} if the detached network requests should be retried. */ - private void removeUnsatisfiedNetworkRequests() { + private void removeUnsatisfiedNetworkRequests(boolean shouldRetry) { for (TelephonyNetworkRequest networkRequest : mAttachedNetworkRequestList) { if (!networkRequest.canBeSatisfiedBy(mNetworkCapabilities)) { log("removeUnsatisfiedNetworkRequests: " + networkRequest + " can't be satisfied anymore. Will be detached."); - detachNetworkRequest(networkRequest); + detachNetworkRequest(networkRequest, shouldRetry); } } } @@ -1780,6 +1888,31 @@ } /** + * In some rare cases we need to re-create the network agent, for example, underlying network + * IP changed, or when we unfortunately need to remove/add a immutable network capability. + */ + private void recreateNetworkAgent() { + if (isConnecting() || isDisconnected() || isDisconnecting()) { + loge("Incorrect state for re-creating the network agent."); + return; + } + + // Abandon the network agent because we are going to create a new one. + mNetworkAgent.abandon(); + // Create a new network agent and register with connectivity service. Note that the agent + // will always be registered with NOT_SUSPENDED capability. + mNetworkAgent = createNetworkAgent(); + mNetworkAgent.markConnected(); + // Because network agent is always created with NOT_SUSPENDED, we need to update + // the suspended if it's was in suspended state. + if (mSuspended) { + log("recreateNetworkAgent: The network is in suspended state. Update the network" + + " capability again. nc=" + mNetworkCapabilities); + mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities); + } + } + + /** * Update the network capabilities. */ private void updateNetworkCapabilities() { @@ -1821,7 +1954,8 @@ DataSpecificRegistrationInfo dsri = nri.getDataSpecificInfo(); // Check if the network is non-VoPS. if (dsri != null && dsri.getVopsSupportInfo() != null - && !dsri.getVopsSupportInfo().isVopsSupported()) { + && !dsri.getVopsSupportInfo().isVopsSupported() + && !mDataConfigManager.shouldKeepNetworkUpInNonVops()) { builder.removeCapability(NetworkCapabilities.NET_CAPABILITY_MMTEL); } log("updateNetworkCapabilities: dsri=" + dsri); @@ -1977,13 +2111,8 @@ logl("updateNetworkCapabilities: Immutable capabilities changed. Re-create the " + "network agent. Attempted to change from " + mNetworkCapabilities + " to " + nc); - // Abandon the network agent because we are going to create a new one. - mNetworkAgent.abandon(); - // Update the capabilities first so the new network agent would be created with the - // new capabilities. mNetworkCapabilities = nc; - mNetworkAgent = createNetworkAgent(); - mNetworkAgent.markConnected(); + recreateNetworkAgent(); } else { // Now we need to inform connectivity service and data network controller // about the capabilities changed. @@ -1992,7 +2121,11 @@ mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities); } - removeUnsatisfiedNetworkRequests(); + // Only retry the request when the network is in connected or handover state. This is to + // prevent request is detached during connecting state, and then become a setup/detach + // infinite loop. + boolean shouldRetry = isConnected() || isHandoverInProgress(); + removeUnsatisfiedNetworkRequests(shouldRetry); mDataNetworkCallback.invokeFromExecutor(() -> mDataNetworkCallback .onNetworkCapabilitiesChanged(DataNetwork.this)); } else { @@ -2173,8 +2306,13 @@ if (mLinkStatus != response.getLinkStatus()) { mLinkStatus = response.getLinkStatus(); log("Link status updated to " + DataUtils.linkStatusToString(mLinkStatus)); - mDataNetworkCallback.invokeFromExecutor( - () -> mDataNetworkCallback.onLinkStatusChanged(DataNetwork.this, mLinkStatus)); + if (isConnected()) { + // If the data network is in a transition state, the link status will be notified + // upon entering connected or disconnected state. If the data network is already + // connected, send the updated link status from the updated data call response. + mDataNetworkCallback.invokeFromExecutor(() -> mDataNetworkCallback + .onLinkStatusChanged(DataNetwork.this, mLinkStatus)); + } } // Set link addresses @@ -2272,20 +2410,13 @@ // If the new link properties is not compatible (e.g. IP changes, interface changes), // then we should de-register the network agent and re-create a new one. if ((isConnected() || isHandoverInProgress()) - && !isLinkPropertiesCompatible(linkProperties, mLinkProperties)) { + && !isLinkPropertiesCompatible(mLinkProperties, linkProperties)) { logl("updateDataNetwork: Incompatible link properties detected. Re-create the " + "network agent. Changed from " + mLinkProperties + " to " + linkProperties); mLinkProperties = linkProperties; - - // Abandon the network agent because we are going to create a new one. - mNetworkAgent.abandon(); - // Update the link properties first so the new network agent would be created with - // the new link properties. - mLinkProperties = linkProperties; - mNetworkAgent = createNetworkAgent(); - mNetworkAgent.markConnected(); + recreateNetworkAgent(); } else { mLinkProperties = linkProperties; log("sendLinkProperties " + mLinkProperties); @@ -2307,14 +2438,16 @@ logl("onSetupResponse: resultCode=" + DataServiceCallback.resultCodeToString(resultCode) + ", response=" + response); mFailCause = getFailCauseFromDataCallResponse(resultCode, response); - validateDataCallResponse(response); + validateDataCallResponse(response, true /*isSetupResponse*/); if (mFailCause == DataFailCause.NONE) { - if (mDataNetworkController.isNetworkInterfaceExisting(response.getInterfaceName())) { - logl("Interface " + response.getInterfaceName() + " already existing. Silently " - + "tear down now."); + DataNetwork dataNetwork = mDataNetworkController.getDataNetworkByInterface( + response.getInterfaceName()); + if (dataNetwork != null) { + logl("Interface " + response.getInterfaceName() + " has been already used by " + + dataNetwork + ". Silently tear down now."); // If this is a pre-5G data setup, that means APN database has some problems. For // example, different APN settings have the same APN name. - if (response.getTrafficDescriptors().isEmpty()) { + if (response.getTrafficDescriptors().isEmpty() && dataNetwork.isConnected()) { reportAnomaly("Duplicate network interface " + response.getInterfaceName() + " detected.", "62f66e7e-8d71-45de-a57b-dc5c78223fd5"); } @@ -2370,15 +2503,18 @@ getDataNetworkType(), apnTypeBitmask, protocol, - mFailCause); + // Log the raw fail cause to avoid large amount of UNKNOWN showing on metrics. + response != null ? response.getCause() : mFailCause); } /** * If the {@link DataCallResponse} contains invalid info, triggers an anomaly report. * * @param response The response to be validated + * @param isSetupResponse {@code true} if the response is for initial data call setup */ - private void validateDataCallResponse(@Nullable DataCallResponse response) { + private void validateDataCallResponse(@Nullable DataCallResponse response, + boolean isSetupResponse) { if (response == null || response.getLinkStatus() == DataCallResponse.LINK_STATUS_INACTIVE) return; int failCause = response.getCause(); @@ -2398,6 +2534,37 @@ reportAnomaly("Invalid DataCallResponse detected", "1f273e9d-b09c-46eb-ad1c-421d01f61164"); } + // Check IP for initial setup response + NetworkRegistrationInfo nri = getNetworkRegistrationInfo(); + if (isSetupResponse + && mDataProfile.getApnSetting() != null && nri != null && nri.isInService()) { + boolean isRoaming = nri.getNetworkRegistrationState() + == NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING; + int protocol = isRoaming ? mDataProfile.getApnSetting().getRoamingProtocol() + : mDataProfile.getApnSetting().getProtocol(); + String underlyingDataService = mTransport + == AccessNetworkConstants.TRANSPORT_TYPE_WWAN + ? "RIL" : "IWLAN data service"; + if (protocol == ApnSetting.PROTOCOL_IP) { + if (response.getAddresses().stream().anyMatch( + la -> la.getAddress() instanceof java.net.Inet6Address)) { + loge("Invalid DataCallResponse. Requested IPv4 but got IPv6 address. " + + response); + reportAnomaly(underlyingDataService + " reported mismatched IP " + + "type. Requested IPv4 but got IPv6 address.", + "7744f920-fb64-4db0-ba47-de0eae485a80"); + } + } else if (protocol == ApnSetting.PROTOCOL_IPV6) { + if (response.getAddresses().stream().anyMatch( + la -> la.getAddress() instanceof java.net.Inet4Address)) { + loge("Invalid DataCallResponse. Requested IPv6 but got IPv4 address. " + + response); + reportAnomaly(underlyingDataService + " reported mismatched IP " + + "type. Requested IPv6 but got IPv4 address.", + "7744f920-fb64-4db0-ba47-de0eae485a80"); + } + } + } } else if (!DataFailCause.isFailCauseExisting(failCause)) { // Setup data failed. loge("Invalid DataFailCause in " + response); reportAnomaly("Invalid DataFailCause: (0x" + Integer.toHexString(failCause) @@ -2418,9 +2585,9 @@ log("Remove network since deactivate request returned an error."); mFailCause = DataFailCause.RADIO_NOT_AVAILABLE; transitionTo(mDisconnectedState); - } else if (mPhone.getHalVersion().less(RIL.RADIO_HAL_VERSION_2_0)) { + } else if (mPhone.getHalVersion(HAL_SERVICE_DATA).less(RIL.RADIO_HAL_VERSION_2_0)) { log("Remove network on deactivate data response on old HAL " - + mPhone.getHalVersion()); + + mPhone.getHalVersion(HAL_SERVICE_DATA)); mFailCause = DataFailCause.LOST_CONNECTION; transitionTo(mDisconnectedState); } @@ -2435,6 +2602,7 @@ if (getCurrentState() == null || isDisconnected()) { return; } + mTearDownReason = reason; sendMessage(obtainMessage(EVENT_TEAR_DOWN_NETWORK, reason)); } @@ -2454,16 +2622,17 @@ reason == TEAR_DOWN_REASON_AIRPLANE_MODE_ON ? DataService.REQUEST_REASON_SHUTDOWN : DataService.REQUEST_REASON_NORMAL, obtainMessage(EVENT_DEACTIVATE_DATA_NETWORK_RESPONSE)); - mDataCallSessionStats.setDeactivateDataCallReason(DataService.REQUEST_REASON_NORMAL); + mDataCallSessionStats.setDeactivateDataCallReason(reason); mInvokedDataDeactivation = true; } /** - * @return {@code true} if this is an IMS network and tear down should be delayed until call - * ends on this data network. + * @return {@code true} if we shall delay tear down this network because an active voice call is + * relying on it and + * {@link CarrierConfigManager#KEY_DELAY_IMS_TEAR_DOWN_UNTIL_CALL_END_BOOL} is enabled. */ - public boolean shouldDelayImsTearDown() { - return mDataConfigManager.isImsDelayTearDownEnabled() + public boolean shouldDelayImsTearDownDueToInCall() { + return mDataConfigManager.isImsDelayTearDownUntilVoiceCallEndEnabled() && mNetworkCapabilities != null && mNetworkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_MMTEL) && mPhone.getImsPhone() != null @@ -2529,7 +2698,7 @@ if (response != null) { if (!response.equals(mDataCallResponse)) { log("onDataStateChanged: " + response); - validateDataCallResponse(response); + validateDataCallResponse(response, false /*isSetupResponse*/); mDataCallResponse = response; if (response.getLinkStatus() != DataCallResponse.LINK_STATUS_INACTIVE) { updateDataNetwork(response); @@ -2557,10 +2726,10 @@ } /** - * Called when data config updated. + * Called when carrier config updated. */ - private void onDataConfigUpdated() { - log("onDataConfigUpdated"); + private void onCarrierConfigUpdated() { + log("onCarrierConfigUpdated"); updateBandwidthFromDataConfig(); updateTcpBufferSizes(); @@ -2686,9 +2855,12 @@ log("updateMeteredAndCongested: mTempNotMeteredSupported changed to " + mTempNotMeteredSupported); } - if ((mDataNetworkController.getUnmeteredOverrideNetworkTypes().contains(networkType) - || isNetworkTypeUnmetered(networkType)) != mTempNotMetered) { - mTempNotMetered = !mTempNotMetered; + boolean isTempNotMetered = mDataConfigManager.isNetworkTypeUnmetered( + mTelephonyDisplayInfo, mPhone.getServiceState()) + && (mDataNetworkController.getUnmeteredOverrideNetworkTypes().contains(networkType) + || isNetworkTypeUnmetered(networkType)); + if (isTempNotMetered != mTempNotMetered) { + mTempNotMetered = isTempNotMetered; changed = true; log("updateMeteredAndCongested: mTempNotMetered changed to " + mTempNotMetered); } @@ -2701,6 +2873,10 @@ if (changed) { updateNetworkCapabilities(); } + if (mTempNotMetered && isInternetSupported()) { + // NR NSA and NR have the same network type: NR + mDataCallSessionStats.onUnmeteredUpdate(networkType); + } } /** @@ -2965,8 +3141,6 @@ && mNetworkCapabilities.hasCapability( NetworkCapabilities.NET_CAPABILITY_TRUSTED) && mNetworkCapabilities.hasCapability( - NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED) - && mNetworkCapabilities.hasCapability( NetworkCapabilities.NET_CAPABILITY_NOT_VPN); } @@ -3008,6 +3182,12 @@ /** * Request the data network to handover to the target transport. * + * This is the starting point of initiating IWLAN/cellular handover. It will first call + * {@link DataServiceManager#startHandover(int, Message)} to notify source transport that + * handover is about to start, and then call {@link DataServiceManager#setupDataCall(int, + * DataProfile, boolean, boolean, int, LinkProperties, int, NetworkSliceInfo, TrafficDescriptor, + * boolean, Message)} on target transport to initiate the handover process. + * * @param targetTransport The target transport. * @param retryEntry Data handover retry entry. This would be {@code null} for first time * handover attempt. @@ -3020,12 +3200,16 @@ if (retryEntry != null) retryEntry.setState(DataRetryEntry.RETRY_STATE_CANCELLED); return false; } - sendMessage(obtainMessage(EVENT_START_HANDOVER, targetTransport, 0, retryEntry)); + + // Before we really initiate the handover process on target transport, we need to notify + // source transport that handover is about to start. Handover will be eventually initiated + // in onStartHandover(). + sendMessage(obtainMessage(EVENT_NOTIFY_HANDOVER_STARTED, 0, targetTransport, retryEntry)); return true; } /** - * Called when handover between IWLAN and cellular is needed. + * Called when starting IWLAN/cellular handover process on the target transport. * * @param targetTransport The target transport. * @param retryEntry Data handover retry entry. This would be {@code null} for first time @@ -3058,7 +3242,7 @@ && !mAttachedNetworkRequestList.isEmpty()) { TelephonyNetworkRequest networkRequest = mAttachedNetworkRequestList.get(0); DataProfile dataProfile = mDataNetworkController.getDataProfileManager() - .getDataProfileForNetworkRequest(networkRequest, targetNetworkType); + .getDataProfileForNetworkRequest(networkRequest, targetNetworkType, false); // Some carriers have different profiles between cellular and IWLAN. We need to // dynamically switch profile, but only when those profiles have same APN name. if (dataProfile != null && dataProfile.getApnSetting() != null @@ -3080,7 +3264,6 @@ DataService.REQUEST_REASON_HANDOVER, mLinkProperties, mPduSessionId, mNetworkSliceInfo, mHandoverDataProfile.getTrafficDescriptor(), true, obtainMessage(EVENT_HANDOVER_RESPONSE, retryEntry)); - transitionTo(mHandoverState); } /** @@ -3096,7 +3279,7 @@ logl("onHandoverResponse: resultCode=" + DataServiceCallback.resultCodeToString(resultCode) + ", response=" + response); mFailCause = getFailCauseFromDataCallResponse(resultCode, response); - validateDataCallResponse(response); + validateDataCallResponse(response, false /*isSetupResponse*/); if (mFailCause == DataFailCause.NONE) { // Handover succeeded. @@ -3123,15 +3306,23 @@ () -> mDataNetworkCallback.onHandoverSucceeded(DataNetwork.this)); } else { // Handover failed. + + // Notify source transport that handover failed on target transport so that PDU session + // id can be released if it is preserved for handover. + mDataServiceManagers.get(mTransport).cancelHandover(mCid.get(mTransport), + obtainMessage(EVENT_NOTIFY_HANDOVER_CANCELLED_RESPONSE)); + long retry = response != null ? response.getRetryDurationMillis() : DataCallResponse.RETRY_DURATION_UNDEFINED; + // If the handover mode is unspecified, default to HANDOVER_FAILURE_MODE_UNKNOWN, + // which will retry handover if retry rules are defined. int handoverFailureMode = response != null ? response.getHandoverFailureMode() - : DataCallResponse.HANDOVER_FAILURE_MODE_LEGACY; + : DataCallResponse.HANDOVER_FAILURE_MODE_UNKNOWN; if (retryEntry != null) retryEntry.setState(DataRetryEntry.RETRY_STATE_FAILED); mDataNetworkCallback.invokeFromExecutor( () -> mDataNetworkCallback.onHandoverFailed(DataNetwork.this, mFailCause, retry, handoverFailureMode)); - mDataCallSessionStats.onHandoverFailure(mFailCause); + trackHandoverFailure(response != null ? response.getCause() : mFailCause); } // No matter handover succeeded or not, transit back to connected state. @@ -3139,7 +3330,21 @@ } /** - * Called when PCO data changes. + * Called when handover failed. Record the source and target RAT{@link NetworkType} and the + * failure cause {@link android.telephony.DataFailCause}. + * + * @param cause The fail cause. + */ + private void trackHandoverFailure(int cause) { + int sourceRat = getDataNetworkType(); + int targetTransport = DataUtils.getTargetTransport(mTransport); + int targetRat = getDataNetworkType(targetTransport); + + mDataCallSessionStats.onHandoverFailure(cause, sourceRat, targetRat); + } + + /** + * Called when receiving PCO (Protocol Configuration Options) data from the cellular network. * * @param pcoData The PCO data. */ @@ -3179,8 +3384,14 @@ } /** - * @return The PCO data map of the network. The key is the PCO id, the value is the PCO data. - * An empty map if PCO data is not available (or when the network is on IWLAN). + * @return The last known data network type of the data network. + */ + public @NetworkType int getLastKnownDataNetworkType() { + return mLastKnownDataNetworkType; + } + + /** + * @return The PCO data received from the network. */ public @NonNull Map<Integer, PcoData> getPcoData() { if (mTransport == AccessNetworkConstants.TRANSPORT_TYPE_WLAN @@ -3205,6 +3416,17 @@ } /** + * Check if any of the attached request has the specified network capability. + * + * @param netCapability The network capability to check. + * @return {@code true} if at least one network request has specified network capability. + */ + public boolean hasNetworkCapabilityInNetworkRequests(@NetCapability int netCapability) { + return mAttachedNetworkRequestList.stream().anyMatch( + request -> request.hasCapability(netCapability)); + } + + /** * Convert the data tear down reason to string. * * @param reason Data deactivation reason. @@ -3212,6 +3434,8 @@ */ public static @NonNull String tearDownReasonToString(@TearDownReason int reason) { switch (reason) { + case TEAR_DOWN_REASON_NONE: + return "NONE"; case TEAR_DOWN_REASON_CONNECTIVITY_SERVICE_UNWANTED: return "CONNECTIVITY_SERVICE_UNWANTED"; case TEAR_DOWN_REASON_SIM_REMOVAL: @@ -3289,6 +3513,8 @@ return "EVENT_ATTACH_NETWORK_REQUEST"; case EVENT_DETACH_NETWORK_REQUEST: return "EVENT_DETACH_NETWORK_REQUEST"; + case EVENT_RADIO_NOT_AVAILABLE: + return "EVENT_RADIO_NOT_AVAILABLE"; case EVENT_ALLOCATE_PDU_SESSION_ID_RESPONSE: return "EVENT_ALLOCATE_PDU_SESSION_ID_RESPONSE"; case EVENT_SETUP_DATA_NETWORK_RESPONSE: @@ -3305,8 +3531,6 @@ return "EVENT_BANDWIDTH_ESTIMATE_FROM_MODEM_CHANGED"; case EVENT_DISPLAY_INFO_CHANGED: return "EVENT_DISPLAY_INFO_CHANGED"; - case EVENT_START_HANDOVER: - return "EVENT_START_HANDOVER"; case EVENT_HANDOVER_RESPONSE: return "EVENT_HANDOVER_RESPONSE"; case EVENT_SUBSCRIPTION_PLAN_OVERRIDE: @@ -3327,6 +3551,12 @@ return "EVENT_VOICE_CALL_ENDED"; case EVENT_CSS_INDICATOR_CHANGED: return "EVENT_CSS_INDICATOR_CHANGED"; + case EVENT_NOTIFY_HANDOVER_STARTED: + return "EVENT_NOTIFY_HANDOVER_STARTED"; + case EVENT_NOTIFY_HANDOVER_STARTED_RESPONSE: + return "EVENT_NOTIFY_HANDOVER_STARTED_RESPONSE"; + case EVENT_NOTIFY_HANDOVER_CANCELLED_RESPONSE: + return "EVENT_NOTIFY_HANDOVER_CANCELLED_RESPONSE"; default: return "Unknown(" + event + ")"; } @@ -3412,6 +3642,8 @@ pw.increaseIndent(); pw.println("mSubId=" + mSubId); pw.println("mTransport=" + AccessNetworkConstants.transportTypeToString(mTransport)); + pw.println("mLastKnownDataNetworkType=" + TelephonyManager + .getNetworkTypeName(mLastKnownDataNetworkType)); pw.println("WWAN cid=" + mCid.get(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)); pw.println("WLAN cid=" + mCid.get(AccessNetworkConstants.TRANSPORT_TYPE_WLAN)); pw.println("mNetworkScore=" + mNetworkScore);
diff --git a/src/java/com/android/internal/telephony/data/DataNetworkController.java b/src/java/com/android/internal/telephony/data/DataNetworkController.java index 4b02428..127400b 100644 --- a/src/java/com/android/internal/telephony/data/DataNetworkController.java +++ b/src/java/com/android/internal/telephony/data/DataNetworkController.java
@@ -16,7 +16,6 @@ package com.android.internal.telephony.data; - import android.annotation.CallbackExecutor; import android.annotation.IntDef; import android.annotation.NonNull; @@ -35,6 +34,7 @@ import android.os.Handler; import android.os.Looper; import android.os.Message; +import android.telecom.TelecomManager; import android.telephony.AccessNetworkConstants; import android.telephony.AccessNetworkConstants.AccessNetworkType; import android.telephony.AccessNetworkConstants.RadioAccessNetworkType; @@ -83,9 +83,9 @@ import com.android.internal.telephony.Phone; import com.android.internal.telephony.PhoneConstants; import com.android.internal.telephony.SlidingWindowEventCounter; -import com.android.internal.telephony.SubscriptionInfoUpdater; import com.android.internal.telephony.TelephonyComponentFactory; import com.android.internal.telephony.data.AccessNetworksManager.AccessNetworksManagerCallback; +import com.android.internal.telephony.data.DataConfigManager.DataConfigManagerCallback; import com.android.internal.telephony.data.DataEvaluation.DataAllowedReason; import com.android.internal.telephony.data.DataEvaluation.DataDisallowedReason; import com.android.internal.telephony.data.DataEvaluation.DataEvaluationReason; @@ -111,6 +111,7 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.Comparator; import java.util.Iterator; import java.util.LinkedList; import java.util.List; @@ -134,15 +135,15 @@ public class DataNetworkController extends Handler { private static final boolean VDBG = false; - /** Event for data config updated. */ - private static final int EVENT_DATA_CONFIG_UPDATED = 1; - /** Event for adding a network request. */ private static final int EVENT_ADD_NETWORK_REQUEST = 2; /** Event for removing a network request. */ private static final int EVENT_REMOVE_NETWORK_REQUEST = 3; + /** Event for SRVCC state changed. */ + private static final int EVENT_SRVCC_STATE_CHANGED = 4; + /** Re-evaluate all unsatisfied network requests. */ private static final int EVENT_REEVALUATE_UNSATISFIED_NETWORK_REQUESTS = 5; @@ -222,6 +223,13 @@ private static final long REEVALUATE_UNSATISFIED_NETWORK_REQUESTS_TAC_CHANGED_DELAY_MILLIS = TimeUnit.MILLISECONDS.toMillis(100); + /** + * The delay in milliseconds to re-evaluate unsatisfied network requests after network request + * detached. + */ + private static final long REEVALUATE_UNSATISFIED_NETWORK_REQUESTS_AFTER_DETACHED_DELAY_MILLIS = + TimeUnit.SECONDS.toMillis(1); + private final Phone mPhone; private final String mLogTag; private final LocalLog mLocalLog = new LocalLog(128); @@ -233,6 +241,7 @@ private final @NonNull AccessNetworksManager mAccessNetworksManager; private final @NonNull DataRetryManager mDataRetryManager; private final @NonNull ImsManager mImsManager; + private final @NonNull TelecomManager mTelecomManager; private final @NonNull NetworkPolicyManager mNetworkPolicyManager; private final @NonNull SparseArray<DataServiceManager> mDataServiceManagers = new SparseArray<>(); @@ -299,12 +308,15 @@ private final @NonNull Set<DataNetworkControllerCallback> mDataNetworkControllerCallbacks = new ArraySet<>(); - /** Indicates if packet switch data is restricted by the network. */ + /** Indicates if packet switch data is restricted by the cellular network. */ private boolean mPsRestricted = false; /** Indicates if NR advanced is allowed by PCO. */ private boolean mNrAdvancedCapableByPco = false; + /** Indicates if srvcc is going on. */ + private boolean mIsSrvccHandoverInProcess = false; + /** * Indicates if the data services are bound. Key if the transport type, and value is the boolean * indicating service is bound or not. @@ -463,6 +475,7 @@ } return true; } + /** * Get the first network request that contains all the provided network capabilities. * @@ -548,10 +561,19 @@ /** * Called when internet data network is connected. * - * @param dataProfiles The data profiles of the connected internet data network. It should - * be only one in most of the cases. + * @param internetNetworks The connected internet data network. It should be only one in + * most of the cases. */ - public void onInternetDataNetworkConnected(@NonNull List<DataProfile> dataProfiles) {} + public void onInternetDataNetworkConnected(@NonNull List<DataNetwork> internetNetworks) {} + + /** + * Called when data network is connected. + * + * @param transport Transport for the connected network. + * @param dataProfile The data profile of the connected data network. + */ + public void onDataNetworkConnected(@TransportType int transport, + @NonNull DataProfile dataProfile) {} /** Called when internet data network is disconnected. */ public void onInternetDataNetworkDisconnected() {} @@ -713,9 +735,9 @@ + "\"" + ruleString + "\""); } - if (source.contains(AccessNetworkType.UNKNOWN)) { - throw new IllegalArgumentException("Source access networks contains unknown. " - + "\"" + ruleString + "\""); + if (source.contains(AccessNetworkType.UNKNOWN) && type != RULE_TYPE_DISALLOWED) { + throw new IllegalArgumentException("Unknown access network can be only specified in" + + " the disallowed rule. \"" + ruleString + "\""); } if (target.contains(AccessNetworkType.UNKNOWN)) { @@ -772,13 +794,10 @@ log("DataNetworkController created."); mAccessNetworksManager = phone.getAccessNetworksManager(); - mDataServiceManagers.put(AccessNetworkConstants.TRANSPORT_TYPE_WWAN, - new DataServiceManager(mPhone, looper, AccessNetworkConstants.TRANSPORT_TYPE_WWAN)); - if (!mAccessNetworksManager.isInLegacyMode()) { - mDataServiceManagers.put(AccessNetworkConstants.TRANSPORT_TYPE_WLAN, - new DataServiceManager(mPhone, looper, - AccessNetworkConstants.TRANSPORT_TYPE_WLAN)); + for (int transport : mAccessNetworksManager.getAvailableTransports()) { + mDataServiceManagers.put(transport, new DataServiceManager(mPhone, looper, transport)); } + mDataConfigManager = new DataConfigManager(mPhone, looper); // ========== Anomaly counters ========== @@ -884,28 +903,12 @@ public void onDataNetworkHandoverRetryStopped( @NonNull DataNetwork dataNetwork) { Objects.requireNonNull(dataNetwork); - int preferredTransport = mAccessNetworksManager - .getPreferredTransportByNetworkCapability( - dataNetwork.getApnTypeNetworkCapability()); - if (dataNetwork.getTransport() == preferredTransport) { - log("onDataNetworkHandoverRetryStopped: " + dataNetwork + " is already " - + "on the preferred transport " - + AccessNetworkConstants.transportTypeToString( - preferredTransport)); - return; - } - if (dataNetwork.shouldDelayImsTearDown()) { - log("onDataNetworkHandoverRetryStopped: Delay IMS tear down until call " - + "ends. " + dataNetwork); - return; - } - - tearDownGracefully(dataNetwork, - DataNetwork.TEAR_DOWN_REASON_HANDOVER_FAILED); + DataNetworkController.this.onDataNetworkHandoverRetryStopped(dataNetwork); } }); mImsManager = mPhone.getContext().getSystemService(ImsManager.class); mNetworkPolicyManager = mPhone.getContext().getSystemService(NetworkPolicyManager.class); + mTelecomManager = mPhone.getContext().getSystemService(TelecomManager.class); // Use the raw one from ServiceStateTracker instead of the combined one from // mPhone.getServiceState(). @@ -961,8 +964,17 @@ }); mPhone.getServiceStateTracker().registerForServiceStateChanged(this, - EVENT_SERVICE_STATE_CHANGED); - mDataConfigManager.registerForConfigUpdate(this, EVENT_DATA_CONFIG_UPDATED); + EVENT_SERVICE_STATE_CHANGED, null); + mDataConfigManager.registerCallback(new DataConfigManagerCallback(this::post) { + @Override + public void onCarrierConfigChanged() { + DataNetworkController.this.onCarrierConfigUpdated(); + } + @Override + public void onDeviceConfigChanged() { + DataNetworkController.this.onDeviceConfigUpdated(); + } + }); mPhone.getServiceStateTracker().registerForPsRestrictedEnabled(this, EVENT_PS_RESTRICT_ENABLED, null); mPhone.getServiceStateTracker().registerForPsRestrictedDisabled(this, @@ -972,12 +984,10 @@ mDataServiceManagers.get(AccessNetworkConstants.TRANSPORT_TYPE_WWAN) .registerForServiceBindingChanged(this, EVENT_DATA_SERVICE_BINDING_CHANGED); - if (!mAccessNetworksManager.isInLegacyMode()) { - mPhone.getServiceStateTracker().registerForServiceStateChanged(this, - EVENT_SERVICE_STATE_CHANGED); - mDataServiceManagers.get(AccessNetworkConstants.TRANSPORT_TYPE_WLAN) - .registerForServiceBindingChanged(this, EVENT_DATA_SERVICE_BINDING_CHANGED); - } + mPhone.getServiceStateTracker().registerForServiceStateChanged(this, + EVENT_SERVICE_STATE_CHANGED, null); + mDataServiceManagers.get(AccessNetworkConstants.TRANSPORT_TYPE_WLAN) + .registerForServiceBindingChanged(this, EVENT_DATA_SERVICE_BINDING_CHANGED); mPhone.getContext().getSystemService(TelephonyRegistryManager.class) .addOnSubscriptionsChangedListener(new OnSubscriptionsChangedListener() { @@ -999,6 +1009,7 @@ this, EVENT_VOICE_CALL_ENDED, null); } mPhone.mCi.registerForSlicingConfigChanged(this, EVENT_SLICE_CONFIG_CHANGED, null); + mPhone.mCi.registerForSrvccStateChanged(this, EVENT_SRVCC_STATE_CHANGED, null); mPhone.getLinkBandwidthEstimator().registerCallback( new LinkBandwidthEstimatorCallback(this::post) { @@ -1012,10 +1023,8 @@ @Override public void handleMessage(@NonNull Message msg) { + AsyncResult ar; switch (msg.what) { - case EVENT_DATA_CONFIG_UPDATED: - onDataConfigUpdated(); - break; case EVENT_REGISTER_ALL_EVENTS: onRegisterAllEvents(); break; @@ -1049,6 +1058,12 @@ sendMessage(obtainMessage(EVENT_REEVALUATE_UNSATISFIED_NETWORK_REQUESTS, DataEvaluationReason.SLICE_CONFIG_CHANGED)); break; + case EVENT_SRVCC_STATE_CHANGED: + ar = (AsyncResult) msg.obj; + if (ar.exception == null) { + onSrvccStateChanged((int[]) ar.result); + } + break; case EVENT_PS_RESTRICT_ENABLED: mPsRestricted = true; break; @@ -1065,7 +1080,7 @@ REEVALUATE_UNSATISFIED_NETWORK_REQUESTS_TAC_CHANGED_DELAY_MILLIS); break; case EVENT_DATA_SERVICE_BINDING_CHANGED: - AsyncResult ar = (AsyncResult) msg.obj; + ar = (AsyncResult) msg.obj; int transport = (int) ar.userObj; boolean bound = (boolean) ar.result; onDataServiceBindingChanged(transport, bound); @@ -1078,7 +1093,13 @@ onTearDownAllDataNetworks(msg.arg1); break; case EVENT_REGISTER_DATA_NETWORK_CONTROLLER_CALLBACK: - mDataNetworkControllerCallbacks.add((DataNetworkControllerCallback) msg.obj); + DataNetworkControllerCallback callback = (DataNetworkControllerCallback) msg.obj; + mDataNetworkControllerCallbacks.add(callback); + // Notify upon registering if no data networks currently exist. + if (mDataNetworkList.isEmpty()) { + callback.invokeFromExecutor( + () -> callback.onAnyDataNetworkExistingChanged(false)); + } break; case EVENT_UNREGISTER_DATA_NETWORK_CONTROLLER_CALLBACK: mDataNetworkControllerCallbacks.remove((DataNetworkControllerCallback) msg.obj); @@ -1106,8 +1127,8 @@ log("Subscription plans changed: " + Arrays.toString(plans)); mSubscriptionPlans.clear(); mSubscriptionPlans.addAll(Arrays.asList(plans)); - mDataNetworkControllerCallbacks.forEach(callback -> callback.invokeFromExecutor( - () -> callback.onSubscriptionPlanOverride())); + mDataNetworkControllerCallbacks.forEach(cb -> cb.invokeFromExecutor( + () -> cb.onSubscriptionPlanOverride())); break; case EVENT_SUBSCRIPTION_OVERRIDE: int overrideMask = msg.arg1; @@ -1126,8 +1147,8 @@ mUnmeteredOverrideNetworkTypes.remove(networkType); } } - mDataNetworkControllerCallbacks.forEach(callback -> callback.invokeFromExecutor( - () -> callback.onSubscriptionPlanOverride())); + mDataNetworkControllerCallbacks.forEach(cb -> cb.invokeFromExecutor( + () -> cb.onSubscriptionPlanOverride())); } else if (overrideMask == NetworkPolicyManager.SUBSCRIPTION_OVERRIDE_CONGESTED) { log("Congested subscription override: override=" + override + ", networkTypes=" + Arrays.stream(networkTypes) @@ -1140,8 +1161,8 @@ mCongestedOverrideNetworkTypes.remove(networkType); } } - mDataNetworkControllerCallbacks.forEach(callback -> callback.invokeFromExecutor( - () -> callback.onSubscriptionPlanOverride())); + mDataNetworkControllerCallbacks.forEach(cb -> cb.invokeFromExecutor( + () -> cb.onSubscriptionPlanOverride())); } else { loge("Unknown override mask: " + overrideMask); } @@ -1300,7 +1321,7 @@ if (nriRegState == NetworkRegistrationInfo.REGISTRATION_STATE_HOME || nriRegState == NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING) return true; - // If data is OOS on the non-DDS, + // If data is OOS as this device slot is not modem preferred(i.e. not active for internet), // attempt to attach PS on 2G/3G if CS connection is available. return ss.getVoiceRegState() == ServiceState.STATE_IN_SERVICE && mPhone.getPhoneId() != PhoneSwitcher.getInstance().getPreferredDataPhoneId() @@ -1329,6 +1350,17 @@ } /** + * @param capabilities The Network Capabilities to be checked. + * @return {@code true} if the capabilities contain any capability that's exempt from the single + * PDN rule. + */ + private boolean hasCapabilityExemptsFromSinglePdnRule(@NetCapability int[] capabilities) { + Set<Integer> exemptCapabilities = + mDataConfigManager.getCapabilitiesExemptFromSingleDataNetwork(); + return Arrays.stream(capabilities).anyMatch(exemptCapabilities::contains); + } + + /** * Evaluate if telephony frameworks would allow data setup for internet in current environment. * * @return {@code true} if the environment is allowed for internet data. {@code false} if not @@ -1343,13 +1375,31 @@ .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) .build(), mPhone); + // If one of the existing networks can satisfy the internet request, then internet is + // allowed. + if (mDataNetworkList.stream().anyMatch(dataNetwork -> internetRequest.canBeSatisfiedBy( + dataNetwork.getNetworkCapabilities()))) { + return true; + } + + // If no existing network can satisfy the request, then check if we can possibly setup + // the internet network. + DataEvaluation evaluation = evaluateNetworkRequest(internetRequest, DataEvaluationReason.EXTERNAL_QUERY); + if (evaluation.containsOnly(DataDisallowedReason.ONLY_ALLOWED_SINGLE_NETWORK)) { + // If the only failed reason is only single network allowed, then check if the request + // can trump the current network. + return internetRequest.getPriority() > mDataNetworkList.stream() + .map(DataNetwork::getPriority) + .max(Comparator.comparing(Integer::valueOf)) + .orElse(0); + } return !evaluation.containsDisallowedReasons(); } /** - * @return {@code true} internet is unmetered. + * @return {@code true} if internet is unmetered. */ public boolean isInternetUnmetered() { return mDataNetworkList.stream() @@ -1362,6 +1412,17 @@ } /** + * @return {@code true} if all data networks are disconnected. + */ + public boolean areAllDataDisconnected() { + if (!mDataNetworkList.isEmpty()) { + log("areAllDataDisconnected false due to: " + mDataNetworkList.stream() + .map(DataNetwork::name).collect(Collectors.joining(", "))); + } + return mDataNetworkList.isEmpty(); + } + + /** * @return List of the reasons why internet data is not allowed. An empty list if internet * is allowed. */ @@ -1394,7 +1455,7 @@ if (networkRequest.hasCapability(NetworkCapabilities.NET_CAPABILITY_EIMS)) { evaluation.addDataAllowedReason(DataAllowedReason.EMERGENCY_REQUEST); evaluation.setCandidateDataProfile(mDataProfileManager.getDataProfileForNetworkRequest( - networkRequest, getDataNetworkType(transport))); + networkRequest, getDataNetworkType(transport), true)); networkRequest.setEvaluation(evaluation); log(evaluation.toString()); return evaluation; @@ -1446,8 +1507,8 @@ evaluation.addDataDisallowedReason(DataDisallowedReason.ROAMING_DISABLED); } - // Check if data is restricted by the network. - if (mPsRestricted) { + // Check if data is restricted by the cellular network. + if (mPsRestricted && transport == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) { evaluation.addDataDisallowedReason(DataDisallowedReason.DATA_RESTRICTED_BY_NETWORK); } @@ -1481,13 +1542,12 @@ } // Check if only one data network is allowed. - // Note any IMS network is ignored for the single-connection rule. if (isOnlySingleDataNetworkAllowed(transport) - && !networkRequest.hasCapability(NetworkCapabilities.NET_CAPABILITY_IMS)) { - // if exists non-IMS network + && !hasCapabilityExemptsFromSinglePdnRule(networkRequest.getCapabilities())) { + // if exists not-exempt network. if (mDataNetworkList.stream() - .anyMatch(dataNetwork -> !dataNetwork.getNetworkCapabilities() - .hasCapability(NetworkCapabilities.NET_CAPABILITY_IMS))) { + .anyMatch(dataNetwork -> !hasCapabilityExemptsFromSinglePdnRule( + dataNetwork.getNetworkCapabilities().getCapabilities()))) { evaluation.addDataDisallowedReason( DataDisallowedReason.ONLY_ALLOWED_SINGLE_NETWORK); } @@ -1508,20 +1568,23 @@ if (!mDataSettingsManager.isDataEnabled() && networkRequest.hasCapability(NetworkCapabilities.NET_CAPABILITY_MMS) - && mDataSettingsManager.isMmsAlwaysAllowed()) { + && mDataSettingsManager.isMobileDataPolicyEnabled(TelephonyManager + .MOBILE_DATA_POLICY_MMS_ALWAYS_ALLOWED)) { // We reach here when data is disabled, but MMS always-allowed is enabled. // (Note that isDataEnabled(ApnSetting.TYPE_MMS) returns true in this case, so it // would not generate any soft disallowed reason. We need to explicitly handle it.) evaluation.addDataAllowedReason(DataAllowedReason.MMS_REQUEST); } } else if (!evaluation.containsHardDisallowedReasons()) { - if ((mPhone.isInEmergencyCall() || mPhone.isInEcm()) + if ((mTelecomManager.isInEmergencyCall() || mPhone.isInEcm()) && networkRequest.hasCapability(NetworkCapabilities.NET_CAPABILITY_SUPL)) { // Check if it's SUPL during emergency call. evaluation.addDataAllowedReason(DataAllowedReason.EMERGENCY_SUPL); } else if (!networkRequest.hasCapability( - NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)) { - // Check if request is restricted. + NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED) && !networkRequest + .hasCapability(NetworkCapabilities.NET_CAPABILITY_DUN)) { + // Check if request is restricted and not for tethering, which always comes with + // a restricted network request. evaluation.addDataAllowedReason(DataAllowedReason.RESTRICTED_REQUEST); } else if (transport == AccessNetworkConstants.TRANSPORT_TYPE_WLAN) { // Check if request is unmetered (WiFi or unmetered APN). @@ -1534,17 +1597,25 @@ } // Check if there is any compatible data profile + int networkType = getDataNetworkType(transport); + if (networkType == TelephonyManager.NETWORK_TYPE_UNKNOWN + && transport == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) { + // reach here when data is OOS but serviceStateAllowsPSAttach == true, so we adopt the + // voice RAT to select data profile + networkType = mServiceState.getVoiceNetworkType(); + } DataProfile dataProfile = mDataProfileManager - .getDataProfileForNetworkRequest(networkRequest, getDataNetworkType(transport)); + .getDataProfileForNetworkRequest(networkRequest, networkType, + // If the evaluation is due to environmental changes, then we should ignore + // the permanent failure reached earlier. + reason.isConditionBased()); if (dataProfile == null) { evaluation.addDataDisallowedReason(DataDisallowedReason.NO_SUITABLE_DATA_PROFILE); - } else if (reason == DataEvaluationReason.NEW_REQUEST - && (mDataRetryManager.isAnySetupRetryScheduled(dataProfile, transport) - || mDataRetryManager.isSimilarNetworkRequestRetryScheduled( - networkRequest, transport))) { - // If this is a new request, check if there is any retry already scheduled. For all - // other evaluation reasons, since they are all condition changes, so if there is any - // retry scheduled, we still want to go ahead and setup the data network. + } else if (// Check for new requests if we already self-scheduled(as opposed to modem + // demanded) retry for similar requests. + reason == DataEvaluationReason.NEW_REQUEST + && mDataRetryManager.isSimilarNetworkRequestRetryScheduled( + networkRequest, transport)) { evaluation.addDataDisallowedReason(DataDisallowedReason.RETRY_SCHEDULED); } else if (mDataRetryManager.isDataProfileThrottled(dataProfile, transport)) { evaluation.addDataDisallowedReason(DataDisallowedReason.DATA_THROTTLED); @@ -1648,16 +1719,18 @@ } // Check if there are other network that has higher priority, and only single data network - // is allowed. Note IMS network is exempt from the single-connection rule. + // is allowed. if (isOnlySingleDataNetworkAllowed(dataNetwork.getTransport()) - && !dataNetwork.getNetworkCapabilities() - .hasCapability(NetworkCapabilities.NET_CAPABILITY_IMS)) { + && !hasCapabilityExemptsFromSinglePdnRule( + dataNetwork.getNetworkCapabilities().getCapabilities())) { // If there is network request that has higher priority than this data network, then // tear down the network, regardless that network request is satisfied or not. if (mAllNetworkRequestList.stream() .filter(request -> dataNetwork.getTransport() == mAccessNetworksManager.getPreferredTransportByNetworkCapability( request.getApnTypeNetworkCapability())) + .filter(request + -> !hasCapabilityExemptsFromSinglePdnRule(request.getCapabilities())) .anyMatch(request -> request.getPriority() > dataNetwork.getPriority())) { evaluation.addDataDisallowedReason( DataDisallowedReason.ONLY_ALLOWED_SINGLE_NETWORK); @@ -1667,13 +1740,20 @@ } } - // If the data network is IMS that supports voice call, and has MMTEL request (client - // specified VoPS is required.) - if (dataNetwork.getAttachedNetworkRequestList().get( - new int[]{NetworkCapabilities.NET_CAPABILITY_MMTEL}) != null) { - // When reaching here, it means the network supports MMTEL, and also has MMTEL request - // attached to it. - if (!dataNetwork.shouldDelayImsTearDown()) { + boolean vopsIsRequired = dataNetwork.hasNetworkCapabilityInNetworkRequests( + NetworkCapabilities.NET_CAPABILITY_MMTEL); + + // Check an active call relying on this network and config for "delay tear down due to vops + // call" is enabled. + if (dataNetwork.shouldDelayImsTearDownDueToInCall()) { + if (vopsIsRequired) { + log("Ignored VoPS check due to delay IMS tear down until call ends."); + } + } else { + // Reach here means we should ignore active calls even if there are any. + + // Check if VoPS requirement is met. + if (vopsIsRequired) { if (dataNetwork.getTransport() == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) { NetworkRegistrationInfo nri = mServiceState.getNetworkRegistrationInfo( NetworkRegistrationInfo.DOMAIN_PS, @@ -1681,22 +1761,27 @@ if (nri != null) { DataSpecificRegistrationInfo dsri = nri.getDataSpecificInfo(); if (dsri != null && dsri.getVopsSupportInfo() != null - && !dsri.getVopsSupportInfo().isVopsSupported()) { + && !dsri.getVopsSupportInfo().isVopsSupported() + && !mDataConfigManager.shouldKeepNetworkUpInNonVops()) { evaluation.addDataDisallowedReason( DataDisallowedReason.VOPS_NOT_SUPPORTED); } } } - } else { - log("Ignored VoPS check due to delay IMS tear down until call ends."); + } + + // Check if handover retry stopped and preferred transport still not matched. + int preferredTransport = mAccessNetworksManager + .getPreferredTransportByNetworkCapability( + dataNetwork.getApnTypeNetworkCapability()); + if (preferredTransport != dataNetwork.getTransport() + && mDataRetryManager.isDataNetworkHandoverRetryStopped(dataNetwork)) { + evaluation.addDataDisallowedReason(DataDisallowedReason.HANDOVER_RETRY_STOPPED); } } // Check if data is disabled - boolean dataDisabled = false; - if (!mDataSettingsManager.isDataEnabled()) { - dataDisabled = true; - } + boolean dataDisabled = !mDataSettingsManager.isDataEnabled(); // Check if data roaming is disabled if (mServiceState.getDataRoaming() && !mDataSettingsManager.isDataRoamingEnabled()) { @@ -1711,10 +1796,9 @@ DataProfile dataProfile = dataNetwork.getDataProfile(); if (dataProfile.getApnSetting() != null) { // Check if data is disabled for the APN type - dataDisabled = !mDataSettingsManager.isDataEnabled(DataUtils - .networkCapabilityToApnType(DataUtils - .getHighestPriorityNetworkCapabilityFromDataProfile( - mDataConfigManager, dataProfile))); + dataDisabled = !mDataSettingsManager.isDataEnabled( + DataUtils.networkCapabilityToApnType( + dataNetwork.getApnTypeNetworkCapability())); // Sometimes network temporarily OOS and network type becomes UNKNOWN. We don't // tear down network in that case. @@ -1722,9 +1806,11 @@ && !dataProfile.getApnSetting().canSupportLingeringNetworkType(networkType)) { log("networkType=" + TelephonyManager.getNetworkTypeName(networkType) + ", networkTypeBitmask=" - + dataProfile.getApnSetting().getNetworkTypeBitmask() + + TelephonyManager.convertNetworkTypeBitmaskToString( + dataProfile.getApnSetting().getNetworkTypeBitmask()) + ", lingeringNetworkTypeBitmask=" - + dataProfile.getApnSetting().getLingeringNetworkTypeBitmask()); + + TelephonyManager.convertNetworkTypeBitmaskToString( + dataProfile.getApnSetting().getLingeringNetworkTypeBitmask())); evaluation.addDataDisallowedReason( DataDisallowedReason.DATA_NETWORK_TYPE_NOT_ALLOWED); } @@ -1734,20 +1820,18 @@ evaluation.addDataDisallowedReason(DataDisallowedReason.DATA_DISABLED); } - // Check if the data profile is still valid, sometimes the users can remove it from the APN - // editor. We use very loose check here because APN id can change after APN reset to - // default - if (mDataProfileManager.getDataProfile( - dataProfile.getApnSetting() != null - ? dataProfile.getApnSetting().getApnName() : null, - dataProfile.getTrafficDescriptor()) == null) { + // Check if the data profile is still compatible, sometimes the users can remove it from the + // APN editor. If some of the important fields are changed in APN settings, we need to + // tear down the network. Note traffic descriptor from the data profile will not be checked. + if (!mDataProfileManager.isDataProfileCompatible(dataProfile)) { evaluation.addDataDisallowedReason(DataDisallowedReason.DATA_PROFILE_INVALID); } // If users switch preferred profile in APN editor, we need to tear down network. if (dataNetwork.isInternetSupported() && !mDataProfileManager.isDataProfilePreferred(dataProfile) - && mDataProfileManager.isAnyPreferredDataProfileExisting()) { + && mDataProfileManager.canPreferredDataProfileSatisfy( + dataNetwork.getAttachedNetworkRequestList())) { evaluation.addDataDisallowedReason(DataDisallowedReason.DATA_PROFILE_NOT_PREFERRED); } @@ -1759,12 +1843,16 @@ // If there are reasons we should tear down the network, check if those are hard reasons // or soft reasons. In some scenarios, we can make exceptions if they are soft // disallowed reasons. - if ((mPhone.isInEmergencyCall() || mPhone.isInEcm()) && dataNetwork.isEmergencySupl()) { + if ((mTelecomManager.isInEmergencyCall() || mPhone.isInEcm()) + && dataNetwork.isEmergencySupl()) { // Check if it's SUPL during emergency call. evaluation.addDataAllowedReason(DataAllowedReason.EMERGENCY_SUPL); } else if (!dataNetwork.getNetworkCapabilities().hasCapability( - NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)) { - // Check if request is restricted + NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED) + && !dataNetwork.hasNetworkCapabilityInNetworkRequests( + NetworkCapabilities.NET_CAPABILITY_DUN)) { + // Check if request is restricted and there are no DUN network requests attached to + // the network. evaluation.addDataAllowedReason(DataAllowedReason.RESTRICTED_REQUEST); } else if (dataNetwork.getTransport() == AccessNetworkConstants.TRANSPORT_TYPE_WLAN) { // Check if request is unmetered (WiFi or unmetered APN) @@ -1779,7 +1867,16 @@ } } - log("Evaluated " + dataNetwork + ", " + evaluation.toString()); + // Check if we allow additional lingering for active VoPS call network if + // a. this network is SRVCC handover in progress + // or b. "delay tear down due to active VoPS call" is enabled + boolean isInSrvcc = vopsIsRequired && mIsSrvccHandoverInProcess; + if (evaluation.containsOnly(DataDisallowedReason.DATA_NETWORK_TYPE_NOT_ALLOWED) + && (dataNetwork.shouldDelayImsTearDownDueToInCall() || isInSrvcc)) { + evaluation.addDataAllowedReason(DataAllowedReason.IN_VOICE_CALL); + } + + log("Evaluated " + dataNetwork + ", " + evaluation); return evaluation; } @@ -1821,11 +1918,6 @@ return dataEvaluation; } - if (mDataRetryManager.isAnyHandoverRetryScheduled(dataNetwork)) { - dataEvaluation.addDataDisallowedReason(DataDisallowedReason.RETRY_SCHEDULED); - return dataEvaluation; - } - // If enhanced handover check is enabled, perform extra checks. if (mDataConfigManager.isEnhancedIwlanHandoverCheckEnabled()) { int targetTransport = DataUtils.getTargetTransport(dataNetwork.getTransport()); @@ -1845,7 +1937,8 @@ DataSpecificRegistrationInfo dsri = nri.getDataSpecificInfo(); // Check if the network is non-VoPS. if (dsri != null && dsri.getVopsSupportInfo() != null - && !dsri.getVopsSupportInfo().isVopsSupported()) { + && !dsri.getVopsSupportInfo().isVopsSupported() + && !mDataConfigManager.shouldKeepNetworkUpInNonVops()) { dataEvaluation.addDataDisallowedReason( DataDisallowedReason.VOPS_NOT_SUPPORTED); } @@ -1860,8 +1953,18 @@ if (mDataConfigManager.isIwlanHandoverPolicyEnabled()) { List<HandoverRule> handoverRules = mDataConfigManager.getHandoverRules(); + int sourceNetworkType = getDataNetworkType(dataNetwork.getTransport()); + if (sourceNetworkType == TelephonyManager.NETWORK_TYPE_UNKNOWN) { + // Using the data network type stored in the data network. We + // cache the last known network type in data network controller + // because data network has much shorter life cycle. It can prevent + // the obsolete last known network type cached in data network + // type controller. + sourceNetworkType = dataNetwork.getLastKnownDataNetworkType(); + } int sourceAccessNetwork = DataUtils.networkTypeToAccessNetworkType( - getDataNetworkType(dataNetwork.getTransport())); + sourceNetworkType); + int targetAccessNetwork = DataUtils.networkTypeToAccessNetworkType( getDataNetworkType(DataUtils.getTargetTransport(dataNetwork.getTransport()))); NetworkCapabilities capabilities = dataNetwork.getNetworkCapabilities(); @@ -1965,9 +2068,11 @@ return DataNetwork.TEAR_DOWN_REASON_VOPS_NOT_SUPPORTED; case ONLY_ALLOWED_SINGLE_NETWORK: return DataNetwork.TEAR_DOWN_REASON_ONLY_ALLOWED_SINGLE_NETWORK; + case HANDOVER_RETRY_STOPPED: + return DataNetwork.TEAR_DOWN_REASON_HANDOVER_FAILED; } } - return 0; + return DataNetwork.TEAR_DOWN_REASON_NONE; } /** @@ -2050,7 +2155,8 @@ } if (networkRequest.getAttachedNetwork() != null) { - networkRequest.getAttachedNetwork().detachNetworkRequest(networkRequest); + networkRequest.getAttachedNetwork().detachNetworkRequest( + networkRequest, false /* shouldRetry */); } log("onRemoveNetworkRequest: Removed " + networkRequest); } @@ -2067,16 +2173,19 @@ } /** - * Check if there are existing networks having the same interface name. + * Get data network by interface name. * - * @param interfaceName The interface name to check. - * @return {@code true} if the existing network has the same interface name. + * @param interfaceName The network interface name. + * @return The data network if found. */ - public boolean isNetworkInterfaceExisting(@NonNull String interfaceName) { + @Nullable + public DataNetwork getDataNetworkByInterface(@NonNull String interfaceName) { return mDataNetworkList.stream() .filter(dataNetwork -> !dataNetwork.isDisconnecting()) - .anyMatch(dataNetwork -> interfaceName.equals( - dataNetwork.getLinkProperties().getInterfaceName())); + .filter(dataNetwork -> interfaceName.equals( + dataNetwork.getLinkProperties().getInterfaceName())) + .findFirst() + .orElse(null); } /** @@ -2239,19 +2348,23 @@ } /** - * Called when data config was updated. + * Called when carrier config was updated. */ - private void onDataConfigUpdated() { - log("onDataConfigUpdated: config is " + private void onCarrierConfigUpdated() { + log("onCarrierConfigUpdated: config is " + (mDataConfigManager.isConfigCarrierSpecific() ? "" : "not ") + "carrier specific. mSimState=" - + SubscriptionInfoUpdater.simStateString(mSimState) - + ". DeviceConfig updated."); - - updateAnomalySlidingWindowCounters(); + + TelephonyManager.simStateToString(mSimState)); updateNetworkRequestsPriority(); - sendMessage(obtainMessage(EVENT_REEVALUATE_UNSATISFIED_NETWORK_REQUESTS, - DataEvaluationReason.DATA_CONFIG_CHANGED)); + onReevaluateUnsatisfiedNetworkRequests(DataEvaluationReason.DATA_CONFIG_CHANGED); + } + + /** + * Called when device config was updated. + */ + private void onDeviceConfigUpdated() { + log("onDeviceConfigUpdated: DeviceConfig updated."); + updateAnomalySlidingWindowCounters(); } /** @@ -2326,9 +2439,12 @@ + dataSetupRetryEntry + ", allowed reason=" + allowedReason + ", service state=" + mServiceState); for (DataNetwork dataNetwork : mDataNetworkList) { - if (dataNetwork.getDataProfile().equals(dataProfile)) { + DataProfile currentDataProfile = dataNetwork.getDataProfile(); + if (dataProfile.equals(currentDataProfile) + || mDataProfileManager.areDataProfilesSharingApn( + dataProfile, currentDataProfile)) { log("onSetupDataNetwork: Found existing data network " + dataNetwork - + " has the same data profile."); + + " using the same or a similar data profile."); if (dataSetupRetryEntry != null) { dataSetupRetryEntry.setState(DataRetryEntry.RETRY_STATE_CANCELLED); } @@ -2399,9 +2515,9 @@ @Override public void onDisconnected(@NonNull DataNetwork dataNetwork, - @DataFailureCause int cause) { + @DataFailureCause int cause, @TearDownReason int tearDownReason) { DataNetworkController.this.onDataNetworkDisconnected( - dataNetwork, cause); + dataNetwork, cause, tearDownReason); } @Override @@ -2437,6 +2553,13 @@ public void onTrackNetworkUnwanted(@NonNull DataNetwork dataNetwork) { DataNetworkController.this.onTrackNetworkUnwanted(); } + + @Override + public void onRetryUnsatisfiedNetworkRequest( + @NonNull TelephonyNetworkRequest networkRequest) { + DataNetworkController.this.onRetryUnsatisfiedNetworkRequest( + networkRequest); + } } )); if (!mAnyDataNetworkExisting) { @@ -2460,7 +2583,7 @@ logl("onDataNetworkSetupDataFailed: " + dataNetwork + ", cause=" + DataFailCause.toString(cause) + ", retryDelayMillis=" + retryDelayMillis + "ms."); mDataNetworkList.remove(dataNetwork); - trackSetupDataCallFailure(dataNetwork.getTransport()); + trackSetupDataCallFailure(dataNetwork.getTransport(), cause); if (mAnyDataNetworkExisting && mDataNetworkList.isEmpty()) { mPendingTearDownAllNetworks = false; mAnyDataNetworkExisting = false; @@ -2485,8 +2608,10 @@ * {@link AccessNetworkConstants.TransportType} data service. * * @param transport The transport of the data service. + * @param cause The fail cause */ - private void trackSetupDataCallFailure(@TransportType int transport) { + private void trackSetupDataCallFailure(@TransportType int transport, + @DataFailureCause int cause) { switch (transport) { case AccessNetworkConstants.TRANSPORT_TYPE_WWAN: // Skip when poor signal strength @@ -2494,6 +2619,10 @@ <= CellSignalStrength.SIGNAL_STRENGTH_POOR) { return; } + if (cause == DataFailCause.ERROR_UNSPECIFIED || cause == DataFailCause.UNKNOWN) { + reportAnomaly("RIL set up data call fails: unknown/unspecified error", + "ce7d1465-d8e4-404a-b76f-de2c60bee843"); + } if (mSetupDataCallWwanFailureCounter.addOccurrence()) { reportAnomaly("RIL fails setup data call request " + mSetupDataCallWwanFailureCounter.getFrequencyString(), @@ -2501,6 +2630,10 @@ } break; case AccessNetworkConstants.TRANSPORT_TYPE_WLAN: + if (cause == DataFailCause.ERROR_UNSPECIFIED || cause == DataFailCause.UNKNOWN) { + reportAnomaly("IWLAN set up data call fails: unknown/unspecified error", + "a16fc15c-815b-4908-b8e6-5f3bc7cbc20b"); + } if (mSetupDataCallWlanFailureCounter.addOccurrence()) { reportAnomaly("IWLAN data service fails setup data call request " + mSetupDataCallWlanFailureCounter.getFrequencyString(), @@ -2530,6 +2663,11 @@ */ private void onDataNetworkConnected(@NonNull DataNetwork dataNetwork) { logl("onDataNetworkConnected: " + dataNetwork); + + mDataNetworkControllerCallbacks.forEach(callback -> callback.invokeFromExecutor( + () -> callback.onDataNetworkConnected(dataNetwork.getTransport(), + dataNetwork.getDataProfile()))); + mPreviousConnectedDataNetworkList.add(0, dataNetwork); // Preserve the connected data networks for debugging purposes. if (mPreviousConnectedDataNetworkList.size() > MAX_HISTORICAL_CONNECTED_DATA_NETWORKS) { @@ -2558,11 +2696,22 @@ NetworkRequestList requestList = new NetworkRequestList( dataSetupRetryEntry.networkRequestList); requestList.removeIf(request -> !mAllNetworkRequestList.contains(request)); + // Retrieves the newly added unsatisfied NetworkRequest if all NetworkRequests in the + // DataSetupRetryEntry have already been removed. + if (requestList.isEmpty()) { + List<NetworkRequestList> groupRequestLists = getGroupedUnsatisfiedNetworkRequests(); + dataSetupRetryEntry.networkRequestList.stream() + .filter(request -> groupRequestLists.stream() + .anyMatch(groupRequestList -> groupRequestList + .get(request.getCapabilities()) != null)) + .forEach(requestList::add); + } if (requestList.isEmpty()) { loge("onDataNetworkSetupRetry: Request list is empty. Abort retry."); dataSetupRetryEntry.setState(DataRetryEntry.RETRY_STATE_CANCELLED); return; } + log("onDataNetworkSetupRetry: Request list:" + requestList); TelephonyNetworkRequest telephonyNetworkRequest = requestList.get(0); int networkCapability = telephonyNetworkRequest.getApnTypeNetworkCapability(); @@ -2632,10 +2781,36 @@ return; } - logl("Start handover " + dataNetwork + " to " + logl("onDataNetworkHandoverRetry: Start handover " + dataNetwork + " to " + AccessNetworkConstants.transportTypeToString(preferredTransport) + ", " + dataHandoverRetryEntry); - dataNetwork.startHandover(preferredTransport, dataHandoverRetryEntry); + tryHandoverDataNetwork(dataNetwork, preferredTransport, dataHandoverRetryEntry); + } + + /** + * Called when data network reached max handover retry count. + * + * @param dataNetwork The data network. + */ + private void onDataNetworkHandoverRetryStopped(@NonNull DataNetwork dataNetwork) { + int preferredTransport = mAccessNetworksManager + .getPreferredTransportByNetworkCapability( + dataNetwork.getApnTypeNetworkCapability()); + if (dataNetwork.getTransport() == preferredTransport) { + log("onDataNetworkHandoverRetryStopped: " + dataNetwork + " is already " + + "on the preferred transport " + + AccessNetworkConstants.transportTypeToString( + preferredTransport)); + return; + } + if (dataNetwork.shouldDelayImsTearDownDueToInCall()) { + log("onDataNetworkHandoverRetryStopped: Delay IMS tear down until call " + + "ends. " + dataNetwork); + return; + } + + tearDownGracefully(dataNetwork, + DataNetwork.TEAR_DOWN_REASON_HANDOVER_FAILED); } /** @@ -2708,11 +2883,13 @@ * * @param dataNetwork The data network. * @param cause The disconnect cause. + * @param tearDownReason The reason the network was torn down */ private void onDataNetworkDisconnected(@NonNull DataNetwork dataNetwork, - @DataFailureCause int cause) { + @DataFailureCause int cause, @TearDownReason int tearDownReason) { logl("onDataNetworkDisconnected: " + dataNetwork + ", cause=" - + DataFailCause.toString(cause) + "(" + cause + ")"); + + DataFailCause.toString(cause) + "(" + cause + "), tearDownReason=" + + DataNetwork.tearDownReasonToString(tearDownReason)); mDataNetworkList.remove(dataNetwork); mPendingImsDeregDataNetworks.remove(dataNetwork); mDataRetryManager.cancelPendingHandoverRetry(dataNetwork); @@ -2733,11 +2910,13 @@ () -> callback.onAnyDataNetworkExistingChanged(mAnyDataNetworkExisting))); } + // Immediately reestablish on target transport if network was torn down due to policy + long delayMillis = tearDownReason == DataNetwork.TEAR_DOWN_REASON_HANDOVER_NOT_ALLOWED + ? 0 : mDataConfigManager.getRetrySetupAfterDisconnectMillis(); // Sometimes network was unsolicitedly reported lost for reasons. We should re-evaluate // and see if data network can be re-established again. sendMessageDelayed(obtainMessage(EVENT_REEVALUATE_UNSATISFIED_NETWORK_REQUESTS, - DataEvaluationReason.RETRY_AFTER_DISCONNECTED), - mDataConfigManager.getRetrySetupAfterDisconnectMillis()); + DataEvaluationReason.RETRY_AFTER_DISCONNECTED), delayMillis); } /** @@ -2802,6 +2981,16 @@ } else if (handoverFailureMode == DataCallResponse .HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_SETUP_NORMAL || handoverFailureMode == DataCallResponse.HANDOVER_FAILURE_MODE_LEGACY) { + int preferredTransport = mAccessNetworksManager + .getPreferredTransportByNetworkCapability( + dataNetwork.getApnTypeNetworkCapability()); + if (dataNetwork.getTransport() == preferredTransport) { + log("onDataNetworkHandoverFailed: Already on preferred transport " + + AccessNetworkConstants.transportTypeToString(preferredTransport) + + ". No further actions needed."); + return; + } + int targetTransport = DataUtils.getTargetTransport(dataNetwork.getTransport()); mDataRetryManager.evaluateDataSetupRetry(dataNetwork.getDataProfile(), targetTransport, dataNetwork.getAttachedNetworkRequestList(), cause, retryDelayMillis); @@ -2825,6 +3014,20 @@ } /** + * Called when a network request is detached from the data network and should be retried. + * + * @param networkRequest The detached network request. + */ + private void onRetryUnsatisfiedNetworkRequest( + @NonNull TelephonyNetworkRequest networkRequest) { + if (!mAllNetworkRequestList.contains(networkRequest)) return; + + sendMessageDelayed(obtainMessage(EVENT_REEVALUATE_UNSATISFIED_NETWORK_REQUESTS, + DataEvaluationReason.UNSATISFIED_REQUEST_DETACHED), + REEVALUATE_UNSATISFIED_NETWORK_REQUESTS_AFTER_DETACHED_DELAY_MILLIS); + } + + /** * Called when data stall occurs and needed to tear down / setup a new data network for * internet. This event is from {@link DataStallRecoveryManager}. */ @@ -2840,6 +3043,25 @@ } /** + * Called when SRVCC handover state changes. To preserve the voice call, we don't tear down the + * IMS network while handover in process. We reevaluate the network when handover ends. + * + * @param state The handover state of SRVCC + */ + private void onSrvccStateChanged(@NonNull int[] state) { + if (state != null && state.length != 0) { + log("onSrvccStateChanged: " + TelephonyManager.srvccStateToString(state[0])); + mIsSrvccHandoverInProcess = state[0] == TelephonyManager.SRVCC_STATE_HANDOVER_STARTED; + // Reevaluate networks if SRVCC ends. + if (!mIsSrvccHandoverInProcess + && !hasMessages(EVENT_REEVALUATE_EXISTING_DATA_NETWORKS)) { + sendMessage(obtainMessage(EVENT_REEVALUATE_EXISTING_DATA_NETWORKS, + DataEvaluationReason.SRVCC_STATE_CHANGED)); + } + } + } + + /** * Called when data service binding changed. * * @param transport The transport of the changed data service. @@ -2871,7 +3093,7 @@ * @param simState SIM state. (Note this is mixed with card state and application state.) */ private void onSimStateChanged(@SimState int simState) { - log("onSimStateChanged: state=" + SubscriptionInfoUpdater.simStateString(simState)); + log("onSimStateChanged: state=" + TelephonyManager.simStateToString(simState)); if (mSimState != simState) { mSimState = simState; if (simState == TelephonyManager.SIM_STATE_ABSENT) { @@ -2912,34 +3134,64 @@ continue; } - DataEvaluation dataEvaluation = evaluateDataNetworkHandover(dataNetwork); - log("onEvaluatePreferredTransport: " + dataEvaluation + ", " + dataNetwork); - if (!dataEvaluation.containsDisallowedReasons()) { - logl("Start handover " + dataNetwork + " to " - + AccessNetworkConstants.transportTypeToString(preferredTransport)); - dataNetwork.startHandover(preferredTransport, null); - } else if (dataEvaluation.containsAny(DataDisallowedReason.NOT_ALLOWED_BY_POLICY, - DataDisallowedReason.NOT_IN_SERVICE, - DataDisallowedReason.VOPS_NOT_SUPPORTED)) { - logl("onEvaluatePreferredTransport: Handover not allowed. Tear " - + "down " + dataNetwork + " so a new network can be setup on " - + AccessNetworkConstants.transportTypeToString(preferredTransport) - + "."); - tearDownGracefully(dataNetwork, - DataNetwork.TEAR_DOWN_REASON_HANDOVER_NOT_ALLOWED); - } else if (dataEvaluation.containsAny(DataDisallowedReason.ILLEGAL_STATE, - DataDisallowedReason.RETRY_SCHEDULED)) { - logl("onEvaluatePreferredTransport: Handover not allowed. " + dataNetwork - + " will remain on " + AccessNetworkConstants.transportTypeToString( - dataNetwork.getTransport())); - } else { - loge("onEvaluatePreferredTransport: Unexpected handover evaluation result."); - } + tryHandoverDataNetwork(dataNetwork, preferredTransport, null/*handoverRetryEntry*/); } } } /** + * Perform data network handover if condition allows, otherwise tear down the network to allow + * new network setup on the target transport. + * + * @param dataNetwork The network on which the handover occurs + * @param targetTransport The target transport of the handover + * @param dataHandoverRetryEntry {@code null} if the handover attempt is not due to scheduled + * retry + */ + private void tryHandoverDataNetwork(@NonNull DataNetwork dataNetwork, + @TransportType int targetTransport, + @Nullable DataHandoverRetryEntry dataHandoverRetryEntry) { + if (dataHandoverRetryEntry == null // This handover is a new request + && mDataRetryManager.isAnyHandoverRetryScheduled(dataNetwork)) { + log("tryHandoverDataNetwork: retry scheduled for" + dataNetwork + + ", ignore this attempt"); + return; + } + DataEvaluation dataEvaluation = evaluateDataNetworkHandover(dataNetwork); + log("tryHandoverDataNetwork: " + dataEvaluation + ", " + dataNetwork); + if (!dataEvaluation.containsDisallowedReasons()) { + logl("Start handover " + dataNetwork + " to " + + AccessNetworkConstants.transportTypeToString(targetTransport)); + dataNetwork.startHandover(targetTransport, dataHandoverRetryEntry); + } else if (dataEvaluation.containsOnly(DataDisallowedReason.NOT_IN_SERVICE) + && dataNetwork.shouldDelayImsTearDownDueToInCall()) { + // We try to preserve voice call in the case of temporary preferred transport mismatch + if (dataHandoverRetryEntry != null) { + dataHandoverRetryEntry.setState(DataRetryEntry.RETRY_STATE_FAILED); + } + mDataRetryManager.evaluateDataHandoverRetry(dataNetwork, + DataFailCause.HANDOVER_FAILED, + DataCallResponse.RETRY_DURATION_UNDEFINED /* retry mills */); + logl("tryHandoverDataNetwork: Scheduled retry due to in voice call and target OOS"); + } else if (dataEvaluation.containsAny(DataDisallowedReason.NOT_ALLOWED_BY_POLICY, + DataDisallowedReason.NOT_IN_SERVICE, + DataDisallowedReason.VOPS_NOT_SUPPORTED)) { + logl("tryHandoverDataNetwork: Handover not allowed. Tear down" + + dataNetwork + " so a new network can be setup on " + + AccessNetworkConstants.transportTypeToString(targetTransport)); + tearDownGracefully(dataNetwork, + DataNetwork.TEAR_DOWN_REASON_HANDOVER_NOT_ALLOWED); + } else if (dataEvaluation.containsAny(DataDisallowedReason.ILLEGAL_STATE, + DataDisallowedReason.RETRY_SCHEDULED)) { + logl("tryHandoverDataNetwork: Handover not allowed. " + dataNetwork + + " will remain on " + AccessNetworkConstants.transportTypeToString( + dataNetwork.getTransport())); + } else { + loge("tryHandoverDataNetwork: Unexpected handover evaluation result."); + } + } + + /** * Update {@link SubscriptionPlan}s from {@link NetworkPolicyManager}. */ private void updateSubscriptionPlans() { @@ -3034,6 +3286,12 @@ log("Found more network requests that can be satisfied. " + networkRequestList); dataNetwork.attachNetworkRequests(networkRequestList); } + + if (dataNetwork.getNetworkCapabilities().hasCapability( + NetworkCapabilities.NET_CAPABILITY_INTERNET)) { + // Update because DataNetwork#isInternetSupported might have changed with capabilities. + updateOverallInternetDataState(); + } } /** @@ -3184,9 +3442,9 @@ } /** - * Update the internet data network state. For now only {@link TelephonyManager#DATA_CONNECTED} - * , {@link TelephonyManager#DATA_SUSPENDED}, and - * {@link TelephonyManager#DATA_DISCONNECTED} are supported. + * Update the internet data network state. For now only {@link TelephonyManager#DATA_CONNECTED}, + * {@link TelephonyManager#DATA_SUSPENDED}, and {@link TelephonyManager#DATA_DISCONNECTED} + * are supported. */ private void updateOverallInternetDataState() { boolean anyInternetConnected = mDataNetworkList.stream() @@ -3219,9 +3477,7 @@ && mInternetDataNetworkState == TelephonyManager.DATA_DISCONNECTED) { mDataNetworkControllerCallbacks.forEach(callback -> callback.invokeFromExecutor( () -> callback.onInternetDataNetworkConnected( - allConnectedInternetDataNetworks.stream() - .map(DataNetwork::getDataProfile) - .collect(Collectors.toList())))); + allConnectedInternetDataNetworks))); } else if (dataNetworkState == TelephonyManager.DATA_DISCONNECTED && mInternetDataNetworkState == TelephonyManager.DATA_CONNECTED) { mDataNetworkControllerCallbacks.forEach(callback -> callback.invokeFromExecutor( @@ -3454,8 +3710,8 @@ /** * Get the internet data network state. Note that this is the best effort if more than one - * data network supports internet. For now only {@link TelephonyManager#DATA_CONNECTED} - * , {@link TelephonyManager#DATA_SUSPENDED}, and {@link TelephonyManager#DATA_DISCONNECTED} + * data network supports internet. For now only {@link TelephonyManager#DATA_CONNECTED}, + * {@link TelephonyManager#DATA_SUSPENDED}, and {@link TelephonyManager#DATA_DISCONNECTED} * are supported. * * @return The data network state. @@ -3565,7 +3821,8 @@ pw.println("mImsDataNetworkState=" + TelephonyUtils.dataStateToString(mImsDataNetworkState)); pw.println("mDataServiceBound=" + mDataServiceBound); - pw.println("mSimState=" + SubscriptionInfoUpdater.simStateString(mSimState)); + pw.println("mIsSrvccHandoverInProcess=" + mIsSrvccHandoverInProcess); + pw.println("mSimState=" + TelephonyManager.simStateToString(mSimState)); pw.println("mDataNetworkControllerCallbacks=" + mDataNetworkControllerCallbacks); pw.println("Subscription plans:"); pw.increaseIndent();
diff --git a/src/java/com/android/internal/telephony/data/DataProfileManager.java b/src/java/com/android/internal/telephony/data/DataProfileManager.java index 1ec345c..37f0de0 100644 --- a/src/java/com/android/internal/telephony/data/DataProfileManager.java +++ b/src/java/com/android/internal/telephony/data/DataProfileManager.java
@@ -32,6 +32,7 @@ import android.provider.Telephony; import android.telephony.Annotation; import android.telephony.Annotation.NetworkType; +import android.telephony.AnomalyReporter; import android.telephony.CarrierConfigManager; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; @@ -44,6 +45,7 @@ import android.util.LocalLog; import com.android.internal.telephony.Phone; +import com.android.internal.telephony.data.DataConfigManager.DataConfigManagerCallback; import com.android.internal.telephony.data.DataNetworkController.DataNetworkControllerCallback; import com.android.telephony.Rlog; @@ -54,6 +56,7 @@ import java.util.List; import java.util.Objects; import java.util.Set; +import java.util.UUID; import java.util.concurrent.Executor; import java.util.stream.Collectors; @@ -64,9 +67,6 @@ public class DataProfileManager extends Handler { private static final boolean VDBG = true; - /** Event for data config updated. */ - private static final int EVENT_DATA_CONFIG_UPDATED = 1; - /** Event for APN database changed. */ private static final int EVENT_APN_DATABASE_CHANGED = 2; @@ -77,6 +77,13 @@ private final String mLogTag; private final LocalLog mLocalLog = new LocalLog(128); + /** + * Should only be used by update updateDataProfiles() to indicate whether resend IA to modem + * regardless whether IA changed. + **/ + private final boolean FORCED_UPDATE_IA = true; + private final boolean ONLY_UPDATE_IA_IF_CHANGED = false; + /** Data network controller. */ private final @NonNull DataNetworkController mDataNetworkController; @@ -86,7 +93,11 @@ /** Cellular data service. */ private final @NonNull DataServiceManager mWwanDataServiceManager; - /** All data profiles for the current carrier. */ + /** + * All data profiles for the current carrier. Note only data profiles loaded from the APN + * database will be stored here. The on-demand data profiles (generated dynamically, for + * example, enterprise data profiles with differentiator) are not stored here. + */ private final @NonNull List<DataProfile> mAllDataProfiles = new ArrayList<>(); /** The data profile used for initial attach. */ @@ -95,6 +106,9 @@ /** The preferred data profile used for internet. */ private @Nullable DataProfile mPreferredDataProfile = null; + /** The last data profile that's successful for internet connection. */ + private @Nullable DataProfile mLastInternetDataProfile = null; + /** Preferred data profile set id. */ private int mPreferredDataProfileSetId = Telephony.Carriers.NO_APN_SET_ID; @@ -153,10 +167,16 @@ new DataNetworkControllerCallback(this::post) { @Override public void onInternetDataNetworkConnected( - @NonNull List<DataProfile> dataProfiles) { - DataProfileManager.this.onInternetDataNetworkConnected(dataProfiles); - }}); - mDataConfigManager.registerForConfigUpdate(this, EVENT_DATA_CONFIG_UPDATED); + @NonNull List<DataNetwork> internetNetworks) { + DataProfileManager.this.onInternetDataNetworkConnected(internetNetworks); + } + }); + mDataConfigManager.registerCallback(new DataConfigManagerCallback(this::post) { + @Override + public void onCarrierConfigChanged() { + DataProfileManager.this.onCarrierConfigUpdated(); + } + }); mPhone.getContext().getContentResolver().registerContentObserver( Telephony.Carriers.CONTENT_URI, true, new ContentObserver(this) { @Override @@ -171,16 +191,13 @@ @Override public void handleMessage(Message msg) { switch (msg.what) { - case EVENT_DATA_CONFIG_UPDATED: - onDataConfigUpdated(); - break; case EVENT_SIM_REFRESH: log("Update data profiles due to SIM refresh."); - updateDataProfiles(); + updateDataProfiles(FORCED_UPDATE_IA); break; case EVENT_APN_DATABASE_CHANGED: log("Update data profiles due to APN db updated."); - updateDataProfiles(); + updateDataProfiles(ONLY_UPDATE_IA_IF_CHANGED); break; default: loge("Unexpected event " + msg); @@ -189,11 +206,11 @@ } /** - * Called when data config was updated. + * Called when carrier config was updated. */ - private void onDataConfigUpdated() { - log("Update data profiles due to config updated."); - updateDataProfiles(); + private void onCarrierConfigUpdated() { + log("Update data profiles due to carrier config updated."); + updateDataProfiles(FORCED_UPDATE_IA); //TODO: more works needed to be done here. } @@ -231,8 +248,10 @@ /** * Update all data profiles, including preferred data profile, and initial attach data profile. * Also send those profiles down to the modem if needed. + * + * @param forceUpdateIa If {@code true}, we should always send IA again to modem. */ - private void updateDataProfiles() { + private void updateDataProfiles(boolean forceUpdateIa) { List<DataProfile> profiles = new ArrayList<>(); if (mDataConfigManager.isConfigCarrierSpecific()) { Cursor cursor = mPhone.getContext().getContentResolver().query( @@ -242,7 +261,7 @@ loge("Cannot access APN database through telephony provider."); return; } - + boolean isInternetSupported = false; while (cursor.moveToNext()) { ApnSetting apn = ApnSetting.makeApnSetting(cursor); if (apn != null) { @@ -253,14 +272,44 @@ .build(); profiles.add(dataProfile); log("Added " + dataProfile); + + isInternetSupported |= apn.canHandleType(ApnSetting.TYPE_DEFAULT); + if (mDataConfigManager.isApnConfigAnomalyReportEnabled()) { + checkApnSetting(apn); + } } } cursor.close(); + + if (!isInternetSupported + && !profiles.isEmpty() // APN database has been read successfully + && mDataConfigManager.isApnConfigAnomalyReportEnabled()) { + reportAnomaly("Carrier doesn't support internet.", + "9af73e18-b523-4dc5-adab-363eb6613305"); + } + } + + DataProfile dataProfile; + + if (!profiles.isEmpty()) { // APN database has been read successfully after SIM loaded + // Check if any of the profile already supports IMS, if not, add the default one. + dataProfile = profiles.stream() + .filter(dp -> dp.canSatisfy(NetworkCapabilities.NET_CAPABILITY_IMS)) + .findFirst() + .orElse(null); + if (dataProfile == null) { + profiles.add(new DataProfile.Builder() + .setApnSetting(buildDefaultApnSetting("DEFAULT IMS", "ims", + ApnSetting.TYPE_IMS)) + .setTrafficDescriptor(new TrafficDescriptor("ims", null)) + .build()); + log("Added default IMS data profile."); + } } // Check if any of the profile already supports ENTERPRISE, if not, check if DPC has // configured one and retrieve the same. - DataProfile dataProfile = profiles.stream() + dataProfile = profiles.stream() .filter(dp -> dp.canSatisfy(NetworkCapabilities.NET_CAPABILITY_ENTERPRISE)) .findFirst() .orElse(null); @@ -272,20 +321,6 @@ } } - // Check if any of the profile already supports IMS, if not, add the default one. - dataProfile = profiles.stream() - .filter(dp -> dp.canSatisfy(NetworkCapabilities.NET_CAPABILITY_IMS)) - .findFirst() - .orElse(null); - if (dataProfile == null) { - profiles.add(new DataProfile.Builder() - .setApnSetting(buildDefaultApnSetting("DEFAULT IMS", "ims", - ApnSetting.TYPE_IMS)) - .setTrafficDescriptor(new TrafficDescriptor("ims", null)) - .build()); - log("Added default IMS data profile."); - } - // Check if any of the profile already supports EIMS, if not, add the default one. dataProfile = profiles.stream() .filter(dp -> dp.canSatisfy(NetworkCapabilities.NET_CAPABILITY_EIMS)) @@ -302,6 +337,10 @@ dedupeDataProfiles(profiles); + if (mDataConfigManager.isApnConfigAnomalyReportEnabled()) { + checkDataProfiles(profiles); + } + log("Found " + profiles.size() + " data profiles. profiles = " + profiles); boolean profilesChanged = false; @@ -323,7 +362,7 @@ } updateDataProfilesAtModem(); - updateInitialAttachDataProfileAtModem(); + updateInitialAttachDataProfileAtModem(forceUpdateIa); if (profilesChanged) { mDataProfileManagerCallbacks.forEach(callback -> callback.invokeFromExecutor( @@ -365,23 +404,38 @@ /** * Called when internet data is connected. * - * @param dataProfiles The connected internet data networks' profiles. + * @param internetNetworks The connected internet data networks. */ - private void onInternetDataNetworkConnected(@NonNull List<DataProfile> dataProfiles) { - // If there is already a preferred data profile set, then we don't need to do anything. - if (mPreferredDataProfile != null) return; + private void onInternetDataNetworkConnected(@NonNull List<DataNetwork> internetNetworks) { + DataProfile defaultProfile = null; + if (internetNetworks.size() == 1) { + // Most of the cases there should be only one. + defaultProfile = internetNetworks.get(0).getDataProfile(); + } else if (internetNetworks.size() > 1) { + // but in case there are multiple, find the default internet network, and choose the + // one which has longest life cycle. + logv("onInternetDataNetworkConnected: mPreferredDataProfile=" + mPreferredDataProfile + + " internetNetworks=" + internetNetworks); + defaultProfile = internetNetworks.stream() + .filter(network -> mPreferredDataProfile == null + || canPreferredDataProfileSatisfy( + network.getAttachedNetworkRequestList())) + .map(DataNetwork::getDataProfile) + .min(Comparator.comparingLong(DataProfile::getLastSetupTimestamp)) + .orElse(null); + } - // If there is no preferred data profile, then we should use one of the data profiles, - // which is good for internet, as the preferred data profile. + // Update a working internet data profile as a future candidate for preferred data profile + // after APNs are reset to default + mLastInternetDataProfile = defaultProfile; - // Most of the cases there should be only one, but in case there are multiple, choose the - // one which has longest life cycle. - DataProfile dataProfile = dataProfiles.stream() - .max(Comparator.comparingLong(DataProfile::getLastSetupTimestamp).reversed()) - .orElse(null); + // If the live default internet network is not using the preferred data profile, since + // brought up a network means it passed sophisticated checks, update the preferred data + // profile so that this network won't be torn down in future network evaluations. + if (defaultProfile == null || defaultProfile.equals(mPreferredDataProfile)) return; // Save the preferred data profile into database. - setPreferredDataProfile(dataProfile); - updateDataProfiles(); + setPreferredDataProfile(defaultProfile); + updateDataProfiles(ONLY_UPDATE_IA_IF_CHANGED); } /** @@ -434,7 +488,7 @@ * the preferred data profile from database. */ private void setPreferredDataProfile(@Nullable DataProfile dataProfile) { - log("setPreferredDataProfile: " + dataProfile); + logl("setPreferredDataProfile: " + dataProfile); String subId = Long.toString(mPhone.getSubId()); Uri uri = Uri.withAppendedPath(Telephony.Carriers.PREFERRED_APN_URI, subId); @@ -465,6 +519,16 @@ if (preferredDataProfile != null) { // Save the carrier specified preferred data profile into database setPreferredDataProfile(preferredDataProfile); + } else { + preferredDataProfile = mAllDataProfiles.stream() + .filter(dp -> areDataProfilesSharingApn(dp, mLastInternetDataProfile)) + .findFirst() + .orElse(null); + if (preferredDataProfile != null) { + log("updatePreferredDataProfile: preferredDB is empty and no carrier " + + "default configured, setting preferred to be prev internet DP."); + setPreferredDataProfile(preferredDataProfile); + } } } } else { @@ -493,8 +557,10 @@ * Some carriers might explicitly require that using "user-added" APN for initial * attach. In this case, exception can be configured through * {@link CarrierConfigManager#KEY_ALLOWED_INITIAL_ATTACH_APN_TYPES_STRING_ARRAY}. + * + * @param forceUpdateIa If {@code true}, we should always send IA again to modem. */ - private void updateInitialAttachDataProfileAtModem() { + private void updateInitialAttachDataProfileAtModem(boolean forceUpdateIa) { DataProfile initialAttachDataProfile = null; // Sort the data profiles so the preferred data profile is at the beginning. @@ -510,11 +576,12 @@ if (initialAttachDataProfile != null) break; } - if (!Objects.equals(mInitialAttachDataProfile, initialAttachDataProfile)) { + if (forceUpdateIa || !Objects.equals(mInitialAttachDataProfile, initialAttachDataProfile)) { mInitialAttachDataProfile = initialAttachDataProfile; - logl("Initial attach data profile updated as " + mInitialAttachDataProfile); + logl("Initial attach data profile updated as " + mInitialAttachDataProfile + + " or forceUpdateIa= " + forceUpdateIa); // TODO: Push the null data profile to modem on new AIDL HAL. Modem should clear the IA - // APN. + // APN, tracking for U b/227579876, now using forceUpdateIa which always push to modem if (mInitialAttachDataProfile != null) { mWwanDataServiceManager.setInitialAttachApn(mInitialAttachDataProfile, mPhone.getServiceState().getDataRoamingFromRegistration(), null); @@ -557,14 +624,18 @@ * * @param networkRequest The network request. * @param networkType The current data network type. + * @param ignorePermanentFailure {@code true} to ignore {@link ApnSetting#getPermanentFailed()}. + * This should be set to true for condition-based retry/setup. * @return The data profile. {@code null} if can't find any satisfiable data profile. */ public @Nullable DataProfile getDataProfileForNetworkRequest( - @NonNull TelephonyNetworkRequest networkRequest, @NetworkType int networkType) { + @NonNull TelephonyNetworkRequest networkRequest, @NetworkType int networkType, + boolean ignorePermanentFailure) { ApnSetting apnSetting = null; if (networkRequest.hasAttribute(TelephonyNetworkRequest .CAPABILITY_ATTRIBUTE_APN_SETTING)) { - apnSetting = getApnSettingForNetworkRequest(networkRequest, networkType); + apnSetting = getApnSettingForNetworkRequest(networkRequest, networkType, + ignorePermanentFailure); } TrafficDescriptor.Builder trafficDescriptorBuilder = new TrafficDescriptor.Builder(); @@ -623,25 +694,41 @@ * * @param networkRequest The network request. * @param networkType The current data network type. + * @param ignorePermanentFailure {@code true} to ignore {@link ApnSetting#getPermanentFailed()}. + * This should be set to true for condition-based retry/setup. * @return The APN setting. {@code null} if can't find any satisfiable data profile. */ private @Nullable ApnSetting getApnSettingForNetworkRequest( - @NonNull TelephonyNetworkRequest networkRequest, @NetworkType int networkType) { + @NonNull TelephonyNetworkRequest networkRequest, @NetworkType int networkType, + boolean ignorePermanentFailure) { if (!networkRequest.hasAttribute( TelephonyNetworkRequest.CAPABILITY_ATTRIBUTE_APN_SETTING)) { loge("Network request does not have APN setting attribute."); return null; } + // If the preferred data profile can be used, always use it if it can satisfy the network + // request with current network type (even though it's been marked as permanent failed.) + if (mPreferredDataProfile != null + && networkRequest.canBeSatisfiedBy(mPreferredDataProfile) + && mPreferredDataProfile.getApnSetting() != null + && mPreferredDataProfile.getApnSetting().canSupportNetworkType(networkType)) { + if (ignorePermanentFailure || !mPreferredDataProfile.getApnSetting() + .getPermanentFailed()) { + return mPreferredDataProfile.getApnSetting(); + } + log("The preferred data profile is permanently failed. Only condition based retry " + + "can happen."); + return null; + } + // Filter out the data profile that can't satisfy the request. // Preferred data profile should be returned in the top of the list. List<DataProfile> dataProfiles = mAllDataProfiles.stream() .filter(networkRequest::canBeSatisfiedBy) - // Put the preferred data profile at the top of the list, then the longest time - // hasn't used data profile will be in the front so all the data profiles can be - // tried. - .sorted(Comparator.comparing((DataProfile dp) -> !dp.equals(mPreferredDataProfile)) - .thenComparingLong(DataProfile::getLastSetupTimestamp)) + // The longest time hasn't used data profile will be in the front so all the data + // profiles can be tried. + .sorted(Comparator.comparing(DataProfile::getLastSetupTimestamp)) .collect(Collectors.toList()); for (DataProfile dataProfile : dataProfiles) { logv("Satisfied profile: " + dataProfile + ", last setup=" @@ -676,17 +763,39 @@ return null; } + // Check if data profiles are permanently failed. + dataProfiles = dataProfiles.stream() + .filter(dp -> ignorePermanentFailure || !dp.getApnSetting().getPermanentFailed()) + .collect(Collectors.toList()); + if (dataProfiles.size() == 0) { + log("The suitable data profiles are all in permanent failed state."); + return null; + } + return dataProfiles.get(0).getApnSetting(); } /** - * Check if the data profile is the preferred data profile. + * Check if the data profile is essentially the preferred data profile. The non-essential + * elements include e.g.APN Id. * * @param dataProfile The data profile to check. - * @return {@code true} if the data profile is the preferred data profile. + * @return {@code true} if the data profile is essentially the preferred data profile. */ public boolean isDataProfilePreferred(@NonNull DataProfile dataProfile) { - return dataProfile.equals(mPreferredDataProfile); + return areDataProfilesSharingApn(dataProfile, mPreferredDataProfile); + } + + /** + * @param networkRequests The required network requests + * @return {@code true} if we currently have a preferred data profile that's capable of + * satisfying the required network requests; {@code false} if we have no preferred, or the + * preferred cannot satisfy the required requests. + */ + public boolean canPreferredDataProfileSatisfy( + @NonNull DataNetworkController.NetworkRequestList networkRequests) { + return mPreferredDataProfile != null && networkRequests.stream() + .allMatch(request -> request.canBeSatisfiedBy(mPreferredDataProfile)); } /** @@ -707,19 +816,7 @@ new NetworkRequest.Builder() .addCapability(NetworkCapabilities.NET_CAPABILITY_DUN) .build(), mPhone); - return getDataProfileForNetworkRequest(networkRequest, networkType) != null; - } - - /** - * Check if any preferred data profile exists. - * - * @return {@code true} if any preferred data profile exists - */ - public boolean isAnyPreferredDataProfileExisting() { - for (DataProfile dataProfile : mAllDataProfiles) { - if (dataProfile.isPreferred()) return true; - } - return false; + return getDataProfileForNetworkRequest(networkRequest, networkType, true) != null; } /** @@ -750,6 +847,72 @@ } /** + * Trigger anomaly report if APN Setting contains invalid info. + * + * @param setting The Apn setting to be checked. + */ + private void checkApnSetting(@NonNull ApnSetting setting) { + if (setting.canHandleType(ApnSetting.TYPE_MMS)) { + if (setting.getMmsc() == null) { + reportAnomaly("MMS is supported but no MMSC configured " + setting, + "9af73e18-b523-4dc5-adab-19d86c6a3685"); + } else if (!setting.getMmsc().toString().matches("^https?:\\/\\/.+")) { + reportAnomaly("Apn config mmsc should start with http but is " + + setting.getMmsc(), + "9af73e18-b523-4dc5-adab-ec754d959d4d"); + } + if (!TextUtils.isEmpty(setting.getMmsProxyAddressAsString()) + && setting.getMmsProxyAddressAsString().matches("^https?:\\/\\/.+")) { + reportAnomaly("Apn config mmsc_proxy should NOT start with http but is " + + setting.getMmsc(), "9af73e18-b523-4dc5-adab-ec754d959d4d"); + } + } + } + + /** + * Trigger anomaly report if any two Apn Settings share the same APN name while having + * overlapped network types. + * + * @param profiles The list of data profiles to be checked. + */ + private void checkDataProfiles(List<DataProfile> profiles) { + for (int i = 0; i < profiles.size(); i++) { + ApnSetting a = profiles.get(i).getApnSetting(); + if (a == null) continue; + if (// Lingering network is not the default and doesn't cover all the regular networks + (int) TelephonyManager.NETWORK_TYPE_BITMASK_UNKNOWN + != a.getLingeringNetworkTypeBitmask() + && (a.getNetworkTypeBitmask() | a.getLingeringNetworkTypeBitmask()) + != a.getLingeringNetworkTypeBitmask()) { + reportAnomaly("Apn[" + a.getApnName() + "] network " + + TelephonyManager.convertNetworkTypeBitmaskToString( + a.getNetworkTypeBitmask()) + " should be a subset of " + + "the lingering network " + + TelephonyManager.convertNetworkTypeBitmaskToString( + a.getLingeringNetworkTypeBitmask()), + "9af73e18-b523-4dc5-adab-4bb24355d838"); + } + for (int j = i + 1; j < profiles.size(); j++) { + ApnSetting b = profiles.get(j).getApnSetting(); + if (b == null) continue; + String apnNameA = a.getApnName(); + String apnNameB = b.getApnName(); + if (TextUtils.equals(apnNameA, apnNameB) + // TelephonyManager.NETWORK_TYPE_BITMASK_UNKNOWN means all network types + && (a.getNetworkTypeBitmask() + == (int) TelephonyManager.NETWORK_TYPE_BITMASK_UNKNOWN + || b.getNetworkTypeBitmask() + == (int) TelephonyManager.NETWORK_TYPE_BITMASK_UNKNOWN + || (a.getNetworkTypeBitmask() & b.getNetworkTypeBitmask()) != 0)) { + reportAnomaly("Found overlapped network type under the APN name " + + a.getApnName(), + "9af73e18-b523-4dc5-adab-4bb24555d839"); + } + } + } + } + + /** * Merge two data profiles if possible. * * @param dp1 Data profile 1 to be merged. @@ -806,6 +969,9 @@ ? apn1.getMtuV4() : apn2.getMtuV4()); apnBuilder.setMtuV6(apn2.getMtuV6() <= ApnSetting.UNSET_MTU ? apn1.getMtuV6() : apn2.getMtuV6()); + // legacy properties that don't matter + apnBuilder.setMvnoType(apn1.getMvnoType()); + apnBuilder.setMvnoMatchData(apn1.getMvnoMatchData()); // The following fields in apn1 and apn2 should be the same, otherwise ApnSetting.similar() // should fail earlier. @@ -820,8 +986,6 @@ apnBuilder.setMaxConns(apn1.getMaxConns()); apnBuilder.setWaitTime(apn1.getWaitTime()); apnBuilder.setMaxConnsTime(apn1.getMaxConnsTime()); - apnBuilder.setMvnoType(apn1.getMvnoType()); - apnBuilder.setMvnoMatchData(apn1.getMvnoMatchData()); apnBuilder.setApnSetId(apn1.getApnSetId()); apnBuilder.setCarrierId(apn1.getCarrierId()); apnBuilder.setSkip464Xlat(apn1.getSkip464Xlat()); @@ -834,39 +998,43 @@ } /** - * Get data profile by APN name and/or traffic descriptor. + * Check if the provided data profile is still compatible with the current environment. Note + * this method ignores APN id check and traffic descriptor check. A data profile with traffic + * descriptor only can always be used in any condition. * - * @param apnName APN name. - * @param trafficDescriptor Traffic descriptor. - * - * @return Data profile by APN name and/or traffic descriptor. Either one of APN name or - * traffic descriptor should be provided. {@code null} if data profile is not found. + * @param dataProfile The data profile to check. + * @return {@code true} if the provided data profile can be still used in current environment. */ - public @Nullable DataProfile getDataProfile(@Nullable String apnName, - @Nullable TrafficDescriptor trafficDescriptor) { - if (apnName == null && trafficDescriptor == null) return null; - - List<DataProfile> dataProfiles = mAllDataProfiles; - - // Check if any existing data profile has the same traffic descriptor. - if (trafficDescriptor != null) { - dataProfiles = mAllDataProfiles.stream() - .filter(dp -> trafficDescriptor.equals(dp.getTrafficDescriptor())) - .collect(Collectors.toList()); + public boolean isDataProfileCompatible(@NonNull DataProfile dataProfile) { + if (dataProfile == null) { + return false; } - // Check if any existing data profile has the same APN name. - if (apnName != null) { - dataProfiles = dataProfiles.stream() - .filter(dp -> dp.getApnSetting() != null - && (dp.getApnSetting().getApnSetId() - == Telephony.Carriers.MATCH_ALL_APN_SET_ID - || dp.getApnSetting().getApnSetId() == mPreferredDataProfileSetId)) - .filter(dp -> apnName.equals(dp.getApnSetting().getApnName())) - .collect(Collectors.toList()); + if (dataProfile.getApnSetting() == null && dataProfile.getTrafficDescriptor() != null) { + // A traffic descriptor only data profile can be always used. Traffic descriptors are + // always generated on the fly instead loaded from the database. + return true; } - return dataProfiles.isEmpty() ? null : dataProfiles.get(0); + // Check the APN from the profile is compatible and matches preferred data profile set id. + return mAllDataProfiles.stream() + .filter(dp -> dp.getApnSetting() != null + && (dp.getApnSetting().getApnSetId() + == Telephony.Carriers.MATCH_ALL_APN_SET_ID + || dp.getApnSetting().getApnSetId() == mPreferredDataProfileSetId)) + .anyMatch(dp -> areDataProfilesSharingApn(dataProfile, dp)); + } + + /** + * @return {@code true} if both data profiles' APN setting are non-null and essentially the same + * (non-essential elements include e.g.APN Id). + */ + public boolean areDataProfilesSharingApn(@Nullable DataProfile a, @Nullable DataProfile b) { + return a != null + && b != null + && a.getApnSetting() != null + && a.getApnSetting().equals(b.getApnSetting(), + mPhone.getServiceState().getDataRoamingFromRegistration()); } /** @@ -888,6 +1056,17 @@ } /** + * Trigger the anomaly report with the specified UUID. + * + * @param anomalyMsg Description of the event + * @param uuid UUID associated with that event + */ + private void reportAnomaly(@NonNull String anomalyMsg, @NonNull String uuid) { + logl(anomalyMsg); + AnomalyReporter.reportAnomaly(UUID.fromString(uuid), anomalyMsg, mPhone.getCarrierId()); + } + + /** * Log debug messages. * @param s debug messages */ @@ -945,9 +1124,16 @@ pw.println("Preferred data profile from db=" + getPreferredDataProfileFromDb()); pw.println("Preferred data profile from config=" + getPreferredDataProfileFromConfig()); pw.println("Preferred data profile set id=" + mPreferredDataProfileSetId); + pw.println("Last internet data profile=" + mLastInternetDataProfile); pw.println("Initial attach data profile=" + mInitialAttachDataProfile); pw.println("isTetheringDataProfileExisting=" + isTetheringDataProfileExisting( TelephonyManager.NETWORK_TYPE_LTE)); + pw.println("Permanent failed profiles="); + pw.increaseIndent(); + mAllDataProfiles.stream() + .filter(dp -> dp.getApnSetting() != null && dp.getApnSetting().getPermanentFailed()) + .forEach(pw::println); + pw.decreaseIndent(); pw.println("Local logs:"); pw.increaseIndent();
diff --git a/src/java/com/android/internal/telephony/data/DataRetryManager.java b/src/java/com/android/internal/telephony/data/DataRetryManager.java index da86cf4..518689a 100644 --- a/src/java/com/android/internal/telephony/data/DataRetryManager.java +++ b/src/java/com/android/internal/telephony/data/DataRetryManager.java
@@ -21,6 +21,12 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.app.AlarmManager; +import android.app.PendingIntent; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; import android.net.NetworkCapabilities; import android.os.AsyncResult; import android.os.Handler; @@ -46,6 +52,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.telephony.CommandsInterface; import com.android.internal.telephony.Phone; +import com.android.internal.telephony.data.DataConfigManager.DataConfigManagerCallback; import com.android.internal.telephony.data.DataNetworkController.DataNetworkControllerCallback; import com.android.internal.telephony.data.DataNetworkController.NetworkRequestList; import com.android.internal.telephony.data.DataProfileManager.DataProfileManagerCallback; @@ -55,7 +62,6 @@ import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.List; import java.util.Locale; import java.util.Objects; @@ -64,6 +70,7 @@ import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; +import java.util.stream.Stream; /** * DataRetryManager manages data network setup retry and its configurations. @@ -71,8 +78,10 @@ public class DataRetryManager extends Handler { private static final boolean VDBG = false; - /** Event for data config updated. */ - private static final int EVENT_DATA_CONFIG_UPDATED = 1; + /** Intent of Alarm Manager for long retry timer. */ + private static final String ACTION_RETRY = "com.android.internal.telephony.data.ACTION_RETRY"; + /** The extra key for the hashcode of the retry entry for Alarm Manager. */ + private static final String ACTION_RETRY_EXTRA_HASHCODE = "extra_retry_hashcode"; /** Event for data setup retry. */ private static final int EVENT_DATA_SETUP_RETRY = 3; @@ -100,6 +109,12 @@ /** The maximum entries to preserve. */ private static final int MAXIMUM_HISTORICAL_ENTRIES = 100; + /** + * The threshold of retry timer, longer than or equal to which we use alarm manager to schedule + * instead of handler. + */ + private static final long RETRY_LONG_DELAY_TIMER_THRESHOLD_MILLIS = TimeUnit + .MINUTES.toMillis(1); @IntDef(prefix = {"RESET_REASON_"}, value = { @@ -145,6 +160,9 @@ /** Local log. */ private final @NonNull LocalLog mLocalLog = new LocalLog(128); + /** Alarm Manager used to schedule long set up or handover retries. */ + private final @NonNull AlarmManager mAlarmManager; + /** * The data retry callback. This is only used to notify {@link DataNetworkController} to retry * setup data network. @@ -316,10 +334,6 @@ } } - if (mMaxRetries == 0) { - mRetryIntervalsMillis = Collections.emptyList(); - } - if (mMaxRetries < 0) { throw new IllegalArgumentException("Max retries should not be less than 0. " + "mMaxRetries=" + mMaxRetries); @@ -360,39 +374,45 @@ } /** - * Represent a setup data network retry rule. + * Represent a rule for data setup retry. * * The syntax of the retry rule: - * 1. Retry based on {@link NetworkCapabilities}. Note that only APN-type network - * capabilities are supported. - * "capabilities=[netCaps1|netCaps2|...], [retry_interval=n1|n2|n3|n4...], - * [maximum_retries=n]" + * 1. Retry based on {@link NetworkCapabilities}. Note that only APN-type network capabilities + * are supported. If the capabilities are not specified, then the retry rule only applies + * to the current failed APN used in setup data call request. + * "capabilities=[netCaps1|netCaps2|...], [retry_interval=n1|n2|n3|n4...], [maximum_retries=n]" * * 2. Retry based on {@link DataFailCause} - * "fail_causes=[cause1|cause2|cause3|..], [retry_interval=n1|n2|n3|n4...], - * [maximum_retries=n]" + * "fail_causes=[cause1|cause2|cause3|..], [retry_interval=n1|n2|n3|n4...], [maximum_retries=n]" * * 3. Retry based on {@link NetworkCapabilities} and {@link DataFailCause}. Note that only * APN-type network capabilities are supported. * "capabilities=[netCaps1|netCaps2|...], fail_causes=[cause1|cause2|cause3|...], * [retry_interval=n1|n2|n3|n4...], [maximum_retries=n]" * + * 4. Permanent fail causes (no timer-based retry) on the current failed APN. Retry interval + * is specified for retrying the next available APN. + * "permanent_fail_causes=8|27|28|29|30|32|33|35|50|51|111|-5|-6|65537|65538|-3|65543|65547| + * 2252|2253|2254, retry_interval=2500" + * * For example, * "capabilities=eims, retry_interval=1000, maximum_retries=20" means if the attached * network request is emergency, then retry data network setup every 1 second for up to 20 * times. * - * "fail_causes=8|27|28|29|30|32|33|35|50|51|111|-5|-6|65537|65538|-3|2253|2254 - * , maximum_retries=0" means for those fail causes, never retry with timers. Note that - * when environment changes, retry can still happen. - * * "capabilities=internet|enterprise|dun|ims|fota, retry_interval=2500|3000|" * "5000|10000|15000|20000|40000|60000|120000|240000|600000|1200000|1800000" - * "1800000, maximum_retries=20" means for those capabilities, retry happens in 2.5s, 3s, - * 5s, 10s, 15s, 20s, 40s, 1m, 2m, 4m, 10m, 20m, 30m, 30m, 30m, until reaching 20 retries. + * "1800000, maximum_retries=20" means for those capabilities, retry happens in 2.5s, 3s, 5s, + * 10s, 15s, 20s, 40s, 1m, 2m, 4m, 10m, 20m, 30m, 30m, 30m, until reaching 20 retries. + * */ public static class DataSetupRetryRule extends DataRetryRule { + private static final String RULE_TAG_PERMANENT_FAIL_CAUSES = "permanent_fail_causes"; private static final String RULE_TAG_CAPABILITIES = "capabilities"; + + /** {@code true} if this rule is for permanent fail causes. */ + private boolean mIsPermanentFailCauseRule; + /** * Constructor * @@ -411,8 +431,23 @@ } String key = tokens[0].trim(); String value = tokens[1].trim(); - if (key.equals(RULE_TAG_CAPABILITIES)) { - mNetworkCapabilities = DataUtils.getNetworkCapabilitiesFromString(value); + try { + switch (key) { + case RULE_TAG_PERMANENT_FAIL_CAUSES: + mFailCauses = Arrays.stream(value.split("\\s*\\|\\s*")) + .map(String::trim) + .map(Integer::valueOf) + .collect(Collectors.toSet()); + mIsPermanentFailCauseRule = true; + break; + case RULE_TAG_CAPABILITIES: + mNetworkCapabilities = DataUtils + .getNetworkCapabilitiesFromString(value); + break; + } + } catch (Exception e) { + e.printStackTrace(); + throw new IllegalArgumentException("illegal rule " + ruleString + ", e=" + e); } } @@ -435,6 +470,13 @@ } /** + * @return {@code true} if this rule is for permanent fail causes. + */ + public boolean isPermanentFailCauseRule() { + return mIsPermanentFailCauseRule; + } + + /** * Check if this rule can be matched. * * @param networkCapability The network capability for matching. @@ -930,12 +972,17 @@ mDataServiceManagers = dataServiceManagers; mDataConfigManager = dataNetworkController.getDataConfigManager(); mDataProfileManager = dataNetworkController.getDataProfileManager(); - mDataConfigManager.registerForConfigUpdate(this, EVENT_DATA_CONFIG_UPDATED); + mAlarmManager = mPhone.getContext().getSystemService(AlarmManager.class); - mDataServiceManagers.get(AccessNetworkConstants.TRANSPORT_TYPE_WWAN) - .registerForApnUnthrottled(this, EVENT_DATA_PROFILE_UNTHROTTLED); - if (!mPhone.getAccessNetworksManager().isInLegacyMode()) { - mDataServiceManagers.get(AccessNetworkConstants.TRANSPORT_TYPE_WLAN) + mDataConfigManager.registerCallback(new DataConfigManagerCallback(this::post) { + @Override + public void onCarrierConfigChanged() { + DataRetryManager.this.onCarrierConfigUpdated(); + } + }); + + for (int transport : mPhone.getAccessNetworksManager().getAvailableTransports()) { + mDataServiceManagers.get(transport) .registerForApnUnthrottled(this, EVENT_DATA_PROFILE_UNTHROTTLED); } mDataProfileManager.registerCallback(new DataProfileManagerCallback(this::post) { @@ -946,14 +993,44 @@ }); dataNetworkController.registerDataNetworkControllerCallback( new DataNetworkControllerCallback(this::post) { + /** + * Called when data service is bound. + * + * @param transport The transport of the data service. + */ @Override public void onDataServiceBound(@TransportType int transport) { onReset(RESET_REASON_DATA_SERVICE_BOUND); } + + /** + * Called when data network is connected. + * + * @param transport Transport for the connected network. + * @param dataProfile The data profile of the connected data network. + */ + @Override + public void onDataNetworkConnected(@TransportType int transport, + @NonNull DataProfile dataProfile) { + DataRetryManager.this.onDataNetworkConnected(transport, dataProfile); + } }); mRil.registerForOn(this, EVENT_RADIO_ON, null); mRil.registerForModemReset(this, EVENT_MODEM_RESET, null); + // Register intent of alarm manager for long retry timer + IntentFilter intentFilter = new IntentFilter(); + intentFilter.addAction(ACTION_RETRY); + mPhone.getContext().registerReceiver(new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (ACTION_RETRY.equals(intent.getAction())) { + DataRetryManager.this.onAlarmIntentRetry( + intent.getIntExtra(ACTION_RETRY_EXTRA_HASHCODE, -1 /*Bad hashcode*/)); + } + } + }, intentFilter); + if (mDataConfigManager.shouldResetDataThrottlingWhenTacChanges()) { mPhone.getServiceStateTracker().registerForAreaCodeChanged(this, EVENT_TAC_CHANGED, null); @@ -964,23 +1041,18 @@ public void handleMessage(Message msg) { AsyncResult ar; switch (msg.what) { - case EVENT_DATA_CONFIG_UPDATED: - onDataConfigUpdated(); - break; case EVENT_DATA_SETUP_RETRY: DataSetupRetryEntry dataSetupRetryEntry = (DataSetupRetryEntry) msg.obj; - Objects.requireNonNull(dataSetupRetryEntry); - mDataRetryManagerCallbacks.forEach(callback -> callback.invokeFromExecutor( - () -> callback.onDataNetworkSetupRetry(dataSetupRetryEntry))); + if (!isRetryCancelled(dataSetupRetryEntry)) { + mDataRetryManagerCallbacks.forEach(callback -> callback.invokeFromExecutor( + () -> callback.onDataNetworkSetupRetry(dataSetupRetryEntry))); + } break; case EVENT_DATA_HANDOVER_RETRY: DataHandoverRetryEntry dataHandoverRetryEntry = (DataHandoverRetryEntry) msg.obj; - Objects.requireNonNull(dataHandoverRetryEntry); - if (mDataRetryEntries.contains(dataHandoverRetryEntry)) { + if (!isRetryCancelled(dataHandoverRetryEntry)) { mDataRetryManagerCallbacks.forEach(callback -> callback.invokeFromExecutor( () -> callback.onDataNetworkHandoverRetry(dataHandoverRetryEntry))); - } else { - log("Handover was cancelled earlier. " + dataHandoverRetryEntry); } break; case EVENT_RADIO_ON: @@ -1003,7 +1075,7 @@ } else if (ar.result instanceof DataProfile) { dataProfile = (DataProfile) ar.result; } - onDataProfileUnthrottled(dataProfile, apn, transport, true); + onDataProfileUnthrottled(dataProfile, apn, transport, true, true); break; case EVENT_CANCEL_PENDING_HANDOVER_RETRY: onCancelPendingHandoverRetry((DataNetwork) msg.obj); @@ -1014,9 +1086,21 @@ } /** - * Called when data config is updated. + * @param retryEntry The retry entry to check. + * @return {@code true} if the retry is null or not in RETRY_STATE_NOT_RETRIED state. */ - private void onDataConfigUpdated() { + private boolean isRetryCancelled(@Nullable DataRetryEntry retryEntry) { + if (retryEntry != null && retryEntry.getState() == DataRetryEntry.RETRY_STATE_NOT_RETRIED) { + return false; + } + log("Retry was removed earlier. " + retryEntry); + return true; + } + + /** + * Called when carrier config is updated. + */ + private void onCarrierConfigUpdated() { onReset(RESET_REASON_DATA_CONFIG_CHANGED); mDataSetupRetryRuleList = mDataConfigManager.getDataSetupRetryRules(); mDataHandoverRetryRuleList = mDataConfigManager.getDataHandoverRetryRules(); @@ -1025,6 +1109,21 @@ } /** + * Called when data network is connected. + * + * @param transport Transport for the connected network. + * @param dataProfile The data profile of the connected data network. + */ + public void onDataNetworkConnected(@TransportType int transport, + @NonNull DataProfile dataProfile) { + if (dataProfile.getApnSetting() != null) { + dataProfile.getApnSetting().setPermanentFailed(false); + } + + onDataProfileUnthrottled(dataProfile, null, transport, true, false); + } + + /** * Evaluate if data setup retry is needed or not. If needed, retry will be scheduled * automatically after evaluation. * @@ -1062,6 +1161,7 @@ // ThrottleStatus is just for API backwards compatibility reason. updateThrottleStatus(dataProfile, requestList, null, ThrottleStatus.RETRY_TYPE_NEW_CONNECTION, transport, Long.MAX_VALUE); + return; } else if (retryDelayMillis != DataCallResponse.RETRY_DURATION_UNDEFINED) { // Network specifically asks retry the previous data profile again. DataSetupRetryEntry dataSetupRetryEntry = new DataSetupRetryEntry.Builder<>() @@ -1075,67 +1175,90 @@ ThrottleStatus.RETRY_TYPE_NEW_CONNECTION, transport, dataSetupRetryEntry.retryElapsedTime); schedule(dataSetupRetryEntry); - } else { - // Network did not suggest any retry. Use the configured rules to perform retry. - logv("mDataSetupRetryRuleList=" + mDataSetupRetryRuleList); + return; + } - // Support the legacy permanent failure configuration - if (DataFailCause.isPermanentFailure(mPhone.getContext(), cause, mPhone.getSubId())) { - log("Stopped timer-based retry. cause=" + DataFailCause.toString(cause)); + // Network did not suggest any retry. Use the configured rules to perform retry. + logv("mDataSetupRetryRuleList=" + mDataSetupRetryRuleList); + + boolean retryScheduled = false; + List<NetworkRequestList> groupedNetworkRequestLists = + DataUtils.getGroupedNetworkRequestList(requestList); + for (DataSetupRetryRule retryRule : mDataSetupRetryRuleList) { + if (retryRule.isPermanentFailCauseRule() && retryRule.getFailCauses().contains(cause)) { + if (dataProfile.getApnSetting() != null) { + dataProfile.getApnSetting().setPermanentFailed(true); + + // It seems strange to have retry timer in permanent failure rule, but since + // in this case permanent failure is only applicable to the failed profile, so + // we need to see if other profile can be selected for next data setup. + log("Marked " + dataProfile.getApnSetting().getApnName() + " permanently " + + "failed, but still schedule retry to see if another data profile " + + "can be used for setup data."); + // Schedule a data retry to see if another data profile could be selected. + // If the same data profile is selected again, since it's marked as + // permanent failure, it won't be used for setup data call. + schedule(new DataSetupRetryEntry.Builder<>() + .setRetryDelay(retryRule.getRetryIntervalsMillis().get(0)) + .setAppliedRetryRule(retryRule) + .setSetupRetryType(DataSetupRetryEntry.RETRY_TYPE_NETWORK_REQUESTS) + .setTransport(transport) + .setNetworkRequestList(requestList) + .build()); + } else { + // For TD-based data profile, do not do anything for now. Should expand this in + // the future if needed. + log("Stopped timer-based retry for TD-based data profile. Will retry only when " + + "environment changes."); + } return; } - - boolean retryScheduled = false; - List<NetworkRequestList> groupedNetworkRequestLists = - DataUtils.getGroupedNetworkRequestList(requestList); for (NetworkRequestList networkRequestList : groupedNetworkRequestLists) { int capability = networkRequestList.get(0).getApnTypeNetworkCapability(); - - for (DataSetupRetryRule retryRule : mDataSetupRetryRuleList) { - if (retryRule.canBeMatched(capability, cause)) { - // Check if there is already a similar network request retry scheduled. - if (isSimilarNetworkRequestRetryScheduled( - networkRequestList.get(0), transport)) { - log(networkRequestList.get(0) + " already had similar retry " - + "scheduled."); - return; - } - - int failedCount = getRetryFailedCount(capability, retryRule); - log("For capability " + DataUtils.networkCapabilityToString(capability) - + ", found matching rule " + retryRule + ", failed count=" - + failedCount); - if (failedCount == retryRule.getMaxRetries()) { - log("Data retry failed for " + failedCount + " times. Stopped " - + "timer-based data retry for " - + DataUtils.networkCapabilityToString(capability) - + ". Condition-based retry will still happen when condition " - + "changes."); - return; - } - - retryDelayMillis = retryRule.getRetryIntervalsMillis().get( - Math.min(failedCount, retryRule - .getRetryIntervalsMillis().size() - 1)); - - // Schedule a data retry. - schedule(new DataSetupRetryEntry.Builder<>() - .setRetryDelay(retryDelayMillis) - .setAppliedRetryRule(retryRule) - .setSetupRetryType(DataSetupRetryEntry.RETRY_TYPE_NETWORK_REQUESTS) - .setTransport(transport) - .setNetworkRequestList(networkRequestList) - .build()); - retryScheduled = true; - break; + if (retryRule.canBeMatched(capability, cause)) { + // Check if there is already a similar network request retry scheduled. + if (isSimilarNetworkRequestRetryScheduled( + networkRequestList.get(0), transport)) { + log(networkRequestList.get(0) + " already had similar retry " + + "scheduled."); + return; } + + int failedCount = getRetryFailedCount(capability, retryRule, transport); + log("For capability " + DataUtils.networkCapabilityToString(capability) + + ", found matching rule " + retryRule + ", failed count=" + + failedCount); + if (failedCount == retryRule.getMaxRetries()) { + log("Data retry failed for " + failedCount + " times on " + + AccessNetworkConstants.transportTypeToString(transport) + + ". Stopped timer-based data retry for " + + DataUtils.networkCapabilityToString(capability) + + ". Condition-based retry will still happen when condition " + + "changes."); + return; + } + + retryDelayMillis = retryRule.getRetryIntervalsMillis().get( + Math.min(failedCount, retryRule + .getRetryIntervalsMillis().size() - 1)); + + // Schedule a data retry. + schedule(new DataSetupRetryEntry.Builder<>() + .setRetryDelay(retryDelayMillis) + .setAppliedRetryRule(retryRule) + .setSetupRetryType(DataSetupRetryEntry.RETRY_TYPE_NETWORK_REQUESTS) + .setTransport(transport) + .setNetworkRequestList(networkRequestList) + .build()); + retryScheduled = true; + break; } } + } - if (!retryScheduled) { - log("onEvaluateDataSetupRetry: Did not match any retry rule. Stop timer-based " - + "retry."); - } + if (!retryScheduled) { + log("onEvaluateDataSetupRetry: Did not match any retry rule. Stop timer-based " + + "retry."); } } @@ -1211,6 +1334,24 @@ } } + /** + * @param dataNetwork The data network to check. + * @return {@code true} if the data network had failed the maximum number of attempts for + * handover according to any retry rules. + */ + public boolean isDataNetworkHandoverRetryStopped(@NonNull DataNetwork dataNetwork) { + // Matching the rule in configured order. + for (DataHandoverRetryRule retryRule : mDataHandoverRetryRuleList) { + int failedCount = getRetryFailedCount(dataNetwork, retryRule); + if (failedCount == retryRule.getMaxRetries()) { + log("Data handover retry failed for " + failedCount + " times. Stopped " + + "handover retry."); + return true; + } + } + return false; + } + /** Cancel all retries and throttling entries. */ private void onReset(@RetryResetReason int reason) { logl("Remove all retry and throttling entries, reason=" + resetReasonToString(reason)); @@ -1224,7 +1365,7 @@ DataProfile dataProfile = dataThrottlingEntry.dataProfile; String apn = dataProfile.getApnSetting() != null ? dataProfile.getApnSetting().getApnName() : null; - onDataProfileUnthrottled(dataProfile, apn, dataThrottlingEntry.transport, false); + onDataProfileUnthrottled(dataProfile, apn, dataThrottlingEntry.transport, false, true); } mDataThrottlingEntries.clear(); @@ -1263,16 +1404,18 @@ * * @param networkCapability The network capability to check. * @param dataRetryRule The data retry rule. + * @param transport The transport on which setup failure has occurred. * @return The failed count since last successful data setup. */ private int getRetryFailedCount(@NetCapability int networkCapability, - @NonNull DataSetupRetryRule dataRetryRule) { + @NonNull DataSetupRetryRule dataRetryRule, @TransportType int transport) { int count = 0; for (int i = mDataRetryEntries.size() - 1; i >= 0; i--) { if (mDataRetryEntries.get(i) instanceof DataSetupRetryEntry) { DataSetupRetryEntry entry = (DataSetupRetryEntry) mDataRetryEntries.get(i); // count towards the last succeeded data setup. - if (entry.setupRetryType == DataSetupRetryEntry.RETRY_TYPE_NETWORK_REQUESTS) { + if (entry.setupRetryType == DataSetupRetryEntry.RETRY_TYPE_NETWORK_REQUESTS + && entry.transport == transport) { if (entry.networkRequestList.isEmpty()) { String msg = "Invalid data retry entry detected"; logl(msg); @@ -1304,20 +1447,50 @@ * @param dataRetryEntry The data retry entry. */ private void schedule(@NonNull DataRetryEntry dataRetryEntry) { - logl("Scheduled data retry: " + dataRetryEntry); + logl("Scheduled data retry " + dataRetryEntry + + " hashcode=" + dataRetryEntry.hashCode()); mDataRetryEntries.add(dataRetryEntry); if (mDataRetryEntries.size() >= MAXIMUM_HISTORICAL_ENTRIES) { // Discard the oldest retry entry. mDataRetryEntries.remove(0); } - // Using delayed message instead of alarm manager to schedule data retry is intentional. - // When the device enters doze mode, the handler message might be extremely delayed than the - // original scheduled time. There is no need to wake up the device to perform data retry in - // that case. - sendMessageDelayed(obtainMessage(dataRetryEntry instanceof DataSetupRetryEntry - ? EVENT_DATA_SETUP_RETRY : EVENT_DATA_HANDOVER_RETRY, dataRetryEntry), - dataRetryEntry.retryDelayMillis); + // When the device is in doze mode, the handler message might be extremely delayed because + // handler uses relative system time(not counting sleep) which is inaccurate even when we + // enter the maintenance window. + // Therefore, we use alarm manager when we need to schedule long timers. + if (dataRetryEntry.retryDelayMillis <= RETRY_LONG_DELAY_TIMER_THRESHOLD_MILLIS) { + sendMessageDelayed(obtainMessage(dataRetryEntry instanceof DataSetupRetryEntry + ? EVENT_DATA_SETUP_RETRY : EVENT_DATA_HANDOVER_RETRY, dataRetryEntry), + dataRetryEntry.retryDelayMillis); + } else { + Intent intent = new Intent(ACTION_RETRY); + intent.putExtra(ACTION_RETRY_EXTRA_HASHCODE, dataRetryEntry.hashCode()); + // No need to wake up the device at the exact time, the retry can wait util next time + // the device wake up to save power. + mAlarmManager.setAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME, + dataRetryEntry.retryElapsedTime, + PendingIntent.getBroadcast(mPhone.getContext(), + dataRetryEntry.hashCode() /*Unique identifier of this retry attempt*/, + intent, + PendingIntent.FLAG_IMMUTABLE)); + } + } + + /** + * Called when it's time to retry scheduled by Alarm Manager. + * @param retryHashcode The hashcode is the unique identifier of which retry entry to retry. + */ + private void onAlarmIntentRetry(int retryHashcode) { + DataRetryEntry dataRetryEntry = mDataRetryEntries.stream() + .filter(entry -> entry.hashCode() == retryHashcode) + .findAny() + .orElse(null); + logl("onAlarmIntentRetry: found " + dataRetryEntry + " with hashcode " + retryHashcode); + if (dataRetryEntry != null) { + sendMessage(obtainMessage(dataRetryEntry instanceof DataSetupRetryEntry + ? EVENT_DATA_SETUP_RETRY : EVENT_DATA_HANDOVER_RETRY, dataRetryEntry)); + } } /** @@ -1340,15 +1513,20 @@ @TransportType int transport, @ElapsedRealtimeLong long expirationTime) { DataThrottlingEntry entry = new DataThrottlingEntry(dataProfile, networkRequestList, dataNetwork, transport, retryType, expirationTime); - if (mDataThrottlingEntries.size() >= MAXIMUM_HISTORICAL_ENTRIES) { - mDataThrottlingEntries.remove(0); - } - - // Remove previous entry that contains the same data profile. + // Remove previous entry that contains the same data profile. Therefore it should always + // contain at maximum all the distinct data profiles of the current subscription. mDataThrottlingEntries.removeIf( throttlingEntry -> dataProfile.equals(throttlingEntry.dataProfile)); - + if (mDataThrottlingEntries.size() >= MAXIMUM_HISTORICAL_ENTRIES) { + // If we don't see the anomaly report after U release, we should remove this check for + // the commented reason above. + AnomalyReporter.reportAnomaly( + UUID.fromString("24fd4d46-1d0f-4b13-b7d6-7bad70b8289b"), + "DataRetryManager throttling more than 100 data profiles", + mPhone.getCarrierId()); + mDataThrottlingEntries.remove(0); + } logl("Add throttling entry " + entry); mDataThrottlingEntries.add(entry); @@ -1383,10 +1561,11 @@ * When this is set, {@code dataProfile} must be {@code null}. * @param transport The transport that this unthrottling request is on. * @param remove Whether to remove unthrottled entries from the list of entries. + * @param retry Whether schedule retry after unthrottling. */ private void onDataProfileUnthrottled(@Nullable DataProfile dataProfile, @Nullable String apn, - int transport, boolean remove) { - log("onDataProfileUnthrottled: data profile=" + dataProfile + ", apn=" + apn + @TransportType int transport, boolean remove, boolean retry) { + log("onDataProfileUnthrottled: dataProfile=" + dataProfile + ", apn=" + apn + ", transport=" + AccessNetworkConstants.transportTypeToString(transport) + ", remove=" + remove); @@ -1398,18 +1577,18 @@ // equal to the data profiles kept in data profile manager (due to some fields missing // in DataProfileInfo.aidl), so we need to get the equivalent data profile from data // profile manager. - final DataProfile dp = mDataProfileManager.getDataProfile( - dataProfile.getApnSetting() != null - ? dataProfile.getApnSetting().getApnName() : null, - dataProfile.getTrafficDescriptor()); - log("onDataProfileUnthrottled: getDataProfile=" + dp); - if (dp != null) { - dataUnthrottlingEntries = mDataThrottlingEntries.stream() - .filter(entry -> entry.expirationTimeMillis > now - && entry.dataProfile.equals(dp) - && entry.transport == transport) - .collect(Collectors.toList()); + Stream<DataThrottlingEntry> stream = mDataThrottlingEntries.stream(); + stream = stream.filter(entry -> entry.expirationTimeMillis > now); + if (dataProfile.getApnSetting() != null) { + stream = stream + .filter(entry -> entry.dataProfile.getApnSetting() != null) + .filter(entry -> entry.dataProfile.getApnSetting().getApnName() + .equals(dataProfile.getApnSetting().getApnName())); } + stream = stream.filter(entry -> Objects.equals(entry.dataProfile.getTrafficDescriptor(), + dataProfile.getTrafficDescriptor())); + + dataUnthrottlingEntries = stream.collect(Collectors.toList()); } else if (apn != null) { // For HIDL 1.6 or below dataUnthrottlingEntries = mDataThrottlingEntries.stream() @@ -1441,6 +1620,7 @@ final int dataRetryType = retryType; if (unthrottledProfile != null && unthrottledProfile.getApnSetting() != null) { + unthrottledProfile.getApnSetting().setPermanentFailed(false); throttleStatusList.addAll(unthrottledProfile.getApnSetting().getApnTypes().stream() .map(apnType -> new ThrottleStatus.Builder() .setApnType(apnType) @@ -1455,23 +1635,30 @@ mDataRetryManagerCallbacks.forEach(callback -> callback.invokeFromExecutor( () -> callback.onThrottleStatusChanged(throttleStatusList))); + if (unthrottledProfile != null) { + // cancel pending retries since we will soon schedule an immediate retry + cancelRetriesForDataProfile(unthrottledProfile, transport); + } + logl("onDataProfileUnthrottled: Removing the following throttling entries. " + dataUnthrottlingEntries); - for (DataThrottlingEntry entry : dataUnthrottlingEntries) { - if (entry.retryType == ThrottleStatus.RETRY_TYPE_NEW_CONNECTION) { + if (retry) { + for (DataThrottlingEntry entry : dataUnthrottlingEntries) { // Immediately retry after unthrottling. - schedule(new DataSetupRetryEntry.Builder<>() - .setDataProfile(entry.dataProfile) - .setTransport(entry.transport) - .setSetupRetryType(DataSetupRetryEntry.RETRY_TYPE_DATA_PROFILE) - .setNetworkRequestList(entry.networkRequestList) - .setRetryDelay(0) - .build()); - } else if (entry.retryType == ThrottleStatus.RETRY_TYPE_HANDOVER) { - schedule(new DataHandoverRetryEntry.Builder<>() - .setDataNetwork(entry.dataNetwork) - .setRetryDelay(0) - .build()); + if (entry.retryType == ThrottleStatus.RETRY_TYPE_NEW_CONNECTION) { + schedule(new DataSetupRetryEntry.Builder<>() + .setDataProfile(entry.dataProfile) + .setTransport(entry.transport) + .setSetupRetryType(DataSetupRetryEntry.RETRY_TYPE_DATA_PROFILE) + .setNetworkRequestList(entry.networkRequestList) + .setRetryDelay(0) + .build()); + } else if (entry.retryType == ThrottleStatus.RETRY_TYPE_HANDOVER) { + schedule(new DataHandoverRetryEntry.Builder<>() + .setDataNetwork(entry.dataNetwork) + .setRetryDelay(0) + .build()); + } } } if (remove) { @@ -1480,6 +1667,34 @@ } /** + * Cancel pending retries that uses the specified data profile, with specified target transport. + * + * @param dataProfile The data profile to cancel. + * @param transport The target {@link TransportType} on which the retry to cancel. + */ + private void cancelRetriesForDataProfile(@NonNull DataProfile dataProfile, + @TransportType int transport) { + logl("cancelRetriesForDataProfile: Canceling pending retries for " + dataProfile); + mDataRetryEntries.stream() + .filter(entry -> { + if (entry.getState() == DataRetryEntry.RETRY_STATE_NOT_RETRIED) { + if (entry instanceof DataSetupRetryEntry) { + DataSetupRetryEntry retryEntry = (DataSetupRetryEntry) entry; + return dataProfile.equals(retryEntry.dataProfile) + && transport == retryEntry.transport; + } else if (entry instanceof DataHandoverRetryEntry) { + DataHandoverRetryEntry retryEntry = (DataHandoverRetryEntry) entry; + return dataProfile.equals(retryEntry.dataNetwork.getDataProfile()); + } + } + return false; + }) + .forEach(entry -> entry.setState(DataRetryEntry.RETRY_STATE_CANCELLED)); + } + + + + /** * Check if there is any similar network request scheduled to retry. The definition of similar * is that network requests have same APN capability and on the same transport. * @@ -1489,12 +1704,14 @@ */ public boolean isSimilarNetworkRequestRetryScheduled( @NonNull TelephonyNetworkRequest networkRequest, @TransportType int transport) { + long now = SystemClock.elapsedRealtime(); for (int i = mDataRetryEntries.size() - 1; i >= 0; i--) { if (mDataRetryEntries.get(i) instanceof DataSetupRetryEntry) { DataSetupRetryEntry entry = (DataSetupRetryEntry) mDataRetryEntries.get(i); if (entry.getState() == DataRetryEntry.RETRY_STATE_NOT_RETRIED && entry.setupRetryType - == DataSetupRetryEntry.RETRY_TYPE_NETWORK_REQUESTS) { + == DataSetupRetryEntry.RETRY_TYPE_NETWORK_REQUESTS + && entry.retryElapsedTime > now) { if (entry.networkRequestList.isEmpty()) { String msg = "Invalid data retry entry detected"; logl(msg); @@ -1517,23 +1734,6 @@ } /** - * Check if there is any data setup retry scheduled with specified data profile. - * - * @param dataProfile The data profile to retry. - * @param transport The transport that the request is on. - * @return {@code true} if there is retry scheduled for this data profile. - */ - public boolean isAnySetupRetryScheduled(@NonNull DataProfile dataProfile, - @TransportType int transport) { - return mDataRetryEntries.stream() - .filter(DataSetupRetryEntry.class::isInstance) - .map(DataSetupRetryEntry.class::cast) - .anyMatch(entry -> entry.getState() == DataRetryEntry.RETRY_STATE_NOT_RETRIED - && dataProfile.equals(entry.dataProfile) - && entry.transport == transport); - } - - /** * Check if a specific data profile is explicitly throttled by the network. * * @param dataProfile The data profile to check. @@ -1563,9 +1763,11 @@ * @param dataNetwork The data network that was originally scheduled for handover retry. */ private void onCancelPendingHandoverRetry(@NonNull DataNetwork dataNetwork) { - mDataRetryEntries.removeIf(entry -> entry instanceof DataHandoverRetryEntry - && ((DataHandoverRetryEntry) entry).dataNetwork == dataNetwork); - mDataThrottlingEntries.removeIf(entry -> entry.dataNetwork == dataNetwork); + mDataRetryEntries.stream() + .filter(entry -> entry instanceof DataHandoverRetryEntry + && ((DataHandoverRetryEntry) entry).dataNetwork == dataNetwork + && entry.getState() == DataRetryEntry.RETRY_STATE_NOT_RETRIED) + .forEach(entry -> entry.setState(DataRetryEntry.RETRY_STATE_CANCELLED)); } /**
diff --git a/src/java/com/android/internal/telephony/data/DataServiceManager.java b/src/java/com/android/internal/telephony/data/DataServiceManager.java index 41190bf..2733aff 100644 --- a/src/java/com/android/internal/telephony/data/DataServiceManager.java +++ b/src/java/com/android/internal/telephony/data/DataServiceManager.java
@@ -22,11 +22,9 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.app.AppOpsManager; -import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.Intent; -import android.content.IntentFilter; import android.content.ServiceConnection; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; @@ -121,22 +119,6 @@ private List<DataCallResponse> mLastDataCallResponseList = Collections.EMPTY_LIST; - private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - final String action = intent.getAction(); - if (CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED.equals(action) - && mPhone.getPhoneId() == intent.getIntExtra( - CarrierConfigManager.EXTRA_SLOT_INDEX, 0)) { - // We should wait for carrier config changed event because the target binding - // package name can come from the carrier config. Note that we still get this event - // even when SIM is absent. - if (DBG) log("Carrier config changed. Try to bind data service."); - sendEmptyMessage(EVENT_BIND_DATA_SERVICE); - } - } - }; - private class DataServiceManagerDeathRecipient implements IBinder.DeathRecipient { @Override public void binderDied() { @@ -409,16 +391,18 @@ Context.LEGACY_PERMISSION_SERVICE); mAppOps = (AppOpsManager) phone.getContext().getSystemService(Context.APP_OPS_SERVICE); - IntentFilter intentFilter = new IntentFilter(); - intentFilter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED); - try { - Context contextAsUser = phone.getContext().createPackageContextAsUser( - phone.getContext().getPackageName(), 0, UserHandle.ALL); - contextAsUser.registerReceiver(mBroadcastReceiver, intentFilter, - null /* broadcastPermission */, null); - } catch (PackageManager.NameNotFoundException e) { - loge("Package name not found: " + e.getMessage()); - } + // Callback is executed in handler thread to directly handle config change. + mCarrierConfigManager.registerCarrierConfigChangeListener(this::post, + (slotIndex, subId, carrierId, specificCarrierId) -> { + if (slotIndex == mPhone.getPhoneId()) { + // We should wait for carrier config changed event because the + // target binding package name can come from the carrier config. + // Note that we still get this event even when SIM is absent. + if (DBG) log("Carrier config changed. Try to bind data service."); + rebindDataService(); + } + }); + PhoneConfigurationManager.registerForMultiSimConfigChange( this, EVENT_BIND_DATA_SERVICE, null); @@ -587,9 +571,10 @@ // Read package name from resource overlay packageName = mPhone.getContext().getResources().getString(resourceId); - PersistableBundle b = mCarrierConfigManager.getConfigForSubId(mPhone.getSubId()); - - if (b != null && !TextUtils.isEmpty(b.getString(carrierConfig))) { + PersistableBundle b = + CarrierConfigManager.getCarrierConfigSubset( + mPhone.getContext(), mPhone.getSubId(), carrierConfig); + if (!b.isEmpty() && !TextUtils.isEmpty(b.getString(carrierConfig))) { // If carrier config overrides it, use the one from carrier config packageName = b.getString(carrierConfig, packageName); } @@ -636,9 +621,10 @@ // Read package name from resource overlay className = mPhone.getContext().getResources().getString(resourceId); - PersistableBundle b = mCarrierConfigManager.getConfigForSubId(mPhone.getSubId()); - - if (b != null && !TextUtils.isEmpty(b.getString(carrierConfig))) { + PersistableBundle b = + CarrierConfigManager.getCarrierConfigSubset( + mPhone.getContext(), mPhone.getSubId(), carrierConfig); + if (!b.isEmpty() && !TextUtils.isEmpty(b.getString(carrierConfig))) { // If carrier config overrides it, use the one from carrier config className = b.getString(carrierConfig, className); } @@ -760,14 +746,19 @@ * @param onCompleteMessage The result callback for this request. */ public void startHandover(int cid, @NonNull Message onCompleteMessage) { - DataServiceCallbackWrapper callback = - setupCallbackHelper("startHandover", onCompleteMessage); - if (callback == null) { - loge("startHandover: callback == null"); + if (DBG) log("startHandover"); + if (!mBound) { + loge("Data service not bound."); sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE); return; } + DataServiceCallbackWrapper callback = + new DataServiceCallbackWrapper("startHandover"); + if (onCompleteMessage != null) { + mMessageMap.put(callback.asBinder(), onCompleteMessage); + } + try { sendMessageDelayed(obtainMessage(EVENT_WATCHDOG_TIMEOUT, callback), REQUEST_UNRESPONDED_TIMEOUT); @@ -790,13 +781,19 @@ * @param onCompleteMessage The result callback for this request. */ public void cancelHandover(int cid, @NonNull Message onCompleteMessage) { - DataServiceCallbackWrapper callback = - setupCallbackHelper("cancelHandover", onCompleteMessage); - if (callback == null) { + if (DBG) log("cancelHandover"); + if (!mBound) { + loge("Data service not bound."); sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE); return; } + DataServiceCallbackWrapper callback = + new DataServiceCallbackWrapper("cancelHandover"); + if (onCompleteMessage != null) { + mMessageMap.put(callback.asBinder(), onCompleteMessage); + } + try { sendMessageDelayed(obtainMessage(EVENT_WATCHDOG_TIMEOUT, callback), REQUEST_UNRESPONDED_TIMEOUT); @@ -808,26 +805,6 @@ } } - @Nullable - private DataServiceCallbackWrapper setupCallbackHelper( - @NonNull final String operationName, @NonNull final Message onCompleteMessage) { - if (DBG) log(operationName); - if (!mBound) { - sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE); - return null; - } - - DataServiceCallbackWrapper callback = - new DataServiceCallbackWrapper(operationName); - if (onCompleteMessage != null) { - if (DBG) log(operationName + ": onCompleteMessage set"); - mMessageMap.put(callback.asBinder(), onCompleteMessage); - } else { - if (DBG) log(operationName + ": onCompleteMessage not set"); - } - return callback; - } - /** * Set an APN to initial attach network. *
diff --git a/src/java/com/android/internal/telephony/data/DataSettingsManager.java b/src/java/com/android/internal/telephony/data/DataSettingsManager.java index e2c1aff..5178ae4 100644 --- a/src/java/com/android/internal/telephony/data/DataSettingsManager.java +++ b/src/java/com/android/internal/telephony/data/DataSettingsManager.java
@@ -19,7 +19,6 @@ import android.annotation.CallbackExecutor; import android.annotation.NonNull; import android.content.ContentResolver; -import android.content.Context; import android.content.SharedPreferences; import android.os.Handler; import android.os.Looper; @@ -29,12 +28,13 @@ import android.provider.Settings; import android.sysprop.TelephonyProperties; import android.telephony.CarrierConfigManager; -import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener; import android.telephony.TelephonyManager; +import android.telephony.TelephonyManager.MobileDataPolicy; import android.telephony.TelephonyRegistryManager; import android.telephony.data.ApnSetting; import android.telephony.data.ApnSetting.ApnType; +import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; import android.util.IndentingPrintWriter; @@ -42,12 +42,19 @@ import com.android.internal.telephony.GlobalSettingsHelper; import com.android.internal.telephony.Phone; +import com.android.internal.telephony.PhoneConstants; +import com.android.internal.telephony.PhoneFactory; import com.android.internal.telephony.SettingsObserver; -import com.android.internal.telephony.SubscriptionController; +import com.android.internal.telephony.data.DataConfigManager.DataConfigManagerCallback; +import com.android.internal.telephony.metrics.DeviceTelephonyPropertiesStats; +import com.android.internal.telephony.subscription.SubscriptionInfoInternal; +import com.android.internal.telephony.subscription.SubscriptionManagerService; +import com.android.internal.telephony.util.TelephonyUtils; import com.android.telephony.Rlog; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.concurrent.Executor; @@ -58,8 +65,9 @@ * data roaming settings, etc... */ public class DataSettingsManager extends Handler { - /** Event for data config updated. */ - private static final int EVENT_DATA_CONFIG_UPDATED = 1; + /** Invalid mobile data policy **/ + private static final int INVALID_MOBILE_DATA_POLICY = -1; + /** Event for call state changed. */ private static final int EVENT_CALL_STATE_CHANGED = 2; /** Event for subscriptions updated. */ @@ -68,10 +76,9 @@ private static final int EVENT_SET_DATA_ENABLED_FOR_REASON = 5; /** Event for set data roaming enabled. */ private static final int EVENT_SET_DATA_ROAMING_ENABLED = 6; - /** Event for set always allow MMS data. */ - private static final int EVENT_SET_ALWAYS_ALLOW_MMS_DATA = 7; - /** Event for set allow data during voice call. */ - private static final int EVENT_SET_ALLOW_DATA_DURING_VOICE_CALL = 8; + /** Event for set mobile data policy. */ + private static final int EVENT_SET_MOBILE_DATA_POLICY = 7; + /** Event for device provisioned changed. */ private static final int EVENT_PROVISIONED_CHANGED = 9; /** Event for provisioning data enabled setting changed. */ @@ -84,8 +91,8 @@ private final SettingsObserver mSettingsObserver; private final String mLogTag; private final LocalLog mLocalLog = new LocalLog(128); + private Set<Integer> mEnabledMobileDataPolicy = new HashSet<>(); private int mSubId; - private DataEnabledOverride mDataEnabledOverride; /** Data config manager */ private final @NonNull DataConfigManager mDataConfigManager; @@ -172,7 +179,7 @@ mResolver = mPhone.getContext().getContentResolver(); registerCallback(callback); mDataConfigManager = dataNetworkController.getDataConfigManager(); - mDataEnabledOverride = getDataEnabledOverride(); + refreshEnabledMobileDataPolicy(); mSettingsObserver = new SettingsObserver(mPhone.getContext(), this); mDataEnabledSettings.put(TelephonyManager.DATA_ENABLED_REASON_POLICY, true); mDataEnabledSettings.put(TelephonyManager.DATA_ENABLED_REASON_CARRIER, true); @@ -186,19 +193,13 @@ @Override public void handleMessage(Message msg) { switch (msg.what) { - case EVENT_DATA_CONFIG_UPDATED: { - if (mDataConfigManager.isConfigCarrierSpecific()) { - setDefaultDataRoamingEnabled(); - } - break; - } case EVENT_CALL_STATE_CHANGED: { updateDataEnabledAndNotify(TelephonyManager.DATA_ENABLED_REASON_OVERRIDE); break; } case EVENT_SUBSCRIPTIONS_CHANGED: { mSubId = (int) msg.obj; - mDataEnabledOverride = getDataEnabledOverride(); + refreshEnabledMobileDataPolicy(); updateDataEnabledAndNotify(TelephonyManager.DATA_ENABLED_REASON_USER); mPhone.notifyUserMobileDataStateChanged(isUserDataEnabled()); break; @@ -232,34 +233,10 @@ setDataRoamingFromUserAction(); break; } - case EVENT_SET_ALWAYS_ALLOW_MMS_DATA: { - boolean alwaysAllow = (boolean) msg.obj; - if (alwaysAllow == isMmsAlwaysAllowed()) { - break; - } - logl("AlwaysAllowMmsData changed to " + alwaysAllow); - mDataEnabledOverride.setAlwaysAllowMms(alwaysAllow); - if (SubscriptionController.getInstance() - .setDataEnabledOverrideRules(mSubId, mDataEnabledOverride.getRules())) { - updateDataEnabledAndNotify(TelephonyManager.DATA_ENABLED_REASON_OVERRIDE); - notifyDataEnabledOverrideChanged(alwaysAllow, - TelephonyManager.MOBILE_DATA_POLICY_MMS_ALWAYS_ALLOWED); - } - break; - } - case EVENT_SET_ALLOW_DATA_DURING_VOICE_CALL: { - boolean allow = (boolean) msg.obj; - if (allow == isDataAllowedInVoiceCall()) { - break; - } - logl("AllowDataDuringVoiceCall changed to " + allow); - mDataEnabledOverride.setDataAllowedInVoiceCall(allow); - if (SubscriptionController.getInstance() - .setDataEnabledOverrideRules(mSubId, mDataEnabledOverride.getRules())) { - updateDataEnabledAndNotify(TelephonyManager.DATA_ENABLED_REASON_OVERRIDE); - notifyDataEnabledOverrideChanged(allow, TelephonyManager - .MOBILE_DATA_POLICY_DATA_ON_NON_DEFAULT_DURING_VOICE_CALL); - } + case EVENT_SET_MOBILE_DATA_POLICY: { + int mobileDataPolicy = msg.arg1; + boolean enable = msg.arg2 == 1; + onSetMobileDataPolicy(mobileDataPolicy, enable); break; } case EVENT_PROVISIONED_CHANGED: @@ -280,7 +257,14 @@ * Called when needed to register for all events that data network controller is interested. */ private void onInitialize() { - mDataConfigManager.registerForConfigUpdate(this, EVENT_DATA_CONFIG_UPDATED); + mDataConfigManager.registerCallback(new DataConfigManagerCallback(this::post) { + @Override + public void onCarrierConfigChanged() { + if (mDataConfigManager.isConfigCarrierSpecific()) { + setDefaultDataRoamingEnabled(); + } + } + }); mSettingsObserver.observe(Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED), EVENT_PROVISIONED_CHANGED); mSettingsObserver.observe( @@ -399,9 +383,9 @@ return isProvisioningDataEnabled(); } else { boolean userDataEnabled = isUserDataEnabled(); - // Check if we should temporarily enable data in certain conditions. - boolean isDataEnabledOverridden = mDataEnabledOverride - .shouldOverrideDataEnabledSettings(mPhone, apnType); + // Check if we should temporarily enable data based on mobile data policy. + boolean isDataEnabledOverridden = isDataEnabledOverriddenForApn(apnType); + return ((userDataEnabled || isDataEnabledOverridden) && mDataEnabledSettings.get(TelephonyManager.DATA_ENABLED_REASON_POLICY) @@ -410,10 +394,11 @@ } } - private static boolean isStandAloneOpportunistic(int subId, Context context) { - SubscriptionInfo info = SubscriptionController.getInstance().getActiveSubscriptionInfo( - subId, context.getOpPackageName(), context.getAttributionTag()); - return (info != null) && info.isOpportunistic() && info.getGroupUuid() == null; + private boolean isStandAloneOpportunistic(int subId) { + SubscriptionInfoInternal subInfo = SubscriptionManagerService.getInstance() + .getSubscriptionInfoInternal(subId); + return subInfo != null && subInfo.isOpportunistic() + && TextUtils.isEmpty(subInfo.getGroupUuid()); } /** @@ -423,7 +408,7 @@ */ private void setUserDataEnabled(boolean enabled, String callingPackage) { // Can't disable data for stand alone opportunistic subscription. - if (isStandAloneOpportunistic(mSubId, mPhone.getContext()) && !enabled) return; + if (isStandAloneOpportunistic(mSubId) && !enabled) return; boolean changed = GlobalSettingsHelper.setInt(mPhone.getContext(), Settings.Global.MOBILE_DATA, mSubId, (enabled ? 1 : 0)); log("Set user data enabled to " + enabled + ", changed=" + changed + ", callingPackage=" @@ -445,7 +430,7 @@ } // User data should always be true for opportunistic subscription. - if (isStandAloneOpportunistic(mSubId, mPhone.getContext())) return true; + if (isStandAloneOpportunistic(mSubId)) return true; boolean defaultVal = TelephonyProperties.mobile_data().orElse(true); @@ -544,42 +529,37 @@ * has not manually set the value. The default value is {@link #isDefaultDataRoamingEnabled()}. */ public void setDefaultDataRoamingEnabled() { - // For SSSS, this is a per-phone property from DATA_ROAMING_IS_USER_SETTING_KEY. - // For DSDS, this is a per-sub property from Settings.Global.DATA_ROAMING + subId. // If the user has not manually set the value, use the default value. - boolean useCarrierSpecificDefault = false; - if (mPhone.getContext().getSystemService(TelephonyManager.class).getSimCount() != 1) { - String setting = Settings.Global.DATA_ROAMING + mPhone.getSubId(); - try { - Settings.Global.getInt(mResolver, setting); - } catch (Settings.SettingNotFoundException ex) { - // For multi-SIM phones, use the default value if uninitialized. - useCarrierSpecificDefault = true; - } - } else if (!isDataRoamingFromUserAction()) { - // For single-SIM phones, use the default value if user action is not set. - useCarrierSpecificDefault = true; - } - log("setDefaultDataRoamingEnabled: useCarrierSpecificDefault=" + useCarrierSpecificDefault); - if (useCarrierSpecificDefault) { - boolean defaultVal = isDefaultDataRoamingEnabled(); - setDataRoamingEnabledInternal(defaultVal); + if (!isDataRoamingFromUserAction()) { + setDataRoamingEnabledInternal(isDefaultDataRoamingEnabled()); } } /** - * Get whether the user has manually enabled or disabled data roaming from settings. - * @return {@code true} if the user has enabled data roaming and {@code false} if they have not. + * Get whether the user has manually enabled or disabled data roaming from settings for the + * current subscription. + * @return {@code true} if the user has manually enabled data roaming for the current + * subscription and {@code false} if they have not. */ private boolean isDataRoamingFromUserAction() { - final SharedPreferences sp = PreferenceManager - .getDefaultSharedPreferences(mPhone.getContext()); - // Since we don't want to unset user preferences after a system update, default to true if - // the preference does not exist and set it to false explicitly from factory reset. - if (!sp.contains(Phone.DATA_ROAMING_IS_USER_SETTING_KEY)) { - sp.edit().putBoolean(Phone.DATA_ROAMING_IS_USER_SETTING_KEY, false).commit(); + String key = Phone.DATA_ROAMING_IS_USER_SETTING_KEY + mPhone.getSubId(); + final SharedPreferences sp = + PreferenceManager.getDefaultSharedPreferences(mPhone.getContext()); + + // Set the default roaming from user action value if the preference doesn't exist + if (!sp.contains(key)) { + if (sp.contains(Phone.DATA_ROAMING_IS_USER_SETTING_KEY)) { + log("Reusing previous roaming from user action value for backwards compatibility."); + sp.edit().putBoolean(key, true).commit(); + } else { + log("Clearing roaming from user action value for new or upgrading devices."); + sp.edit().putBoolean(key, false).commit(); + } } - return sp.getBoolean(Phone.DATA_ROAMING_IS_USER_SETTING_KEY, true); + + boolean isUserSetting = sp.getBoolean(key, true); + log("isDataRoamingFromUserAction: key=" + key + ", isUserSetting=" + isUserSetting); + return isUserSetting; } /** @@ -588,49 +568,76 @@ * {@link #isDefaultDataRoamingEnabled()} will continue to be used. */ private void setDataRoamingFromUserAction() { - final SharedPreferences.Editor sp = PreferenceManager - .getDefaultSharedPreferences(mPhone.getContext()).edit(); - sp.putBoolean(Phone.DATA_ROAMING_IS_USER_SETTING_KEY, true).commit(); + String key = Phone.DATA_ROAMING_IS_USER_SETTING_KEY + mPhone.getSubId(); + log("setDataRoamingFromUserAction: key=" + key); + final SharedPreferences.Editor sp = + PreferenceManager.getDefaultSharedPreferences(mPhone.getContext()).edit(); + sp.putBoolean(key, true).commit(); } - private @NonNull DataEnabledOverride getDataEnabledOverride() { - return new DataEnabledOverride(SubscriptionController.getInstance() - .getDataEnabledOverrideRules(mSubId)); + /** Refresh the enabled mobile data policies from Telephony database */ + private void refreshEnabledMobileDataPolicy() { + SubscriptionInfoInternal subInfo = SubscriptionManagerService.getInstance() + .getSubscriptionInfoInternal(mSubId); + if (subInfo != null) { + mEnabledMobileDataPolicy = getMobileDataPolicyEnabled( + subInfo.getEnabledMobileDataPolicies()); + } } /** - * Set whether to always allow the MMS data connection. - * @param alwaysAllow {@code true} if MMS data is always allowed and {@code false} otherwise. + * @return {@code true} If the mobile data policy is enabled */ - public void setAlwaysAllowMmsData(boolean alwaysAllow) { - obtainMessage(EVENT_SET_ALWAYS_ALLOW_MMS_DATA, alwaysAllow).sendToTarget(); + public boolean isMobileDataPolicyEnabled(@MobileDataPolicy int mobileDataPolicy) { + return mEnabledMobileDataPolicy.contains(mobileDataPolicy); } /** - * Check whether MMS is always allowed. - * @return {@code true} if MMS is always allowed and {@code false} otherwise. + * Set mobile data policy enabled status + * @param mobileDataPolicy The mobile data policy to set + * @param enable {@code true} to enable the policy; {@code false} to disable. */ - public boolean isMmsAlwaysAllowed() { - return mDataEnabledOverride.isMmsAlwaysAllowed(); + public void setMobileDataPolicy(@MobileDataPolicy int mobileDataPolicy, boolean enable) { + obtainMessage(EVENT_SET_MOBILE_DATA_POLICY, mobileDataPolicy, enable ? 1 : 0) + .sendToTarget(); } /** - * Set whether to allow mobile data during voice call. This is used for allowing data on the - * non-default data SIM. When a voice call is placed on the non-default data SIM on DSDS - * devices, users will not be able to use mobile data. By calling this API, data will be - * temporarily enabled on the non-default data SIM during the life cycle of the voice call. - * @param allow {@code true} if data is allowed during a voice call and {@code false} otherwise. + * Store data mobile policy to Telephony database. + * + * @param mobileDataPolicy The mobile data policy that overrides user data enabled setting. + * @param enable {@code true} to enable the policy; {@code false} to remove the policy. */ - public void setAllowDataDuringVoiceCall(boolean allow) { - obtainMessage(EVENT_SET_ALLOW_DATA_DURING_VOICE_CALL, allow).sendToTarget(); + private void onSetMobileDataPolicy(@MobileDataPolicy int mobileDataPolicy, boolean enable) { + if (enable == isMobileDataPolicyEnabled(mobileDataPolicy)) { + return; + } + metricsRecordSetMobileDataPolicy(mobileDataPolicy); + + if (enable) { + mEnabledMobileDataPolicy.add(mobileDataPolicy); + } else { + mEnabledMobileDataPolicy.remove(mobileDataPolicy); + } + + String enabledMobileDataPolicies = mEnabledMobileDataPolicy.stream().map(String::valueOf) + .collect(Collectors.joining(",")); + SubscriptionManagerService.getInstance().setEnabledMobileDataPolicies(mSubId, + enabledMobileDataPolicies); + logl(TelephonyUtils.mobileDataPolicyToString(mobileDataPolicy) + " changed to " + + enable); + updateDataEnabledAndNotify(TelephonyManager.DATA_ENABLED_REASON_OVERRIDE); + notifyDataEnabledOverrideChanged(enable, mobileDataPolicy); } /** - * Check whether data is allowed during a voice call. - * @return {@code true} if data is allowed during voice call and {@code false} otherwise. + * Record the number of times a mobile data policy is toggled to metrics. + * @param mobileDataPolicy The mobile data policy that's toggled */ - public boolean isDataAllowedInVoiceCall() { - return mDataEnabledOverride.isDataAllowedInVoiceCall(); + private void metricsRecordSetMobileDataPolicy(@MobileDataPolicy int mobileDataPolicy) { + if (mobileDataPolicy == TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH) { + DeviceTelephonyPropertiesStats.recordAutoDataSwitchFeatureToggle(); + } } /** @@ -659,6 +666,85 @@ } /** + * Return the parsed mobile data policies. + * + * @param policies New mobile data policies in String format. + * @return A Set of parsed mobile data policies. + */ + public @NonNull @MobileDataPolicy Set<Integer> getMobileDataPolicyEnabled( + @NonNull String policies) { + Set<Integer> mobileDataPolicies = new HashSet<>(); + String[] rulesString = policies.trim().split("\\s*,\\s*"); + for (String rule : rulesString) { + if (!TextUtils.isEmpty(rule)) { + int parsedDataPolicy = parsePolicyFrom(rule); + if (parsedDataPolicy != INVALID_MOBILE_DATA_POLICY) { + mobileDataPolicies.add(parsedDataPolicy); + } + } + } + return mobileDataPolicies; + } + + /** + * Parse a mobile data policy retrieved from Telephony db. + * If the policy is in legacy format, convert it into the corresponding mobile data policy. + * + * @param policy Mobile data policy to be parsed from. + * @return Parsed mobile data policy. {@link #INVALID_MOBILE_DATA_POLICY} if string can't be + * parsed into a mobile data policy. + */ + private @MobileDataPolicy int parsePolicyFrom(@NonNull String policy) { + int dataPolicy; + try { + // parse as new override policy + dataPolicy = Integer.parseInt(policy); + } catch (NumberFormatException e) { + dataPolicy = INVALID_MOBILE_DATA_POLICY; + loge("parsePolicyFrom: invalid mobile data policy format: " + policy); + } + return dataPolicy; + } + + /** + * Check if data enabled is temporarily overridden in certain conditions. + * + * @param apnType The APN type to check. + * @return {@code true} if data enabled should be overridden. + */ + private boolean isDataEnabledOverriddenForApn(@ApnType int apnType) { + boolean overridden = false; + + // mobile data policy : MMS always allowed + if (isMobileDataPolicyEnabled(TelephonyManager.MOBILE_DATA_POLICY_MMS_ALWAYS_ALLOWED)) { + overridden = apnType == ApnSetting.TYPE_MMS; + } + + boolean isNonDds = mPhone.getSubId() != SubscriptionManagerService.getInstance() + .getDefaultDataSubId(); + + // mobile data policy : data during call + if (isMobileDataPolicyEnabled(TelephonyManager + .MOBILE_DATA_POLICY_DATA_ON_NON_DEFAULT_DURING_VOICE_CALL)) { + overridden = overridden || isNonDds && mPhone.getState() != PhoneConstants.State.IDLE; + } + + // mobile data policy : auto data switch + if (isMobileDataPolicyEnabled(TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH)) { + // check user enabled data on the default data phone + Phone defaultDataPhone = PhoneFactory.getPhone(SubscriptionManagerService.getInstance() + .getPhoneId(SubscriptionManagerService.getInstance() + .getDefaultDataSubId())); + if (defaultDataPhone == null) { + loge("isDataEnabledOverriddenForApn: unexpected defaultDataPhone is null"); + } else { + overridden = overridden || isNonDds && defaultDataPhone.isUserDataEnabled(); + } + } + return overridden; + } + + /** * Register the callback for receiving information from {@link DataSettingsManager}. * * @param callback The callback. @@ -700,7 +786,8 @@ + ", isProvisioningDataEnabled=" + isProvisioningDataEnabled() + ", mIsDataEnabled=" + mIsDataEnabled + ", mDataEnabledSettings=" + mDataEnabledSettings - + ", mDataEnabledOverride=" + mDataEnabledOverride + + ", mEnabledMobileDataPolicy=" + mEnabledMobileDataPolicy.stream() + .map(TelephonyUtils::mobileDataPolicyToString).collect(Collectors.joining(",")) + "]"; } @@ -756,7 +843,8 @@ .map(entry -> dataEnabledChangedReasonToString(entry.getKey()) + "=" + entry.getValue()) .collect(Collectors.joining(", "))); - pw.println("mDataEnabledOverride=" + mDataEnabledOverride); + pw.println("mEnabledMobileDataPolicy=" + mEnabledMobileDataPolicy.stream() + .map(TelephonyUtils::mobileDataPolicyToString).collect(Collectors.joining(","))); pw.println("Local logs:"); pw.increaseIndent(); mLocalLog.dump(fd, pw, args);
diff --git a/src/java/com/android/internal/telephony/data/DataStallRecoveryManager.java b/src/java/com/android/internal/telephony/data/DataStallRecoveryManager.java index 22d0498..6c6f064 100644 --- a/src/java/com/android/internal/telephony/data/DataStallRecoveryManager.java +++ b/src/java/com/android/internal/telephony/data/DataStallRecoveryManager.java
@@ -16,6 +16,8 @@ package com.android.internal.telephony.data; +import static android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE; + import android.annotation.CallbackExecutor; import android.annotation.ElapsedRealtimeLong; import android.annotation.IntDef; @@ -26,18 +28,19 @@ import android.os.Looper; import android.os.Message; import android.os.SystemClock; +import android.provider.Settings; import android.telephony.Annotation.RadioPowerState; import android.telephony.Annotation.ValidationStatus; import android.telephony.CellSignalStrength; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; -import android.telephony.data.DataProfile; import android.util.IndentingPrintWriter; import android.util.LocalLog; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.telephony.Phone; import com.android.internal.telephony.PhoneConstants; +import com.android.internal.telephony.data.DataConfigManager.DataConfigManagerCallback; import com.android.internal.telephony.data.DataNetworkController.DataNetworkControllerCallback; import com.android.internal.telephony.data.DataSettingsManager.DataSettingsManagerCallback; import com.android.internal.telephony.metrics.DataStallRecoveryStats; @@ -124,9 +127,6 @@ /** The data stall recovered by user (Mobile Data Power off/on). */ private static final int RECOVERED_REASON_USER = 3; - /** Event for data config updated. */ - private static final int EVENT_DATA_CONFIG_UPDATED = 1; - /** Event for triggering recovery action. */ private static final int EVENT_DO_RECOVERY = 2; @@ -147,17 +147,20 @@ private final @NonNull DataServiceManager mWwanDataServiceManager; /** The data stall recovery action. */ - private @RecoveryAction int mRecovryAction; + private @RecoveryAction int mRecoveryAction; /** The elapsed real time of last recovery attempted */ private @ElapsedRealtimeLong long mTimeLastRecoveryStartMs; /** Whether current network is good or not */ private boolean mIsValidNetwork; + /** Whether data stall recovery is triggered or not */ + private boolean mRecoveryTriggered = false; /** Whether data stall happened or not. */ private boolean mDataStalled; /** Whether the result of last action(RADIO_RESTART) reported. */ private boolean mLastActionReported; /** The real time for data stall start. */ - private @ElapsedRealtimeLong long mDataStallStartMs; + @VisibleForTesting + public @ElapsedRealtimeLong long mDataStallStartMs; /** Last data stall recovery action. */ private @RecoveryAction int mLastAction; /** Last radio power state. */ @@ -166,12 +169,16 @@ private boolean mNetworkCheckTimerStarted = false; /** Whether radio state changed during data stall. */ private boolean mRadioStateChangedDuringDataStall; + /** Whether airplane mode enabled during data stall. */ + private boolean mIsAirPlaneModeEnableDuringDataStall; /** Whether mobile data change to Enabled during data stall. */ private boolean mMobileDataChangedToEnabledDuringDataStall; /** Whether attempted all recovery steps. */ private boolean mIsAttemptedAllSteps; /** Whether internet network connected. */ private boolean mIsInternetNetworkConnected; + /** The durations for current recovery action */ + private @ElapsedRealtimeLong long mTimeElapsedOfCurrentAction; /** The array for the timers between recovery actions. */ private @NonNull long[] mDataStallRecoveryDelayMillisArray; @@ -245,7 +252,12 @@ /** Register for all events that data stall monitor is interested. */ private void registerAllEvents() { - mDataConfigManager.registerForConfigUpdate(this, EVENT_DATA_CONFIG_UPDATED); + mDataConfigManager.registerCallback(new DataConfigManagerCallback(this::post) { + @Override + public void onCarrierConfigChanged() { + DataStallRecoveryManager.this.onCarrierConfigUpdated(); + } + }); mDataNetworkController.registerDataNetworkControllerCallback( new DataNetworkControllerCallback(this::post) { @Override @@ -256,7 +268,7 @@ @Override public void onInternetDataNetworkConnected( - @NonNull List<DataProfile> dataProfiles) { + @NonNull List<DataNetwork> internetNetworks) { mIsInternetNetworkConnected = true; logl("onInternetDataNetworkConnected"); } @@ -274,9 +286,6 @@ public void handleMessage(Message msg) { logv("handleMessage = " + msg); switch (msg.what) { - case EVENT_DATA_CONFIG_UPDATED: - onDataConfigUpdated(); - break; case EVENT_DO_RECOVERY: doRecovery(); break; @@ -285,6 +294,12 @@ if (mDataStalled) { // Store the radio state changed flag only when data stall occurred. mRadioStateChangedDuringDataStall = true; + if (Settings.Global.getInt( + mPhone.getContext().getContentResolver(), + Settings.Global.AIRPLANE_MODE_ON, + 0) != 0) { + mIsAirPlaneModeEnableDuringDataStall = true; + } } break; default: @@ -319,8 +334,8 @@ return mSkipRecoveryActionArray[recoveryAction]; } - /** Called when data config was updated. */ - private void onDataConfigUpdated() { + /** Called when carrier config was updated. */ + private void onCarrierConfigUpdated() { updateDataStallRecoveryConfigs(); } @@ -343,19 +358,21 @@ */ private void reset() { mIsValidNetwork = true; + mRecoveryTriggered = false; mIsAttemptedAllSteps = false; mRadioStateChangedDuringDataStall = false; + mIsAirPlaneModeEnableDuringDataStall = false; mMobileDataChangedToEnabledDuringDataStall = false; cancelNetworkCheckTimer(); mTimeLastRecoveryStartMs = 0; mLastAction = RECOVERY_ACTION_GET_DATA_CALL_LIST; - mRecovryAction = RECOVERY_ACTION_GET_DATA_CALL_LIST; + mRecoveryAction = RECOVERY_ACTION_GET_DATA_CALL_LIST; } /** * Called when internet validation status changed. * - * @param validationStatus Validation status. + * @param status Validation status. */ private void onInternetValidationStatusChanged(@ValidationStatus int status) { logl("onInternetValidationStatusChanged: " + DataUtils.validationStatusToString(status)); @@ -363,15 +380,12 @@ setNetworkValidationState(isValid); if (isValid) { reset(); - } else { - if (mIsValidNetwork || isRecoveryAlreadyStarted()) { - mIsValidNetwork = false; - if (isRecoveryNeeded(true)) { - log("trigger data stall recovery"); - mTimeLastRecoveryStartMs = SystemClock.elapsedRealtime(); - sendMessage(obtainMessage(EVENT_DO_RECOVERY)); - } - } + } else if (isRecoveryNeeded(true)) { + // Set the network as invalid, because recovery is needed + mIsValidNetwork = false; + log("trigger data stall recovery"); + mTimeLastRecoveryStartMs = SystemClock.elapsedRealtime(); + sendMessage(obtainMessage(EVENT_DO_RECOVERY)); } } @@ -380,6 +394,7 @@ mTimeLastRecoveryStartMs = 0; mMobileDataChangedToEnabledDuringDataStall = false; mRadioStateChangedDuringDataStall = false; + mIsAirPlaneModeEnableDuringDataStall = false; setRecoveryAction(RECOVERY_ACTION_GET_DATA_CALL_LIST); } @@ -391,8 +406,8 @@ @VisibleForTesting @RecoveryAction public int getRecoveryAction() { - log("getRecoveryAction: " + recoveryActionToString(mRecovryAction)); - return mRecovryAction; + log("getRecoveryAction: " + recoveryActionToString(mRecoveryAction)); + return mRecoveryAction; } /** @@ -402,24 +417,24 @@ */ @VisibleForTesting public void setRecoveryAction(@RecoveryAction int action) { - mRecovryAction = action; + mRecoveryAction = action; // Check if the mobile data enabled is TRUE, it means that the mobile data setting changed // from DISABLED to ENABLED, we will set the next recovery action to // RECOVERY_ACTION_RADIO_RESTART due to already did the RECOVERY_ACTION_CLEANUP. if (mMobileDataChangedToEnabledDuringDataStall - && mRecovryAction < RECOVERY_ACTION_RADIO_RESTART) { - mRecovryAction = RECOVERY_ACTION_RADIO_RESTART; + && mRecoveryAction < RECOVERY_ACTION_RADIO_RESTART) { + mRecoveryAction = RECOVERY_ACTION_RADIO_RESTART; } // Check if the radio state changed from off to on, it means that the modem already // did the radio restart, we will set the next action to RECOVERY_ACTION_RESET_MODEM. if (mRadioStateChangedDuringDataStall && mRadioPowerState == TelephonyManager.RADIO_POWER_ON) { - mRecovryAction = RECOVERY_ACTION_RESET_MODEM; + mRecoveryAction = RECOVERY_ACTION_RESET_MODEM; } // To check the flag from DataConfigManager if we need to skip the step. - if (shouldSkipRecoveryAction(mRecovryAction)) { - switch (mRecovryAction) { + if (shouldSkipRecoveryAction(mRecoveryAction)) { + switch (mRecoveryAction) { case RECOVERY_ACTION_GET_DATA_CALL_LIST: setRecoveryAction(RECOVERY_ACTION_CLEANUP); break; @@ -435,7 +450,7 @@ } } - log("setRecoveryAction: " + recoveryActionToString(mRecovryAction)); + log("setRecoveryAction: " + recoveryActionToString(mRecoveryAction)); } /** @@ -444,7 +459,7 @@ * @return {@code true} if recovery already started, {@code false} recovery not started. */ private boolean isRecoveryAlreadyStarted() { - return getRecoveryAction() != RECOVERY_ACTION_GET_DATA_CALL_LIST; + return getRecoveryAction() != RECOVERY_ACTION_GET_DATA_CALL_LIST || mRecoveryTriggered; } /** @@ -457,6 +472,15 @@ } /** + * Get duration time for current recovery action. + * + * @return the time duration for current recovery action. + */ + private long getDurationOfCurrentRecoveryMs() { + return (SystemClock.elapsedRealtime() - mTimeElapsedOfCurrentAction); + } + + /** * Broadcast intent when data stall occurred. * * @param recoveryAction Send the data stall detected intent with RecoveryAction info. @@ -466,7 +490,7 @@ Intent intent = new Intent(TelephonyManager.ACTION_DATA_STALL_DETECTED); SubscriptionManager.putPhoneIdAndSubIdExtra(intent, mPhone.getPhoneId()); intent.putExtra(TelephonyManager.EXTRA_RECOVERY_ACTION, recoveryAction); - mPhone.getContext().sendBroadcast(intent); + mPhone.getContext().sendBroadcast(intent, READ_PRIVILEGED_PHONE_STATE); } /** Recovery Action: RECOVERY_ACTION_GET_DATA_CALL_LIST */ @@ -531,6 +555,12 @@ private boolean isRecoveryNeeded(boolean isNeedToCheckTimer) { logv("enter: isRecoveryNeeded()"); + // Skip if network is invalid and recovery was not started yet + if (!mIsValidNetwork && !isRecoveryAlreadyStarted()) { + logl("skip when network still remains invalid and recovery was not started yet"); + return false; + } + // Skip recovery if we have already attempted all steps. if (mIsAttemptedAllSteps) { logl("skip retrying continue recovery action"); @@ -566,7 +596,6 @@ logl("skip data stall recovery as data not connected"); return false; } - return true; } @@ -576,39 +605,65 @@ * @param isValid true for validation passed & false for validation failed */ private void setNetworkValidationState(boolean isValid) { + boolean isLogNeeded = false; + int timeDuration = 0; + int timeDurationOfCurrentAction = 0; + boolean isFirstDataStall = false; + boolean isFirstValidationAfterDoRecovery = false; + @RecoveredReason int reason = getRecoveredReason(isValid); // Validation status is true and was not data stall. if (isValid && !mDataStalled) { return; } if (!mDataStalled) { + // First data stall + isLogNeeded = true; mDataStalled = true; + isFirstDataStall = true; mDataStallStartMs = SystemClock.elapsedRealtime(); logl("data stall: start time = " + DataUtils.elapsedTimeToString(mDataStallStartMs)); - return; - } - - if (!mLastActionReported) { - @RecoveredReason int reason = getRecoveredReason(isValid); - int timeDuration = (int) (SystemClock.elapsedRealtime() - mDataStallStartMs); - logl( - "data stall: lastaction = " - + recoveryActionToString(mLastAction) - + ", isRecovered = " - + isValid - + ", reason = " - + recoveredReasonToString(reason) - + ", TimeDuration = " - + timeDuration); - DataStallRecoveryStats.onDataStallEvent( - mLastAction, mPhone, isValid, timeDuration, reason); + } else if (!mLastActionReported) { + // When the first validation status appears, enter this block. + isLogNeeded = true; + timeDuration = (int) (SystemClock.elapsedRealtime() - mDataStallStartMs); mLastActionReported = true; + isFirstValidationAfterDoRecovery = true; } if (isValid) { + // When the validation passed(mobile data resume), enter this block. + isLogNeeded = true; + timeDuration = (int) (SystemClock.elapsedRealtime() - mDataStallStartMs); mLastActionReported = false; mDataStalled = false; } + + if (isLogNeeded) { + timeDurationOfCurrentAction = + ((getRecoveryAction() > RECOVERY_ACTION_GET_DATA_CALL_LIST + && !mIsAttemptedAllSteps) + || mLastAction == RECOVERY_ACTION_RESET_MODEM) + ? (int) getDurationOfCurrentRecoveryMs() : 0; + DataStallRecoveryStats.onDataStallEvent( + mLastAction, mPhone, isValid, timeDuration, reason, + isFirstValidationAfterDoRecovery, timeDurationOfCurrentAction); + logl( + "data stall: " + + (isFirstDataStall == true ? "start" : isValid == false ? "in process" : "end") + + ", lastaction=" + + recoveryActionToString(mLastAction) + + ", isRecovered=" + + isValid + + ", reason=" + + recoveredReasonToString(reason) + + ", isFirstValidationAfterDoRecovery=" + + isFirstValidationAfterDoRecovery + + ", TimeDuration=" + + timeDuration + + ", TimeDurationForCurrentRecoveryAction=" + + timeDurationOfCurrentAction); + } } /** @@ -628,6 +683,9 @@ if (mLastAction > RECOVERY_ACTION_CLEANUP) { ret = RECOVERED_REASON_DSRM; } + if (mIsAirPlaneModeEnableDuringDataStall) { + ret = RECOVERED_REASON_USER; + } } else if (mMobileDataChangedToEnabledDuringDataStall) { ret = RECOVERED_REASON_USER; } @@ -638,6 +696,7 @@ private void doRecovery() { @RecoveryAction final int recoveryAction = getRecoveryAction(); final int signalStrength = mPhone.getSignalStrength().getLevel(); + mRecoveryTriggered = true; // DSRM used sendMessageDelayed to process the next event EVENT_DO_RECOVERY, so it need // to check the condition if DSRM need to process the recovery action. @@ -654,6 +713,7 @@ mLastActionReported = false; broadcastDataStallDetected(recoveryAction); mNetworkCheckTimerStarted = false; + mTimeElapsedOfCurrentAction = SystemClock.elapsedRealtime(); switch (recoveryAction) { case RECOVERY_ACTION_GET_DATA_CALL_LIST: @@ -799,6 +859,7 @@ pw.println("mIsValidNetwork=" + mIsValidNetwork); pw.println("mIsInternetNetworkConnected=" + mIsInternetNetworkConnected); + pw.println("mIsAirPlaneModeEnableDuringDataStall=" + mIsAirPlaneModeEnableDuringDataStall); pw.println("mDataStalled=" + mDataStalled); pw.println("mLastAction=" + recoveryActionToString(mLastAction)); pw.println("mIsAttemptedAllSteps=" + mIsAttemptedAllSteps);
diff --git a/src/java/com/android/internal/telephony/data/DataUtils.java b/src/java/com/android/internal/telephony/data/DataUtils.java index 0bfddc2..fc0c4f1 100644 --- a/src/java/com/android/internal/telephony/data/DataUtils.java +++ b/src/java/com/android/internal/telephony/data/DataUtils.java
@@ -41,6 +41,7 @@ import android.util.ArrayMap; import com.android.internal.telephony.data.DataNetworkController.NetworkRequestList; +import com.android.telephony.Rlog; import java.text.SimpleDateFormat; import java.util.ArrayList; @@ -61,6 +62,7 @@ /** The time format for converting time to readable string. */ private static final SimpleDateFormat TIME_FORMAT = new SimpleDateFormat("HH:mm:ss.SSS", Locale.US); + private static final String TAG = "DataUtils"; /** * Get the network capability from the string. @@ -164,6 +166,7 @@ case NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_BANDWIDTH: return "PRIORITIZE_BANDWIDTH"; default: + loge("Unknown network capability(" + netCap + ")"); return "Unknown(" + netCap + ")"; } } @@ -209,7 +212,9 @@ switch (status) { case NetworkAgent.VALIDATION_STATUS_VALID: return "VALID"; case NetworkAgent.VALIDATION_STATUS_NOT_VALID: return "INVALID"; - default: return "UNKNOWN(" + status + ")"; + default: + loge("Unknown validation status(" + status + ")"); + return "UNKNOWN(" + status + ")"; } } @@ -366,6 +371,7 @@ case ImsFeature.FEATURE_MMTEL: return "MMTEL"; case ImsFeature.FEATURE_RCS: return "RCS"; default: + loge("Unknown IMS feature(" + imsFeature + ")"); return "Unknown(" + imsFeature + ")"; } } @@ -404,9 +410,27 @@ .boxed().collect(Collectors.toSet()), v -> new NetworkRequestList()).add(networkRequest); } - // Sort the list, so the network request list contains higher priority will be in the front - // of the list. - return new ArrayList<>(requestsMap.values()).stream() + List<NetworkRequestList> requests = new ArrayList<>(); + // Create separate groups for enterprise requests with different enterprise IDs. + for (NetworkRequestList requestList : requestsMap.values()) { + List<TelephonyNetworkRequest> enterpriseRequests = requestList.stream() + .filter(request -> + request.hasCapability(NetworkCapabilities.NET_CAPABILITY_ENTERPRISE)) + .collect(Collectors.toList()); + if (enterpriseRequests.isEmpty()) { + requests.add(requestList); + continue; + } + // Key is the enterprise ID + Map<Integer, NetworkRequestList> enterpriseRequestsMap = new ArrayMap<>(); + for (TelephonyNetworkRequest request : enterpriseRequests) { + enterpriseRequestsMap.computeIfAbsent(request.getCapabilityDifferentiator(), + v -> new NetworkRequestList()).add(request); + } + requests.addAll(enterpriseRequestsMap.values()); + } + // Sort the requests so the network request list with higher priority will be at the front. + return requests.stream() .sorted((list1, list2) -> Integer.compare( list2.get(0).getPriority(), list1.get(0).getPriority())) .collect(Collectors.toList()); @@ -450,7 +474,9 @@ case DataCallResponse.LINK_STATUS_INACTIVE: return "INACTIVE"; case DataCallResponse.LINK_STATUS_ACTIVE: return "ACTIVE"; case DataCallResponse.LINK_STATUS_DORMANT: return "DORMANT"; - default: return "UNKNOWN(" + linkStatus + ")"; + default: + loge("Unknown link status(" + linkStatus + ")"); + return "UNKNOWN(" + linkStatus + ")"; } } @@ -487,7 +513,13 @@ case TelephonyManager.DATA_ACTIVITY_OUT: return "OUT"; case TelephonyManager.DATA_ACTIVITY_INOUT: return "INOUT"; case TelephonyManager.DATA_ACTIVITY_DORMANT: return "DORMANT"; - default: return "UNKNOWN(" + dataActivity + ")"; + default: + loge("Unknown data activity(" + dataActivity + ")"); + return "UNKNOWN(" + dataActivity + ")"; } } + + private static void loge(String msg) { + Rlog.e(TAG, msg); + } }
diff --git a/src/java/com/android/internal/telephony/data/LinkBandwidthEstimator.java b/src/java/com/android/internal/telephony/data/LinkBandwidthEstimator.java index 919bbc3..5ed12aa 100644 --- a/src/java/com/android/internal/telephony/data/LinkBandwidthEstimator.java +++ b/src/java/com/android/internal/telephony/data/LinkBandwidthEstimator.java
@@ -32,8 +32,6 @@ import android.os.HandlerExecutor; import android.os.Message; import android.os.OutcomeReceiver; -import android.os.Registrant; -import android.os.RegistrantList; import android.preference.PreferenceManager; import android.telephony.AccessNetworkConstants; import android.telephony.Annotation.DataActivityType; @@ -43,12 +41,14 @@ import android.telephony.CellIdentityNr; import android.telephony.CellIdentityTdscdma; import android.telephony.CellIdentityWcdma; +import android.telephony.CellInfo; import android.telephony.ModemActivityInfo; import android.telephony.NetworkRegistrationInfo; import android.telephony.ServiceState; import android.telephony.SignalStrength; import android.telephony.TelephonyCallback; import android.telephony.TelephonyManager; +import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; import android.util.LocalLog; @@ -97,6 +97,9 @@ @VisibleForTesting static final int MSG_DATA_REG_STATE_OR_RAT_CHANGED = 9; + @VisibleForTesting + static final int UNKNOWN_TAC = CellInfo.UNAVAILABLE; + // TODO: move the following parameters to xml file private static final int TRAFFIC_STATS_POLL_INTERVAL_MS = 1_000; private static final int MODEM_POLL_MIN_INTERVAL_MS = 5_000; @@ -180,7 +183,7 @@ private int mSignalLevel; private int mDataRat = TelephonyManager.NETWORK_TYPE_UNKNOWN; private int mTac; - private String mPlmn = UNKNOWN_PLMN; + @NonNull private String mPlmn = UNKNOWN_PLMN; private NetworkCapabilities mNetworkCapabilities; private NetworkBandwidth mPlaceholderNetwork; private long mFilterUpdateTimeMs; @@ -191,7 +194,6 @@ private String mBandwidthUpdatePlmn = UNKNOWN_PLMN; private BandwidthState mTxState = new BandwidthState(LINK_TX); private BandwidthState mRxState = new BandwidthState(LINK_RX); - private RegistrantList mBandwidthChangedRegistrants = new RegistrantList(); private long mLastPlmnOrRatChangeTimeMs; private long mLastDrsOrRatChangeTimeMs; @@ -351,33 +353,6 @@ } /** - * Registers for bandwidth estimation change. The bandwidth will be returned - * * {@link AsyncResult#result} as a {@link Pair} Object. - * * The {@link AsyncResult} will be in the notification {@link Message#obj}. - * @param h handler to notify - * @param what what code of message when delivered - * @param obj placed in Message.obj - * - * @deprecated Use {@link #registerCallback(LinkBandwidthEstimatorCallback)}. - */ - @Deprecated //TODO: Remove once old data stack is removed. - public void registerForBandwidthChanged(Handler h, int what, Object obj) { - Registrant r = new Registrant(h, what, obj); - mBandwidthChangedRegistrants.add(r); - } - - /** - * Unregisters for bandwidth estimation change. - * @param h handler to notify - * - * @deprecated Use {@link #unregisterCallback(LinkBandwidthEstimatorCallback)}. - */ - @Deprecated //TODO: Remove once old data stack is removed. - public void unregisterForBandwidthChanged(Handler h) { - mBandwidthChangedRegistrants.remove(h); - } - - /** * Register the callback for receiving information from {@link LinkBandwidthEstimator}. * * @param callback The callback. @@ -441,6 +416,7 @@ return; } mIsOnDefaultRoute = isOnDefaultRoute; + logd("mIsOnDefaultRoute " + mIsOnDefaultRoute); handleTrafficStatsPollConditionChanged(); } @@ -465,6 +441,13 @@ if (mScreenOn && mIsOnDefaultRoute && mIsOnActiveData) { updateDataRatCellIdentityBandwidth(); handleTrafficStatsPoll(); + } else { + logd("Traffic status poll stopped"); + if (mDataActivity != TelephonyManager.DATA_ACTIVITY_NONE) { + mDataActivity = TelephonyManager.DATA_ACTIVITY_NONE; + mLinkBandwidthEstimatorCallbacks.forEach(callback -> callback.invokeFromExecutor( + () -> callback.onDataActivityChanged(mDataActivity))); + } } } @@ -475,6 +458,23 @@ long txBytesDelta = mobileTxBytes - mLastMobileTxBytes; long rxBytesDelta = mobileRxBytes - mLastMobileRxBytes; + int dataActivity; + if (txBytesDelta > 0 && rxBytesDelta > 0) { + dataActivity = TelephonyManager.DATA_ACTIVITY_INOUT; + } else if (rxBytesDelta > 0) { + dataActivity = TelephonyManager.DATA_ACTIVITY_IN; + } else if (txBytesDelta > 0) { + dataActivity = TelephonyManager.DATA_ACTIVITY_OUT; + } else { + dataActivity = TelephonyManager.DATA_ACTIVITY_NONE; + } + + if (mDataActivity != dataActivity) { + mDataActivity = dataActivity; + mLinkBandwidthEstimatorCallbacks.forEach(callback -> callback.invokeFromExecutor( + () -> callback.onDataActivityChanged(dataActivity))); + } + // Schedule the next traffic stats poll sendEmptyMessageDelayed(MSG_TRAFFIC_STATS_POLL, TRAFFIC_STATS_POLL_INTERVAL_MS); @@ -523,23 +523,6 @@ return; } - int dataActivity; - if (txBytesDelta > 0 && rxBytesDelta > 0) { - dataActivity = TelephonyManager.DATA_ACTIVITY_INOUT; - } else if (rxBytesDelta > 0) { - dataActivity = TelephonyManager.DATA_ACTIVITY_IN; - } else if (txBytesDelta > 0) { - dataActivity = TelephonyManager.DATA_ACTIVITY_OUT; - } else { - dataActivity = TelephonyManager.DATA_ACTIVITY_NONE; - } - - if (mDataActivity != dataActivity) { - mDataActivity = dataActivity; - mLinkBandwidthEstimatorCallbacks.forEach(callback -> callback.invokeFromExecutor( - () -> callback.onDataActivityChanged(dataActivity))); - } - long timeSinceLastFilterUpdateMs = currTimeMs - mFilterUpdateTimeMs; // Update filter if (timeSinceLastFilterUpdateMs >= FILTER_UPDATE_MAX_INTERVAL_MS) { @@ -695,7 +678,7 @@ return; } int linkBandwidthKbps = (int) linkBandwidthLongKbps; - mBwSampleValid = true; + mBwSampleValid = linkBandwidthKbps > 0; mBwSampleKbps = linkBandwidthKbps; String dataRatName = getDataRatName(mDataRat); @@ -715,7 +698,7 @@ int filterInKbps = mBwSampleValid ? mBwSampleKbps : avgKbps; long currTimeMs = mTelephonyFacade.getElapsedSinceBootMillis(); - int timeDeltaSec = (int) (currTimeMs - mBwSampleValidTimeMs) / 1000; + int timeDeltaSec = (int) ((currTimeMs - mBwSampleValidTimeMs) / 1000); // If the operation condition changes significantly since the last update // or the sample has higher BW, use a faster filter. Otherwise, use a slow filter @@ -930,9 +913,6 @@ private void sendLinkBandwidthToDataConnection(int linkBandwidthTxKps, int linkBandwidthRxKps) { logv("send to DC tx " + linkBandwidthTxKps + " rx " + linkBandwidthRxKps); - Pair<Integer, Integer> bandwidthInfo = - new Pair<Integer, Integer>(linkBandwidthTxKps, linkBandwidthRxKps); - mBandwidthChangedRegistrants.notifyRegistrants(new AsyncResult(null, bandwidthInfo, null)); mLinkBandwidthEstimatorCallbacks.forEach(callback -> callback.invokeFromExecutor( () -> callback.onBandwidthChanged(linkBandwidthTxKps, linkBandwidthRxKps))); } @@ -1054,22 +1034,28 @@ } private boolean updateDataRatCellIdentityBandwidth() { - boolean updatedPlmn = false; - CellIdentity cellIdentity = mPhone.getCurrentCellIdentity(); - mTac = getTac(cellIdentity); + final ServiceState ss = mPhone.getServiceState(); + final CellIdentity cellIdentity = mPhone.getCurrentCellIdentity(); + + boolean hasChanged = false; String plmn; - if (mPhone.getServiceState().getOperatorNumeric() != null) { - plmn = mPhone.getServiceState().getOperatorNumeric(); + // Why does updating the TAC not result in hasChanged == true? + // Legacy behavior is currently being preserved for now, but it might very + // well be incorrect. The TAC is part of the network key. This is very fishy. + mTac = getTac(cellIdentity); + + /* ss should always be non-null */ + if (!TextUtils.isEmpty(ss.getOperatorNumeric())) { + plmn = ss.getOperatorNumeric(); + } else if (cellIdentity != null && !TextUtils.isEmpty(cellIdentity.getPlmn())) { + plmn = cellIdentity.getPlmn(); } else { - if (cellIdentity.getPlmn() != null) { - plmn = cellIdentity.getPlmn(); - } else { - plmn = UNKNOWN_PLMN; - } + plmn = UNKNOWN_PLMN; } - if (mPlmn == null || !plmn.equals(mPlmn)) { - updatedPlmn = true; + + if (!mPlmn.equals(plmn)) { + hasChanged = true; mPlmn = plmn; } @@ -1078,20 +1064,19 @@ if (nri != null) { int dataRat = nri.getAccessNetworkTechnology(); if (dataRat != mDataRat) { - updatedRat = true; + hasChanged = true; mDataRat = dataRat; updateStaticBwValue(mDataRat); updateByteCountThr(); } } - boolean updatedPlmnOrRat = updatedPlmn || updatedRat; - if (updatedPlmnOrRat) { + if (hasChanged) { resetBandwidthFilter(); updateTxRxBandwidthFilterSendToDataConnection(); mLastPlmnOrRatChangeTimeMs = mTelephonyFacade.getElapsedSinceBootMillis(); } - return updatedPlmnOrRat; + return hasChanged; } private int getTac(@NonNull CellIdentity cellIdentity) { @@ -1110,7 +1095,7 @@ if (cellIdentity instanceof CellIdentityGsm) { return ((CellIdentityGsm) cellIdentity).getLac(); } - return 0; + return UNKNOWN_TAC; } private class TelephonyCallbackImpl extends TelephonyCallback implements @@ -1135,16 +1120,17 @@ mLocalLog.log(msg); } - @VisibleForTesting - static final int UNKNOWN_TAC = -1; // Map with NetworkKey as the key and NetworkBandwidth as the value. // NetworkKey is specified by the PLMN, data RAT and TAC of network. // NetworkBandwidth represents the bandwidth related stats of each network. private final Map<NetworkKey, NetworkBandwidth> mNetworkMap = new ArrayMap<>(); + private static class NetworkKey { + private final String mPlmn; private final String mDataRat; private final int mTac; + NetworkKey(String plmn, int tac, String dataRat) { mPlmn = plmn; mTac = tac; @@ -1175,22 +1161,21 @@ StringBuilder sb = new StringBuilder(); sb.append("Plmn").append(mPlmn) .append("Rat").append(mDataRat) - .append("Tac").append(mTac) - .toString(); + .append("Tac").append(mTac); return sb.toString(); } } @NonNull - private NetworkBandwidth lookupNetwork(String plmn, String dataRat) { + private NetworkBandwidth lookupNetwork(@NonNull String plmn, String dataRat) { return lookupNetwork(plmn, UNKNOWN_TAC, dataRat); } /** Look up NetworkBandwidth and create a new one if it doesn't exist */ @VisibleForTesting @NonNull - public NetworkBandwidth lookupNetwork(String plmn, int tac, String dataRat) { - if (plmn == null || dataRat.equals( + public NetworkBandwidth lookupNetwork(@NonNull String plmn, int tac, String dataRat) { + if (dataRat.equals( TelephonyManager.getNetworkTypeName(TelephonyManager.NETWORK_TYPE_UNKNOWN))) { return mPlaceholderNetwork; } @@ -1206,7 +1191,9 @@ /** A class holding link bandwidth related stats */ @VisibleForTesting public class NetworkBandwidth { + private final String mKey; + NetworkBandwidth(String key) { mKey = key; } @@ -1302,5 +1289,4 @@ pw.println(); pw.flush(); } - }
diff --git a/src/java/com/android/internal/telephony/data/NotifyQosSessionInterface.java b/src/java/com/android/internal/telephony/data/NotifyQosSessionInterface.java deleted file mode 100644 index 554177a..0000000 --- a/src/java/com/android/internal/telephony/data/NotifyQosSessionInterface.java +++ /dev/null
@@ -1,54 +0,0 @@ -/* - * 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 com.android.internal.telephony.data; - -import android.annotation.NonNull; -import android.net.NetworkAgent; -import android.net.QosSessionAttributes; - -/** - * The temporary interface that is shared by - * {@link com.android.internal.telephony.dataconnection.DcNetworkAgent} and - * {@link com.android.internal.telephony.data.TelephonyNetworkAgent} so they can both interact - * with {@link QosCallbackTracker}. - */ -// TODO: Remove after DcNetworkAgent is removed. -public interface NotifyQosSessionInterface { - /** - * Sends the attributes of Qos Session back to the Application. This method is create for - * Mockito to mock since - * {@link NetworkAgent#sendQosSessionAvailable(int, int, QosSessionAttributes)} is - * {@code final} that can't be mocked. - * - * @param qosCallbackId the callback id that the session belongs to. - * @param sessionId the unique session id across all Qos Sessions. - * @param attributes the attributes of the Qos Session. - */ - void notifyQosSessionAvailable(int qosCallbackId, int sessionId, - @NonNull QosSessionAttributes attributes); - - /** - * Sends event that the Qos Session was lost. This method is create for Mockito to mock - * since {@link NetworkAgent#sendQosSessionLost(int, int, int)} is {@code final} that can't be - * mocked.. - * - * @param qosCallbackId the callback id that the session belongs to. - * @param sessionId the unique session id across all Qos Sessions. - * @param qosSessionType the session type {@code QosSession#QosSessionType}. - */ - void notifyQosSessionLost(int qosCallbackId, int sessionId, int qosSessionType); -}
diff --git a/src/java/com/android/internal/telephony/data/PhoneSwitcher.java b/src/java/com/android/internal/telephony/data/PhoneSwitcher.java index cc5f447..5e13f6b 100644 --- a/src/java/com/android/internal/telephony/data/PhoneSwitcher.java +++ b/src/java/com/android/internal/telephony/data/PhoneSwitcher.java
@@ -31,6 +31,10 @@ import static java.util.Arrays.copyOf; import android.annotation.NonNull; +import android.annotation.Nullable; +import android.app.Notification; +import android.app.NotificationManager; +import android.app.PendingIntent; import android.compat.annotation.UnsupportedAppUsage; import android.content.BroadcastReceiver; import android.content.Context; @@ -46,6 +50,7 @@ import android.net.TelephonyNetworkSpecifier; import android.os.AsyncResult; import android.os.Build; +import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.os.Message; @@ -53,20 +58,23 @@ import android.os.Registrant; import android.os.RegistrantList; import android.os.RemoteException; +import android.provider.Settings; +import android.telephony.AccessNetworkConstants; import android.telephony.CarrierConfigManager; +import android.telephony.NetworkRegistrationInfo; import android.telephony.PhoneCapability; import android.telephony.PhoneStateListener; import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import android.telephony.TelephonyRegistryManager; -import android.telephony.data.ApnSetting; import android.telephony.ims.ImsReasonInfo; import android.telephony.ims.ImsRegistrationAttributes; import android.telephony.ims.RegistrationManager; import android.telephony.ims.stub.ImsRegistrationImplBase; import android.util.ArrayMap; import android.util.LocalLog; +import android.util.Log; import com.android.ims.ImsException; import com.android.ims.ImsManager; @@ -78,17 +86,17 @@ import com.android.internal.telephony.PhoneConfigurationManager; import com.android.internal.telephony.PhoneFactory; import com.android.internal.telephony.RadioConfig; -import com.android.internal.telephony.SubscriptionController; -import com.android.internal.telephony.SubscriptionController.WatchedInt; import com.android.internal.telephony.TelephonyIntents; import com.android.internal.telephony.data.DataNetworkController.NetworkRequestList; import com.android.internal.telephony.data.DataSettingsManager.DataSettingsManagerCallback; -import com.android.internal.telephony.dataconnection.ApnConfigTypeRepository; -import com.android.internal.telephony.dataconnection.DcRequest; import com.android.internal.telephony.metrics.TelephonyMetrics; import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent; import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.DataSwitch; import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.OnDemandDataSwitch; +import com.android.internal.telephony.subscription.SubscriptionInfoInternal; +import com.android.internal.telephony.subscription.SubscriptionManagerService; +import com.android.internal.telephony.subscription.SubscriptionManagerService.WatchedInt; +import com.android.internal.telephony.util.NotificationChannelController; import com.android.internal.util.IndentingPrintWriter; import com.android.telephony.Rlog; @@ -96,7 +104,6 @@ import java.io.PrintWriter; import java.util.ArrayList; import java.util.Calendar; -import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -112,9 +119,23 @@ */ public class PhoneSwitcher extends Handler { private static final String LOG_TAG = "PhoneSwitcher"; - protected static final boolean VDBG = false; + protected static final boolean VDBG = Rlog.isLoggable(LOG_TAG, Log.VERBOSE); - private static final int DEFAULT_NETWORK_CHANGE_TIMEOUT_MS = 5000; + /** Fragment "key" argument passed thru {@link #SETTINGS_EXTRA_SHOW_FRAGMENT_ARGUMENTS} */ + private static final String SETTINGS_EXTRA_FRAGMENT_ARG_KEY = ":settings:fragment_args_key"; + /** + * When starting this activity, this extra can also be specified to supply a Bundle of arguments + * to pass to that fragment when it is instantiated during the initial creation of the activity. + */ + private static final String SETTINGS_EXTRA_SHOW_FRAGMENT_ARGUMENTS = + ":settings:show_fragment_args"; + /** The res Id of the auto data switch fragment in settings. **/ + private static final String AUTO_DATA_SWITCH_SETTING_R_ID = "auto_data_switch"; + /** Notification tag **/ + private static final String AUTO_DATA_SWITCH_NOTIFICATION_TAG = "auto_data_switch"; + /** Notification ID **/ + private static final int AUTO_DATA_SWITCH_NOTIFICATION_ID = 1; + private static final int MODEM_COMMAND_RETRY_PERIOD_MS = 5000; // After the emergency call ends, wait for a few seconds to see if we enter ECBM before starting // the countdown to remove the emergency DDS override. @@ -182,10 +203,9 @@ } } - protected final List<DcRequest> mPrioritizedDcRequests = new ArrayList<>(); private final @NonNull NetworkRequestList mNetworkRequestList = new NetworkRequestList(); protected final RegistrantList mActivePhoneRegistrants; - protected final SubscriptionController mSubscriptionController; + private final SubscriptionManagerService mSubscriptionManagerService; protected final Context mContext; private final LocalLog mLocalLog; protected PhoneState[] mPhoneStates; @@ -194,6 +214,10 @@ @VisibleForTesting protected final CellularNetworkValidator mValidator; private int mPendingSwitchSubId = INVALID_SUBSCRIPTION_ID; + /** The reason for the last time changing preferred data sub **/ + private int mLastSwitchPreferredDataReason = -1; + /** {@code true} if we've displayed the notification the first time auto switch occurs **/ + private boolean mDisplayedAutoSwitchNotification = false; private boolean mPendingSwitchNeedValidation; @VisibleForTesting public final CellularNetworkValidator.ValidationCallback mValidationCallback = @@ -226,9 +250,15 @@ // Internet data if mOpptDataSubId is not set. protected int mPrimaryDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; - // mOpptDataSubId must be an active subscription. If it's set, it overrides mPrimaryDataSubId - // to be used for Internet data. - private int mOpptDataSubId = SubscriptionManager.DEFAULT_SUBSCRIPTION_ID; + // The automatically suggested preferred data subId (by e.g. CBRS or auto data switch), a + // candidate for preferred data subId, which is eventually presided by + // updatePreferredDataPhoneId(). + // If CBRS/auto switch feature selects the primary data subId as the preferred data subId, + // its value will be DEFAULT_SUBSCRIPTION_ID. + private int mAutoSelectedDataSubId = SubscriptionManager.DEFAULT_SUBSCRIPTION_ID; + + /** The count of consecutive auto switch validation failure **/ + private int mAutoSwitchRetryFailedCount = 0; // The phone ID that has an active voice call. If set, and its mobile data setting is on, // it will become the mPreferredDataPhoneId. @@ -244,14 +274,8 @@ protected int mPreferredDataPhoneId = SubscriptionManager.INVALID_PHONE_INDEX; // Subscription ID corresponds to mPreferredDataPhoneId. - protected WatchedInt mPreferredDataSubId = - new WatchedInt(SubscriptionManager.INVALID_SUBSCRIPTION_ID) { - @Override - public void set(int newValue) { - super.set(newValue); - SubscriptionController.invalidateActiveDataSubIdCaches(); - } - }; + protected WatchedInt mPreferredDataSubId = new WatchedInt( + SubscriptionManager.INVALID_SUBSCRIPTION_ID); // If non-null, An emergency call is about to be started, is ongoing, or has just ended and we // are overriding the DDS. @@ -261,7 +285,7 @@ private ISetOpportunisticDataCallback mSetOpptSubCallback; private static final int EVENT_PRIMARY_DATA_SUB_CHANGED = 101; - protected static final int EVENT_SUBSCRIPTION_CHANGED = 102; + protected static final int EVENT_SUBSCRIPTION_CHANGED = 102; private static final int EVENT_REQUEST_NETWORK = 103; private static final int EVENT_RELEASE_NETWORK = 104; // ECBM has started/ended. If we just ended an emergency call and mEmergencyOverride is not @@ -276,14 +300,12 @@ // mEmergencyOverride, start the countdown to remove the override using the message // EVENT_REMOVE_DDS_EMERGENCY_OVERRIDE. The only exception to this is if the device moves to // ECBM, which is detected by EVENT_EMERGENCY_TOGGLE. - @VisibleForTesting - public static final int EVENT_PRECISE_CALL_STATE_CHANGED = 109; + private static final int EVENT_PRECISE_CALL_STATE_CHANGED = 109; private static final int EVENT_NETWORK_VALIDATION_DONE = 110; - private static final int EVENT_REMOVE_DEFAULT_NETWORK_CHANGE_CALLBACK = 111; + private static final int EVENT_EVALUATE_AUTO_SWITCH = 111; private static final int EVENT_MODEM_COMMAND_DONE = 112; private static final int EVENT_MODEM_COMMAND_RETRY = 113; - @VisibleForTesting - public static final int EVENT_DATA_ENABLED_CHANGED = 114; + private static final int EVENT_SERVICE_STATE_CHANGED = 114; // An emergency call is about to be originated and requires the DDS to be overridden. // Uses EVENT_PRECISE_CALL_STATE_CHANGED message to start countdown to finish override defined // in mEmergencyOverride. If EVENT_PRECISE_CALL_STATE_CHANGED does not come in @@ -292,12 +314,11 @@ // If it exists, remove the current mEmergencyOverride DDS override. private static final int EVENT_REMOVE_DDS_EMERGENCY_OVERRIDE = 116; // If it exists, remove the current mEmergencyOverride DDS override. - @VisibleForTesting - public static final int EVENT_MULTI_SIM_CONFIG_CHANGED = 117; + private static final int EVENT_MULTI_SIM_CONFIG_CHANGED = 117; private static final int EVENT_NETWORK_AVAILABLE = 118; private static final int EVENT_PROCESS_SIM_STATE_CHANGE = 119; - @VisibleForTesting - public static final int EVENT_IMS_RADIO_TECH_CHANGED = 120; + private static final int EVENT_IMS_RADIO_TECH_CHANGED = 120; + private static final int EVENT_MEETS_AUTO_DATA_SWITCH_STATE = 121; // List of events triggers re-evaluations private static final String EVALUATION_REASON_RADIO_ON = "EVENT_RADIO_ON"; @@ -317,13 +338,29 @@ // Default timeout value of network validation in millisecond. private final static int DEFAULT_VALIDATION_EXPIRATION_TIME = 2000; - private Boolean mHasRegisteredDefaultNetworkChangeCallback = false; - private ConnectivityManager mConnectivityManager; private int mImsRegistrationTech = REGISTRATION_TECH_NONE; private List<Set<CommandException.Error>> mCurrentDdsSwitchFailure; + /** + * {@code true} if requires ping test before switching preferred data modem; otherwise, switch + * even if ping test fails. + */ + private boolean mRequirePingTestBeforeDataSwitch = true; + + /** + * Time threshold in ms to define a internet connection status to be stable(e.g. out of service, + * in service, wifi is the default active network.etc), while -1 indicates auto switch + * feature disabled. + */ + private long mAutoDataSwitchAvailabilityStabilityTimeThreshold = -1; + + /** + * The maximum number of retries when a validation for switching failed. + */ + private int mAutoDataSwitchValidationMaxRetry; + /** Data settings manager callback. Key is the phone id. */ private final @NonNull Map<Integer, DataSettingsManagerCallback> mDataSettingsManagerCallbacks = new ArrayMap<>(); @@ -331,18 +368,37 @@ private class DefaultNetworkCallback extends ConnectivityManager.NetworkCallback { public int mExpectedSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; public int mSwitchReason = TelephonyEvent.DataSwitch.Reason.DATA_SWITCH_REASON_UNKNOWN; + public boolean isDefaultNetworkOnCellular = false; @Override public void onCapabilitiesChanged(Network network, NetworkCapabilities networkCapabilities) { - if (networkCapabilities.hasTransport(TRANSPORT_CELLULAR) - && SubscriptionManager.isValidSubscriptionId(mExpectedSubId) - && mExpectedSubId == getSubIdFromNetworkSpecifier( - networkCapabilities.getNetworkSpecifier())) { - logDataSwitchEvent( - mExpectedSubId, - TelephonyEvent.EventState.EVENT_STATE_END, - mSwitchReason); - removeDefaultNetworkChangeCallback(); + if (networkCapabilities.hasTransport(TRANSPORT_CELLULAR)) { + isDefaultNetworkOnCellular = true; + if (SubscriptionManager.isValidSubscriptionId(mExpectedSubId) + && mExpectedSubId == getSubIdFromNetworkSpecifier( + networkCapabilities.getNetworkSpecifier())) { + logDataSwitchEvent( + mExpectedSubId, + TelephonyEvent.EventState.EVENT_STATE_END, + mSwitchReason); + mExpectedSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; + mSwitchReason = TelephonyEvent.DataSwitch.Reason.DATA_SWITCH_REASON_UNKNOWN; + } + } else { + if (isDefaultNetworkOnCellular) { + // non-cellular transport is active + isDefaultNetworkOnCellular = false; + log("default network is active on non cellular"); + evaluateIfAutoSwitchIsNeeded(); + } + } + } + + @Override + public void onLost(Network network) { + // try find an active sub to switch to + if (!hasMessages(EVENT_EVALUATE_AUTO_SWITCH)) { + sendEmptyMessage(EVENT_EVALUATE_AUTO_SWITCH); } } } @@ -395,49 +451,26 @@ public static PhoneSwitcher make(int maxDataAttachModemCount, Context context, Looper looper) { if (sPhoneSwitcher == null) { sPhoneSwitcher = new PhoneSwitcher(maxDataAttachModemCount, context, looper); - SubscriptionController.invalidateActiveDataSubIdCaches(); } return sPhoneSwitcher; } - /** - * Whether this phone IMS registration is on its original network. This result impacts - * whether we want to do DDS switch to the phone having voice call. - * If it's registered on IWLAN or cross SIM in multi-SIM case, return false. Otherwise, - * return true. - */ - private boolean isImsOnOriginalNetwork(Phone phone) { - if (phone == null) return false; - int phoneId = phone.getPhoneId(); - if (!SubscriptionManager.isValidPhoneId(phoneId)) return false; - - int imsRegTech = mImsRegTechProvider.get(mContext, phoneId); - // If IMS is registered on IWLAN or cross SIM, return false. - boolean isOnOriginalNetwork = (imsRegTech != REGISTRATION_TECH_IWLAN) - && (imsRegTech != REGISTRATION_TECH_CROSS_SIM); - if (!isOnOriginalNetwork) { - log("IMS call on IWLAN or cross SIM. Call will be ignored for DDS switch"); - } - return isOnOriginalNetwork; - } - - private boolean isPhoneInVoiceCallChanged() { + private boolean updatesIfPhoneInVoiceCallChanged() { int oldPhoneIdInVoiceCall = mPhoneIdInVoiceCall; // If there's no active call, the value will become INVALID_PHONE_INDEX // and internet data will be switched back to system selected or user selected // subscription. mPhoneIdInVoiceCall = SubscriptionManager.INVALID_PHONE_INDEX; for (Phone phone : PhoneFactory.getPhones()) { - if (isPhoneInVoiceCall(phone) || (isPhoneInVoiceCall(phone.getImsPhone()) - && isImsOnOriginalNetwork(phone))) { + if (isPhoneInVoiceCall(phone) || isPhoneInVoiceCall(phone.getImsPhone())) { mPhoneIdInVoiceCall = phone.getPhoneId(); break; } } if (mPhoneIdInVoiceCall != oldPhoneIdInVoiceCall) { - log("isPhoneInVoiceCallChanged from phoneId " + oldPhoneIdInVoiceCall + logl("isPhoneInVoiceCallChanged from phoneId " + oldPhoneIdInVoiceCall + " to phoneId " + mPhoneIdInVoiceCall); return true; } else { @@ -464,13 +497,13 @@ } } - private void evaluateIfDataSwitchIsNeeded(String reason) { - if (onEvaluate(REQUESTS_UNCHANGED, reason)) { + private void evaluateIfImmediateDataSwitchIsNeeded(String evaluationReason, int switchReason) { + if (onEvaluate(REQUESTS_UNCHANGED, evaluationReason)) { logDataSwitchEvent(mPreferredDataSubId.get(), TelephonyEvent.EventState.EVENT_STATE_START, - DataSwitch.Reason.DATA_SWITCH_REASON_IN_CALL); + switchReason); registerDefaultNetworkChangeCallback(mPreferredDataSubId.get(), - DataSwitch.Reason.DATA_SWITCH_REASON_IN_CALL); + switchReason); } } @@ -484,7 +517,8 @@ mMaxDataAttachModemCount = maxActivePhones; mLocalLog = new LocalLog(MAX_LOCAL_LOG_LINES); - mSubscriptionController = SubscriptionController.getInstance(); + mSubscriptionManagerService = SubscriptionManagerService.getInstance(); + mRadioConfig = RadioConfig.getInstance(); mValidator = CellularNetworkValidator.getInstance(); @@ -494,9 +528,9 @@ mContext.registerReceiver(mSimStateIntentReceiver, filter); mActivePhoneRegistrants = new RegistrantList(); - for (int i = 0; i < mActiveModemCount; i++) { - mPhoneStates[i] = new PhoneState(); - Phone phone = PhoneFactory.getPhone(i); + for (int phoneId = 0; phoneId < mActiveModemCount; phoneId++) { + mPhoneStates[phoneId] = new PhoneState(); + Phone phone = PhoneFactory.getPhone(phoneId); if (phone != null) { phone.registerForEmergencyCallToggle( this, EVENT_EMERGENCY_TOGGLE, null); @@ -507,23 +541,19 @@ phone.getImsPhone().registerForPreciseCallStateChanged( this, EVENT_PRECISE_CALL_STATE_CHANGED, null); } - if (phone.isUsingNewDataStack()) { - mDataSettingsManagerCallbacks.computeIfAbsent(phone.getPhoneId(), - v -> new DataSettingsManagerCallback(this::post) { - @Override - public void onDataEnabledChanged(boolean enabled, - @TelephonyManager.DataEnabledChangedReason int reason, - @NonNull String callingPackage) { - evaluateIfDataSwitchIsNeeded("EVENT_DATA_ENABLED_CHANGED"); - }}); - phone.getDataSettingsManager().registerCallback( - mDataSettingsManagerCallbacks.get(phone.getPhoneId())); - } else { - phone.getDataEnabledSettings().registerForDataEnabledChanged( - this, EVENT_DATA_ENABLED_CHANGED, null); - } - - registerForImsRadioTechChange(context, i); + mDataSettingsManagerCallbacks.computeIfAbsent(phoneId, + v -> new DataSettingsManagerCallback(this::post) { + @Override + public void onDataEnabledChanged(boolean enabled, + @TelephonyManager.DataEnabledChangedReason int reason, + @NonNull String callingPackage) { + PhoneSwitcher.this.onDataEnabledChanged(); + }}); + phone.getDataSettingsManager().registerCallback( + mDataSettingsManagerCallbacks.get(phoneId)); + phone.getServiceStateTracker().registerForServiceStateChanged(this, + EVENT_SERVICE_STATE_CHANGED, phoneId); + registerForImsRadioTechChange(context, phoneId); } Set<CommandException.Error> ddsFailure = new HashSet<CommandException.Error>(); mCurrentDdsSwitchFailure.add(ddsFailure); @@ -533,6 +563,8 @@ PhoneFactory.getPhone(0).mCi.registerForOn(this, EVENT_RADIO_ON, null); } + readDeviceResourceConfig(); + TelephonyRegistryManager telephonyRegistryManager = (TelephonyRegistryManager) context.getSystemService(Context.TELEPHONY_REGISTRY_SERVICE); telephonyRegistryManager.addOnSubscriptionsChangedListener( @@ -547,6 +579,8 @@ PhoneConfigurationManager.registerForMultiSimConfigChange( this, EVENT_MULTI_SIM_CONFIG_CHANGED, null); + mConnectivityManager.registerDefaultNetworkCallback(mDefaultNetworkCallback, this); + final NetworkCapabilities.Builder builder = new NetworkCapabilities.Builder() .addTransportType(TRANSPORT_CELLULAR) .addCapability(NetworkCapabilities.NET_CAPABILITY_MMS) @@ -580,7 +614,7 @@ updateHalCommandToUse(); - log("PhoneSwitcher started"); + logl("PhoneSwitcher started"); } private final BroadcastReceiver mDefaultDataChangedReceiver = new BroadcastReceiver() { @@ -600,7 +634,7 @@ TelephonyManager.SIM_STATE_UNKNOWN); int slotIndex = intent.getIntExtra(SubscriptionManager.EXTRA_SLOT_INDEX, SubscriptionManager.INVALID_SIM_SLOT_INDEX); - log("mSimStateIntentReceiver: slotIndex = " + slotIndex + " state = " + state); + logl("mSimStateIntentReceiver: slotIndex = " + slotIndex + " state = " + state); obtainMessage(EVENT_PROCESS_SIM_STATE_CHANGE, slotIndex, state).sendToTarget(); } } @@ -611,14 +645,14 @@ return false; } - SubscriptionInfo info = SubscriptionController.getInstance() + SubscriptionInfo info = mSubscriptionManagerService .getActiveSubscriptionInfoForSimSlotIndex(slotIndex, - mContext.getOpPackageName(), null); + mContext.getOpPackageName(), mContext.getAttributionTag()); boolean uiccAppsEnabled = info != null && info.areUiccApplicationsEnabled(); IccCard iccCard = PhoneFactory.getPhone(slotIndex).getIccCard(); if (!iccCard.isEmptyProfile() && uiccAppsEnabled) { - log("isSimApplicationReady: SIM is ready for slotIndex: " + slotIndex); + logl("isSimApplicationReady: SIM is ready for slotIndex: " + slotIndex); return true; } else { return false; @@ -638,17 +672,25 @@ public void handleMessage(Message msg) { switch (msg.what) { case EVENT_SUBSCRIPTION_CHANGED: { - onEvaluate(REQUESTS_UNCHANGED, "subChanged"); + onEvaluate(REQUESTS_UNCHANGED, "subscription changed"); + break; + } + case EVENT_SERVICE_STATE_CHANGED: { + AsyncResult ar = (AsyncResult) msg.obj; + final int phoneId = (int) ar.userObj; + onServiceStateChanged(phoneId); + break; + } + case EVENT_MEETS_AUTO_DATA_SWITCH_STATE: { + final int targetSubId = msg.arg1; + final boolean needValidation = (boolean) msg.obj; + validate(targetSubId, needValidation, + DataSwitch.Reason.DATA_SWITCH_REASON_AUTO, null); break; } case EVENT_PRIMARY_DATA_SUB_CHANGED: { - if (onEvaluate(REQUESTS_UNCHANGED, "primary data subId changed")) { - logDataSwitchEvent(mPreferredDataSubId.get(), - TelephonyEvent.EventState.EVENT_STATE_START, - DataSwitch.Reason.DATA_SWITCH_REASON_MANUAL); - registerDefaultNetworkChangeCallback(mPreferredDataSubId.get(), - DataSwitch.Reason.DATA_SWITCH_REASON_MANUAL); - } + evaluateIfImmediateDataSwitchIsNeeded("primary data sub changed", + DataSwitch.Reason.DATA_SWITCH_REASON_MANUAL); break; } case EVENT_REQUEST_NETWORK: { @@ -662,7 +704,7 @@ case EVENT_EMERGENCY_TOGGLE: { boolean isInEcm = isInEmergencyCallbackMode(); if (mEmergencyOverride != null) { - log("Emergency override - ecbm status = " + isInEcm); + logl("Emergency override - ecbm status = " + isInEcm); if (isInEcm) { // The device has gone into ECBM. Wait until it's out. removeMessages(EVENT_REMOVE_DDS_EMERGENCY_OVERRIDE); @@ -676,6 +718,9 @@ onEvaluate(REQUESTS_CHANGED, "emergencyToggle"); break; } + case EVENT_EVALUATE_AUTO_SWITCH: + evaluateIfAutoSwitchIsNeeded(); + break; case EVENT_RADIO_CAPABILITY_CHANGED: { final int phoneId = msg.arg1; sendRilCommands(phoneId); @@ -694,24 +739,25 @@ onEvaluate(REQUESTS_UNCHANGED, EVALUATION_REASON_RADIO_ON); break; } - case EVENT_IMS_RADIO_TECH_CHANGED: + case EVENT_IMS_RADIO_TECH_CHANGED: { // register for radio tech change to listen to radio tech handover in case previous // attempt was not successful registerForImsRadioTechChange(); - // If the phoneId in voice call didn't change, do nothing. - if (!isPhoneInVoiceCallChanged()) { - break; + // if voice call state changes or in voice call didn't change + // but RAT changes(e.g. Iwlan -> cross sim), reevaluate for data switch. + if (updatesIfPhoneInVoiceCallChanged() || isAnyVoiceCallActiveOnDevice()) { + evaluateIfImmediateDataSwitchIsNeeded("Ims radio tech changed", + DataSwitch.Reason.DATA_SWITCH_REASON_IN_CALL); } - evaluateIfDataSwitchIsNeeded("EVENT_IMS_RADIO_TECH_CHANGED"); break; - + } case EVENT_PRECISE_CALL_STATE_CHANGED: { // register for radio tech change to listen to radio tech handover in case previous // attempt was not successful registerForImsRadioTechChange(); // If the phoneId in voice call didn't change, do nothing. - if (!isPhoneInVoiceCallChanged()) { + if (!updatesIfPhoneInVoiceCallChanged()) { break; } @@ -740,13 +786,16 @@ mEmergencyOverride.mPendingOriginatingCall = false; } } - evaluateIfDataSwitchIsNeeded("EVENT_PRECISE_CALL_STATE_CHANGED"); + // Always update data modem via data during call code path, because + // mAutoSelectedDataSubId doesn't know about any data switch due to voice call + evaluateIfImmediateDataSwitchIsNeeded("precise call state changed", + DataSwitch.Reason.DATA_SWITCH_REASON_IN_CALL); + if (!isAnyVoiceCallActiveOnDevice()) { + // consider auto switch on hang up all voice call + evaluateIfAutoSwitchIsNeeded(); + } break; } - - case EVENT_DATA_ENABLED_CHANGED: - evaluateIfDataSwitchIsNeeded("EVENT_DATA_ENABLED_CHANGED"); - break; case EVENT_NETWORK_VALIDATION_DONE: { int subId = msg.arg1; boolean passed = (msg.arg2 == 1); @@ -759,10 +808,6 @@ onNetworkAvailable(subId, network); break; } - case EVENT_REMOVE_DEFAULT_NETWORK_CHANGE_CALLBACK: { - removeDefaultNetworkChangeCallback(); - break; - } case EVENT_MODEM_COMMAND_DONE: { AsyncResult ar = (AsyncResult) msg.obj; onDdsSwitchResponse(ar); @@ -770,11 +815,14 @@ } case EVENT_MODEM_COMMAND_RETRY: { int phoneId = (int) msg.obj; + if (mActiveModemCount <= phoneId) { + break; + } if (isPhoneIdValidForRetry(phoneId)) { - log("EVENT_MODEM_COMMAND_RETRY: resend modem command on phone " + phoneId); + logl("EVENT_MODEM_COMMAND_RETRY: resend modem command on phone " + phoneId); sendRilCommands(phoneId); } else { - log("EVENT_MODEM_COMMAND_RETRY: skip retry as DDS sub changed"); + logl("EVENT_MODEM_COMMAND_RETRY: skip retry as DDS sub changed"); mCurrentDdsSwitchFailure.get(phoneId).clear(); } break; @@ -786,7 +834,7 @@ // being overridden, ignore. We should not try to switch DDS while already // waiting for SUPL. if (mEmergencyOverride.mPhoneId != req.mPhoneId) { - log("emergency override requested for phone id " + req.mPhoneId + " when " + logl("emergency override requested for phone id " + req.mPhoneId + " when " + "there is already an override in place for phone id " + mEmergencyOverride.mPhoneId + ". Ignoring."); if (req.isCallbackAvailable()) { @@ -806,7 +854,7 @@ mEmergencyOverride = req; } - log("new emergency override - " + mEmergencyOverride); + logl("new emergency override - " + mEmergencyOverride); // a new request has been created, remove any previous override complete scheduled. removeMessages(EVENT_REMOVE_DDS_EMERGENCY_OVERRIDE); Message msg2 = obtainMessage(EVENT_REMOVE_DDS_EMERGENCY_OVERRIDE); @@ -824,7 +872,7 @@ break; } case EVENT_REMOVE_DDS_EMERGENCY_OVERRIDE: { - log("Emergency override removed - " + mEmergencyOverride); + logl("Emergency override removed - " + mEmergencyOverride); mEmergencyOverride = null; onEvaluate(REQUESTS_UNCHANGED, "emer_rm_override_dds"); break; @@ -835,23 +883,42 @@ break; } case EVENT_PROCESS_SIM_STATE_CHANGE: { - int slotIndex = (int) msg.arg1; - int simState = (int) msg.arg2; + int slotIndex = msg.arg1; + int simState = msg.arg2; if (!SubscriptionManager.isValidSlotIndex(slotIndex)) { - log("EVENT_PROCESS_SIM_STATE_CHANGE: skip processing due to invalid slotId: " + logl("EVENT_PROCESS_SIM_STATE_CHANGE: skip processing due to invalid slotId: " + slotIndex); - } else if (mCurrentDdsSwitchFailure.get(slotIndex).contains( + } else if (TelephonyManager.SIM_STATE_LOADED == simState) { + if (mCurrentDdsSwitchFailure.get(slotIndex).contains( CommandException.Error.INVALID_SIM_STATE) && (TelephonyManager.SIM_STATE_LOADED == simState) && isSimApplicationReady(slotIndex)) { - sendRilCommands(slotIndex); + sendRilCommands(slotIndex); + } + // SIM loaded after subscriptions slot mapping are done. Evaluate for auto + // data switch. + sendEmptyMessage(EVENT_EVALUATE_AUTO_SWITCH); } break; } } } + /** + * Read the default device config from any default phone because the resource config are per + * device. No need to register callback for the same reason. + */ + private void readDeviceResourceConfig() { + Phone phone = PhoneFactory.getDefaultPhone(); + DataConfigManager dataConfig = phone.getDataNetworkController().getDataConfigManager(); + mRequirePingTestBeforeDataSwitch = dataConfig.isPingTestBeforeAutoDataSwitchRequired(); + mAutoDataSwitchAvailabilityStabilityTimeThreshold = + dataConfig.getAutoDataSwitchAvailabilityStabilityTimeThreshold(); + mAutoDataSwitchValidationMaxRetry = + dataConfig.getAutoDataSwitchValidationMaxRetry(); + } + private synchronized void onMultiSimConfigChanged(int activeModemCount) { // No change. if (mActiveModemCount == activeModemCount) return; @@ -880,22 +947,19 @@ this, EVENT_PRECISE_CALL_STATE_CHANGED, null); } - if (phone.isUsingNewDataStack()) { - mDataSettingsManagerCallbacks.computeIfAbsent(phone.getPhoneId(), - v -> new DataSettingsManagerCallback(this::post) { - @Override - public void onDataEnabledChanged(boolean enabled, - @TelephonyManager.DataEnabledChangedReason int reason, - @NonNull String callingPackage) { - evaluateIfDataSwitchIsNeeded("EVENT_DATA_ENABLED_CHANGED"); - } - }); - phone.getDataSettingsManager().registerCallback( - mDataSettingsManagerCallbacks.get(phone.getPhoneId())); - } else { - phone.getDataEnabledSettings().registerForDataEnabledChanged( - this, EVENT_DATA_ENABLED_CHANGED, null); - } + mDataSettingsManagerCallbacks.computeIfAbsent(phone.getPhoneId(), + v -> new DataSettingsManagerCallback(this::post) { + @Override + public void onDataEnabledChanged(boolean enabled, + @TelephonyManager.DataEnabledChangedReason int reason, + @NonNull String callingPackage) { + PhoneSwitcher.this.onDataEnabledChanged(); + } + }); + phone.getDataSettingsManager().registerCallback( + mDataSettingsManagerCallbacks.get(phone.getPhoneId())); + phone.getServiceStateTracker().registerForServiceStateChanged(this, + EVENT_SERVICE_STATE_CHANGED, phoneId); Set<CommandException.Error> ddsFailure = new HashSet<CommandException.Error>(); mCurrentDdsSwitchFailure.add(ddsFailure); @@ -903,6 +967,23 @@ } } + /** + * Called when + * 1. user changed mobile data settings + * 2. OR user changed auto data switch feature + */ + private void onDataEnabledChanged() { + logl("user changed data related settings"); + if (isAnyVoiceCallActiveOnDevice()) { + // user changed data related settings during call, switch or turn off immediately + evaluateIfImmediateDataSwitchIsNeeded( + "user changed data settings during call", + DataSwitch.Reason.DATA_SWITCH_REASON_IN_CALL); + } else { + evaluateIfAutoSwitchIsNeeded(); + } + } + private boolean isInEmergencyCallbackMode() { for (Phone p : PhoneFactory.getPhones()) { if (p == null) continue; @@ -941,79 +1022,26 @@ } private void onRequestNetwork(NetworkRequest networkRequest) { - if (PhoneFactory.getDefaultPhone().isUsingNewDataStack()) { - TelephonyNetworkRequest telephonyNetworkRequest = new TelephonyNetworkRequest( - networkRequest, PhoneFactory.getDefaultPhone()); - if (!mNetworkRequestList.contains(telephonyNetworkRequest)) { - mNetworkRequestList.add(telephonyNetworkRequest); - onEvaluate(REQUESTS_CHANGED, "netRequest"); - } - return; - } - final DcRequest dcRequest = - DcRequest.create(networkRequest, createApnRepository(networkRequest)); - if (dcRequest != null) { - if (!mPrioritizedDcRequests.contains(dcRequest)) { - collectRequestNetworkMetrics(networkRequest); - mPrioritizedDcRequests.add(dcRequest); - Collections.sort(mPrioritizedDcRequests); - onEvaluate(REQUESTS_CHANGED, "netRequest"); - if (VDBG) log("Added DcRequest, size: " + mPrioritizedDcRequests.size()); - } + TelephonyNetworkRequest telephonyNetworkRequest = new TelephonyNetworkRequest( + networkRequest, PhoneFactory.getDefaultPhone()); + if (!mNetworkRequestList.contains(telephonyNetworkRequest)) { + mNetworkRequestList.add(telephonyNetworkRequest); + onEvaluate(REQUESTS_CHANGED, "netRequest"); } } private void onReleaseNetwork(NetworkRequest networkRequest) { - if (PhoneFactory.getDefaultPhone().isUsingNewDataStack()) { - TelephonyNetworkRequest telephonyNetworkRequest = new TelephonyNetworkRequest( - networkRequest, PhoneFactory.getDefaultPhone()); - if (mNetworkRequestList.remove(telephonyNetworkRequest)) { - onEvaluate(REQUESTS_CHANGED, "netReleased"); - collectReleaseNetworkMetrics(networkRequest); - } - return; + TelephonyNetworkRequest telephonyNetworkRequest = new TelephonyNetworkRequest( + networkRequest, PhoneFactory.getDefaultPhone()); + if (mNetworkRequestList.remove(telephonyNetworkRequest)) { + onEvaluate(REQUESTS_CHANGED, "netReleased"); + collectReleaseNetworkMetrics(networkRequest); } - final DcRequest dcRequest = - DcRequest.create(networkRequest, createApnRepository(networkRequest)); - if (dcRequest != null) { - if (mPrioritizedDcRequests.remove(dcRequest)) { - onEvaluate(REQUESTS_CHANGED, "netReleased"); - collectReleaseNetworkMetrics(networkRequest); - if (VDBG) log("Removed DcRequest, size: " + mPrioritizedDcRequests.size()); - } - } - } - - private ApnConfigTypeRepository createApnRepository(NetworkRequest networkRequest) { - int phoneIdForRequest = phoneIdForRequest(networkRequest); - int subId = mSubscriptionController.getSubIdUsingPhoneId(phoneIdForRequest); - CarrierConfigManager configManager = (CarrierConfigManager) mContext - .getSystemService(Context.CARRIER_CONFIG_SERVICE); - - PersistableBundle carrierConfig; - if (configManager != null) { - carrierConfig = configManager.getConfigForSubId(subId); - } else { - carrierConfig = null; - } - return new ApnConfigTypeRepository(carrierConfig); - } - - private void removeDefaultNetworkChangeCallback() { - removeMessages(EVENT_REMOVE_DEFAULT_NETWORK_CHANGE_CALLBACK); - mDefaultNetworkCallback.mExpectedSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; - mDefaultNetworkCallback.mSwitchReason = - TelephonyEvent.DataSwitch.Reason.DATA_SWITCH_REASON_UNKNOWN; - mConnectivityManager.unregisterNetworkCallback(mDefaultNetworkCallback); } private void registerDefaultNetworkChangeCallback(int expectedSubId, int reason) { mDefaultNetworkCallback.mExpectedSubId = expectedSubId; mDefaultNetworkCallback.mSwitchReason = reason; - mConnectivityManager.registerDefaultNetworkCallback(mDefaultNetworkCallback, this); - sendMessageDelayed( - obtainMessage(EVENT_REMOVE_DEFAULT_NETWORK_CHANGE_CALLBACK), - DEFAULT_NETWORK_CHANGE_TIMEOUT_MS); } private void collectRequestNetworkMetrics(NetworkRequest networkRequest) { @@ -1040,6 +1068,195 @@ } } + /** + * Called when service state changed. + */ + private void onServiceStateChanged(int phoneId) { + Phone phone = findPhoneById(phoneId); + if (phone != null) { + int newRegState = phone.getServiceState() + .getNetworkRegistrationInfo( + NetworkRegistrationInfo.DOMAIN_PS, + AccessNetworkConstants.TRANSPORT_TYPE_WWAN) + .getRegistrationState(); + if (newRegState != mPhoneStates[phoneId].dataRegState) { + mPhoneStates[phoneId].dataRegState = newRegState; + logl("onServiceStateChanged: phoneId:" + phoneId + " dataReg-> " + + NetworkRegistrationInfo.registrationStateToString(newRegState)); + if (!hasMessages(EVENT_EVALUATE_AUTO_SWITCH)) { + sendEmptyMessage(EVENT_EVALUATE_AUTO_SWITCH); + } + } + } + } + + /** + * Evaluate if auto switch is suitable at the moment. + */ + private void evaluateIfAutoSwitchIsNeeded() { + // auto data switch feature is disabled from server + if (mAutoDataSwitchAvailabilityStabilityTimeThreshold < 0) return; + // check is valid DSDS + if (!isActiveSubId(mPrimaryDataSubId) || mSubscriptionManagerService + .getActiveSubIdList(true).length <= 1) { + return; + } + + Phone primaryDataPhone = getPhoneBySubId(mPrimaryDataSubId); + if (primaryDataPhone == null) { + loge("evaluateIfAutoSwitchIsNeeded: cannot find primary data phone. subId=" + + mPrimaryDataSubId); + return; + } + + int primaryPhoneId = primaryDataPhone.getPhoneId(); + log("evaluateIfAutoSwitchIsNeeded: primaryPhoneId: " + primaryPhoneId + + " preferredPhoneId: " + mPreferredDataPhoneId); + Phone secondaryDataPhone; + + if (mPreferredDataPhoneId == primaryPhoneId) { + // on primary data sub + + int candidateSubId = getAutoSwitchTargetSubIdIfExists(); + if (candidateSubId != INVALID_SUBSCRIPTION_ID) { + startAutoDataSwitchStabilityCheck(candidateSubId, mRequirePingTestBeforeDataSwitch); + } else { + cancelPendingAutoDataSwitch(); + } + } else if ((secondaryDataPhone = findPhoneById(mPreferredDataPhoneId)) != null) { + // on secondary data sub + + if (!primaryDataPhone.isUserDataEnabled() + || !secondaryDataPhone.isDataAllowed()) { + // immediately switch back if user setting changes + mAutoSelectedDataSubId = DEFAULT_SUBSCRIPTION_ID; + evaluateIfImmediateDataSwitchIsNeeded("User disabled data settings", + DataSwitch.Reason.DATA_SWITCH_REASON_MANUAL); + return; + } + + NetworkCapabilities defaultNetworkCapabilities = mConnectivityManager + .getNetworkCapabilities(mConnectivityManager.getActiveNetwork()); + if (defaultNetworkCapabilities != null && !defaultNetworkCapabilities + .hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) { + log("evaluateIfAutoSwitchIsNeeded: " + + "Default network is active on non-cellular transport"); + startAutoDataSwitchStabilityCheck(DEFAULT_SUBSCRIPTION_ID, false); + return; + } + + if (mPhoneStates[secondaryDataPhone.getPhoneId()].dataRegState + != NetworkRegistrationInfo.REGISTRATION_STATE_HOME) { + // secondary phone lost its HOME availability + startAutoDataSwitchStabilityCheck(DEFAULT_SUBSCRIPTION_ID, false); + return; + } + + if (isInService(mPhoneStates[primaryPhoneId])) { + // primary becomes available + startAutoDataSwitchStabilityCheck(DEFAULT_SUBSCRIPTION_ID, + mRequirePingTestBeforeDataSwitch); + return; + } + + // cancel any previous attempts of switching back to primary + cancelPendingAutoDataSwitch(); + } + } + + /** + * @param phoneState The phone state to check + * @return {@code true} if the phone state is considered in service. + */ + private boolean isInService(@NonNull PhoneState phoneState) { + return phoneState.dataRegState == NetworkRegistrationInfo.REGISTRATION_STATE_HOME + || phoneState.dataRegState == NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING; + } + + /** + * Called when the current environment suits auto data switch. + * Start pre-switch validation if the current environment suits auto data switch for + * {@link #mAutoDataSwitchAvailabilityStabilityTimeThreshold} MS. + * @param targetSubId the target sub Id. + * @param needValidation {@code true} if validation is needed. + */ + private void startAutoDataSwitchStabilityCheck(int targetSubId, boolean needValidation) { + log("startAutoDataSwitchStabilityCheck: targetSubId=" + targetSubId + + " needValidation=" + needValidation); + if (!hasMessages(EVENT_MEETS_AUTO_DATA_SWITCH_STATE, needValidation)) { + sendMessageDelayed(obtainMessage(EVENT_MEETS_AUTO_DATA_SWITCH_STATE, targetSubId, + 0/*placeholder*/, + needValidation), + mAutoDataSwitchAvailabilityStabilityTimeThreshold); + } + } + + /** + * Cancel any auto switch attempts when the current environment is not suitable for auto switch. + */ + private void cancelPendingAutoDataSwitch() { + mAutoSwitchRetryFailedCount = 0; + removeMessages(EVENT_MEETS_AUTO_DATA_SWITCH_STATE); + if (mValidator.isValidating()) { + mValidator.stopValidation(); + + removeMessages(EVENT_NETWORK_VALIDATION_DONE); + removeMessages(EVENT_NETWORK_AVAILABLE); + mPendingSwitchSubId = INVALID_SUBSCRIPTION_ID; + mPendingSwitchNeedValidation = false; + } + } + + /** + * Called when consider switching from primary default data sub to another data sub. + * @return the target subId if a suitable candidate is found, otherwise return + * {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID} + */ + private int getAutoSwitchTargetSubIdIfExists() { + Phone primaryDataPhone = getPhoneBySubId(mPrimaryDataSubId); + if (primaryDataPhone == null) { + log("getAutoSwitchTargetSubId: no sim loaded"); + return INVALID_SUBSCRIPTION_ID; + } + + int primaryPhoneId = primaryDataPhone.getPhoneId(); + + if (!primaryDataPhone.isUserDataEnabled()) { + log("getAutoSwitchTargetSubId: user disabled data"); + return INVALID_SUBSCRIPTION_ID; + } + + NetworkCapabilities defaultNetworkCapabilities = mConnectivityManager + .getNetworkCapabilities(mConnectivityManager.getActiveNetwork()); + if (defaultNetworkCapabilities != null && !defaultNetworkCapabilities + .hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) { + // Exists other active default transport + log("getAutoSwitchTargetSubId: Default network is active on non-cellular transport"); + return INVALID_SUBSCRIPTION_ID; + } + + // check whether primary and secondary signal status worth switching + if (isInService(mPhoneStates[primaryPhoneId])) { + log("getAutoSwitchTargetSubId: primary is in service"); + return INVALID_SUBSCRIPTION_ID; + } + for (int phoneId = 0; phoneId < mPhoneStates.length; phoneId++) { + if (phoneId != primaryPhoneId) { + // the alternative phone must have HOME availability + if (mPhoneStates[phoneId].dataRegState + == NetworkRegistrationInfo.REGISTRATION_STATE_HOME) { + log("getAutoSwitchTargetSubId: found phone " + phoneId + " in HOME service"); + Phone secondaryDataPhone = findPhoneById(phoneId); + if (secondaryDataPhone != null && // check auto switch feature enabled + secondaryDataPhone.isDataAllowed()) { + return secondaryDataPhone.getSubId(); + } + } + } + } + return INVALID_SUBSCRIPTION_ID; + } + private TelephonyManager getTm() { return (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE); } @@ -1062,11 +1279,12 @@ boolean diffDetected = mHalCommandToUse != HAL_COMMAND_PREFERRED_DATA && requestsChanged; // Check if user setting of default non-opportunistic data sub is changed. - final int primaryDataSubId = mSubscriptionController.getDefaultDataSubId(); + int primaryDataSubId = mSubscriptionManagerService.getDefaultDataSubId(); if (primaryDataSubId != mPrimaryDataSubId) { sb.append(" mPrimaryDataSubId ").append(mPrimaryDataSubId).append("->") .append(primaryDataSubId); mPrimaryDataSubId = primaryDataSubId; + mLastSwitchPreferredDataReason = DataSwitch.Reason.DATA_SWITCH_REASON_MANUAL; } // Check to see if there is any active subscription on any phone @@ -1074,14 +1292,21 @@ // Check if phoneId to subId mapping is changed. for (int i = 0; i < mActiveModemCount; i++) { - int sub = mSubscriptionController.getSubIdUsingPhoneId(i); + int sub = SubscriptionManager.getSubscriptionId(i); if (SubscriptionManager.isValidSubscriptionId(sub)) hasAnyActiveSubscription = true; if (sub != mPhoneSubscriptions[i]) { sb.append(" phone[").append(i).append("] ").append(mPhoneSubscriptions[i]); sb.append("->").append(sub); + if (mAutoSelectedDataSubId == mPhoneSubscriptions[i]) { + mAutoSelectedDataSubId = DEFAULT_SUBSCRIPTION_ID; + } mPhoneSubscriptions[i] = sub; + // Listen to IMS radio tech change for new sub + if (SubscriptionManager.isValidSubscriptionId(sub)) { + registerForImsRadioTechChange(mContext, i); + } diffDetected = true; } } @@ -1106,11 +1331,11 @@ if (hasAnyActiveSubscription) updatePreferredDataPhoneId(); if (oldPreferredDataPhoneId != mPreferredDataPhoneId) { - sb.append(" preferred phoneId ").append(oldPreferredDataPhoneId) + sb.append(" preferred data phoneId ").append(oldPreferredDataPhoneId) .append("->").append(mPreferredDataPhoneId); diffDetected = true; } else if (oldPreferredDataSubId != mPreferredDataSubId.get()) { - log("SIM refresh, notify dds change"); + logl("SIM refresh, notify dds change"); // Inform connectivity about the active data phone notifyPreferredDataSubIdChanged(); } @@ -1119,7 +1344,7 @@ // DDS are out of sync after APM, AP should force DDS when radio on. long term solution // should be having API to query preferred data modem to detect the out-of-sync scenarios. if (diffDetected || EVALUATION_REASON_RADIO_ON.equals(reason)) { - log("evaluating due to " + sb.toString()); + logl("evaluating due to " + sb); if (mHalCommandToUse == HAL_COMMAND_PREFERRED_DATA) { // With HAL_COMMAND_PREFERRED_DATA, all phones are assumed to allow PS attach. // So marking all phone as active, and the phone with mPreferredDataPhoneId @@ -1147,27 +1372,17 @@ } if (newActivePhones.size() < mMaxDataAttachModemCount) { - if (PhoneFactory.getDefaultPhone().isUsingNewDataStack()) { - for (TelephonyNetworkRequest networkRequest : mNetworkRequestList) { - int phoneIdForRequest = phoneIdForRequest(networkRequest); - if (phoneIdForRequest == INVALID_PHONE_INDEX) continue; - if (newActivePhones.contains(phoneIdForRequest)) continue; - newActivePhones.add(phoneIdForRequest); - if (newActivePhones.size() >= mMaxDataAttachModemCount) break; - } - } else { - for (DcRequest dcRequest : mPrioritizedDcRequests) { - int phoneIdForRequest = phoneIdForRequest(dcRequest.networkRequest); - if (phoneIdForRequest == INVALID_PHONE_INDEX) continue; - if (newActivePhones.contains(phoneIdForRequest)) continue; - newActivePhones.add(phoneIdForRequest); - if (newActivePhones.size() >= mMaxDataAttachModemCount) break; - } + for (TelephonyNetworkRequest networkRequest : mNetworkRequestList) { + int phoneIdForRequest = phoneIdForRequest(networkRequest); + if (phoneIdForRequest == INVALID_PHONE_INDEX) continue; + if (newActivePhones.contains(phoneIdForRequest)) continue; + newActivePhones.add(phoneIdForRequest); + if (newActivePhones.size() >= mMaxDataAttachModemCount) break; } } if (newActivePhones.size() < mMaxDataAttachModemCount - && newActivePhones.contains(mPreferredDataPhoneId) + && !newActivePhones.contains(mPreferredDataPhoneId) && SubscriptionManager.isUsableSubIdValue(mPreferredDataPhoneId)) { newActivePhones.add(mPreferredDataPhoneId); } @@ -1175,7 +1390,7 @@ if (VDBG) { log("mPrimaryDataSubId = " + mPrimaryDataSubId); - log("mOpptDataSubId = " + mOpptDataSubId); + log("mAutoSelectedDataSubId = " + mAutoSelectedDataSubId); for (int i = 0; i < mActiveModemCount; i++) { log(" phone[" + i + "] using sub[" + mPhoneSubscriptions[i] + "]"); } @@ -1200,6 +1415,8 @@ protected static class PhoneState { public volatile boolean active = false; + public @NetworkRegistrationInfo.RegistrationState int dataRegState = + NetworkRegistrationInfo.REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING; public long lastRequested = 0; } @@ -1217,7 +1434,7 @@ PhoneState state = mPhoneStates[phoneId]; if (state.active == active) return; state.active = active; - log((active ? "activate " : "deactivate ") + phoneId); + logl((active ? "activate " : "deactivate ") + phoneId); state.lastRequested = System.currentTimeMillis(); sendRilCommands(phoneId); } @@ -1264,7 +1481,7 @@ protected void sendRilCommands(int phoneId) { if (!SubscriptionManager.isValidPhoneId(phoneId)) { - log("sendRilCommands: skip dds switch due to invalid phoneId=" + phoneId); + logl("sendRilCommands: skip dds switch due to invalid phoneId=" + phoneId); return; } @@ -1276,7 +1493,7 @@ } } else if (phoneId == mPreferredDataPhoneId) { // Only setPreferredDataModem if the phoneId equals to current mPreferredDataPhoneId - log("sendRilCommands: setPreferredDataModem - phoneId: " + phoneId); + logl("sendRilCommands: setPreferredDataModem - phoneId: " + phoneId); mRadioConfig.setPreferredDataModem(mPreferredDataPhoneId, message); } } @@ -1286,18 +1503,13 @@ .getNumberOfModemsWithSimultaneousDataConnections(); if (mMaxDataAttachModemCount != newMaxDataAttachModemCount) { mMaxDataAttachModemCount = newMaxDataAttachModemCount; - log("Max active phones changed to " + mMaxDataAttachModemCount); + logl("Max active phones changed to " + mMaxDataAttachModemCount); onEvaluate(REQUESTS_UNCHANGED, "phoneCfgChanged"); } } - // Merge phoneIdForRequest(NetworkRequest netRequest) after Phone.isUsingNewDataStack() is - // cleaned up. private int phoneIdForRequest(TelephonyNetworkRequest networkRequest) { - return phoneIdForRequest(networkRequest.getNativeNetworkRequest()); - } - - private int phoneIdForRequest(NetworkRequest netRequest) { + NetworkRequest netRequest = networkRequest.getNativeNetworkRequest(); int subId = getSubIdFromNetworkSpecifier(netRequest.getNetworkSpecifier()); if (subId == DEFAULT_SUBSCRIPTION_ID) return mPreferredDataPhoneId; @@ -1341,71 +1553,82 @@ return INVALID_SUBSCRIPTION_ID; } - private int getSubIdForDefaultNetworkRequests() { - if (mSubscriptionController.isActiveSubId(mOpptDataSubId)) { - return mOpptDataSubId; - } else { - return mPrimaryDataSubId; - } + private boolean isActiveSubId(int subId) { + SubscriptionInfoInternal subInfo = mSubscriptionManagerService + .getSubscriptionInfoInternal(subId); + return subInfo != null && subInfo.isActive(); } // This updates mPreferredDataPhoneId which decides which phone should handle default network // requests. protected void updatePreferredDataPhoneId() { - Phone voicePhone = findPhoneById(mPhoneIdInVoiceCall); - boolean isDataEnabled = false; - if (voicePhone != null) { - if (voicePhone.isUsingNewDataStack()) { - isDataEnabled = voicePhone.getDataSettingsManager() - .isDataEnabled(ApnSetting.TYPE_DEFAULT); - } else { - isDataEnabled = voicePhone.getDataEnabledSettings() - .isDataEnabled(ApnSetting.TYPE_DEFAULT); - } - } - if (mEmergencyOverride != null && findPhoneById(mEmergencyOverride.mPhoneId) != null) { // Override DDS for emergency even if user data is not enabled, since it is an // emergency. // TODO: Provide a notification to the user that metered data is currently being // used during this period. - log("updatePreferredDataPhoneId: preferred data overridden for emergency." + logl("updatePreferredDataPhoneId: preferred data overridden for emergency." + " phoneId = " + mEmergencyOverride.mPhoneId); mPreferredDataPhoneId = mEmergencyOverride.mPhoneId; - } else if (isDataEnabled) { - // If a phone is in call and user enabled its mobile data, we - // should switch internet connection to it. Because the other modem - // will lose data connection anyway. - // TODO: validate network first. - mPreferredDataPhoneId = mPhoneIdInVoiceCall; + mLastSwitchPreferredDataReason = DataSwitch.Reason.DATA_SWITCH_REASON_UNKNOWN; } else { - int subId = getSubIdForDefaultNetworkRequests(); - int phoneId = SubscriptionManager.INVALID_PHONE_INDEX; - - if (SubscriptionManager.isUsableSubIdValue(subId)) { - for (int i = 0; i < mActiveModemCount; i++) { - if (mPhoneSubscriptions[i] == subId) { - phoneId = i; - break; - } + int imsRegTech = mImsRegTechProvider.get(mContext, mPhoneIdInVoiceCall); + if (isAnyVoiceCallActiveOnDevice() && imsRegTech != REGISTRATION_TECH_IWLAN) { + if (imsRegTech != REGISTRATION_TECH_CROSS_SIM) { + mPreferredDataPhoneId = shouldSwitchDataDueToInCall() + ? mPhoneIdInVoiceCall : getFallbackDataPhoneIdForInternetRequests(); + } else { + logl("IMS call on cross-SIM, skip switching data to phone " + + mPhoneIdInVoiceCall); } + } else { + mPreferredDataPhoneId = getFallbackDataPhoneIdForInternetRequests(); } - - mPreferredDataPhoneId = phoneId; } - mPreferredDataSubId.set( - mSubscriptionController.getSubIdUsingPhoneId(mPreferredDataPhoneId)); + mPreferredDataSubId.set(SubscriptionManager.getSubscriptionId(mPreferredDataPhoneId)); + } + + /** + * @return the default data phone Id (or auto selected phone Id in auto data switch/CBRS case) + */ + private int getFallbackDataPhoneIdForInternetRequests() { + int fallbackSubId = isActiveSubId(mAutoSelectedDataSubId) + ? mAutoSelectedDataSubId : mPrimaryDataSubId; + + if (SubscriptionManager.isUsableSubIdValue(fallbackSubId)) { + for (int phoneId = 0; phoneId < mActiveModemCount; phoneId++) { + if (mPhoneSubscriptions[phoneId] == fallbackSubId) { + return phoneId; + } + } + } + return SubscriptionManager.INVALID_PHONE_INDEX; + } + + /** + * If a phone is in call and user enabled its mobile data and auto data switch feature, we + * should switch internet connection to it because the other modem will lose data connection + * anyway. + * @return {@code true} if should switch data to the phone in voice call + */ + private boolean shouldSwitchDataDueToInCall() { + Phone voicePhone = findPhoneById(mPhoneIdInVoiceCall); + Phone defaultDataPhone = getPhoneBySubId(mPrimaryDataSubId); + return defaultDataPhone != null // check user enabled data + && defaultDataPhone.isUserDataEnabled() + && voicePhone != null // check user enabled voice during call feature + && voicePhone.isDataAllowed(); } protected void transitionToEmergencyPhone() { if (mActiveModemCount <= 0) { - log("No phones: unable to reset preferred phone for emergency"); + logl("No phones: unable to reset preferred phone for emergency"); return; } if (mPreferredDataPhoneId != DEFAULT_EMERGENCY_PHONE_ID) { - log("No active subscriptions: resetting preferred phone to 0 for emergency"); + logl("No active subscriptions: resetting preferred phone to 0 for emergency"); mPreferredDataPhoneId = DEFAULT_EMERGENCY_PHONE_ID; } @@ -1415,6 +1638,10 @@ } } + private Phone getPhoneBySubId(int subId) { + return findPhoneById(mSubscriptionManagerService.getPhoneId(subId)); + } + private Phone findPhoneById(final int phoneId) { if (!SubscriptionManager.isValidPhoneId(phoneId)) { return null; @@ -1426,15 +1653,16 @@ TelephonyNetworkRequest networkRequest, int phoneId) { if (!SubscriptionManager.isValidPhoneId(phoneId)) return false; + int subId = SubscriptionManager.getSubscriptionId(phoneId); + // In any case, if phone state is inactive, don't apply the network request. - if (!isPhoneActive(phoneId) || ( - mSubscriptionController.getSubIdUsingPhoneId(phoneId) == INVALID_SUBSCRIPTION_ID + if (!isPhoneActive(phoneId) || (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID && !isEmergencyNetworkRequest(networkRequest))) { return false; } NetworkRequest netRequest = networkRequest.getNativeNetworkRequest(); - int subId = getSubIdFromNetworkSpecifier(netRequest.getNetworkSpecifier()); + subId = getSubIdFromNetworkSpecifier(netRequest.getNetworkSpecifier()); //if this phone is an emergency networkRequest //and subId is not specified that is invalid or default @@ -1488,45 +1716,67 @@ */ private void setOpportunisticDataSubscription(int subId, boolean needValidation, ISetOpportunisticDataCallback callback) { - if (!mSubscriptionController.isActiveSubId(subId) - && subId != SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) { - log("Can't switch data to inactive subId " + subId); + validate(subId, needValidation, + DataSwitch.Reason.DATA_SWITCH_REASON_CBRS, callback); + } + + /** + * Try setup a new internet connection on the subId that's pending validation. If the validation + * succeeds, this subId will be evaluated for being the preferred data subId; If fails, nothing + * happens. + * Callback will be updated with the validation result. + * + * @param subId Sub Id that's pending switch, awaiting validation. + * @param needValidation {@code false} if switch to the subId even if validation fails. + * @param switchReason The switch reason for this validation + * @param callback Optional - specific for external opportunistic sub validation request. + */ + private void validate(int subId, boolean needValidation, int switchReason, + @Nullable ISetOpportunisticDataCallback callback) { + logl("Validate subId " + subId + " due to " + switchReasonToString(switchReason) + + " needValidation=" + needValidation); + int subIdToValidate = (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) + ? mPrimaryDataSubId : subId; + if (!isActiveSubId(subIdToValidate)) { + logl("Can't switch data to inactive subId " + subIdToValidate); + if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) { + // the default data sub is not selected yet, store the intent of switching to + // default subId once it becomes available. + mAutoSelectedDataSubId = SubscriptionManager.DEFAULT_SUBSCRIPTION_ID; + } sendSetOpptCallbackHelper(callback, SET_OPPORTUNISTIC_SUB_INACTIVE_SUBSCRIPTION); return; } - // Remove EVENT_NETWORK_VALIDATION_DONE. Don't handle validation result of previously subId - // if queued. - removeMessages(EVENT_NETWORK_VALIDATION_DONE); - removeMessages(EVENT_NETWORK_AVAILABLE); - - int subIdToValidate = (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) - ? mPrimaryDataSubId : subId; - - mPendingSwitchSubId = INVALID_SUBSCRIPTION_ID; - if (mValidator.isValidating()) { mValidator.stopValidation(); sendSetOpptCallbackHelper(mSetOpptSubCallback, SET_OPPORTUNISTIC_SUB_VALIDATION_FAILED); mSetOpptSubCallback = null; } - if (subId == mOpptDataSubId) { + // Remove EVENT_NETWORK_VALIDATION_DONE. Don't handle validation result of previous subId + // if queued. + removeMessages(EVENT_NETWORK_VALIDATION_DONE); + removeMessages(EVENT_NETWORK_AVAILABLE); + + mPendingSwitchSubId = INVALID_SUBSCRIPTION_ID; + + if (subIdToValidate == mPreferredDataSubId.get()) { sendSetOpptCallbackHelper(callback, SET_OPPORTUNISTIC_SUB_SUCCESS); return; } - logDataSwitchEvent(subId == DEFAULT_SUBSCRIPTION_ID ? mPrimaryDataSubId : subId, + mLastSwitchPreferredDataReason = switchReason; + logDataSwitchEvent(subIdToValidate, TelephonyEvent.EventState.EVENT_STATE_START, - DataSwitch.Reason.DATA_SWITCH_REASON_CBRS); - registerDefaultNetworkChangeCallback( - subId == DEFAULT_SUBSCRIPTION_ID ? mPrimaryDataSubId : subId, - DataSwitch.Reason.DATA_SWITCH_REASON_CBRS); + switchReason); + registerDefaultNetworkChangeCallback(subIdToValidate, + switchReason); // If validation feature is not supported, set it directly. Otherwise, // start validation on the subscription first. if (!mValidator.isValidationFeatureSupported()) { - setOpportunisticSubscriptionInternal(subId); + setAutoSelectedDataSubIdInternal(subIdToValidate); sendSetOpptCallbackHelper(callback, SET_OPPORTUNISTIC_SUB_SUCCESS); return; } @@ -1563,36 +1813,46 @@ try { callback.onComplete(result); } catch (RemoteException exception) { - log("RemoteException " + exception); + logl("RemoteException " + exception); } } /** - * Set opportunistic data subscription. + * Evaluate whether the specified sub Id can be set to be the preferred data sub Id. + * + * @param subId The subId that we tried to validate: could possibly be unvalidated if validation + * feature is not supported. */ - private void setOpportunisticSubscriptionInternal(int subId) { - if (mOpptDataSubId != subId) { - mOpptDataSubId = subId; - onEvaluate(REQUESTS_UNCHANGED, "oppt data subId changed"); + private void setAutoSelectedDataSubIdInternal(int subId) { + if (mAutoSelectedDataSubId != subId) { + mAutoSelectedDataSubId = subId; + onEvaluate(REQUESTS_UNCHANGED, switchReasonToString(mLastSwitchPreferredDataReason)); } } private void confirmSwitch(int subId, boolean confirm) { - log("confirmSwitch: subId " + subId + (confirm ? " confirmed." : " cancelled.")); + logl("confirmSwitch: subId " + subId + (confirm ? " confirmed." : " cancelled.")); int resultForCallBack; - if (!mSubscriptionController.isActiveSubId(subId)) { - log("confirmSwitch: subId " + subId + " is no longer active"); + if (!isActiveSubId(subId)) { + logl("confirmSwitch: subId " + subId + " is no longer active"); resultForCallBack = SET_OPPORTUNISTIC_SUB_INACTIVE_SUBSCRIPTION; + mAutoSwitchRetryFailedCount = 0; } else if (!confirm) { resultForCallBack = SET_OPPORTUNISTIC_SUB_VALIDATION_FAILED; + + // retry for auto data switch validation failure + if (mLastSwitchPreferredDataReason == DataSwitch.Reason.DATA_SWITCH_REASON_AUTO) { + scheduleAutoSwitchRetryEvaluation(); + mAutoSwitchRetryFailedCount++; + } } else { - if (mSubscriptionController.isOpportunistic(subId)) { - setOpportunisticSubscriptionInternal(subId); + if (subId == mPrimaryDataSubId) { + setAutoSelectedDataSubIdInternal(DEFAULT_SUBSCRIPTION_ID); } else { - // Switching data back to primary subscription. - setOpportunisticSubscriptionInternal(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID); + setAutoSelectedDataSubIdInternal(subId); } resultForCallBack = SET_OPPORTUNISTIC_SUB_SUCCESS; + mAutoSwitchRetryFailedCount = 0; } // Trigger callback if needed @@ -1601,6 +1861,23 @@ mPendingSwitchSubId = INVALID_SUBSCRIPTION_ID; } + /** + * Schedule auto data switch evaluation retry if haven't reached the max retry count. + */ + private void scheduleAutoSwitchRetryEvaluation() { + if (mAutoSwitchRetryFailedCount < mAutoDataSwitchValidationMaxRetry) { + if (!hasMessages(EVENT_EVALUATE_AUTO_SWITCH)) { + sendMessageDelayed(obtainMessage(EVENT_EVALUATE_AUTO_SWITCH), + mAutoDataSwitchAvailabilityStabilityTimeThreshold + << mAutoSwitchRetryFailedCount); + } + } else { + logl("scheduleAutoSwitchEvaluation: reached max auto switch retry count " + + mAutoDataSwitchValidationMaxRetry); + mAutoSwitchRetryFailedCount = 0; + } + } + private void onNetworkAvailable(int subId, Network network) { log("onNetworkAvailable: on subId " + subId); // Do nothing unless pending switch matches target subId and it doesn't require @@ -1613,7 +1890,7 @@ } private void onValidationDone(int subId, boolean passed) { - log("onValidationDone: " + (passed ? "passed" : "failed") + " on subId " + subId); + logl("onValidationDone: " + (passed ? "passed" : "failed") + " on subId " + subId); if (mPendingSwitchSubId == INVALID_SUBSCRIPTION_ID || mPendingSwitchSubId != subId) return; // If validation failed and mPendingSwitch.mNeedValidation is false, we still confirm @@ -1639,7 +1916,7 @@ */ public void trySetOpportunisticDataSubscription(int subId, boolean needValidation, ISetOpportunisticDataCallback callback) { - log("Try set opportunistic data subscription to subId " + subId + logl("Try set opportunistic data subscription to subId " + subId + (needValidation ? " with " : " without ") + "validation"); PhoneSwitcher.this.obtainMessage(EVENT_OPPT_DATA_SUB_CHANGED, subId, needValidation ? 1 : 0, callback).sendToTarget(); @@ -1660,21 +1937,38 @@ ? HAL_COMMAND_PREFERRED_DATA : HAL_COMMAND_ALLOW_DATA; } - public int getOpportunisticDataSubscriptionId() { - return mOpptDataSubId; - } - public int getPreferredDataPhoneId() { return mPreferredDataPhoneId; } + /** + * Log debug messages and also log into the local log. + * @param l debug messages + */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - protected void log(String l) { - Rlog.d(LOG_TAG, l); + protected void logl(String l) { + log(l); mLocalLog.log(l); } /** + * Log debug messages. + * @param s debug messages + */ + private void log(@NonNull String s) { + Rlog.d(LOG_TAG, s); + } + + /** + * Log debug error messages. + * @param s debug messages + */ + private void loge(@NonNull String s) { + Rlog.e(LOG_TAG, s); + } + + + /** * Convert data switch reason into string. * * @param reason The switch reason. @@ -1690,6 +1984,8 @@ return "IN_CALL"; case TelephonyEvent.DataSwitch.Reason.DATA_SWITCH_REASON_CBRS: return "CBRS"; + case TelephonyEvent.DataSwitch.Reason.DATA_SWITCH_REASON_AUTO: + return "AUTO"; default: return "UNKNOWN(" + reason + ")"; } } @@ -1720,8 +2016,8 @@ * @param reason The switching reason. */ private void logDataSwitchEvent(int subId, int state, int reason) { - log("Data switch event. subId=" + subId + ", state=" + switchStateToString(state) - + ", reason=" + switchReasonToString(reason)); + logl("Data switch state=" + switchStateToString(state) + " due to reason=" + + switchReasonToString(reason) + " on subId " + subId); DataSwitch dataSwitch = new DataSwitch(); dataSwitch.state = state; dataSwitch.reason = reason; @@ -1734,7 +2030,7 @@ protected void notifyPreferredDataSubIdChanged() { TelephonyRegistryManager telephonyRegistryManager = (TelephonyRegistryManager) mContext .getSystemService(Context.TELEPHONY_REGISTRY_SERVICE); - log("notifyPreferredDataSubIdChanged to " + mPreferredDataSubId.get()); + logl("notifyPreferredDataSubIdChanged to " + mPreferredDataSubId.get()); telephonyRegistryManager.notifyActiveDataSubIdChanged(mPreferredDataSubId.get()); } @@ -1745,6 +2041,13 @@ return mPreferredDataSubId.get(); } + /** + * @return The auto selected data subscription id. + */ + public int getAutoSelectedDataSubId() { + return mAutoSelectedDataSubId; + } + // TODO (b/148396668): add an internal callback method to monitor phone capability change, // and hook this call to that callback. private void onPhoneCapabilityChanged(PhoneCapability capability) { @@ -1759,23 +2062,32 @@ for (int i = 0; i < mActiveModemCount; i++) { PhoneState ps = mPhoneStates[i]; c.setTimeInMillis(ps.lastRequested); - pw.println("PhoneId(" + i + ") active=" + ps.active + ", lastRequest=" + - (ps.lastRequested == 0 ? "never" : + pw.println("PhoneId(" + i + ") active=" + ps.active + ", dataRegState=" + + NetworkRegistrationInfo.registrationStateToString(ps.dataRegState) + + ", lastRequest=" + + (ps.lastRequested == 0 ? "never" : String.format("%tm-%td %tH:%tM:%tS.%tL", c, c, c, c, c, c))); } pw.println("mPreferredDataPhoneId=" + mPreferredDataPhoneId); pw.println("mPreferredDataSubId=" + mPreferredDataSubId.get()); - pw.println("DefaultDataSubId=" + mSubscriptionController.getDefaultDataSubId()); - pw.println("DefaultDataPhoneId=" + mSubscriptionController.getPhoneId( - mSubscriptionController.getDefaultDataSubId())); + pw.println("DefaultDataSubId=" + mSubscriptionManagerService.getDefaultDataSubId()); + pw.println("DefaultDataPhoneId=" + mSubscriptionManagerService.getPhoneId( + mSubscriptionManagerService.getDefaultDataSubId())); pw.println("mPrimaryDataSubId=" + mPrimaryDataSubId); - pw.println("mOpptDataSubId=" + mOpptDataSubId); + pw.println("mAutoSelectedDataSubId=" + mAutoSelectedDataSubId); pw.println("mIsRegisteredForImsRadioTechChange=" + mIsRegisteredForImsRadioTechChange); pw.println("mPendingSwitchNeedValidation=" + mPendingSwitchNeedValidation); pw.println("mMaxDataAttachModemCount=" + mMaxDataAttachModemCount); pw.println("mActiveModemCount=" + mActiveModemCount); pw.println("mPhoneIdInVoiceCall=" + mPhoneIdInVoiceCall); pw.println("mCurrentDdsSwitchFailure=" + mCurrentDdsSwitchFailure); + pw.println("mAutoDataSwitchAvailabilityStabilityTimeThreshold=" + + mAutoDataSwitchAvailabilityStabilityTimeThreshold); + pw.println("mAutoDataSwitchValidationMaxRetry=" + mAutoDataSwitchValidationMaxRetry); + pw.println("mRequirePingTestBeforeDataSwitch=" + mRequirePingTestBeforeDataSwitch); + pw.println("mLastSwitchPreferredDataReason=" + + switchReasonToString(mLastSwitchPreferredDataReason)); + pw.println("mDisplayedAutoSwitchNotification=" + mDisplayedAutoSwitchNotification); pw.println("Local logs:"); pw.increaseIndent(); mLocalLog.dump(fd, pw, args); @@ -1793,64 +2105,109 @@ boolean commandSuccess = ar != null && ar.exception == null; int phoneId = (int) ar.userObj; if (mEmergencyOverride != null) { - log("Emergency override result sent = " + commandSuccess); + logl("Emergency override result sent = " + commandSuccess); mEmergencyOverride.sendOverrideCompleteCallbackResultAndClear(commandSuccess); // Do not retry , as we do not allow changes in onEvaluate during an emergency // call. When the call ends, we will start the countdown to remove the override. } else if (!commandSuccess) { - log("onDdsSwitchResponse: DDS switch failed. with exception " + ar.exception); + logl("onDdsSwitchResponse: DDS switch failed. with exception " + ar.exception); if (ar.exception instanceof CommandException) { CommandException.Error error = ((CommandException) (ar.exception)).getCommandError(); mCurrentDdsSwitchFailure.get(phoneId).add(error); if (error == CommandException.Error.OP_NOT_ALLOWED_DURING_VOICE_CALL) { - log("onDdsSwitchResponse: Wait for call end indication"); + logl("onDdsSwitchResponse: Wait for call end indication"); return; } else if (error == CommandException.Error.INVALID_SIM_STATE) { /* If there is a attach failure due to sim not ready then hold the retry until sim gets ready */ - log("onDdsSwitchResponse: Wait for SIM to get READY"); + logl("onDdsSwitchResponse: Wait for SIM to get READY"); return; } } - log("onDdsSwitchResponse: Scheduling DDS switch retry"); + logl("onDdsSwitchResponse: Scheduling DDS switch retry"); sendMessageDelayed(Message.obtain(this, EVENT_MODEM_COMMAND_RETRY, phoneId), MODEM_COMMAND_RETRY_PERIOD_MS); return; } - if (commandSuccess) log("onDdsSwitchResponse: DDS switch success on phoneId = " + phoneId); + if (commandSuccess) logl("onDdsSwitchResponse: DDS switch success on phoneId = " + phoneId); mCurrentDdsSwitchFailure.get(phoneId).clear(); // Notify all registrants mActivePhoneRegistrants.notifyRegistrants(); notifyPreferredDataSubIdChanged(); + displayAutoDataSwitchNotification(); + } + + /** + * Display a notification the first time auto data switch occurs. + */ + private void displayAutoDataSwitchNotification() { + NotificationManager notificationManager = (NotificationManager) + mContext.getSystemService(Context.NOTIFICATION_SERVICE); + + if (mDisplayedAutoSwitchNotification) { + // cancel posted notification if any exist + log("displayAutoDataSwitchNotification: canceling any notifications for subId " + + mAutoSelectedDataSubId); + notificationManager.cancel(AUTO_DATA_SWITCH_NOTIFICATION_TAG, + AUTO_DATA_SWITCH_NOTIFICATION_ID); + return; + } + // proceed only the first time auto data switch occurs, which includes data during call + if (mLastSwitchPreferredDataReason != DataSwitch.Reason.DATA_SWITCH_REASON_AUTO) { + log("displayAutoDataSwitchNotification: Ignore DDS switch due to " + + switchReasonToString(mLastSwitchPreferredDataReason)); + return; + } + SubscriptionInfo subInfo = mSubscriptionManagerService + .getSubscriptionInfo(mAutoSelectedDataSubId); + if (subInfo == null || subInfo.isOpportunistic()) { + loge("displayAutoDataSwitchNotification: mAutoSelectedDataSubId=" + + mAutoSelectedDataSubId + " unexpected subInfo " + subInfo); + return; + } + logl("displayAutoDataSwitchNotification: display for subId=" + mAutoSelectedDataSubId); + // "Mobile network settings" screen / dialog + Intent intent = new Intent(Settings.ACTION_NETWORK_OPERATOR_SETTINGS); + final Bundle fragmentArgs = new Bundle(); + // Special contract for Settings to highlight permission row + fragmentArgs.putString(SETTINGS_EXTRA_FRAGMENT_ARG_KEY, AUTO_DATA_SWITCH_SETTING_R_ID); + intent.putExtra(Settings.EXTRA_SUB_ID, mAutoSelectedDataSubId); + intent.putExtra(SETTINGS_EXTRA_SHOW_FRAGMENT_ARGUMENTS, fragmentArgs); + PendingIntent contentIntent = PendingIntent.getActivity( + mContext, mAutoSelectedDataSubId, intent, PendingIntent.FLAG_IMMUTABLE); + + CharSequence activeCarrierName = subInfo.getDisplayName(); + CharSequence contentTitle = mContext.getString( + com.android.internal.R.string.auto_data_switch_title, activeCarrierName); + CharSequence contentText = mContext.getText( + com.android.internal.R.string.auto_data_switch_content); + + final Notification notif = new Notification.Builder(mContext) + .setContentTitle(contentTitle) + .setContentText(contentText) + .setSmallIcon(android.R.drawable.stat_sys_warning) + .setColor(mContext.getResources().getColor( + com.android.internal.R.color.system_notification_accent_color)) + .setChannelId(NotificationChannelController.CHANNEL_ID_MOBILE_DATA_STATUS) + .setContentIntent(contentIntent) + .setStyle(new Notification.BigTextStyle().bigText(contentText)) + .build(); + notificationManager.notify(AUTO_DATA_SWITCH_NOTIFICATION_TAG, + AUTO_DATA_SWITCH_NOTIFICATION_ID, notif); + mDisplayedAutoSwitchNotification = true; } private boolean isPhoneIdValidForRetry(int phoneId) { - int phoneIdForRequest = INVALID_PHONE_INDEX; - int ddsPhoneId = mSubscriptionController.getPhoneId( - mSubscriptionController.getDefaultDataSubId()); + int ddsPhoneId = mSubscriptionManagerService.getPhoneId( + mSubscriptionManagerService.getDefaultDataSubId()); if (ddsPhoneId != INVALID_PHONE_INDEX && ddsPhoneId == phoneId) { return true; } else { - if (PhoneFactory.getDefaultPhone().isUsingNewDataStack()) { - if (mNetworkRequestList.isEmpty()) return false; - for (TelephonyNetworkRequest networkRequest : mNetworkRequestList) { - phoneIdForRequest = phoneIdForRequest(networkRequest); - if (phoneIdForRequest == phoneId) { - return true; - } - } - } else { - if (mPrioritizedDcRequests.size() == 0) { - return false; - } - for (DcRequest dcRequest : mPrioritizedDcRequests) { - if (dcRequest != null) { - phoneIdForRequest = phoneIdForRequest(dcRequest.networkRequest); - if (phoneIdForRequest == phoneId) { - return true; - } - } + if (mNetworkRequestList.isEmpty()) return false; + for (TelephonyNetworkRequest networkRequest : mNetworkRequestList) { + if (phoneIdForRequest(networkRequest) == phoneId) { + return true; } } }
diff --git a/src/java/com/android/internal/telephony/data/QosCallbackTracker.java b/src/java/com/android/internal/telephony/data/QosCallbackTracker.java index b7cecbd..ac04627 100644 --- a/src/java/com/android/internal/telephony/data/QosCallbackTracker.java +++ b/src/java/com/android/internal/telephony/data/QosCallbackTracker.java
@@ -37,6 +37,7 @@ import java.net.InetAddress; import java.net.InetSocketAddress; +import java.net.UnknownHostException; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; @@ -53,8 +54,7 @@ private static final int DEDICATED_BEARER_EVENT_STATE_DELETED = 3; private final @NonNull String mLogTag; - // TODO: Change this to TelephonyNetworkAgent - private final @NonNull NotifyQosSessionInterface mNetworkAgent; + private final @NonNull TelephonyNetworkAgent mNetworkAgent; private final @NonNull Map<Integer, QosBearerSession> mQosBearerSessions; private final @NonNull RcsStats mRcsStats; @@ -80,12 +80,20 @@ /** * Filter using the remote address. * - * @param address The local address. + * @param address The remote address. * @param startPort Starting port. * @param endPort Ending port. * @return {@code true} if matches, {@code false} otherwise. */ boolean matchesRemoteAddress(InetAddress address, int startPort, int endPort); + + /** + * Filter using the protocol + * + * @param protocol ID + * @return {@code true} if matches, {@code false} otherwise. + */ + boolean matchesProtocol(int protocol); } /** @@ -94,8 +102,7 @@ * @param networkAgent The network agent to send events to. * @param phone The phone instance. */ - public QosCallbackTracker(@NonNull NotifyQosSessionInterface networkAgent, - @NonNull Phone phone) { + public QosCallbackTracker(@NonNull TelephonyNetworkAgent networkAgent, @NonNull Phone phone) { mQosBearerSessions = new HashMap<>(); mCallbacksToFilter = new HashMap<>(); mNetworkAgent = networkAgent; @@ -103,40 +110,41 @@ mRcsStats = RcsStats.getInstance(); mLogTag = "QOSCT" + "-" + ((NetworkAgent) mNetworkAgent).getNetwork().getNetId(); - if (phone.isUsingNewDataStack()) { - //TODO: Replace the NetworkAgent in the constructor with TelephonyNetworkAgent - // after mPhone.isUsingNewDataStack() check is removed. - ((TelephonyNetworkAgent) networkAgent).registerCallback( - new TelephonyNetworkAgent.TelephonyNetworkAgentCallback(this::post) { - @Override - public void onQosCallbackRegistered(int qosCallbackId, - @NonNull QosFilter filter) { - addFilter(qosCallbackId, - new QosCallbackTracker.IFilter() { - @Override - public boolean matchesLocalAddress( - @NonNull InetAddress address, int startPort, - int endPort) { - return filter.matchesLocalAddress(address, startPort, - endPort); - } + networkAgent.registerCallback( + new TelephonyNetworkAgent.TelephonyNetworkAgentCallback(this::post) { + @Override + public void onQosCallbackRegistered(int qosCallbackId, + @NonNull QosFilter filter) { + addFilter(qosCallbackId, + new QosCallbackTracker.IFilter() { + @Override + public boolean matchesLocalAddress( + @NonNull InetAddress address, int startPort, + int endPort) { + return filter.matchesLocalAddress(address, startPort, + endPort); + } - @Override - public boolean matchesRemoteAddress( - @NonNull InetAddress address, int startPort, - int endPort) { - return filter.matchesRemoteAddress(address, startPort, - endPort); - } - }); - } + @Override + public boolean matchesRemoteAddress( + @NonNull InetAddress address, int startPort, + int endPort) { + return filter.matchesRemoteAddress(address, startPort, + endPort); + } - @Override - public void onQosCallbackUnregistered(int qosCallbackId) { + @Override + public boolean matchesProtocol(int protocol) { + return filter.matchesProtocol(protocol); + } + }); + } - } - }); - } + @Override + public void onQosCallbackUnregistered(int qosCallbackId) { + + } + }); } /** @@ -280,41 +288,76 @@ private boolean matchesByLocalAddress(final @NonNull QosBearerFilter sessionFilter, final @NonNull IFilter filter) { - if (sessionFilter.getLocalPortRange() == null) return false; - for (final LinkAddress qosAddress : sessionFilter.getLocalAddresses()) { - return filter.matchesLocalAddress(qosAddress.getAddress(), - sessionFilter.getLocalPortRange().getStart(), - sessionFilter.getLocalPortRange().getEnd()); + int portStart; + int portEnd; + if (sessionFilter.getLocalPortRange() == null) { + portStart = QosBearerFilter.QOS_MIN_PORT; + portEnd = QosBearerFilter.QOS_MAX_PORT; + } else if (sessionFilter.getLocalPortRange().isValid()) { + portStart = sessionFilter.getLocalPortRange().getStart(); + portEnd = sessionFilter.getLocalPortRange().getEnd(); + } else { + return false; + } + if (sessionFilter.getLocalAddresses().isEmpty()) { + InetAddress anyAddress; + try { + anyAddress = InetAddress.getByAddress(new byte[] {0, 0, 0, 0}); + } catch (UnknownHostException e) { + return false; + } + return filter.matchesLocalAddress(anyAddress, portStart, portEnd); + } else { + for (final LinkAddress qosAddress : sessionFilter.getLocalAddresses()) { + return filter.matchesLocalAddress(qosAddress.getAddress(), portStart, portEnd); + } } return false; } private boolean matchesByRemoteAddress(@NonNull QosBearerFilter sessionFilter, final @NonNull IFilter filter) { - if (sessionFilter.getRemotePortRange() == null) return false; - for (final LinkAddress qosAddress : sessionFilter.getRemoteAddresses()) { - return filter.matchesRemoteAddress(qosAddress.getAddress(), - sessionFilter.getRemotePortRange().getStart(), - sessionFilter.getRemotePortRange().getEnd()); + int portStart; + int portEnd; + boolean result = false; + if (sessionFilter.getRemotePortRange() == null) { + portStart = QosBearerFilter.QOS_MIN_PORT; + portEnd = QosBearerFilter.QOS_MAX_PORT; + } else if (sessionFilter.getRemotePortRange().isValid()) { + portStart = sessionFilter.getRemotePortRange().getStart(); + portEnd = sessionFilter.getRemotePortRange().getEnd(); + } else { + return false; } - return false; - } - - private boolean matchesByRemoteAndLocalAddress(@NonNull QosBearerFilter sessionFilter, - final @NonNull IFilter filter) { - if (sessionFilter.getLocalPortRange() == null - || sessionFilter.getRemotePortRange() == null) return false; - for (final LinkAddress remoteAddress : sessionFilter.getRemoteAddresses()) { - for (final LinkAddress localAddress : sessionFilter.getLocalAddresses()) { - return filter.matchesRemoteAddress(remoteAddress.getAddress(), - sessionFilter.getRemotePortRange().getStart(), - sessionFilter.getRemotePortRange().getEnd()) - && filter.matchesLocalAddress(localAddress.getAddress(), - sessionFilter.getLocalPortRange().getStart(), - sessionFilter.getLocalPortRange().getEnd()); + if (sessionFilter.getRemoteAddresses().isEmpty()) { + InetAddress anyAddress; + try { + anyAddress = InetAddress.getByAddress(new byte[] {0, 0, 0, 0}); + } catch (UnknownHostException e) { + return false; + } + result = filter.matchesRemoteAddress(anyAddress, portStart, portEnd); + } else { + for (final LinkAddress qosAddress : sessionFilter.getRemoteAddresses()) { + result = filter.matchesRemoteAddress(qosAddress.getAddress(), portStart, portEnd); } } - return false; + return result; + } + + private boolean matchesByProtocol(@NonNull QosBearerFilter sessionFilter, + final @NonNull IFilter filter, boolean hasMatchedFilter) { + boolean result = false; + int protocol = sessionFilter.getProtocol(); + if (protocol == QosBearerFilter.QOS_PROTOCOL_TCP + || protocol == QosBearerFilter.QOS_PROTOCOL_UDP) { + result = filter.matchesProtocol(protocol); + } else { + // FWK currently doesn't support filtering based on protocol ID ESP & AH. We will follow + // match results of other filters. + result = hasMatchedFilter; + } + return result; } private QosBearerFilter getFilterByPrecedence( @@ -329,28 +372,36 @@ QosBearerFilter qosFilter = null; for (final QosBearerFilter sessionFilter : qosBearerSession.getQosBearerFilterList()) { + boolean unMatched = false; + boolean hasMatchedFilter = false; if (!sessionFilter.getLocalAddresses().isEmpty() - && !sessionFilter.getRemoteAddresses().isEmpty() - && sessionFilter.getLocalPortRange() != null - && sessionFilter.getLocalPortRange().isValid() - && sessionFilter.getRemotePortRange() != null - && sessionFilter.getRemotePortRange().isValid()) { - if (matchesByRemoteAndLocalAddress(sessionFilter, filter)) { - qosFilter = getFilterByPrecedence(qosFilter, sessionFilter); + || sessionFilter.getLocalPortRange() != null) { + if (!matchesByLocalAddress(sessionFilter, filter)) { + unMatched = true; + } else { + hasMatchedFilter = true; } - } else if (!sessionFilter.getRemoteAddresses().isEmpty() - && sessionFilter.getRemotePortRange() != null - && sessionFilter.getRemotePortRange().isValid()) { - if (matchesByRemoteAddress(sessionFilter, filter)) { - qosFilter = getFilterByPrecedence(qosFilter, sessionFilter); + } + if (!sessionFilter.getRemoteAddresses().isEmpty() + || sessionFilter.getRemotePortRange() != null) { + if (!matchesByRemoteAddress(sessionFilter, filter)) { + unMatched = true; + } else { + hasMatchedFilter = true; } - } else if (!sessionFilter.getLocalAddresses().isEmpty() - && sessionFilter.getLocalPortRange() != null - && sessionFilter.getLocalPortRange().isValid()) { - if (matchesByLocalAddress(sessionFilter, filter)) { - qosFilter = getFilterByPrecedence(qosFilter, sessionFilter); + } + + if (sessionFilter.getProtocol() != QosBearerFilter.QOS_PROTOCOL_UNSPECIFIED) { + if (!matchesByProtocol(sessionFilter, filter, hasMatchedFilter)) { + unMatched = true; + } else { + hasMatchedFilter = true; } } + + if (!unMatched && hasMatchedFilter) { + qosFilter = getFilterByPrecedence(qosFilter, sessionFilter); + } } return qosFilter; } @@ -375,7 +426,7 @@ qos.getDownlinkBandwidth().getGuaranteedBitrateKbps(), qos.getUplinkBandwidth().getGuaranteedBitrateKbps(), remoteAddresses); - mNetworkAgent.notifyQosSessionAvailable( + mNetworkAgent.sendQosSessionAvailable( callbackId, session.getQosBearerSessionId(), epsBearerAttr); } else { NrQos qos = (NrQos) session.getQos(); @@ -386,7 +437,7 @@ qos.getDownlinkBandwidth().getGuaranteedBitrateKbps(), qos.getUplinkBandwidth().getGuaranteedBitrateKbps(), qos.getAveragingWindow(), remoteAddresses); - mNetworkAgent.notifyQosSessionAvailable( + mNetworkAgent.sendQosSessionAvailable( callbackId, session.getQosBearerSessionId(), nrQosAttr); } @@ -397,7 +448,7 @@ } private void sendSessionLost(int callbackId, @NonNull QosBearerSession session) { - mNetworkAgent.notifyQosSessionLost(callbackId, session.getQosBearerSessionId(), + mNetworkAgent.sendQosSessionLost(callbackId, session.getQosBearerSessionId(), session.getQos() instanceof EpsQos ? QosSession.TYPE_EPS_BEARER : QosSession.TYPE_NR_BEARER); log("sendSessionLost, callbackId=" + callbackId);
diff --git a/src/java/com/android/internal/telephony/data/TelephonyNetworkAgent.java b/src/java/com/android/internal/telephony/data/TelephonyNetworkAgent.java index 3b12562..5ed09e6 100644 --- a/src/java/com/android/internal/telephony/data/TelephonyNetworkAgent.java +++ b/src/java/com/android/internal/telephony/data/TelephonyNetworkAgent.java
@@ -22,10 +22,10 @@ import android.net.KeepalivePacketData; import android.net.NetworkAgent; import android.net.NetworkAgentConfig; +import android.net.NetworkCapabilities; import android.net.NetworkProvider; import android.net.NetworkScore; import android.net.QosFilter; -import android.net.QosSessionAttributes; import android.net.Uri; import android.os.Looper; import android.util.ArraySet; @@ -46,11 +46,13 @@ * for telephony to propagate network related information to the connectivity service. It always * has an associated parent {@link DataNetwork}. */ -public class TelephonyNetworkAgent extends NetworkAgent implements NotifyQosSessionInterface { +public class TelephonyNetworkAgent extends NetworkAgent { private final String mLogTag; - private final Phone mPhone; private final LocalLog mLocalLog = new LocalLog(128); + /** Max unregister network agent delay. */ + private static final int NETWORK_AGENT_TEARDOWN_DELAY_MS = 5_000; + /** The parent data network. */ private final @NonNull DataNetwork mDataNetwork; @@ -153,18 +155,25 @@ @NonNull NetworkAgentConfig config, @NonNull NetworkProvider provider, @NonNull TelephonyNetworkAgentCallback callback) { super(phone.getContext(), looper, "TelephonyNetworkAgent", - dataNetwork.getNetworkCapabilities(), dataNetwork.getLinkProperties(), score, + // Connectivity service does not allow an agent created in suspended state. + // Always create the network agent with NOT_SUSPENDED and immediately update the + // suspended state afterwards. + new NetworkCapabilities.Builder(dataNetwork.getNetworkCapabilities()) + .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED) + .build(), + dataNetwork.getLinkProperties(), score, config, provider); register(); mDataNetwork = dataNetwork; mNetworkAgentConfig = config; mTelephonyNetworkAgentCallbacks.add(callback); - mPhone = phone; mId = getNetwork().getNetId(); mLogTag = "TNA-" + mId; log("TelephonyNetworkAgent created, nc=" - + dataNetwork.getNetworkCapabilities() + ", score=" + score); + + new NetworkCapabilities.Builder(dataNetwork.getNetworkCapabilities()) + .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED) + .build() + ", score=" + score); } /** @@ -289,46 +298,16 @@ } /** - * Sends the attributes of Qos Session back to the Application. This method is create for - * Mockito to mock since - * {@link NetworkAgent#sendQosSessionAvailable(int, int, QosSessionAttributes)} is - * {@code final} that can't be mocked. - * - * @param qosCallbackId the callback id that the session belongs to. - * @param sessionId the unique session id across all Qos Sessions. - * @param attributes the attributes of the Qos Session. - */ - @Override - public void notifyQosSessionAvailable(final int qosCallbackId, final int sessionId, - @NonNull final QosSessionAttributes attributes) { - super.sendQosSessionAvailable(qosCallbackId, sessionId, attributes); - } - - /** - * Sends event that the Qos Session was lost. This method is create for Mockito to mock - * since {@link NetworkAgent#sendQosSessionLost(int, int, int)} is {@code final} that can't be - * mocked.. - * - * @param qosCallbackId the callback id that the session belongs to. - * @param sessionId the unique session id across all Qos Sessions. - * @param qosSessionType the session type {@code QosSession#QosSessionType}. - */ - @Override - public void notifyQosSessionLost(final int qosCallbackId, - final int sessionId, final int qosSessionType) { - super.sendQosSessionLost(qosCallbackId, sessionId, qosSessionType); - } - - /** * Abandon the network agent. This is used for telephony to re-create the network agent when - * immutable capabilities got changed, where telephony calls {@link NetworkAgent#unregister()} - * and then create another network agent with new capabilities. Abandon this network agent - * allowing it ignore the subsequent {@link #onNetworkUnwanted()} invocation caused by - * {@link NetworkAgent#unregister()}. + * immutable capabilities got changed, where telephony calls + * {@link NetworkAgent#unregisterAfterReplacement} and then create another network agent with + * new capabilities. Abandon this network agent allowing it ignore the subsequent + * {@link #onNetworkUnwanted()} invocation caused by + * {@link NetworkAgent#unregisterAfterReplacement}. */ public void abandon() { mAbandoned = true; - unregister(); + unregisterAfterReplacement(NETWORK_AGENT_TEARDOWN_DELAY_MS); } /**
diff --git a/src/java/com/android/internal/telephony/data/TelephonyNetworkFactory.java b/src/java/com/android/internal/telephony/data/TelephonyNetworkFactory.java index 85f0ae1..2cdf807 100644 --- a/src/java/com/android/internal/telephony/data/TelephonyNetworkFactory.java +++ b/src/java/com/android/internal/telephony/data/TelephonyNetworkFactory.java
@@ -20,27 +20,15 @@ import android.net.NetworkFactory; 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; import android.telephony.AccessNetworkConstants; -import android.telephony.Annotation.ApnType; import android.telephony.SubscriptionManager; -import android.telephony.data.ApnSetting; import android.util.LocalLog; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.telephony.Phone; -import com.android.internal.telephony.PhoneFactory; -import com.android.internal.telephony.SubscriptionController; -import com.android.internal.telephony.dataconnection.ApnContext; -import com.android.internal.telephony.dataconnection.DataConnection; -import com.android.internal.telephony.dataconnection.DcTracker; -import com.android.internal.telephony.dataconnection.DcTracker.ReleaseNetworkType; -import com.android.internal.telephony.dataconnection.DcTracker.RequestNetworkType; -import com.android.internal.telephony.dataconnection.TransportManager.HandoverParams; import com.android.internal.telephony.metrics.NetworkRequestsStats; import com.android.internal.util.IndentingPrintWriter; import com.android.telephony.Rlog; @@ -72,19 +60,14 @@ public static final int EVENT_SUBSCRIPTION_CHANGED = 2; private static final int EVENT_NETWORK_REQUEST = 3; private static final int EVENT_NETWORK_RELEASE = 4; - private static final int EVENT_DATA_HANDOVER_NEEDED = 5; - private static final int EVENT_DATA_HANDOVER_COMPLETED = 6; private final PhoneSwitcher mPhoneSwitcher; - private final SubscriptionController mSubscriptionController; private final LocalLog mLocalLog = new LocalLog(REQUEST_LOG_SIZE); - // Key: network request. Value: the transport of DcTracker it applies to, + // Key: network request. Value: the transport of the network request applies to, // AccessNetworkConstants.TRANSPORT_TYPE_INVALID if not applied. private final Map<TelephonyNetworkRequest, Integer> mNetworkRequests = new HashMap<>(); - private final Map<Message, HandoverParams> mPendingHandovers = new HashMap<>(); - private final Phone mPhone; private AccessNetworksManager mAccessNetworksManager; @@ -101,10 +84,9 @@ mPhone = phone; mInternalHandler = new InternalHandler(looper); - mSubscriptionController = SubscriptionController.getInstance(); mAccessNetworksManager = mPhone.getAccessNetworksManager(); - setCapabilityFilter(makeNetworkFilter(mSubscriptionController, mPhone.getPhoneId())); + setCapabilityFilter(makeNetworkFilterByPhoneId(mPhone.getPhoneId())); setScoreFilter(TELEPHONY_NETWORK_SCORE); mPhoneSwitcher = PhoneSwitcher.getInstance(); @@ -112,10 +94,6 @@ mPhoneSwitcher.registerForActivePhoneSwitch(mInternalHandler, EVENT_ACTIVE_PHONE_SWITCH, null); - if (!phone.isUsingNewDataStack()) { - mPhone.getTransportManager().registerForHandoverNeededEvent(mInternalHandler, - EVENT_DATA_HANDOVER_NEEDED); - } mSubscriptionId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; SubscriptionManager.from(mPhone.getContext()).addOnSubscriptionsChangedListener( @@ -132,10 +110,8 @@ } }; - private NetworkCapabilities makeNetworkFilter(SubscriptionController subscriptionController, - int phoneId) { - final int subscriptionId = subscriptionController.getSubIdUsingPhoneId(phoneId); - return makeNetworkFilter(subscriptionId); + private NetworkCapabilities makeNetworkFilterByPhoneId(int phoneId) { + return makeNetworkFilter(SubscriptionManager.getSubscriptionId(phoneId)); } /** @@ -201,93 +177,18 @@ onReleaseNetworkFor(msg); break; } - case EVENT_DATA_HANDOVER_NEEDED: { - AsyncResult ar = (AsyncResult) msg.obj; - HandoverParams handoverParams = (HandoverParams) ar.result; - onDataHandoverNeeded(handoverParams.apnType, handoverParams.targetTransport, - handoverParams); - break; - } - case EVENT_DATA_HANDOVER_COMPLETED: { - Bundle bundle = msg.getData(); - NetworkRequest nr = bundle.getParcelable( - DcTracker.DATA_COMPLETE_MSG_EXTRA_NETWORK_REQUEST); - boolean success = bundle.getBoolean( - DcTracker.DATA_COMPLETE_MSG_EXTRA_SUCCESS); - int transport = bundle.getInt( - DcTracker.DATA_COMPLETE_MSG_EXTRA_TRANSPORT_TYPE); - boolean fallback = bundle.getBoolean( - DcTracker.DATA_COMPLETE_MSG_EXTRA_HANDOVER_FAILURE_FALLBACK); - HandoverParams handoverParams = mPendingHandovers.remove(msg); - if (handoverParams != null) { - onDataHandoverSetupCompleted(nr, success, transport, fallback, - handoverParams); - } else { - logl("Handover completed but cannot find handover entry!"); - } - break; - } } } } private int getTransportTypeFromNetworkRequest(TelephonyNetworkRequest networkRequest) { - if (PhoneFactory.getDefaultPhone().isUsingNewDataStack()) { - int transport = AccessNetworkConstants.TRANSPORT_TYPE_WWAN; - int capability = networkRequest.getApnTypeNetworkCapability(); - if (capability >= 0) { - transport = mAccessNetworksManager - .getPreferredTransportByNetworkCapability(capability); - } - return transport; - } else { - int apnType = ApnContext.getApnTypeFromNetworkRequest( - networkRequest.getNativeNetworkRequest()); - return mAccessNetworksManager.getCurrentTransport(apnType); + int transport = AccessNetworkConstants.TRANSPORT_TYPE_WWAN; + int capability = networkRequest.getApnTypeNetworkCapability(); + if (capability >= 0) { + transport = mAccessNetworksManager + .getPreferredTransportByNetworkCapability(capability); } - } - - /** - * Request network - * - * @param networkRequest Network request from clients - * @param requestType The request type - * @param transport Transport type - * @param onHandoverCompleteMsg When request type is handover, this message will be sent when - * handover is completed. For normal request, this should be null. - */ - private void requestNetworkInternal(TelephonyNetworkRequest networkRequest, - @RequestNetworkType int requestType, int transport, Message onHandoverCompleteMsg) { - NetworkRequestsStats.addNetworkRequest(networkRequest.getNativeNetworkRequest(), - mSubscriptionId); - - if (mPhone.isUsingNewDataStack()) { - mPhone.getDataNetworkController().addNetworkRequest(networkRequest); - } else { - if (mPhone.getDcTracker(transport) != null) { - mPhone.getDcTracker(transport).requestNetwork( - networkRequest.getNativeNetworkRequest(), requestType, - onHandoverCompleteMsg); - } - } - } - - private void releaseNetworkInternal(TelephonyNetworkRequest networkRequest) { - mPhone.getDataNetworkController().removeNetworkRequest(networkRequest); - } - - // TODO: Clean this up after old data stack removed. - private void releaseNetworkInternal(TelephonyNetworkRequest networkRequest, - @ReleaseNetworkType int releaseType, - int transport) { - if (mPhone.isUsingNewDataStack()) { - mPhone.getDataNetworkController().removeNetworkRequest(networkRequest); - } else { - if (mPhone.getDcTracker(transport) != null) { - mPhone.getDcTracker(transport).releaseNetwork( - networkRequest.getNativeNetworkRequest(), releaseType); - } - } + return transport; } private static int getAction(boolean wasActive, boolean isActive) { @@ -317,15 +218,11 @@ ? "Requesting" : "Releasing") + " network request " + networkRequest); int transportType = getTransportTypeFromNetworkRequest(networkRequest); if (action == ACTION_REQUEST) { - requestNetworkInternal(networkRequest, DcTracker.REQUEST_TYPE_NORMAL, - getTransportTypeFromNetworkRequest(networkRequest), null); + NetworkRequestsStats.addNetworkRequest(networkRequest.getNativeNetworkRequest(), + mSubscriptionId); + mPhone.getDataNetworkController().addNetworkRequest(networkRequest); } else if (action == ACTION_RELEASE) { - if (mPhone.isUsingNewDataStack()) { - releaseNetworkInternal(networkRequest); - } else { - releaseNetworkInternal(networkRequest, DcTracker.RELEASE_TYPE_DETACH, - getTransportTypeFromNetworkRequest(networkRequest)); - } + mPhone.getDataNetworkController().removeNetworkRequest(networkRequest); } mNetworkRequests.put(networkRequest, @@ -336,8 +233,7 @@ // watch for phone->subId changes, reapply new filter and let // that flow through to apply/revoke of requests private void onSubIdChange() { - final int newSubscriptionId = mSubscriptionController.getSubIdUsingPhoneId( - mPhone.getPhoneId()); + int newSubscriptionId = SubscriptionManager.getSubscriptionId(mPhone.getPhoneId()); if (mSubscriptionId != newSubscriptionId) { if (DBG) logl("onSubIdChange " + mSubscriptionId + "->" + newSubscriptionId); mSubscriptionId = newSubscriptionId; @@ -365,8 +261,9 @@ logl("onNeedNetworkFor " + networkRequest + " shouldApply " + shouldApply); if (shouldApply) { - requestNetworkInternal(networkRequest, DcTracker.REQUEST_TYPE_NORMAL, - getTransportTypeFromNetworkRequest(networkRequest), null); + NetworkRequestsStats.addNetworkRequest(networkRequest.getNativeNetworkRequest(), + mSubscriptionId); + mPhone.getDataNetworkController().addNetworkRequest(networkRequest); } } @@ -388,136 +285,10 @@ logl("onReleaseNetworkFor " + networkRequest + " applied " + applied); if (applied) { - if (mPhone.isUsingNewDataStack()) { - releaseNetworkInternal(networkRequest); - } else { - // 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); - } + mPhone.getDataNetworkController().removeNetworkRequest(networkRequest); } } - private void onDataHandoverNeeded(@ApnType int apnType, int targetTransport, - HandoverParams handoverParams) { - log("onDataHandoverNeeded: apnType=" + ApnSetting.getApnTypeString(apnType) - + ", target transport=" - + AccessNetworkConstants.transportTypeToString(targetTransport)); - if (mAccessNetworksManager.getCurrentTransport(apnType) == targetTransport) { - log("APN type " + ApnSetting.getApnTypeString(apnType) + " is already on " - + AccessNetworkConstants.transportTypeToString(targetTransport)); - return; - } - - boolean handoverPending = false; - for (Map.Entry<TelephonyNetworkRequest, Integer> entry : mNetworkRequests.entrySet()) { - TelephonyNetworkRequest networkRequest = entry.getKey(); - int currentTransport = entry.getValue(); - boolean applied = currentTransport != AccessNetworkConstants.TRANSPORT_TYPE_INVALID; - if (ApnContext.getApnTypeFromNetworkRequest( - networkRequest.getNativeNetworkRequest()) == apnType - && applied - && currentTransport != targetTransport) { - DcTracker dcTracker = mPhone.getDcTracker(currentTransport); - if (dcTracker != null) { - DataConnection dc = dcTracker.getDataConnectionByApnType( - ApnSetting.getApnTypeString(apnType)); - if (dc != null && (dc.isActive())) { - Message onCompleteMsg = mInternalHandler.obtainMessage( - EVENT_DATA_HANDOVER_COMPLETED); - onCompleteMsg.getData().putParcelable( - DcTracker.DATA_COMPLETE_MSG_EXTRA_NETWORK_REQUEST, - networkRequest.getNativeNetworkRequest()); - mPendingHandovers.put(onCompleteMsg, handoverParams); - requestNetworkInternal(networkRequest, DcTracker.REQUEST_TYPE_HANDOVER, - targetTransport, onCompleteMsg); - log("Requested handover " + ApnSetting.getApnTypeString(apnType) - + " to " - + AccessNetworkConstants.transportTypeToString(targetTransport) - + ". " + networkRequest); - handoverPending = true; - } else { - // Request is there, but no actual data connection. In this case, just move - // the request to the new transport. - log("The network request is on transport " + AccessNetworkConstants - .transportTypeToString(currentTransport) + ", but no live data " - + "connection. Just move the request to transport " - + AccessNetworkConstants.transportTypeToString(targetTransport) - + ", dc=" + dc); - entry.setValue(targetTransport); - releaseNetworkInternal(networkRequest, DcTracker.RELEASE_TYPE_NORMAL, - currentTransport); - requestNetworkInternal(networkRequest, DcTracker.REQUEST_TYPE_NORMAL, - targetTransport, null); - } - } else { - log("DcTracker on " + AccessNetworkConstants.transportTypeToString( - currentTransport) + " is not available."); - } - } - } - - if (!handoverPending) { - log("No handover request pending. Handover process is now completed"); - handoverParams.callback.onCompleted(true, false); - } - } - - private void onDataHandoverSetupCompleted(NetworkRequest request, boolean success, - int targetTransport, boolean fallback, - HandoverParams handoverParams) { - log("onDataHandoverSetupCompleted: " + request + ", success=" + success - + ", targetTransport=" - + AccessNetworkConstants.transportTypeToString(targetTransport) - + ", fallback=" + fallback); - - TelephonyNetworkRequest networkRequest = new TelephonyNetworkRequest(request, mPhone); - // At this point, handover setup has been completed on the target transport. - // If it succeeded, or it failed without falling back to the original transport, - // we should release the request from the original transport. - if (!fallback) { - int originTransport = DataUtils.getSourceTransport(targetTransport); - int releaseType = success - ? DcTracker.RELEASE_TYPE_HANDOVER - // If handover fails, we need to tear down the existing connection, so the - // new data connection can be re-established on the new transport. If we leave - // the existing data connection in current transport, then DCT and qualified - // network service will be out of sync. Specifying release type to detach - // the transport is moved to the other transport, but network request is still - // there, connectivity service will not call unwanted to tear down the network. - // We need explicitly tear down the data connection here so the new data - // connection can be re-established on the other transport. - : DcTracker.RELEASE_TYPE_DETACH; - releaseNetworkInternal(networkRequest, releaseType, originTransport); - - // Before updating the network request with the target transport, make sure the request - // is still there because it's possible that connectivity service has already released - // the network while handover is ongoing. If connectivity service already released - // the network request, we need to tear down the just-handovered data connection on the - // target transport. - if (mNetworkRequests.containsKey(networkRequest)) { - // Update it with the target transport. - mNetworkRequests.put(networkRequest, targetTransport); - } - } else { - // If handover fails and requires to fallback, the context of target transport needs to - // be released - if (!success) { - releaseNetworkInternal(networkRequest, - DcTracker.RELEASE_TYPE_NORMAL, targetTransport); - } - } - - handoverParams.callback.onCompleted(success, fallback); - } - protected void log(String s) { Rlog.d(LOG_TAG, s); }
diff --git a/src/java/com/android/internal/telephony/data/TelephonyNetworkRequest.java b/src/java/com/android/internal/telephony/data/TelephonyNetworkRequest.java index b55304a..b334b89 100644 --- a/src/java/com/android/internal/telephony/data/TelephonyNetworkRequest.java +++ b/src/java/com/android/internal/telephony/data/TelephonyNetworkRequest.java
@@ -157,8 +157,7 @@ /** * Data config manager for retrieving data config. */ - // TODO: Make this @NonNull after old data stack removed. - private final @Nullable DataConfigManager mDataConfigManager; + private final @NonNull DataConfigManager mDataConfigManager; /** * The attached data network. Note that the data network could be in any state. {@code null} @@ -204,12 +203,8 @@ // to satisfy it. mState = REQUEST_STATE_UNSATISFIED; mCreatedTimeMillis = SystemClock.elapsedRealtime(); - if (phone.isUsingNewDataStack()) { - mDataConfigManager = phone.getDataNetworkController().getDataConfigManager(); - updatePriority(); - } else { - mDataConfigManager = null; - } + mDataConfigManager = phone.getDataNetworkController().getDataConfigManager(); + updatePriority(); } /** @@ -401,8 +396,7 @@ * @return {@code true} if this network request can result in bringing up a metered network. */ public boolean isMeteredRequest() { - // TODO: Remove null check after old data stack removed. - return mDataConfigManager != null && mDataConfigManager.isAnyMeteredCapability( + return mDataConfigManager.isAnyMeteredCapability( getCapabilities(), mPhone.getServiceState().getDataRoaming()); }
diff --git a/src/java/com/android/internal/telephony/dataconnection/ApnConfigType.java b/src/java/com/android/internal/telephony/dataconnection/ApnConfigType.java deleted file mode 100644 index 827dbdd..0000000 --- a/src/java/com/android/internal/telephony/dataconnection/ApnConfigType.java +++ /dev/null
@@ -1,50 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.internal.telephony.dataconnection; - -import android.telephony.Annotation; - -/** - * Container of network configuration settings relevant for telephony module. - * - */ -public class ApnConfigType { - - private final int mType; - private final int mPriority; - - public ApnConfigType(@Annotation.ApnType int type, int priority) { - mType = type; - mPriority = priority; - } - - /** - * Returns the apn type of this config type - * @return Type of apn. - */ - public int getType() { - return mType; - } - - /** - * Returns the priority of this apn config type. - * @return The priority of this apn. - */ - public int getPriority() { - return mPriority; - } -}
diff --git a/src/java/com/android/internal/telephony/dataconnection/ApnConfigTypeRepository.java b/src/java/com/android/internal/telephony/dataconnection/ApnConfigTypeRepository.java deleted file mode 100644 index 156ac92..0000000 --- a/src/java/com/android/internal/telephony/dataconnection/ApnConfigTypeRepository.java +++ /dev/null
@@ -1,128 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.internal.telephony.dataconnection; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.os.PersistableBundle; -import android.telephony.Annotation; -import android.telephony.CarrierConfigManager; -import android.telephony.Rlog; -import android.telephony.data.ApnSetting; -import android.util.ArrayMap; - -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; - -/** - * Hard coded configuration of specific network types that the telephony module needs. - * Formerly stored in network attributes within the resources file. - */ -public class ApnConfigTypeRepository { - - private static final String TAG = ApnConfigTypeRepository.class.getSimpleName(); - - private final Map<Integer, ApnConfigType> mConfigTypeMap; - - public ApnConfigTypeRepository(PersistableBundle carrierConfig) { - mConfigTypeMap = new HashMap<>(); - setup(carrierConfig); - } - - /** - * Gets list of apn config types. - * @return All apn config types. - */ - public Collection<ApnConfigType> getTypes() { - return mConfigTypeMap.values(); - } - - /** - * Gets the apn config type by apn type. - * @param type The ApnType to search for. - * @return The config type matching the given apn type. - */ - @Nullable - public ApnConfigType getByType(@Annotation.ApnType int type) { - return mConfigTypeMap.get(type); - } - - private void setup(PersistableBundle carrierConfig) { - addApns(getCarrierApnTypeMap(CarrierConfigManager.getDefaultConfig())); - addApns(getCarrierApnTypeMap(carrierConfig)); - } - - private void addApns(Map<Integer, Integer> apnTypeMap) { - add(ApnSetting.TYPE_DEFAULT, apnTypeMap); - add(ApnSetting.TYPE_MMS, apnTypeMap); - add(ApnSetting.TYPE_SUPL, apnTypeMap); - add(ApnSetting.TYPE_DUN, apnTypeMap); - add(ApnSetting.TYPE_HIPRI, apnTypeMap); - add(ApnSetting.TYPE_FOTA, apnTypeMap); - add(ApnSetting.TYPE_IMS, apnTypeMap); - add(ApnSetting.TYPE_CBS, apnTypeMap); - add(ApnSetting.TYPE_IA, apnTypeMap); - add(ApnSetting.TYPE_EMERGENCY, apnTypeMap); - add(ApnSetting.TYPE_MCX, apnTypeMap); - add(ApnSetting.TYPE_XCAP, apnTypeMap); - add(ApnSetting.TYPE_ENTERPRISE, apnTypeMap); - } - - @NonNull - private Map<Integer, Integer> getCarrierApnTypeMap(PersistableBundle carrierConfig) { - if (carrierConfig == null) { - Rlog.w(TAG, "carrier config is null"); - return new ArrayMap<>(); - } - - final String[] apnTypeConfig = - carrierConfig.getStringArray(CarrierConfigManager.KEY_APN_PRIORITY_STRING_ARRAY); - - final Map<Integer, Integer> apnTypeMap = new ArrayMap<>(); - if (apnTypeConfig != null) { - for (final String entry : apnTypeConfig) { - try { - final String[] keyValue = entry.split(":"); - if (keyValue.length != 2) { - Rlog.e(TAG, "Apn type entry must have exactly one ':'"); - } else if (keyValue[0].contains(",")) { - //getApnTypesBitmaskFromString parses commas to a list, not valid here. - Rlog.e(TAG, "Invalid apn type name, entry: " + entry); - } else { - int apnTypeBitmask = ApnSetting.getApnTypesBitmaskFromString(keyValue[0]); - if (apnTypeBitmask > 0) { - apnTypeMap.put(apnTypeBitmask, Integer.parseInt(keyValue[1])); - } else { - Rlog.e(TAG, "Invalid apn type name, entry: " + entry); - } - } - - } catch (Exception ex) { - Rlog.e(TAG, "Exception on apn type entry: " + entry + "\n", ex); - } - } - } - return apnTypeMap; - } - - private void add(@Annotation.ApnType int type, Map<Integer, Integer> apnTypeMap) { - if (apnTypeMap.containsKey(type)) { - mConfigTypeMap.put(type, new ApnConfigType(type, apnTypeMap.get(type))); - } - } -}
diff --git a/src/java/com/android/internal/telephony/dataconnection/ApnContext.java b/src/java/com/android/internal/telephony/dataconnection/ApnContext.java deleted file mode 100644 index 3f487cd..0000000 --- a/src/java/com/android/internal/telephony/dataconnection/ApnContext.java +++ /dev/null
@@ -1,675 +0,0 @@ -/* - * Copyright (C) 2006 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.internal.telephony.dataconnection; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.net.NetworkCapabilities; -import android.net.NetworkRequest; -import android.os.Message; -import android.telephony.Annotation.ApnType; -import android.telephony.data.ApnSetting; -import android.text.TextUtils; -import android.util.ArraySet; -import android.util.LocalLog; -import android.util.SparseIntArray; - -import com.android.internal.R; -import com.android.internal.telephony.DctConstants; -import com.android.internal.telephony.Phone; -import com.android.internal.telephony.RetryManager; -import com.android.internal.telephony.dataconnection.DcTracker.ReleaseNetworkType; -import com.android.internal.telephony.dataconnection.DcTracker.RequestNetworkType; -import com.android.internal.util.IndentingPrintWriter; -import com.android.telephony.Rlog; - -import java.io.FileDescriptor; -import java.io.PrintWriter; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; - -/** - * Maintain the Apn context - */ -public class ApnContext { - - public final String LOG_TAG; - private final static String SLOG_TAG = "ApnContext"; - - protected static final boolean DBG = false; - - private final Phone mPhone; - - private final String mApnType; - - private DctConstants.State mState; - - private int mPriority; - - private ApnSetting mApnSetting; - - private DataConnection mDataConnection; - - private String mReason; - - /** - * user/app requested connection on this APN - */ - AtomicBoolean mDataEnabled; - - private final Object mRefCountLock = new Object(); - - private final DcTracker mDcTracker; - - - /** - * Remember this as a change in this value to a more permissive state - * should cause us to retry even permanent failures - */ - private boolean mConcurrentVoiceAndDataAllowed; - - /** - * used to track a single connection request so disconnects can get ignored if - * obsolete. - */ - private final AtomicInteger mConnectionGeneration = new AtomicInteger(0); - - /** - * Retry manager that handles the APN retry and delays. - */ - private final RetryManager mRetryManager; - - /** - * ApnContext constructor - * @param phone phone object - * @param typeId APN type Id - * @param logTag Tag for logging - * @param tracker Data call tracker - * @param priority Priority of APN type - */ - public ApnContext(Phone phone, int typeId, String logTag, DcTracker tracker, int priority) { - this(phone, ApnSetting.getApnTypeString(typeId), logTag, tracker, priority); - } - - /** - * ApnContext constructor - * @param phone phone object - * @param apnType APN type (e.g. default, supl, mms, etc...) - * @param logTag Tag for logging - * @param tracker Data call tracker - * @param priority Priority of APN type - */ - public ApnContext(Phone phone, String apnType, String logTag, DcTracker tracker, int priority) { - mPhone = phone; - mApnType = apnType; - mState = DctConstants.State.IDLE; - setReason(Phone.REASON_DATA_ENABLED); - mDataEnabled = new AtomicBoolean(false); - mPriority = priority; - LOG_TAG = logTag; - mDcTracker = tracker; - mRetryManager = new RetryManager(phone, tracker.getDataThrottler(), - ApnSetting.getApnTypesBitmaskFromString(apnType)); - } - - - - /** - * Get the APN type - * @return The APN type - */ - public String getApnType() { - return mApnType; - } - - /** - * Gets the APN type bitmask. - * @return The APN type bitmask - */ - public int getApnTypeBitmask() { - return ApnSetting.getApnTypesBitmaskFromString(mApnType); - } - - /** - * Get the associated data connection - * @return The data connection - */ - public synchronized DataConnection getDataConnection() { - return mDataConnection; - } - - /** - * This priority is taken into account when concurrent data connections are not allowed. The - * APN with the HIGHER priority is given preference. - * @return The priority of the APN type - */ - public int getPriority() { - return mPriority; - } - - /** - * Updates the priority of this context. - * @param priority The priority of the APN type - */ - public void setPriority(int priority) { - mPriority = priority; - } - - /** - * Keeping for backwards compatibility and in case it's needed in the future - * @return true - */ - public boolean isDependencyMet() { - return true; - } - - /** - * Set the associated data connection. - * @param dc data connection - */ - public synchronized void setDataConnection(DataConnection dc) { - log("setDataConnectionAc: old=" + mDataConnection + ",new=" + dc + " this=" + this); - mDataConnection = dc; - } - - /** - * Release data connection. - * @param reason The reason of releasing data connection - */ - public synchronized void releaseDataConnection(String reason) { - if (mDataConnection != null) { - mDataConnection.tearDown(this, reason, null); - mDataConnection = null; - } - setState(DctConstants.State.IDLE); - } - - /** - * Get the current APN setting. - * @return APN setting - */ - public synchronized ApnSetting getApnSetting() { - log("getApnSetting: apnSetting=" + mApnSetting); - return mApnSetting; - } - - /** - * Set the APN setting. - * @param apnSetting APN setting - */ - public synchronized void setApnSetting(ApnSetting apnSetting) { - log("setApnSetting: apnSetting=" + apnSetting); - mApnSetting = apnSetting; - } - - /** - * Set the list of APN candidates which will be used for data call setup later. - * @param waitingApns List of APN candidates - */ - public synchronized void setWaitingApns(ArrayList<ApnSetting> waitingApns) { - mRetryManager.setWaitingApns(waitingApns); - } - - /** - * Get the next available APN to try. - * @return APN setting which will be used for data call setup.{@code null} if there is no - * APN can be retried. - */ - public @Nullable ApnSetting getNextApnSetting() { - return mRetryManager.getNextApnSetting(); - } - - /** - * Get the delay for trying the next APN setting if the current one failed. - * @param failFastEnabled True if fail fast mode enabled. In this case we'll use a shorter - * delay. - * @return The delay in milliseconds - */ - public long getDelayForNextApn(boolean failFastEnabled) { - return mRetryManager.getDelayForNextApn(failFastEnabled || isFastRetryReason()); - } - - /** - * Mark the current APN setting permanently failed, which means it will not be retried anymore. - * @param apn APN setting - */ - public void markApnPermanentFailed(ApnSetting apn) { - mRetryManager.markApnPermanentFailed(apn); - } - - /** - * Get the list of waiting APNs. - * @return the list of waiting APNs - */ - public @NonNull ArrayList<ApnSetting> getWaitingApns() { - return mRetryManager.getWaitingApns(); - } - - /** - * Save the state indicating concurrent voice/data allowed. - * @param allowed True if concurrent voice/data is allowed - */ - public synchronized void setConcurrentVoiceAndDataAllowed(boolean allowed) { - mConcurrentVoiceAndDataAllowed = allowed; - } - - /** - * Get the state indicating concurrent voice/data allowed. - * @return True if concurrent voice/data is allowed - */ - public synchronized boolean isConcurrentVoiceAndDataAllowed() { - return mConcurrentVoiceAndDataAllowed; - } - - /** - * Set the current data call state. - * @param s Current data call state - */ - public synchronized void setState(DctConstants.State s) { - log("setState: " + s + ", previous state:" + mState); - - if (mState != s) { - mStateLocalLog.log("State changed from " + mState + " to " + s); - mState = s; - } - - if (mState == DctConstants.State.FAILED) { - // when teardown the connection and set to IDLE - mRetryManager.getWaitingApns().clear(); - } - } - - /** - * Get the current data call state. - * @return The current data call state - */ - public synchronized DctConstants.State getState() { - return mState; - } - - /** - * Check whether the data call is disconnected or not. - * @return True if the data call is disconnected - */ - public boolean isDisconnected() { - DctConstants.State currentState = getState(); - return ((currentState == DctConstants.State.IDLE) || - currentState == DctConstants.State.FAILED); - } - - /** - * Set the reason for data call connection. - * @param reason Reason for data call connection - */ - public synchronized void setReason(String reason) { - log("set reason as " + reason + ",current state " + mState); - mReason = reason; - } - - /** - * Get the reason for data call connection. - * @return The reason for data call connection - */ - public synchronized String getReason() { - return mReason; - } - - /** - * Check if ready for data call connection - * @return True if ready, otherwise false. - */ - public boolean isReady() { - return mDataEnabled.get() && isDependencyMet(); - } - - /** - * Check if the data call is in the state which allow connecting. - * @return True if allowed, otherwise false. - */ - public boolean isConnectable() { - return isReady() && ((mState == DctConstants.State.IDLE) - || (mState == DctConstants.State.RETRYING) - || (mState == DctConstants.State.FAILED)); - } - - /** - * Check if apn reason is fast retry reason which should apply shorter delay between apn re-try. - * @return True if it is fast retry reason, otherwise false. - */ - private boolean isFastRetryReason() { - return Phone.REASON_NW_TYPE_CHANGED.equals(mReason) || - Phone.REASON_APN_CHANGED.equals(mReason); - } - - /** Check if the data call is in connected or connecting state. - * @return True if the data call is in connected or connecting state - */ - public boolean isConnectedOrConnecting() { - return isReady() && ((mState == DctConstants.State.CONNECTED) - || (mState == DctConstants.State.CONNECTING) - || (mState == DctConstants.State.RETRYING)); - } - - /** - * Set data call enabled/disabled state. - * @param enabled True if data call is enabled - */ - public void setEnabled(boolean enabled) { - log("set enabled as " + enabled + ", current state is " + mDataEnabled.get()); - mDataEnabled.set(enabled); - } - - /** - * Check if the data call is enabled or not. - * @return True if enabled - */ - public boolean isEnabled() { - return mDataEnabled.get(); - } - - public boolean isProvisioningApn() { - String provisioningApn = mPhone.getContext().getResources() - .getString(R.string.mobile_provisioning_apn); - if (!TextUtils.isEmpty(provisioningApn) && - (mApnSetting != null) && (mApnSetting.getApnName() != null)) { - return (mApnSetting.getApnName().equals(provisioningApn)); - } else { - return false; - } - } - - private final ArraySet<NetworkRequest> mNetworkRequests = new ArraySet<>(); - private final LocalLog mStateLocalLog = new LocalLog(32); - - private static final LocalLog sLocalLog = new LocalLog(256); - - /** Add a line to the ApnContext local log. */ - public static void requestLog(ApnContext apnContext, String str) { - if (apnContext != null) { - String logString = "[ApnContext:" + apnContext.getApnType() + "] " + str; - if (DBG) { - Rlog.d(SLOG_TAG, logString); - } - synchronized (sLocalLog) { - sLocalLog.log(logString); - } - } - } - - /** - * Request a network - * - * @param networkRequest Network request from clients - * @param type The request type - * @param onHandoverCompleteMsg When request type is handover, this message will be sent when - * handover is completed. For normal request, this should be null. - */ - public void requestNetwork(NetworkRequest networkRequest, @RequestNetworkType int type, - Message onHandoverCompleteMsg) { - synchronized (mRefCountLock) { - mNetworkRequests.add(networkRequest); - requestLog(this, "requestNetwork for " + networkRequest + ", type=" - + DcTracker.requestTypeToString(type)); - mDcTracker.enableApn(ApnSetting.getApnTypesBitmaskFromString(mApnType), type, - onHandoverCompleteMsg); - if (mDataConnection != null) { - // New network request added. Should re-evaluate properties of - // the data connection. For example, the score may change. - mDataConnection.reevaluateDataConnectionProperties(); - } - } - } - - public void releaseNetwork(NetworkRequest networkRequest, @ReleaseNetworkType int type) { - synchronized (mRefCountLock) { - if (mNetworkRequests.contains(networkRequest)) { - mNetworkRequests.remove(networkRequest); - if (mDataConnection != null) { - // New network request added. Should re-evaluate properties of - // the data connection. For example, the score may change. - mDataConnection.reevaluateDataConnectionProperties(); - } - requestLog(this, "releaseNetwork left with " + mNetworkRequests.size() - + " requests."); - if (mNetworkRequests.size() == 0 - || type == DcTracker.RELEASE_TYPE_DETACH - || type == DcTracker.RELEASE_TYPE_HANDOVER) { - mDcTracker.disableApn(ApnSetting.getApnTypesBitmaskFromString(mApnType), type); - } - } - } - } - - /** - * @param excludeDun True if excluding requests that have DUN capability - * @return True if the attached network requests contain restricted capability. - */ - public boolean hasRestrictedRequests(boolean excludeDun) { - synchronized (mRefCountLock) { - for (NetworkRequest nr : mNetworkRequests) { - if (excludeDun && - nr.hasCapability(NetworkCapabilities.NET_CAPABILITY_DUN)) { - continue; - } - if (!nr.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)) { - return true; - } - } - } - return false; - } - - private final SparseIntArray mRetriesLeftPerErrorCode = new SparseIntArray(); - - public void resetErrorCodeRetries() { - requestLog(this, "resetErrorCodeRetries"); - - String[] config = mPhone.getContext().getResources().getStringArray( - com.android.internal.R.array.config_cell_retries_per_error_code); - synchronized (mRetriesLeftPerErrorCode) { - mRetriesLeftPerErrorCode.clear(); - - for (String c : config) { - String errorValue[] = c.split(","); - if (errorValue != null && errorValue.length == 2) { - int count = 0; - int errorCode = 0; - try { - errorCode = Integer.parseInt(errorValue[0]); - count = Integer.parseInt(errorValue[1]); - } catch (NumberFormatException e) { - log("Exception parsing config_retries_per_error_code: " + e); - continue; - } - if (count > 0 && errorCode > 0) { - mRetriesLeftPerErrorCode.put(errorCode, count); - } - } else { - log("Exception parsing config_retries_per_error_code: " + c); - } - } - } - } - - public boolean restartOnError(int errorCode) { - boolean result = false; - int retriesLeft = 0; - synchronized(mRetriesLeftPerErrorCode) { - retriesLeft = mRetriesLeftPerErrorCode.get(errorCode); - switch (retriesLeft) { - case 0: { - // not set, never restart modem - break; - } - case 1: { - resetErrorCodeRetries(); - result = true; - break; - } - default: { - mRetriesLeftPerErrorCode.put(errorCode, retriesLeft - 1); - result = false; - } - } - } - requestLog(this, "restartOnError(" + errorCode + ") found " + retriesLeft - + " and returned " + result); - return result; - } - - public int incAndGetConnectionGeneration() { - return mConnectionGeneration.incrementAndGet(); - } - - public int getConnectionGeneration() { - return mConnectionGeneration.get(); - } - - long getRetryAfterDisconnectDelay() { - return mRetryManager.getRetryAfterDisconnectDelay(); - } - - /** - * Get APN type from the network request. - * - * @param nr The network request. - * @return The APN type. - */ - public static @ApnType int getApnTypeFromNetworkRequest(NetworkRequest nr) { - // For now, ignore the bandwidth stuff - if (nr.getTransportTypes().length > 0 - && !nr.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) { - return ApnSetting.TYPE_NONE; - } - - // in the near term just do 1-1 matches. - // TODO - actually try to match the set of capabilities - int apnType = ApnSetting.TYPE_NONE; - boolean error = false; - - if (nr.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) { - apnType = ApnSetting.TYPE_DEFAULT; - } - if (nr.hasCapability(NetworkCapabilities.NET_CAPABILITY_MMS)) { - if (apnType != ApnSetting.TYPE_NONE) error = true; - apnType = ApnSetting.TYPE_MMS; - } - if (nr.hasCapability(NetworkCapabilities.NET_CAPABILITY_SUPL)) { - if (apnType != ApnSetting.TYPE_NONE) error = true; - apnType = ApnSetting.TYPE_SUPL; - } - if (nr.hasCapability(NetworkCapabilities.NET_CAPABILITY_DUN)) { - if (apnType != ApnSetting.TYPE_NONE) error = true; - apnType = ApnSetting.TYPE_DUN; - } - if (nr.hasCapability(NetworkCapabilities.NET_CAPABILITY_FOTA)) { - if (apnType != ApnSetting.TYPE_NONE) error = true; - apnType = ApnSetting.TYPE_FOTA; - } - if (nr.hasCapability(NetworkCapabilities.NET_CAPABILITY_IMS)) { - if (apnType != ApnSetting.TYPE_NONE) error = true; - apnType = ApnSetting.TYPE_IMS; - } - if (nr.hasCapability(NetworkCapabilities.NET_CAPABILITY_CBS)) { - if (apnType != ApnSetting.TYPE_NONE) error = true; - apnType = ApnSetting.TYPE_CBS; - } - if (nr.hasCapability(NetworkCapabilities.NET_CAPABILITY_IA)) { - if (apnType != ApnSetting.TYPE_NONE) error = true; - apnType = ApnSetting.TYPE_IA; - } - if (nr.hasCapability(NetworkCapabilities.NET_CAPABILITY_EIMS)) { - if (apnType != ApnSetting.TYPE_NONE) error = true; - apnType = ApnSetting.TYPE_EMERGENCY; - } - if (nr.hasCapability(NetworkCapabilities.NET_CAPABILITY_MCX)) { - if (apnType != ApnSetting.TYPE_NONE) error = true; - apnType = ApnSetting.TYPE_MCX; - } - if (nr.hasCapability(NetworkCapabilities.NET_CAPABILITY_XCAP)) { - if (apnType != ApnSetting.TYPE_NONE) error = true; - apnType = ApnSetting.TYPE_XCAP; - } - if (nr.hasCapability(NetworkCapabilities.NET_CAPABILITY_ENTERPRISE)) { - if (apnType != ApnSetting.TYPE_NONE) error = true; - apnType = ApnSetting.TYPE_ENTERPRISE; - } - if (error) { - // TODO: If this error condition is removed, the framework's handling of - // NET_CAPABILITY_NOT_RESTRICTED will need to be updated so requests for - // say FOTA and INTERNET are marked as restricted. This is not how - // NetworkCapabilities.maybeMarkCapabilitiesRestricted currently works. - Rlog.d(SLOG_TAG, "Multiple apn types specified in request - result is unspecified!"); - } - if (apnType == ApnSetting.TYPE_NONE) { - Rlog.d(SLOG_TAG, "Unsupported NetworkRequest in Telephony: nr=" + nr); - } - return apnType; - } - - public List<NetworkRequest> getNetworkRequests() { - synchronized (mRefCountLock) { - return new ArrayList<NetworkRequest>(mNetworkRequests); - } - } - - @Override - public synchronized String toString() { - // We don't print mDataConnection because its recursive. - return "{mApnType=" + mApnType + " mState=" + getState() + " mWaitingApns={" - + mRetryManager.getWaitingApns() + " priority=" + mPriority + "}" - + " mApnSetting={" + mApnSetting - + "} mReason=" + mReason + " mDataEnabled=" + mDataEnabled + "}"; - } - - private void log(String s) { - if (DBG) { - Rlog.d(LOG_TAG, "[ApnContext:" + mApnType + "] " + s); - } - } - - public void dump(FileDescriptor fd, PrintWriter printWriter, String[] args) { - final IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, " "); - synchronized (mRefCountLock) { - pw.println(toString()); - if (mNetworkRequests.size() > 0) { - pw.println("NetworkRequests:"); - pw.increaseIndent(); - for (NetworkRequest nr : mNetworkRequests) { - pw.println(nr); - } - pw.decreaseIndent(); - } - pw.println("Historical APN state:"); - pw.increaseIndent(); - mStateLocalLog.dump(fd, pw, args); - pw.decreaseIndent(); - pw.println(mRetryManager); - pw.println("--------------------------"); - } - } - - /** Dumps the ApnContext local log. */ - public static void dumpLocalLog(FileDescriptor fd, PrintWriter printWriter, String[] args) { - printWriter.println("Local log:"); - synchronized (sLocalLog) { - sLocalLog.dump(fd, printWriter, args); - } - } -}
diff --git a/src/java/com/android/internal/telephony/dataconnection/ApnSettingUtils.java b/src/java/com/android/internal/telephony/dataconnection/ApnSettingUtils.java deleted file mode 100644 index 3c2a6ef..0000000 --- a/src/java/com/android/internal/telephony/dataconnection/ApnSettingUtils.java +++ /dev/null
@@ -1,129 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.internal.telephony.dataconnection; - -import android.content.Context; -import android.os.PersistableBundle; -import android.telephony.Annotation.ApnType; -import android.telephony.CarrierConfigManager; -import android.telephony.data.ApnSetting; - -import com.android.internal.telephony.Phone; -import com.android.telephony.Rlog; - -import java.util.Arrays; -import java.util.HashSet; - -/** - * This class represents a apn setting for create PDP link - */ -public class ApnSettingUtils { - - static final String LOG_TAG = "ApnSetting"; - - private static final boolean DBG = false; - - /** - * Check if this APN type is metered. - * - * @param apnType the APN type - * @param phone the phone object - * @return {@code true} if the APN type is metered, {@code false} otherwise. - */ - public static boolean isMeteredApnType(@ApnType int apnType, Phone phone) { - if (phone == null) { - return true; - } - - boolean isRoaming = phone.getServiceState().getDataRoaming(); - int subId = phone.getSubId(); - - String carrierConfig; - // First check if the device is roaming. If yes, use the roaming metered APN list. - // Otherwise use the normal metered APN list. - if (isRoaming) { - carrierConfig = CarrierConfigManager.KEY_CARRIER_METERED_ROAMING_APN_TYPES_STRINGS; - } else { - carrierConfig = CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS; - } - - if (DBG) { - Rlog.d(LOG_TAG, "isMeteredApnType: isRoaming=" + isRoaming); - } - - CarrierConfigManager configManager = (CarrierConfigManager) - phone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE); - if (configManager == null) { - Rlog.e(LOG_TAG, "Carrier config service is not available"); - return true; - } - - PersistableBundle b = configManager.getConfigForSubId(subId); - if (b == null) { - Rlog.e(LOG_TAG, "Can't get the config. subId = " + subId); - return true; - } - - String[] meteredApnTypes = b.getStringArray(carrierConfig); - if (meteredApnTypes == null) { - Rlog.e(LOG_TAG, carrierConfig + " is not available. " + "subId = " + subId); - return true; - } - - HashSet<String> meteredApnSet = new HashSet<>(Arrays.asList(meteredApnTypes)); - if (DBG) { - Rlog.d(LOG_TAG, "For subId = " + subId + ", metered APN types are " - + Arrays.toString(meteredApnSet.toArray())); - } - - if (meteredApnSet.contains(ApnSetting.getApnTypeString(apnType))) { - if (DBG) Rlog.d(LOG_TAG, ApnSetting.getApnTypeString(apnType) + " is metered."); - return true; - } else if (apnType == ApnSetting.TYPE_ALL) { - // Assuming no configuration error, if at least one APN type is - // metered, then this APN setting is metered. - if (meteredApnSet.size() > 0) { - if (DBG) Rlog.d(LOG_TAG, "APN_TYPE_ALL APN is metered."); - return true; - } - } - - if (DBG) Rlog.d(LOG_TAG, ApnSetting.getApnTypeString(apnType) + " is not metered."); - return false; - } - - /** - * Check if this APN setting is metered. - * - * @param apn APN setting - * @param phone The phone object - * @return True if this APN setting is metered, otherwise false. - */ - public static boolean isMetered(ApnSetting apn, Phone phone) { - if (phone == null || apn == null) { - return true; - } - - for (int apnType : apn.getApnTypes()) { - // If one of the APN type is metered, then this APN setting is metered. - if (isMeteredApnType(apnType, phone)) { - return true; - } - } - return false; - } -}
diff --git a/src/java/com/android/internal/telephony/dataconnection/DataConnection.java b/src/java/com/android/internal/telephony/dataconnection/DataConnection.java deleted file mode 100644 index 348908a..0000000 --- a/src/java/com/android/internal/telephony/dataconnection/DataConnection.java +++ /dev/null
@@ -1,4088 +0,0 @@ -/* - * Copyright (C) 2006 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.internal.telephony.dataconnection; - -import static android.telephony.data.DataCallResponse.PDU_SESSION_ID_NOT_SET; - -import static com.android.internal.telephony.dataconnection.DcTracker.REQUEST_TYPE_HANDOVER; - -import android.annotation.IntDef; -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.app.PendingIntent; -import android.content.Context; -import android.content.pm.PackageManager; -import android.content.pm.PackageManager.NameNotFoundException; -import android.content.pm.UserInfo; -import android.net.ConnectivityManager; -import android.net.InetAddresses; -import android.net.KeepalivePacketData; -import android.net.LinkAddress; -import android.net.LinkProperties; -import android.net.NetworkAgentConfig; -import android.net.NetworkCapabilities; -import android.net.NetworkFactory; -import android.net.NetworkProvider; -import android.net.NetworkRequest; -import android.net.ProxyInfo; -import android.net.RouteInfo; -import android.net.SocketKeepalive; -import android.net.TelephonyNetworkSpecifier; -import android.net.vcn.VcnManager; -import android.net.vcn.VcnManager.VcnNetworkPolicyChangeListener; -import android.net.vcn.VcnNetworkPolicyResult; -import android.os.AsyncResult; -import android.os.HandlerExecutor; -import android.os.Message; -import android.os.PersistableBundle; -import android.os.Process; -import android.os.SystemClock; -import android.os.SystemProperties; -import android.os.UserManager; -import android.provider.Telephony; -import android.telephony.AccessNetworkConstants; -import android.telephony.AccessNetworkConstants.TransportType; -import android.telephony.Annotation.ApnType; -import android.telephony.Annotation.DataFailureCause; -import android.telephony.Annotation.DataState; -import android.telephony.Annotation.NetworkType; -import android.telephony.CarrierConfigManager; -import android.telephony.DataFailCause; -import android.telephony.LinkCapacityEstimate; -import android.telephony.NetworkRegistrationInfo; -import android.telephony.PreciseDataConnectionState; -import android.telephony.ServiceState; -import android.telephony.SubscriptionManager; -import android.telephony.TelephonyManager; -import android.telephony.data.ApnSetting; -import android.telephony.data.DataCallResponse; -import android.telephony.data.DataCallResponse.HandoverFailureMode; -import android.telephony.data.DataProfile; -import android.telephony.data.DataService; -import android.telephony.data.DataServiceCallback; -import android.telephony.data.NetworkSliceInfo; -import android.telephony.data.Qos; -import android.telephony.data.QosBearerSession; -import android.telephony.data.TrafficDescriptor; -import android.text.TextUtils; -import android.util.LocalLog; -import android.util.Pair; -import android.util.TimeUtils; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.telephony.CarrierPrivilegesTracker; -import com.android.internal.telephony.CarrierSignalAgent; -import com.android.internal.telephony.DctConstants; -import com.android.internal.telephony.Phone; -import com.android.internal.telephony.PhoneConstants; -import com.android.internal.telephony.PhoneFactory; -import com.android.internal.telephony.RIL; -import com.android.internal.telephony.RILConstants; -import com.android.internal.telephony.RetryManager; -import com.android.internal.telephony.TelephonyStatsLog; -import com.android.internal.telephony.data.DataConfigManager; -import com.android.internal.telephony.data.KeepaliveStatus; -import com.android.internal.telephony.dataconnection.DcTracker.ReleaseNetworkType; -import com.android.internal.telephony.dataconnection.DcTracker.RequestNetworkType; -import com.android.internal.telephony.metrics.DataCallSessionStats; -import com.android.internal.telephony.metrics.TelephonyMetrics; -import com.android.internal.telephony.nano.TelephonyProto.RilDataCall; -import com.android.internal.telephony.uicc.IccUtils; -import com.android.internal.telephony.util.ArrayUtils; -import com.android.internal.util.AsyncChannel; -import com.android.internal.util.IndentingPrintWriter; -import com.android.internal.util.Protocol; -import com.android.internal.util.State; -import com.android.internal.util.StateMachine; -import com.android.net.module.util.NetworkCapabilitiesUtils; -import com.android.telephony.Rlog; - -import java.io.FileDescriptor; -import java.io.PrintWriter; -import java.io.StringWriter; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.net.InetAddress; -import java.net.UnknownHostException; -import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.UUID; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.function.Consumer; - -/** - * {@hide} - * - * DataConnection StateMachine. - * - * This a class for representing a single data connection, with instances of this - * class representing a connection via the cellular network. There may be multiple - * data connections and all of them are managed by the <code>DataConnectionTracker</code>. - * - * NOTE: All DataConnection objects must be running on the same looper, which is the default - * as the coordinator has members which are used without synchronization. - */ -public class DataConnection extends StateMachine { - private static final boolean DBG = true; - private static final boolean VDBG = true; - - private static final String NETWORK_TYPE = "MOBILE"; - - private static final String RAT_NAME_5G = "nr"; - private static final String RAT_NAME_EVDO = "evdo"; - - /** - * OSId for "Android", using UUID version 5 with namespace ISO OSI. - * Prepended to the OsAppId in TrafficDescriptor to use for URSP matching. - */ - private static final UUID OS_ID = UUID.fromString("97a498e3-fc92-5c94-8986-0333d06e4e47"); - - /** - * The data connection is not being or been handovered. Note this is the state for the source - * data connection, not destination data connection - */ - private static final int HANDOVER_STATE_IDLE = 1; - - /** - * The data connection is being handovered. Note this is the state for the source - * data connection, not destination data connection. - */ - private static final int HANDOVER_STATE_BEING_TRANSFERRED = 2; - - /** - * The data connection is already handovered. Note this is the state for the source - * data connection, not destination data connection. - */ - private static final int HANDOVER_STATE_COMPLETED = 3; - - - /** @hide */ - @Retention(RetentionPolicy.SOURCE) - @IntDef(prefix = {"HANDOVER_STATE_"}, value = { - HANDOVER_STATE_IDLE, - HANDOVER_STATE_BEING_TRANSFERRED, - HANDOVER_STATE_COMPLETED}) - public @interface HandoverState {} - - // The data connection providing default Internet connection will have a higher score of 50. - // Other connections will have a slightly lower score of 45. The intention is other connections - // will not cause ConnectivityService to tear down default internet connection. For example, - // to validate Internet connection on non-default data SIM, we'll set up a temporary Internet - // connection on that data SIM. In this case, score of 45 is assigned so ConnectivityService - // will not replace the default Internet connection with it. - private static final int DEFAULT_INTERNET_CONNECTION_SCORE = 50; - private static final int OTHER_CONNECTION_SCORE = 45; - - // The score we report to connectivity service - private int mScore; - - // The subscription id associated with this data connection. - private int mSubId; - - // The data connection controller - private DcController mDcController; - - // The Tester for failing all bringup's - private DcTesterFailBringUpAll mDcTesterFailBringUpAll; - - // Whether or not the data connection should allocate its own pdu session id - private boolean mDoAllocatePduSessionId; - - private static AtomicInteger mInstanceNumber = new AtomicInteger(0); - private AsyncChannel mAc; - - // The DCT that's talking to us, we only support one! - private DcTracker mDct = null; - - private String[] mPcscfAddr; - - private final String mTagSuffix; - - private final LocalLog mHandoverLocalLog = new LocalLog(64); - - private int[] mAdministratorUids = new int[0]; - - // stats per data call - private DataCallSessionStats mDataCallSessionStats; - - /** - * Used internally for saving connecting parameters. - */ - public static class ConnectionParams { - int mTag; - ApnContext mApnContext; - int mProfileId; - int mRilRat; - Message mOnCompletedMsg; - final int mConnectionGeneration; - @RequestNetworkType - final int mRequestType; - final int mSubId; - final boolean mIsPreferredApn; - - ConnectionParams(ApnContext apnContext, int profileId, int rilRadioTechnology, - Message onCompletedMsg, int connectionGeneration, - @RequestNetworkType int requestType, int subId, - boolean isPreferredApn) { - mApnContext = apnContext; - mProfileId = profileId; - mRilRat = rilRadioTechnology; - mOnCompletedMsg = onCompletedMsg; - mConnectionGeneration = connectionGeneration; - mRequestType = requestType; - mSubId = subId; - mIsPreferredApn = isPreferredApn; - } - - @Override - public String toString() { - return "{mTag=" + mTag + " mApnContext=" + mApnContext - + " mProfileId=" + mProfileId - + " mRat=" + mRilRat - + " mOnCompletedMsg=" + msgToString(mOnCompletedMsg) - + " mRequestType=" + DcTracker.requestTypeToString(mRequestType) - + " mSubId=" + mSubId - + " mIsPreferredApn=" + mIsPreferredApn - + "}"; - } - } - - /** - * Used internally for saving disconnecting parameters. - */ - public static class DisconnectParams { - int mTag; - public ApnContext mApnContext; - String mReason; - @ReleaseNetworkType - final int mReleaseType; - Message mOnCompletedMsg; - - DisconnectParams(ApnContext apnContext, String reason, @ReleaseNetworkType int releaseType, - Message onCompletedMsg) { - mApnContext = apnContext; - mReason = reason; - mReleaseType = releaseType; - mOnCompletedMsg = onCompletedMsg; - } - - @Override - public String toString() { - return "{mTag=" + mTag + " mApnContext=" + mApnContext - + " mReason=" + mReason - + " mReleaseType=" + DcTracker.releaseTypeToString(mReleaseType) - + " mOnCompletedMsg=" + msgToString(mOnCompletedMsg) + "}"; - } - } - - private volatile ApnSetting mApnSetting; - private ConnectionParams mConnectionParams; - private DisconnectParams mDisconnectParams; - @DataFailureCause - private int mDcFailCause; - - @HandoverFailureMode - private int mHandoverFailureMode; - - private Phone mPhone; - private DataServiceManager mDataServiceManager; - private VcnManager mVcnManager; - private final int mTransportType; - private LinkProperties mLinkProperties = new LinkProperties(); - private int mPduSessionId; - private long mCreateTime; - private long mLastFailTime; - @DataFailureCause - private int mLastFailCause; - private static final String NULL_IP = "0.0.0.0"; - private Object mUserData; - private boolean mCongestedOverride; - 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; - private Qos mDefaultQos = null; - private List<QosBearerSession> mQosBearerSessions = new ArrayList<>(); - private NetworkSliceInfo mSliceInfo; - private List<TrafficDescriptor> mTrafficDescriptors = new ArrayList<>(); - - /** The corresponding network agent for this data connection. */ - private DcNetworkAgent mNetworkAgent; - - /** - * The network agent from handover source data connection. This is the potential network agent - * that will be transferred here after handover completed. - */ - private DcNetworkAgent mHandoverSourceNetworkAgent; - - private int mDisabledApnTypeBitMask = 0; - - int mTag; - - /** Data connection id assigned by the modem. This is unique across transports */ - public int mCid; - - @HandoverState - private int mHandoverState = HANDOVER_STATE_IDLE; - private final Map<ApnContext, ConnectionParams> mApnContexts = new ConcurrentHashMap<>(); - PendingIntent mReconnectIntent = null; - - /** Class used to track VCN-defined Network policies for this DcNetworkAgent. */ - private final VcnNetworkPolicyChangeListener mVcnPolicyChangeListener = - new DataConnectionVcnNetworkPolicyChangeListener(); - - // ***** Event codes for driving the state machine, package visible for Dcc - static final int BASE = Protocol.BASE_DATA_CONNECTION; - static final int EVENT_CONNECT = BASE + 0; - static final int EVENT_SETUP_DATA_CONNECTION_DONE = BASE + 1; - static final int EVENT_DEACTIVATE_DONE = BASE + 3; - static final int EVENT_DISCONNECT = BASE + 4; - static final int EVENT_DISCONNECT_ALL = BASE + 6; - static final int EVENT_DATA_STATE_CHANGED = BASE + 7; - static final int EVENT_TEAR_DOWN_NOW = BASE + 8; - static final int EVENT_LOST_CONNECTION = BASE + 9; - static final int EVENT_DATA_CONNECTION_DRS_OR_RAT_CHANGED = BASE + 11; - static final int EVENT_DATA_CONNECTION_ROAM_ON = BASE + 12; - static final int EVENT_DATA_CONNECTION_ROAM_OFF = BASE + 13; - static final int EVENT_BW_REFRESH_RESPONSE = BASE + 14; - static final int EVENT_DATA_CONNECTION_VOICE_CALL_STARTED = BASE + 15; - static final int EVENT_DATA_CONNECTION_VOICE_CALL_ENDED = BASE + 16; - static final int EVENT_DATA_CONNECTION_CONGESTEDNESS_CHANGED = BASE + 17; - static final int EVENT_KEEPALIVE_STATUS = BASE + 18; - static final int EVENT_KEEPALIVE_STARTED = BASE + 19; - static final int EVENT_KEEPALIVE_STOPPED = BASE + 20; - static final int EVENT_KEEPALIVE_START_REQUEST = BASE + 21; - static final int EVENT_KEEPALIVE_STOP_REQUEST = BASE + 22; - static final int EVENT_LINK_CAPACITY_CHANGED = BASE + 23; - static final int EVENT_RESET = BASE + 24; - static final int EVENT_REEVALUATE_RESTRICTED_STATE = BASE + 25; - static final int EVENT_REEVALUATE_DATA_CONNECTION_PROPERTIES = BASE + 26; - static final int EVENT_NR_STATE_CHANGED = BASE + 27; - static final int EVENT_DATA_CONNECTION_METEREDNESS_CHANGED = BASE + 28; - static final int EVENT_NR_FREQUENCY_CHANGED = BASE + 29; - static final int EVENT_CARRIER_CONFIG_LINK_BANDWIDTHS_CHANGED = BASE + 30; - static final int EVENT_CARRIER_PRIVILEGED_UIDS_CHANGED = BASE + 31; - static final int EVENT_CSS_INDICATOR_CHANGED = BASE + 32; - static final int EVENT_UPDATE_SUSPENDED_STATE = BASE + 33; - static final int EVENT_START_HANDOVER = BASE + 34; - static final int EVENT_CANCEL_HANDOVER = BASE + 35; - static final int EVENT_START_HANDOVER_ON_TARGET = BASE + 36; - static final int EVENT_ALLOCATE_PDU_SESSION_ID = BASE + 37; - static final int EVENT_RELEASE_PDU_SESSION_ID = BASE + 38; - static final int EVENT_LINK_BANDWIDTH_ESTIMATOR_UPDATE = BASE + 39; - private static final int CMD_TO_STRING_COUNT = EVENT_LINK_BANDWIDTH_ESTIMATOR_UPDATE - BASE + 1; - - private static String[] sCmdToString = new String[CMD_TO_STRING_COUNT]; - static { - sCmdToString[EVENT_CONNECT - BASE] = "EVENT_CONNECT"; - sCmdToString[EVENT_SETUP_DATA_CONNECTION_DONE - BASE] = - "EVENT_SETUP_DATA_CONNECTION_DONE"; - sCmdToString[EVENT_DEACTIVATE_DONE - BASE] = "EVENT_DEACTIVATE_DONE"; - sCmdToString[EVENT_DISCONNECT - BASE] = "EVENT_DISCONNECT"; - sCmdToString[EVENT_DISCONNECT_ALL - BASE] = "EVENT_DISCONNECT_ALL"; - sCmdToString[EVENT_DATA_STATE_CHANGED - BASE] = "EVENT_DATA_STATE_CHANGED"; - sCmdToString[EVENT_TEAR_DOWN_NOW - BASE] = "EVENT_TEAR_DOWN_NOW"; - sCmdToString[EVENT_LOST_CONNECTION - BASE] = "EVENT_LOST_CONNECTION"; - sCmdToString[EVENT_DATA_CONNECTION_DRS_OR_RAT_CHANGED - BASE] = - "EVENT_DATA_CONNECTION_DRS_OR_RAT_CHANGED"; - sCmdToString[EVENT_DATA_CONNECTION_ROAM_ON - BASE] = "EVENT_DATA_CONNECTION_ROAM_ON"; - sCmdToString[EVENT_DATA_CONNECTION_ROAM_OFF - BASE] = "EVENT_DATA_CONNECTION_ROAM_OFF"; - sCmdToString[EVENT_BW_REFRESH_RESPONSE - BASE] = "EVENT_BW_REFRESH_RESPONSE"; - sCmdToString[EVENT_DATA_CONNECTION_VOICE_CALL_STARTED - BASE] = - "EVENT_DATA_CONNECTION_VOICE_CALL_STARTED"; - sCmdToString[EVENT_DATA_CONNECTION_VOICE_CALL_ENDED - BASE] = - "EVENT_DATA_CONNECTION_VOICE_CALL_ENDED"; - sCmdToString[EVENT_DATA_CONNECTION_CONGESTEDNESS_CHANGED - BASE] = - "EVENT_DATA_CONNECTION_CONGESTEDNESS_CHANGED"; - sCmdToString[EVENT_KEEPALIVE_STATUS - BASE] = "EVENT_KEEPALIVE_STATUS"; - sCmdToString[EVENT_KEEPALIVE_STARTED - BASE] = "EVENT_KEEPALIVE_STARTED"; - sCmdToString[EVENT_KEEPALIVE_STOPPED - BASE] = "EVENT_KEEPALIVE_STOPPED"; - sCmdToString[EVENT_KEEPALIVE_START_REQUEST - BASE] = "EVENT_KEEPALIVE_START_REQUEST"; - sCmdToString[EVENT_KEEPALIVE_STOP_REQUEST - BASE] = "EVENT_KEEPALIVE_STOP_REQUEST"; - sCmdToString[EVENT_LINK_CAPACITY_CHANGED - BASE] = "EVENT_LINK_CAPACITY_CHANGED"; - sCmdToString[EVENT_RESET - BASE] = "EVENT_RESET"; - sCmdToString[EVENT_REEVALUATE_RESTRICTED_STATE - BASE] = - "EVENT_REEVALUATE_RESTRICTED_STATE"; - sCmdToString[EVENT_REEVALUATE_DATA_CONNECTION_PROPERTIES - BASE] = - "EVENT_REEVALUATE_DATA_CONNECTION_PROPERTIES"; - sCmdToString[EVENT_NR_STATE_CHANGED - BASE] = "EVENT_NR_STATE_CHANGED"; - sCmdToString[EVENT_DATA_CONNECTION_METEREDNESS_CHANGED - BASE] = - "EVENT_DATA_CONNECTION_METEREDNESS_CHANGED"; - sCmdToString[EVENT_NR_FREQUENCY_CHANGED - BASE] = "EVENT_NR_FREQUENCY_CHANGED"; - sCmdToString[EVENT_CARRIER_CONFIG_LINK_BANDWIDTHS_CHANGED - BASE] = - "EVENT_CARRIER_CONFIG_LINK_BANDWIDTHS_CHANGED"; - sCmdToString[EVENT_CARRIER_PRIVILEGED_UIDS_CHANGED - BASE] = - "EVENT_CARRIER_PRIVILEGED_UIDS_CHANGED"; - sCmdToString[EVENT_CSS_INDICATOR_CHANGED - BASE] = "EVENT_CSS_INDICATOR_CHANGED"; - sCmdToString[EVENT_UPDATE_SUSPENDED_STATE - BASE] = "EVENT_UPDATE_SUSPENDED_STATE"; - sCmdToString[EVENT_START_HANDOVER - BASE] = "EVENT_START_HANDOVER"; - sCmdToString[EVENT_CANCEL_HANDOVER - BASE] = "EVENT_CANCEL_HANDOVER"; - sCmdToString[EVENT_START_HANDOVER_ON_TARGET - BASE] = "EVENT_START_HANDOVER_ON_TARGET"; - sCmdToString[EVENT_ALLOCATE_PDU_SESSION_ID - BASE] = "EVENT_ALLOCATE_PDU_SESSION_ID"; - sCmdToString[EVENT_RELEASE_PDU_SESSION_ID - BASE] = "EVENT_RELEASE_PDU_SESSION_ID"; - sCmdToString[EVENT_LINK_BANDWIDTH_ESTIMATOR_UPDATE - BASE] = - "EVENT_LINK_BANDWIDTH_ESTIMATOR_UPDATE"; - } - // Convert cmd to string or null if unknown - static String cmdToString(int cmd) { - String value = null; - cmd -= BASE; - if ((cmd >= 0) && (cmd < sCmdToString.length)) { - value = sCmdToString[cmd]; - } - if (value == null) { - value = "0x" + Integer.toHexString(cmd + BASE); - } - return value; - } - - /** - * Create the connection object - * - * @param phone the Phone - * @param id the connection id - * @return DataConnection that was created. - */ - public static DataConnection makeDataConnection(Phone phone, int id, DcTracker dct, - DataServiceManager dataServiceManager, - DcTesterFailBringUpAll failBringUpAll, - DcController dcc) { - String transportType = (dataServiceManager.getTransportType() - == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) - ? "C" // Cellular - : "I"; // IWLAN - DataConnection dc = new DataConnection(phone, transportType + "-" - + mInstanceNumber.incrementAndGet(), id, dct, dataServiceManager, failBringUpAll, - dcc); - dc.start(); - if (DBG) dc.log("Made " + dc.getName()); - return dc; - } - - void dispose() { - log("dispose: call quiteNow()"); - quitNow(); - } - - /* Getter functions */ - - LinkProperties getLinkProperties() { - return new LinkProperties(mLinkProperties); - } - - boolean isDisconnecting() { - return getCurrentState() == mDisconnectingState - || getCurrentState() == mDisconnectingErrorCreatingConnection; - } - - @VisibleForTesting - public boolean isActive() { - return getCurrentState() == mActiveState; - } - - @VisibleForTesting - public boolean isInactive() { - return getCurrentState() == mInactiveState; - } - - boolean isActivating() { - return getCurrentState() == mActivatingState; - } - - boolean hasBeenTransferred() { - return mHandoverState == HANDOVER_STATE_COMPLETED; - } - - int getCid() { - return mCid; - } - - /** - * @return DataConnection's ApnSetting. - */ - public ApnSetting getApnSetting() { - return mApnSetting; - } - - /** - * Update http proxy of link properties based on current apn setting - */ - private void updateLinkPropertiesHttpProxy() { - if (mApnSetting == null - || TextUtils.isEmpty(mApnSetting.getProxyAddressAsString())) { - return; - } - try { - int port = mApnSetting.getProxyPort(); - if (port == -1) { - port = 8080; - } - ProxyInfo proxy = ProxyInfo.buildDirectProxy( - mApnSetting.getProxyAddressAsString(), port); - mLinkProperties.setHttpProxy(proxy); - } catch (NumberFormatException e) { - loge("onDataSetupComplete: NumberFormatException making ProxyProperties (" - + mApnSetting.getProxyPort() + "): " + e); - } - } - - public static class UpdateLinkPropertyResult { - public SetupResult setupResult = SetupResult.SUCCESS; - public LinkProperties oldLp; - public LinkProperties newLp; - public UpdateLinkPropertyResult(LinkProperties curLp) { - oldLp = curLp; - newLp = curLp; - } - } - - /** - * Class returned by onSetupConnectionCompleted. - */ - public enum SetupResult { - SUCCESS, - ERROR_RADIO_NOT_AVAILABLE, - ERROR_INVALID_ARG, - ERROR_STALE, - ERROR_DATA_SERVICE_SPECIFIC_ERROR, - ERROR_DUPLICATE_CID, - ERROR_NO_DEFAULT_CONNECTION; - - public int mFailCause; - - SetupResult() { - mFailCause = DataFailCause.getFailCause(0); - } - - @Override - public String toString() { - return name() + " SetupResult.mFailCause=" + DataFailCause.toString(mFailCause); - } - } - - public boolean isIpv4Connected() { - boolean ret = false; - Collection <InetAddress> addresses = mLinkProperties.getAddresses(); - - for (InetAddress addr: addresses) { - if (addr instanceof java.net.Inet4Address) { - java.net.Inet4Address i4addr = (java.net.Inet4Address) addr; - if (!i4addr.isAnyLocalAddress() && !i4addr.isLinkLocalAddress() && - !i4addr.isLoopbackAddress() && !i4addr.isMulticastAddress()) { - ret = true; - break; - } - } - } - return ret; - } - - public boolean isIpv6Connected() { - boolean ret = false; - Collection <InetAddress> addresses = mLinkProperties.getAddresses(); - - for (InetAddress addr: addresses) { - if (addr instanceof java.net.Inet6Address) { - java.net.Inet6Address i6addr = (java.net.Inet6Address) addr; - if (!i6addr.isAnyLocalAddress() && !i6addr.isLinkLocalAddress() && - !i6addr.isLoopbackAddress() && !i6addr.isMulticastAddress()) { - ret = true; - break; - } - } - } - return ret; - } - - public int getPduSessionId() { - return mPduSessionId; - } - - public NetworkSliceInfo getSliceInfo() { - return mSliceInfo; - } - - public List<TrafficDescriptor> getTrafficDescriptors() { - return mTrafficDescriptors; - } - - /** - * Update DC fields based on a new DataCallResponse - * @param response the response to use to update DC fields - */ - public void updateResponseFields(DataCallResponse response) { - updateQosParameters(response); - updateSliceInfo(response); - updateTrafficDescriptors(response); - } - - public void updateQosParameters(final @Nullable DataCallResponse response) { - if (response == null) { - mDefaultQos = null; - mQosBearerSessions.clear(); - return; - } - - mDefaultQos = response.getDefaultQos(); - mQosBearerSessions = response.getQosBearerSessions(); - - if (mNetworkAgent != null) { - syncQosToNetworkAgent(); - } - } - - private void syncQosToNetworkAgent() { - final DcNetworkAgent networkAgent = mNetworkAgent; - final List<QosBearerSession> qosBearerSessions = mQosBearerSessions; - if (qosBearerSessions == null) { - networkAgent.updateQosBearerSessions(new ArrayList<>()); - return; - } - networkAgent.updateQosBearerSessions(qosBearerSessions); - } - - /** - * Update the latest slice info on this data connection with - * {@link DataCallResponse#getSliceInfo}. - */ - public void updateSliceInfo(DataCallResponse response) { - mSliceInfo = response.getSliceInfo(); - } - - /** - * Update the latest traffic descriptor on this data connection with - * {@link DataCallResponse#getTrafficDescriptors}. - */ - public void updateTrafficDescriptors(DataCallResponse response) { - mTrafficDescriptors = response.getTrafficDescriptors(); - mDcController.updateTrafficDescriptorsForCid(response.getId(), - response.getTrafficDescriptors()); - } - - @VisibleForTesting - public UpdateLinkPropertyResult updateLinkProperty(DataCallResponse newState) { - UpdateLinkPropertyResult result = new UpdateLinkPropertyResult(mLinkProperties); - - if (newState == null) return result; - - result.newLp = new LinkProperties(); - - // set link properties based on data call response - result.setupResult = setLinkProperties(newState, result.newLp); - if (result.setupResult != SetupResult.SUCCESS) { - if (DBG) log("updateLinkProperty failed : " + result.setupResult); - return result; - } - // copy HTTP proxy as it is not part DataCallResponse. - result.newLp.setHttpProxy(mLinkProperties.getHttpProxy()); - - checkSetMtu(mApnSetting, result.newLp); - - mLinkProperties = result.newLp; - - updateTcpBufferSizes(mRilRat); - - if (DBG && (! result.oldLp.equals(result.newLp))) { - log("updateLinkProperty old LP=" + result.oldLp); - log("updateLinkProperty new LP=" + result.newLp); - } - - if (result.newLp.equals(result.oldLp) == false && - mNetworkAgent != null) { - mNetworkAgent.sendLinkProperties(mLinkProperties, DataConnection.this); - } - - return result; - } - - /** - * Sets the pdu session id of the data connection - * @param pduSessionId pdu session id to set - */ - @VisibleForTesting - public void setPduSessionId(int pduSessionId) { - if (mPduSessionId != pduSessionId) { - logd("Changing pdu session id from: " + mPduSessionId + " to: " + pduSessionId + ", " - + "Handover state: " + handoverStateToString(this.mHandoverState)); - mPduSessionId = pduSessionId; - } - } - - /** - * Read the MTU value from link properties where it can be set from network. In case - * not set by the network, set it again using the mtu szie value defined in the APN - * database for the connected APN - */ - private void checkSetMtu(ApnSetting apn, LinkProperties lp) { - if (lp == null) return; - - if (apn == null || lp == null) return; - - if (lp.getMtu() != PhoneConstants.UNSET_MTU) { - if (DBG) log("MTU set by call response to: " + lp.getMtu()); - return; - } - - if (apn != null && apn.getMtuV4() != PhoneConstants.UNSET_MTU) { - lp.setMtu(apn.getMtuV4()); - if (DBG) log("MTU set by APN to: " + apn.getMtuV4()); - return; - } - - int mtu = mPhone.getContext().getResources().getInteger( - com.android.internal.R.integer.config_mobile_mtu); - if (mtu != PhoneConstants.UNSET_MTU) { - lp.setMtu(mtu); - if (DBG) log("MTU set by config resource to: " + mtu); - } - } - - //***** Constructor (NOTE: uses dcc.getHandler() as its Handler) - private DataConnection(Phone phone, String tagSuffix, int id, - DcTracker dct, DataServiceManager dataServiceManager, - DcTesterFailBringUpAll failBringUpAll, DcController dcc) { - super("DC-" + tagSuffix, dcc); - mTagSuffix = tagSuffix; - setLogRecSize(300); - setLogOnlyTransitions(true); - if (DBG) log("DataConnection created"); - - mPhone = phone; - mDct = dct; - mDataServiceManager = dataServiceManager; - mVcnManager = mPhone.getContext().getSystemService(VcnManager.class); - mTransportType = dataServiceManager.getTransportType(); - mDcTesterFailBringUpAll = failBringUpAll; - mDcController = dcc; - mId = id; - mCid = -1; - mDataRegState = mPhone.getServiceState().getDataRegistrationState(); - mIsSuspended = false; - mDataCallSessionStats = new DataCallSessionStats(mPhone); - mDoAllocatePduSessionId = false; - - int networkType = getNetworkType(); - mRilRat = ServiceState.networkTypeToRilRadioTechnology(networkType); - updateLinkBandwidthsFromCarrierConfig(mRilRat); - - addState(mDefaultState); - addState(mInactiveState, mDefaultState); - addState(mActivatingState, mDefaultState); - addState(mActiveState, mDefaultState); - addState(mDisconnectingState, mDefaultState); - addState(mDisconnectingErrorCreatingConnection, mDefaultState); - setInitialState(mInactiveState); - } - - private @NetworkType int getNetworkType() { - ServiceState ss = mPhone.getServiceState(); - int networkType = TelephonyManager.NETWORK_TYPE_UNKNOWN; - - NetworkRegistrationInfo nri = ss.getNetworkRegistrationInfo( - NetworkRegistrationInfo.DOMAIN_PS, mTransportType); - if (nri != null) { - networkType = nri.getAccessNetworkTechnology(); - } - - return networkType; - } - - /** - * Get the source transport for handover. For example, handover from WWAN to WLAN, WWAN is the - * source transport, and vice versa. - */ - private @TransportType int getHandoverSourceTransport() { - return mTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WWAN - ? AccessNetworkConstants.TRANSPORT_TYPE_WLAN - : AccessNetworkConstants.TRANSPORT_TYPE_WWAN; - } - - /** - * API to generate the OsAppId for enterprise traffic category. - * @return byte[] representing OsId + length of OsAppId + OsAppId - */ - @VisibleForTesting - public static byte[] getEnterpriseOsAppId() { - byte[] osAppId = NetworkCapabilities.getCapabilityCarrierName( - NetworkCapabilities.NET_CAPABILITY_ENTERPRISE).getBytes(); - // 16 bytes for UUID, 1 byte for length of osAppId, and up to 255 bytes for osAppId - ByteBuffer bb = ByteBuffer.allocate(16 + 1 + osAppId.length); - bb.putLong(OS_ID.getMostSignificantBits()); - bb.putLong(OS_ID.getLeastSignificantBits()); - bb.put((byte) osAppId.length); - bb.put(osAppId); - if (VDBG) { - Rlog.d("DataConnection", "getEnterpriseOsAppId: " - + IccUtils.bytesToHexString(bb.array())); - } - return bb.array(); - } - - /** - * Begin setting up a data connection, calls setupDataCall - * and the ConnectionParams will be returned with the - * EVENT_SETUP_DATA_CONNECTION_DONE - * - * @param cp is the connection parameters - * - * @return Fail cause if failed to setup data connection. {@link DataFailCause#NONE} if success. - */ - private @DataFailureCause int connect(ConnectionParams cp) { - log("connect: carrier='" + mApnSetting.getEntryName() - + "' APN='" + mApnSetting.getApnName() - + "' proxy='" + mApnSetting.getProxyAddressAsString() - + "' port='" + mApnSetting.getProxyPort() + "'"); - ApnContext.requestLog(cp.mApnContext, "DataConnection.connect"); - - // Check if we should fake an error. - if (mDcTesterFailBringUpAll.getDcFailBringUp().mCounter > 0) { - DataCallResponse response = new DataCallResponse.Builder() - .setCause(mDcTesterFailBringUpAll.getDcFailBringUp().mFailCause) - .setRetryDurationMillis( - mDcTesterFailBringUpAll.getDcFailBringUp().mSuggestedRetryTime) - .setMtuV4(PhoneConstants.UNSET_MTU) - .setMtuV6(PhoneConstants.UNSET_MTU) - .build(); - - Message msg = obtainMessage(EVENT_SETUP_DATA_CONNECTION_DONE, cp); - AsyncResult.forMessage(msg, response, null); - sendMessage(msg); - if (DBG) { - log("connect: FailBringUpAll=" + mDcTesterFailBringUpAll.getDcFailBringUp() - + " send error response=" + response); - } - mDcTesterFailBringUpAll.getDcFailBringUp().mCounter -= 1; - return DataFailCause.NONE; - } - - mCreateTime = -1; - mLastFailTime = -1; - mLastFailCause = DataFailCause.NONE; - - Message msg = obtainMessage(EVENT_SETUP_DATA_CONNECTION_DONE, cp); - msg.obj = cp; - - DataProfile dp = new DataProfile.Builder() - .setApnSetting(mApnSetting) - .setPreferred(cp.mIsPreferredApn) - .build(); - - // We need to use the actual modem roaming state instead of the framework roaming state - // here. This flag is only passed down to ril_service for picking the correct protocol (for - // old modem backward compatibility). - boolean isModemRoaming = mPhone.getServiceState().getDataRoamingFromRegistration(); - - // If the apn is NOT metered, we will allow data roaming regardless of the setting. - boolean isUnmeteredApnType = !ApnSettingUtils.isMeteredApnType( - cp.mApnContext.getApnTypeBitmask(), mPhone); - - // Set this flag to true if the user turns on data roaming. Or if we override the roaming - // state in framework, we should set this flag to true as well so the modem will not reject - // the data call setup (because the modem actually thinks the device is roaming). - boolean allowRoaming = mPhone.getDataRoamingEnabled() - || (isModemRoaming && (!mPhone.getServiceState().getDataRoaming() - || isUnmeteredApnType)); - - String dnn = null; - byte[] osAppId = null; - if (cp.mApnContext.getApnTypeBitmask() == ApnSetting.TYPE_ENTERPRISE) { - osAppId = getEnterpriseOsAppId(); - } else { - dnn = mApnSetting.getApnName(); - } - final TrafficDescriptor td = osAppId == null && dnn == null ? null - : new TrafficDescriptor(dnn, osAppId); - final boolean matchAllRuleAllowed = td == null || td.getOsAppId() == null; - - if (DBG) { - log("allowRoaming=" + allowRoaming - + ", mPhone.getDataRoamingEnabled()=" + mPhone.getDataRoamingEnabled() - + ", isModemRoaming=" + isModemRoaming - + ", mPhone.getServiceState().getDataRoaming()=" - + mPhone.getServiceState().getDataRoaming() - + ", isUnmeteredApnType=" + isUnmeteredApnType - + ", trafficDescriptor=" + td - + ", matchAllRuleAllowed=" + matchAllRuleAllowed - ); - } - - // Check if this data setup is a handover. - LinkProperties linkProperties = null; - int reason = DataService.REQUEST_REASON_NORMAL; - if (cp.mRequestType == REQUEST_TYPE_HANDOVER) { - // If this is a data setup for handover, we need to pass the link properties - // of the existing data connection to the modem. - DcTracker srcDcTracker = mPhone.getDcTracker(getHandoverSourceTransport()); - if (srcDcTracker == null || cp.mApnContext == null) { - loge("connect: Handover failed. dcTracker=" + srcDcTracker + ", apnContext=" - + cp.mApnContext); - return DataFailCause.HANDOVER_FAILED; - } - - - // srcDc is the source data connection while the current instance is the target - DataConnection srcDc = - srcDcTracker.getDataConnectionByApnType(cp.mApnContext.getApnType()); - if (srcDc == null) { - loge("connect: Can't find data connection for handover."); - return DataFailCause.HANDOVER_FAILED; - } - - // Helpful for logging purposes - DataServiceManager srcDsm = srcDc.mDataServiceManager; - String srcDsmTag = (srcDsm == null ? "(null)" : srcDsm.getTag()); - logd("connect: REQUEST_TYPE_HANDOVER - Request handover from " + srcDc.getName() - + ", targetDsm=" + mDataServiceManager.getTag() - + ", sourceDsm=" + srcDsmTag); - - - /* startHandover is called on the source data connection, and if successful, - we ask the target data connection (which is the current instance) to call - #setupDataCall with request type handover. - */ - Consumer<Integer> onCompleted = (dataServiceCallbackResultCode) -> - /* startHandover is called on the srcDc handler, but the callback needs to - be called on the current (which is the targetDc) handler which is why we - call sendRunnableMessage. */ - sendRunnableMessage(EVENT_START_HANDOVER_ON_TARGET, - (inCorrectState) -> requestHandover(inCorrectState, srcDc, - dataServiceCallbackResultCode, - cp, msg, dp, isModemRoaming, allowRoaming)); - srcDc.startHandover(onCompleted); - return DataFailCause.NONE; - } - - // setup data call for REQUEST_TYPE_NORMAL - mDoAllocatePduSessionId = mTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WLAN; - allocatePduSessionId(psi -> { - this.setPduSessionId(psi); - mDataServiceManager.setupDataCall( - ServiceState.rilRadioTechnologyToAccessNetworkType(cp.mRilRat), - dp, - isModemRoaming, - allowRoaming, - reason, - linkProperties, - psi, - null, //slice info is null since this is not a handover - td, - matchAllRuleAllowed, - msg); - TelephonyMetrics.getInstance().writeSetupDataCall(mPhone.getPhoneId(), cp.mRilRat, - dp.getProfileId(), dp.getApn(), dp.getProtocolType()); - }); - return DataFailCause.NONE; - } - - private void allocatePduSessionId(Consumer<Integer> allocateCallback) { - if (mDoAllocatePduSessionId) { - Message msg = this.obtainMessage(EVENT_ALLOCATE_PDU_SESSION_ID); - msg.obj = allocateCallback; - mPhone.mCi.allocatePduSessionId(msg); - } else { - allocateCallback.accept(PDU_SESSION_ID_NOT_SET); - } - } - - private void onRquestHandoverFailed(ConnectionParams cp) { - sendMessage(obtainMessage(EVENT_CANCEL_HANDOVER)); - notifyConnectCompleted(cp, DataFailCause.UNKNOWN, - DataCallResponse.HANDOVER_FAILURE_MODE_UNKNOWN, false); - } - - private void requestHandover(boolean inCorrectState, DataConnection srcDc, - @DataServiceCallback.ResultCode int resultCode, - ConnectionParams cp, Message msg, DataProfile dp, boolean isModemRoaming, - boolean allowRoaming) { - - if (!inCorrectState) { - logd("requestHandover: Not in correct state"); - if (isResultCodeSuccess(resultCode)) { - if (srcDc != null) { - logd("requestHandover: Not in correct state - Success result code"); - // We need to cancel the handover on source if we ended up in the wrong state. - srcDc.cancelHandover(); - } else { - logd("requestHandover: Not in correct state - Success result code - " - + "srcdc = null"); - } - } - onRquestHandoverFailed(cp); - return; - } else if (!isResultCodeSuccess(resultCode)) { - if (DBG) { - logd("requestHandover: Non success result code from DataService, " - + "setupDataCall will not be called, result code = " - + DataServiceCallback.resultCodeToString(resultCode)); - } - onRquestHandoverFailed(cp); - return; - } - - if (srcDc == null) { - loge("requestHandover: Cannot find source data connection."); - onRquestHandoverFailed(cp); - return; - } - - LinkProperties linkProperties; - int reason; - - // Preserve the potential network agent from the source data connection. The ownership - // is not transferred at this moment. - mHandoverSourceNetworkAgent = srcDc.getNetworkAgent(); - if (mHandoverSourceNetworkAgent == null) { - loge("requestHandover: Cannot get network agent from the source dc " + srcDc.getName()); - onRquestHandoverFailed(cp); - return; - } - - linkProperties = srcDc.getLinkProperties(); - if (linkProperties == null || linkProperties.getLinkAddresses().isEmpty()) { - loge("requestHandover: Can't find link properties of handover data connection. dc=" - + srcDc); - onRquestHandoverFailed(cp); - return; - } - - mHandoverLocalLog.log("Handover started. Preserved the agent."); - log("Get the handover source network agent: " + mHandoverSourceNetworkAgent); - - reason = DataService.REQUEST_REASON_HANDOVER; - - TrafficDescriptor td = dp.getApn() == null ? null - : new TrafficDescriptor(dp.getApn(), null); - boolean matchAllRuleAllowed = true; - - mDataServiceManager.setupDataCall( - ServiceState.rilRadioTechnologyToAccessNetworkType(cp.mRilRat), - dp, - isModemRoaming, - allowRoaming, - reason, - linkProperties, - srcDc.getPduSessionId(), - srcDc.getSliceInfo(), - td, - matchAllRuleAllowed, - msg); - TelephonyMetrics.getInstance().writeSetupDataCall(mPhone.getPhoneId(), cp.mRilRat, - dp.getProfileId(), dp.getApn(), dp.getProtocolType()); - } - - /** - * Called on the source data connection from the target data connection. - */ - @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); - - Consumer<Integer> onSrcDcComplete = - resultCode -> onHandoverStarted(resultCode, onTargetDcComplete); - /* - The flow here is: - srcDc#startHandover -> dataService#startHandover -> (onHandoverStarted) -> - onSrcDcComplete -> onTargetDcComplete - */ - mDataServiceManager.startHandover(mCid, - this.obtainMessage(EVENT_START_HANDOVER, - onSrcDcComplete)); - } - - /** - * Called on the source data connection when the async call to start handover is complete - */ - private void onHandoverStarted(@DataServiceCallback.ResultCode int resultCode, - Consumer<Integer> onTargetDcComplete) { - logd("onHandoverStarted: " + toStringSimple()); - if (!isResultCodeSuccess(resultCode)) { - setHandoverState(HANDOVER_STATE_IDLE); - } - onTargetDcComplete.accept(resultCode); - } - - private void cancelHandover() { - if (mHandoverState != HANDOVER_STATE_BEING_TRANSFERRED) { - logd("cancelHandover: handover state is " + handoverStateToString(mHandoverState) - + ", expecting HANDOVER_STATE_BEING_TRANSFERRED"); - } - mDataServiceManager.cancelHandover(mCid, this.obtainMessage(EVENT_CANCEL_HANDOVER)); - setHandoverState(HANDOVER_STATE_IDLE); - } - - /** - * Update NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED based on congested override - * @param isCongested whether this DC should be set to congested or not - */ - public void onCongestednessChanged(boolean isCongested) { - sendMessage(obtainMessage(EVENT_DATA_CONNECTION_CONGESTEDNESS_CHANGED, isCongested)); - } - - /** - * Update NetworkCapabilities.NET_CAPABILITY_NOT_METERED based on metered override - * @param isUnmetered whether this DC should be set to unmetered or not - */ - public void onMeterednessChanged(boolean isUnmetered) { - sendMessage(obtainMessage(EVENT_DATA_CONNECTION_METEREDNESS_CHANGED, isUnmetered)); - } - - /** - * TearDown the data connection when the deactivation is complete a Message with - * msg.what == EVENT_DEACTIVATE_DONE - * - * @param o is the object returned in the AsyncResult.obj. - */ - private void tearDownData(Object o) { - int discReason = DataService.REQUEST_REASON_NORMAL; - ApnContext apnContext = null; - if ((o != null) && (o instanceof DisconnectParams)) { - DisconnectParams dp = (DisconnectParams) o; - apnContext = dp.mApnContext; - if (TextUtils.equals(dp.mReason, Phone.REASON_RADIO_TURNED_OFF) - || TextUtils.equals(dp.mReason, Phone.REASON_PDP_RESET)) { - discReason = DataService.REQUEST_REASON_SHUTDOWN; - } else if (dp.mReleaseType == DcTracker.RELEASE_TYPE_HANDOVER) { - discReason = DataService.REQUEST_REASON_HANDOVER; - } - } - - String str = "tearDownData. mCid=" + mCid + ", reason=" + discReason; - if (DBG) log(str); - ApnContext.requestLog(apnContext, str); - - - //Needed to be final to work in a closure - final int fDiscReason = discReason; - releasePduSessionId(() -> { - // This is run after release pdu session id is complete - this.setPduSessionId(PDU_SESSION_ID_NOT_SET); - mDataServiceManager.deactivateDataCall(mCid, fDiscReason, - obtainMessage(EVENT_DEACTIVATE_DONE, mTag, 0, o)); - mDataCallSessionStats.setDeactivateDataCallReason(fDiscReason); - }); - } - - private void releasePduSessionId(Runnable releaseCallback) { - // If the transport is IWLAN, and there is a valid PDU session id, also the data connection - // is not being handovered, we should release the pdu session id. - if (mTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WLAN - && mHandoverState == HANDOVER_STATE_IDLE - && this.getPduSessionId() != PDU_SESSION_ID_NOT_SET) { - Message msg = this.obtainMessage(EVENT_RELEASE_PDU_SESSION_ID); - msg.obj = releaseCallback; - mPhone.mCi.releasePduSessionId(msg, this.getPduSessionId()); - } else { - // Just go and run the callback since we either have no pdu session id to release - // or we are in the middle of a handover - releaseCallback.run(); - } - } - - private void notifyAllWithEvent(ApnContext alreadySent, int event, String reason) { - for (ConnectionParams cp : mApnContexts.values()) { - ApnContext apnContext = cp.mApnContext; - if (apnContext == alreadySent) continue; - if (reason != null) apnContext.setReason(reason); - Pair<ApnContext, Integer> pair = new Pair<>(apnContext, cp.mConnectionGeneration); - Message msg = mDct.obtainMessage(event, cp.mRequestType, - DataCallResponse.HANDOVER_FAILURE_MODE_UNKNOWN, pair); - AsyncResult.forMessage(msg); - msg.sendToTarget(); - } - } - - /** - * Send the connectionCompletedMsg. - * - * @param cp is the ConnectionParams - * @param cause and if no error the cause is DataFailCause.NONE - * @param handoverFailureMode The action on handover failure - * @param sendAll is true if all contexts are to be notified - */ - private void notifyConnectCompleted(ConnectionParams cp, @DataFailureCause int cause, - @HandoverFailureMode int handoverFailureMode, boolean sendAll) { - ApnContext alreadySent = null; - - if (cp != null && cp.mOnCompletedMsg != null) { - // Get the completed message but only use it once - Message connectionCompletedMsg = cp.mOnCompletedMsg; - cp.mOnCompletedMsg = null; - alreadySent = cp.mApnContext; - - long timeStamp = System.currentTimeMillis(); - connectionCompletedMsg.arg1 = cp.mRequestType; - connectionCompletedMsg.arg2 = handoverFailureMode; - - if (cause == DataFailCause.NONE) { - mCreateTime = timeStamp; - AsyncResult.forMessage(connectionCompletedMsg); - } else { - mLastFailCause = cause; - mLastFailTime = timeStamp; - - // Return message with a Throwable exception to signify an error. - if (cause == DataFailCause.NONE) cause = DataFailCause.UNKNOWN; - AsyncResult.forMessage(connectionCompletedMsg, cause, - new Throwable(DataFailCause.toString(cause))); - } - if (DBG) { - log("notifyConnectCompleted at " + timeStamp + " cause=" - + DataFailCause.toString(cause) + " connectionCompletedMsg=" - + msgToString(connectionCompletedMsg)); - } - - connectionCompletedMsg.sendToTarget(); - } - if (sendAll) { - log("Send to all. " + alreadySent + " " + DataFailCause.toString(cause)); - notifyAllWithEvent(alreadySent, DctConstants.EVENT_DATA_SETUP_COMPLETE_ERROR, - DataFailCause.toString(cause)); - } - } - - /** - * Send ar.userObj if its a message, which is should be back to originator. - * - * @param dp is the DisconnectParams. - */ - private void notifyDisconnectCompleted(DisconnectParams dp, boolean sendAll) { - if (VDBG) log("NotifyDisconnectCompleted"); - - ApnContext alreadySent = null; - String reason = null; - - if (dp != null && dp.mOnCompletedMsg != null) { - // Get the completed message but only use it once - Message msg = dp.mOnCompletedMsg; - dp.mOnCompletedMsg = null; - if (msg.obj instanceof ApnContext) { - alreadySent = (ApnContext)msg.obj; - } - reason = dp.mReason; - if (VDBG) { - log(String.format("msg=%s msg.obj=%s", msg.toString(), - ((msg.obj instanceof String) ? (String) msg.obj : "<no-reason>"))); - } - AsyncResult.forMessage(msg); - msg.sendToTarget(); - } - if (sendAll) { - if (reason == null) { - reason = DataFailCause.toString(DataFailCause.UNKNOWN); - } - notifyAllWithEvent(alreadySent, DctConstants.EVENT_DISCONNECT_DONE, reason); - } - if (DBG) log("NotifyDisconnectCompleted DisconnectParams=" + dp); - } - - private void sendRunnableMessage(int eventCode, @NonNull final Consumer<Boolean> r) { - sendMessage(eventCode, r); - } - - /* - * ************************************************************************** - * Begin Members and methods owned by DataConnectionTracker but stored - * in a DataConnection because there is one per connection. - * ************************************************************************** - */ - - /* - * The id is owned by DataConnectionTracker. - */ - private int mId; - - /** - * Get the DataConnection ID - */ - public int getDataConnectionId() { - return mId; - } - - /* - * ************************************************************************** - * End members owned by DataConnectionTracker - * ************************************************************************** - */ - - /** - * Clear all settings called when entering mInactiveState. - */ - private synchronized void clearSettings() { - if (DBG) log("clearSettings"); - - mCreateTime = -1; - mLastFailTime = -1; - mLastFailCause = DataFailCause.NONE; - mCid = -1; - - mPcscfAddr = new String[5]; - - mLinkProperties = new LinkProperties(); - mApnContexts.clear(); - mApnSetting = null; - mUnmeteredUseOnly = false; - mMmsUseOnly = false; - mEnterpriseUse = false; - mRestrictedNetworkOverride = false; - mDcFailCause = DataFailCause.NONE; - mDisabledApnTypeBitMask = 0; - mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; - mCongestedOverride = false; - mUnmeteredOverride = false; - mDownlinkBandwidth = 14; - mUplinkBandwidth = 14; - mIsSuspended = false; - mHandoverState = HANDOVER_STATE_IDLE; - mHandoverFailureMode = DataCallResponse.HANDOVER_FAILURE_MODE_UNKNOWN; - mSliceInfo = null; - mDefaultQos = null; - mDoAllocatePduSessionId = false; - mQosBearerSessions.clear(); - mTrafficDescriptors.clear(); - } - - /** - * Process setup data completion result from data service - * - * @param resultCode The result code returned by data service - * @param response Data call setup response from data service - * @param cp The original connection params used for data call setup - * @return Setup result - */ - private SetupResult onSetupConnectionCompleted(@DataServiceCallback.ResultCode int resultCode, - DataCallResponse response, - ConnectionParams cp) { - SetupResult result; - - log("onSetupConnectionCompleted: resultCode=" + resultCode + ", response=" + response); - if (cp.mTag != mTag) { - if (DBG) { - log("onSetupConnectionCompleted stale cp.tag=" + cp.mTag + ", mtag=" + mTag); - } - result = SetupResult.ERROR_STALE; - } else if (resultCode == DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE) { - result = SetupResult.ERROR_RADIO_NOT_AVAILABLE; - result.mFailCause = DataFailCause.RADIO_NOT_AVAILABLE; - } else if (resultCode == DataServiceCallback.RESULT_ERROR_TEMPORARILY_UNAVAILABLE) { - result = SetupResult.ERROR_DATA_SERVICE_SPECIFIC_ERROR; - result.mFailCause = DataFailCause.SERVICE_TEMPORARILY_UNAVAILABLE; - } else if (resultCode == DataServiceCallback.RESULT_ERROR_INVALID_ARG) { - result = SetupResult.ERROR_INVALID_ARG; - result.mFailCause = DataFailCause.UNACCEPTABLE_NETWORK_PARAMETER; - } else if (response.getCause() != 0) { - if (response.getCause() == DataFailCause.RADIO_NOT_AVAILABLE) { - result = SetupResult.ERROR_RADIO_NOT_AVAILABLE; - result.mFailCause = DataFailCause.RADIO_NOT_AVAILABLE; - } else { - result = SetupResult.ERROR_DATA_SERVICE_SPECIFIC_ERROR; - result.mFailCause = DataFailCause.getFailCause(response.getCause()); - } - } else if (cp.mApnContext.getApnTypeBitmask() == ApnSetting.TYPE_ENTERPRISE - && mDcController.getActiveDcByCid(response.getId()) != null) { - if (!mDcController.getTrafficDescriptorsForCid(response.getId()) - .equals(response.getTrafficDescriptors())) { - if (DBG) log("Updating traffic descriptors: " + response.getTrafficDescriptors()); - mDcController.getActiveDcByCid(response.getId()).updateTrafficDescriptors(response); - mDct.obtainMessage(DctConstants.EVENT_TRAFFIC_DESCRIPTORS_UPDATED).sendToTarget(); - } - if (DBG) log("DataConnection already exists for cid: " + response.getId()); - result = SetupResult.ERROR_DUPLICATE_CID; - result.mFailCause = DataFailCause.DUPLICATE_CID; - } 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 { - if (DBG) log("onSetupConnectionCompleted received successful DataCallResponse"); - mCid = response.getId(); - setPduSessionId(response.getPduSessionId()); - updatePcscfAddr(response); - updateResponseFields(response); - result = updateLinkProperty(response).setupResult; - } - - return result; - } - - private static boolean isResultCodeSuccess(int resultCode) { - return resultCode == DataServiceCallback.RESULT_SUCCESS - || resultCode == DataServiceCallback.RESULT_ERROR_UNSUPPORTED; - } - - private boolean isDnsOk(String[] domainNameServers) { - if (NULL_IP.equals(domainNameServers[0]) && NULL_IP.equals(domainNameServers[1]) - && !mPhone.isDnsCheckDisabled()) { - // Work around a race condition where QMI does not fill in DNS: - // Deactivate PDP and let DataConnectionTracker retry. - // Do not apply the race condition workaround for MMS APN - // if Proxy is an IP-address. - // Otherwise, the default APN will not be restored anymore. - if (!isIpAddress(mApnSetting.getMmsProxyAddressAsString())) { - log(String.format( - "isDnsOk: return false apn.types=%d APN_TYPE_MMS=%s isIpAddress(%s)=%s", - mApnSetting.getApnTypeBitmask(), ApnSetting.TYPE_MMS_STRING, - mApnSetting.getMmsProxyAddressAsString(), - isIpAddress(mApnSetting.getMmsProxyAddressAsString()))); - return false; - } - } - return true; - } - - /** - * TCP buffer size config based on the ril technology. There are 6 parameters - * read_min, read_default, read_max, write_min, write_default, write_max in the TCP buffer - * config string and they are separated by a comma. The unit of these parameters is byte. - */ - private static final String TCP_BUFFER_SIZES_GPRS = "4092,8760,48000,4096,8760,48000"; - private static final String TCP_BUFFER_SIZES_EDGE = "4093,26280,70800,4096,16384,70800"; - private static final String TCP_BUFFER_SIZES_UMTS = "58254,349525,1048576,58254,349525,1048576"; - private static final String TCP_BUFFER_SIZES_1XRTT = "16384,32768,131072,4096,16384,102400"; - private static final String TCP_BUFFER_SIZES_EVDO = "4094,87380,262144,4096,16384,262144"; - private static final String TCP_BUFFER_SIZES_EHRPD = "131072,262144,1048576,4096,16384,524288"; - private static final String TCP_BUFFER_SIZES_HSDPA = "61167,367002,1101005,8738,52429,262114"; - private static final String TCP_BUFFER_SIZES_HSPA = "40778,244668,734003,16777,100663,301990"; - private static final String TCP_BUFFER_SIZES_LTE = - "524288,1048576,2097152,262144,524288,1048576"; - private static final String TCP_BUFFER_SIZES_HSPAP = - "122334,734003,2202010,32040,192239,576717"; - private static final String TCP_BUFFER_SIZES_NR = - "2097152,6291456,16777216,512000,2097152,8388608"; - private static final String TCP_BUFFER_SIZES_LTE_CA = - "4096,6291456,12582912,4096,1048576,2097152"; - - private void updateTcpBufferSizes(int rilRat) { - String sizes = null; - ServiceState ss = mPhone.getServiceState(); - if (rilRat == ServiceState.RIL_RADIO_TECHNOLOGY_LTE && - ss.isUsingCarrierAggregation()) { - rilRat = ServiceState.RIL_RADIO_TECHNOLOGY_LTE_CA; - } - String ratName = ServiceState.rilRadioTechnologyToString(rilRat).toLowerCase(Locale.ROOT); - // ServiceState gives slightly different names for EVDO tech ("evdo-rev.0" for ex) - // - patch it up: - if (rilRat == ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_0 || - rilRat == ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_A || - rilRat == ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_B) { - ratName = RAT_NAME_EVDO; - } - - // NR 5G Non-Standalone use LTE cell as the primary cell, the ril technology is LTE in this - // case. We use NR 5G TCP buffer size when connected to NR 5G Non-Standalone network. - if (mTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WWAN - && ((rilRat == ServiceState.RIL_RADIO_TECHNOLOGY_LTE || - rilRat == ServiceState.RIL_RADIO_TECHNOLOGY_LTE_CA) && isNRConnected()) - && mPhone.getServiceStateTracker().getNrContextIds().contains(mCid)) { - ratName = RAT_NAME_5G; - } - - log("updateTcpBufferSizes: " + ratName); - - // in the form: "ratname:rmem_min,rmem_def,rmem_max,wmem_min,wmem_def,wmem_max" - String[] configOverride = mPhone.getContext().getResources().getStringArray( - com.android.internal.R.array.config_mobile_tcp_buffers); - for (int i = 0; i < configOverride.length; i++) { - String[] split = configOverride[i].split(":"); - if (ratName.equals(split[0]) && split.length == 2) { - sizes = split[1]; - break; - } - } - - if (sizes == null) { - // no override - use telephony defaults - // doing it this way allows device or carrier to just override the types they - // care about and inherit the defaults for the others. - switch (rilRat) { - case ServiceState.RIL_RADIO_TECHNOLOGY_GPRS: - sizes = TCP_BUFFER_SIZES_GPRS; - break; - case ServiceState.RIL_RADIO_TECHNOLOGY_EDGE: - sizes = TCP_BUFFER_SIZES_EDGE; - break; - case ServiceState.RIL_RADIO_TECHNOLOGY_UMTS: - sizes = TCP_BUFFER_SIZES_UMTS; - break; - case ServiceState.RIL_RADIO_TECHNOLOGY_1xRTT: - sizes = TCP_BUFFER_SIZES_1XRTT; - break; - case ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_0: - case ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_A: - case ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_B: - sizes = TCP_BUFFER_SIZES_EVDO; - break; - case ServiceState.RIL_RADIO_TECHNOLOGY_EHRPD: - sizes = TCP_BUFFER_SIZES_EHRPD; - break; - case ServiceState.RIL_RADIO_TECHNOLOGY_HSDPA: - sizes = TCP_BUFFER_SIZES_HSDPA; - break; - case ServiceState.RIL_RADIO_TECHNOLOGY_HSPA: - case ServiceState.RIL_RADIO_TECHNOLOGY_HSUPA: - sizes = TCP_BUFFER_SIZES_HSPA; - break; - case ServiceState.RIL_RADIO_TECHNOLOGY_LTE: - // Use NR 5G TCP buffer size when connected to NR 5G Non-Standalone network. - if (RAT_NAME_5G.equals(ratName)) { - sizes = TCP_BUFFER_SIZES_NR; - } else { - sizes = TCP_BUFFER_SIZES_LTE; - } - break; - case ServiceState.RIL_RADIO_TECHNOLOGY_LTE_CA: - // Use NR 5G TCP buffer size when connected to NR 5G Non-Standalone network. - if (RAT_NAME_5G.equals(ratName)) { - sizes = TCP_BUFFER_SIZES_NR; - } else { - sizes = TCP_BUFFER_SIZES_LTE_CA; - } - break; - case ServiceState.RIL_RADIO_TECHNOLOGY_HSPAP: - sizes = TCP_BUFFER_SIZES_HSPAP; - break; - case ServiceState.RIL_RADIO_TECHNOLOGY_NR: - sizes = TCP_BUFFER_SIZES_NR; - break; - default: - // Leave empty - this will let ConnectivityService use the system default. - break; - } - } - mLinkProperties.setTcpBufferSizes(sizes); - } - - private void updateLinkBandwidthsFromCarrierConfig(int rilRat) { - String ratName = DataConfigManager.getDataConfigNetworkType( - mPhone.getDisplayInfoController().getTelephonyDisplayInfo()); - - if (DBG) log("updateLinkBandwidthsFromCarrierConfig: " + ratName); - - Pair<Integer, Integer> values = mDct.getLinkBandwidthsFromCarrierConfig(ratName); - if (values == null) { - values = new Pair<>(14, 14); - } - mDownlinkBandwidth = values.first; - mUplinkBandwidth = values.second; - } - - - private void updateLinkBandwidthsFromModem(List<LinkCapacityEstimate> lceList) { - if (DBG) log("updateLinkBandwidthsFromModem: lceList=" + lceList); - boolean downlinkUpdated = false; - boolean uplinkUpdated = false; - LinkCapacityEstimate lce = lceList.get(0); - // LCE status deprecated in IRadio 1.2, so only check for IRadio < 1.2 - if (mPhone.getHalVersion().greaterOrEqual(RIL.RADIO_HAL_VERSION_1_2) - || mPhone.getLceStatus() == RILConstants.LCE_ACTIVE) { - if (lce.getDownlinkCapacityKbps() != LinkCapacityEstimate.INVALID) { - mDownlinkBandwidth = lce.getDownlinkCapacityKbps(); - downlinkUpdated = true; - } - if (lce.getUplinkCapacityKbps() != LinkCapacityEstimate.INVALID) { - mUplinkBandwidth = lce.getUplinkCapacityKbps(); - uplinkUpdated = true; - } - } - - if (!downlinkUpdated || !uplinkUpdated) { - fallBackToCarrierConfigValues(downlinkUpdated, uplinkUpdated); - } - - if (mNetworkAgent != null) { - mNetworkAgent.sendNetworkCapabilities(getNetworkCapabilities(), DataConnection.this); - } - } - - private void updateLinkBandwidthsFromBandwidthEstimator(int uplinkBandwidthKbps, - int downlinkBandwidthKbps) { - if (DBG) { - log("updateLinkBandwidthsFromBandwidthEstimator, UL= " - + uplinkBandwidthKbps + " DL= " + downlinkBandwidthKbps); - } - boolean downlinkUpdated = false; - boolean uplinkUpdated = false; - if (downlinkBandwidthKbps > 0) { - mDownlinkBandwidth = downlinkBandwidthKbps; - downlinkUpdated = true; - } - if (uplinkBandwidthKbps > 0) { - mUplinkBandwidth = uplinkBandwidthKbps; - uplinkUpdated = true; - } - - if (!downlinkUpdated || !uplinkUpdated) { - fallBackToCarrierConfigValues(downlinkUpdated, uplinkUpdated); - } - if (mNetworkAgent != null) { - mNetworkAgent.sendNetworkCapabilities(getNetworkCapabilities(), DataConnection.this); - } - } - - private void fallBackToCarrierConfigValues(boolean downlinkUpdated, boolean uplinkUpdated) { - String ratName = ServiceState.rilRadioTechnologyToString(mRilRat); - if (mRilRat == ServiceState.RIL_RADIO_TECHNOLOGY_LTE && isNRConnected()) { - ratName = mPhone.getServiceState().getNrFrequencyRange() - == ServiceState.FREQUENCY_RANGE_MMWAVE - ? DctConstants.RAT_NAME_NR_NSA_MMWAVE : DctConstants.RAT_NAME_NR_NSA; - } - Pair<Integer, Integer> values = mDct.getLinkBandwidthsFromCarrierConfig(ratName); - if (values != null) { - if (!downlinkUpdated) { - mDownlinkBandwidth = values.first; - } - if (!uplinkUpdated) { - mUplinkBandwidth = values.second; - } - mUplinkBandwidth = Math.min(mUplinkBandwidth, mDownlinkBandwidth); - } - } - - private boolean isBandwidthSourceKey(String source) { - return source.equals(mPhone.getContext().getResources().getString( - com.android.internal.R.string.config_bandwidthEstimateSource)); - } - - /** - * Indicates if this data connection was established for unmetered use only. Note that this - * flag should be populated when data becomes active. And if it is set to true, it can be set to - * false later when we are reevaluating the data connection. But if it is set to false, it - * can never become true later because setting it to true will cause this data connection - * losing some immutable network capabilities, which can cause issues in connectivity service. - */ - private boolean mUnmeteredUseOnly = false; - - /** - * Indicates if this data connection was established for MMS use only. This is true only when - * mobile data is disabled but the user allows sending and receiving MMS messages. If the data - * enabled settings indicate that MMS data is allowed unconditionally, MMS can be sent when data - * is disabled even if it is a metered APN type. - */ - private boolean mMmsUseOnly = false; - - /** - * Indicates if when this connection was established we had a restricted/privileged - * NetworkRequest and needed it to overcome data-enabled limitations. - * - * This flag overrides the APN-based restriction capability, restricting the network - * based on both having a NetworkRequest with restricted AND needing a restricted - * bit to overcome user-disabled status. This allows us to handle the common case - * of having both restricted requests and unrestricted requests for the same apn: - * if conditions require a restricted network to overcome user-disabled then it must - * be restricted, otherwise it is unrestricted (or restricted based on APN type). - * - * This supports a privileged app bringing up a network without general apps having access - * to it when the network is otherwise unavailable (hipri). The first use case is - * pre-paid SIM reprovisioning over internet, where the carrier insists on no traffic - * other than from the privileged carrier-app. - * - * Note that the data connection cannot go from unrestricted to restricted because the - * connectivity service does not support dynamically closing TCP connections at this point. - */ - private boolean mRestrictedNetworkOverride = false; - - /** - * Indicates if this data connection supports enterprise use. Note that this flag should be - * populated when data becomes active. Once it is set, the value cannot be changed because - * setting it will cause this data connection to lose immutable network capabilities, which can - * cause issues in connectivity service. - */ - private boolean mEnterpriseUse = false; - - /** - * Check if this data connection should be restricted. We should call this when data connection - * becomes active, or when we want to re-evaluate the conditions to decide if we need to - * unstrict the data connection. - * - * @return True if this data connection needs to be restricted. - */ - private boolean shouldRestrictNetwork() { - // first, check if there is any network request that containing restricted capability - // (i.e. Do not have NET_CAPABILITY_NOT_RESTRICTED in the request) - boolean isAnyRestrictedRequest = false; - for (ApnContext apnContext : mApnContexts.keySet()) { - if (apnContext.hasRestrictedRequests(true /* exclude DUN */)) { - isAnyRestrictedRequest = true; - break; - } - } - - // If all of the network requests are non-restricted, then we don't need to restrict - // the network. - if (!isAnyRestrictedRequest) { - return false; - } - - // If the network is unmetered, then we don't need to restrict the network because users - // won't be charged anyway. - if (!ApnSettingUtils.isMetered(mApnSetting, mPhone)) { - return false; - } - - // If the data is disabled, then we need to restrict the network so only privileged apps can - // use the restricted network while data is disabled. - if (!mPhone.getDataEnabledSettings().isDataEnabled()) { - return true; - } - - // If the device is roaming, and the user does not turn on data roaming, then we need to - // restrict the network so only privileged apps can use it. - if (!mDct.getDataRoamingEnabled() && mPhone.getServiceState().getDataRoaming()) { - return true; - } - - // Otherwise we should not restrict the network so anyone who requests can use it. - return false; - } - - /** - * @return True if this data connection should only be used for unmetered purposes. - */ - private boolean isUnmeteredUseOnly() { - // If this data connection is on IWLAN, then it's unmetered and can be used by everyone. - // Should not be for unmetered used only. - if (mTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WLAN) { - return false; - } - - // If data is enabled, this data connection can't be for unmetered used only because - // everyone should be able to use it if: - // 1. Device is not roaming, or - // 2. Device is roaming and data roaming is turned on - if (mPhone.getDataEnabledSettings().isDataEnabled()) { - if (!mPhone.getServiceState().getDataRoaming() || mDct.getDataRoamingEnabled()) { - return false; - } - } - - // The data connection can only be unmetered used only if all attached APN contexts - // attached to this data connection are unmetered. - for (ApnContext apnContext : mApnContexts.keySet()) { - if (ApnSettingUtils.isMeteredApnType(apnContext.getApnTypeBitmask(), mPhone)) { - return false; - } - } - return true; - } - - /** - * @return True if this data connection should only be used for MMS purposes. - */ - private boolean isMmsUseOnly() { - // MMS use only if data is disabled, MMS is allowed unconditionally, and MMS is the only - // APN type for this data connection. - DataEnabledSettings des = mPhone.getDataEnabledSettings(); - boolean mmsAllowedUnconditionally = !des.isDataEnabled() && des.isMmsAlwaysAllowed(); - boolean mmsApnOnly = isApnContextAttached(ApnSetting.TYPE_MMS, true); - return mmsAllowedUnconditionally && mmsApnOnly; - } - - /** - * Check if this data connection supports enterprise use. We call this when the data connection - * becomes active or when we want to reevaluate the conditions to decide if we need to update - * the network agent capabilities. - * - * @return True if this data connection supports enterprise use. - */ - private boolean isEnterpriseUse() { - return mApnContexts.keySet().stream().anyMatch( - ac -> ac.getApnTypeBitmask() == ApnSetting.TYPE_ENTERPRISE); - } - - /** - * Get the network capabilities for this data connection. - * - * @return the {@link NetworkCapabilities} of this data connection. - */ - public NetworkCapabilities getNetworkCapabilities() { - final NetworkCapabilities.Builder builder = new NetworkCapabilities.Builder() - .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR); - boolean unmeteredApns = false; - - if (mApnSetting != null && !mEnterpriseUse && !mMmsUseOnly) { - final int[] types = ApnSetting.getApnTypesFromBitmask( - mApnSetting.getApnTypeBitmask() & ~mDisabledApnTypeBitMask); - for (int type : types) { - if ((!mRestrictedNetworkOverride && mUnmeteredUseOnly) - && ApnSettingUtils.isMeteredApnType(type, mPhone)) { - log("Dropped the metered " + ApnSetting.getApnTypeString(type) - + " type for the unmetered data call."); - continue; - } - switch (type) { - case ApnSetting.TYPE_ALL: { - builder.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET); - builder.addCapability(NetworkCapabilities.NET_CAPABILITY_MMS); - builder.addCapability(NetworkCapabilities.NET_CAPABILITY_SUPL); - builder.addCapability(NetworkCapabilities.NET_CAPABILITY_FOTA); - builder.addCapability(NetworkCapabilities.NET_CAPABILITY_IMS); - builder.addCapability(NetworkCapabilities.NET_CAPABILITY_CBS); - builder.addCapability(NetworkCapabilities.NET_CAPABILITY_IA); - builder.addCapability(NetworkCapabilities.NET_CAPABILITY_DUN); - break; - } - case ApnSetting.TYPE_DEFAULT: { - builder.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET); - break; - } - case ApnSetting.TYPE_MMS: { - builder.addCapability(NetworkCapabilities.NET_CAPABILITY_MMS); - break; - } - case ApnSetting.TYPE_SUPL: { - builder.addCapability(NetworkCapabilities.NET_CAPABILITY_SUPL); - break; - } - case ApnSetting.TYPE_DUN: { - builder.addCapability(NetworkCapabilities.NET_CAPABILITY_DUN); - break; - } - case ApnSetting.TYPE_FOTA: { - builder.addCapability(NetworkCapabilities.NET_CAPABILITY_FOTA); - break; - } - case ApnSetting.TYPE_IMS: { - builder.addCapability(NetworkCapabilities.NET_CAPABILITY_IMS); - break; - } - case ApnSetting.TYPE_CBS: { - builder.addCapability(NetworkCapabilities.NET_CAPABILITY_CBS); - break; - } - case ApnSetting.TYPE_IA: { - builder.addCapability(NetworkCapabilities.NET_CAPABILITY_IA); - break; - } - case ApnSetting.TYPE_EMERGENCY: { - builder.addCapability(NetworkCapabilities.NET_CAPABILITY_EIMS); - break; - } - case ApnSetting.TYPE_MCX: { - builder.addCapability(NetworkCapabilities.NET_CAPABILITY_MCX); - break; - } - case ApnSetting.TYPE_XCAP: { - builder.addCapability(NetworkCapabilities.NET_CAPABILITY_XCAP); - break; - } - default: - } - } - - if (!ApnSettingUtils.isMetered(mApnSetting, mPhone)) { - unmeteredApns = true; - } - } - - // Mark NOT_METERED in the following cases: - // 1. All APNs in the APN settings are unmetered. - // 2. The non-restricted data is intended for unmetered use only. - if (unmeteredApns || (mUnmeteredUseOnly && !mRestrictedNetworkOverride)) { - builder.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED); - } else { - builder.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED); - } - - if (mEnterpriseUse) { - builder.addCapability(NetworkCapabilities.NET_CAPABILITY_ENTERPRISE); - builder.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET); - } - - if (NetworkCapabilitiesUtils.inferRestrictedCapability(builder.build())) { - builder.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED); - } - - if (mMmsUseOnly) { - if (ApnSettingUtils.isMeteredApnType(ApnSetting.TYPE_MMS, mPhone)) { - log("Adding unmetered capability for the unmetered MMS-only data connection"); - builder.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED); - } - log("Adding MMS capability for the MMS-only data connection"); - builder.addCapability(NetworkCapabilities.NET_CAPABILITY_MMS); - } - - if (mRestrictedNetworkOverride) { - builder.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED); - // don't use dun on restriction-overriden networks. - builder.removeCapability(NetworkCapabilities.NET_CAPABILITY_DUN); - } - - builder.setLinkDownstreamBandwidthKbps(mDownlinkBandwidth); - builder.setLinkUpstreamBandwidthKbps(mUplinkBandwidth); - - builder.setNetworkSpecifier(new TelephonyNetworkSpecifier.Builder() - .setSubscriptionId(mSubId).build()); - builder.setSubscriptionIds(Collections.singleton(mSubId)); - - if (!mPhone.getServiceState().getDataRoaming()) { - builder.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING); - } - - if (!mCongestedOverride) { - builder.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED); - } - - if (mUnmeteredOverride) { - builder.addCapability(NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED); - } - - if (!mIsSuspended) { - builder.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED); - } - - final int carrierServicePackageUid = getCarrierServicePackageUid(); - - // TODO(b/205736323): Owner and Admin UIDs currently come from separate data sources. Unify - // them, and remove ArrayUtils.contains() check. - if (carrierServicePackageUid != Process.INVALID_UID - && ArrayUtils.contains(mAdministratorUids, carrierServicePackageUid)) { - builder.setOwnerUid(carrierServicePackageUid); - builder.setAllowedUids(Collections.singleton(carrierServicePackageUid)); - } - builder.setAdministratorUids(mAdministratorUids); - - // Always start with NOT_VCN_MANAGED, then remove if VcnManager indicates this is part of a - // VCN. - builder.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED); - final VcnNetworkPolicyResult vcnPolicy = getVcnPolicy(builder.build()); - if (!vcnPolicy.getNetworkCapabilities() - .hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED)) { - builder.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED); - } - if (!vcnPolicy.getNetworkCapabilities() - .hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)) { - builder.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED); - } - - return builder.build(); - } - - // TODO(b/205736323): Once TelephonyManager#getCarrierServicePackageNameForLogicalSlot() is - // plumbed to CarrierPrivilegesTracker's cache, query the cached UIDs. - private int getFirstUidForPackage(String pkgName) { - if (pkgName == null) { - return Process.INVALID_UID; - } - - List<UserInfo> users = mPhone.getContext().getSystemService(UserManager.class).getUsers(); - for (UserInfo user : users) { - int userId = user.getUserHandle().getIdentifier(); - try { - PackageManager pm = mPhone.getContext().getPackageManager(); - - if (pm != null) { - return pm.getPackageUidAsUser(pkgName, userId); - } - } catch (NameNotFoundException exception) { - // Didn't find package. Try other users - Rlog.i( - "DataConnection", - "Unable to find uid for package " + pkgName + " and user " + userId); - } - } - return Process.INVALID_UID; - } - - private int getCarrierServicePackageUid() { - String pkgName = - mPhone.getContext() - .getSystemService(TelephonyManager.class) - .getCarrierServicePackageNameForLogicalSlot(mPhone.getPhoneId()); - - return getFirstUidForPackage(pkgName); - } - - /** - * Check if the this data network is VCN-managed. - * - * @param networkCapabilities The network capabilities of this data network. - * @return The VCN's policy for this DataNetwork. - */ - private VcnNetworkPolicyResult getVcnPolicy(NetworkCapabilities networkCapabilities) { - return mVcnManager.applyVcnNetworkPolicy(networkCapabilities, getLinkProperties()); - } - - /** @return {@code true} if validation is required, {@code false} otherwise. */ - public boolean isValidationRequired() { - final NetworkCapabilities nc = getNetworkCapabilities(); - return nc != null - && nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) - && nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED) - && nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED) - && nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN); - } - - /** - * @return {@code True} if 464xlat should be skipped. - */ - @VisibleForTesting - public boolean shouldSkip464Xlat() { - switch (mApnSetting.getSkip464Xlat()) { - case Telephony.Carriers.SKIP_464XLAT_ENABLE: - return true; - case Telephony.Carriers.SKIP_464XLAT_DISABLE: - return false; - case Telephony.Carriers.SKIP_464XLAT_DEFAULT: - default: - break; - } - - // As default, return true if ims and no internet - final NetworkCapabilities nc = getNetworkCapabilities(); - return nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_IMS) - && !nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET); - } - - /** - * @return {@code} true iff. {@code address} is a literal IPv4 or IPv6 address. - */ - @VisibleForTesting - public static boolean isIpAddress(String address) { - if (address == null) return false; - - // Accept IPv6 addresses (only) in square brackets for compatibility. - if (address.startsWith("[") && address.endsWith("]") && address.indexOf(':') != -1) { - address = address.substring(1, address.length() - 1); - } - return InetAddresses.isNumericAddress(address); - } - - private SetupResult setLinkProperties(DataCallResponse response, - LinkProperties linkProperties) { - // Check if system property dns usable - String propertyPrefix = "net." + response.getInterfaceName() + "."; - String dnsServers[] = new String[2]; - dnsServers[0] = SystemProperties.get(propertyPrefix + "dns1"); - dnsServers[1] = SystemProperties.get(propertyPrefix + "dns2"); - boolean okToUseSystemPropertyDns = isDnsOk(dnsServers); - - SetupResult result; - - // Start with clean network properties and if we have - // a failure we'll clear again at the bottom of this code. - linkProperties.clear(); - - if (response.getCause() == DataFailCause.NONE) { - try { - // set interface name - linkProperties.setInterfaceName(response.getInterfaceName()); - - // set link addresses - if (response.getAddresses().size() > 0) { - for (LinkAddress la : response.getAddresses()) { - if (!la.getAddress().isAnyLocalAddress()) { - if (DBG) { - log("addr/pl=" + la.getAddress() + "/" - + la.getPrefixLength()); - } - linkProperties.addLinkAddress(la); - } - } - } else { - throw new UnknownHostException("no address for ifname=" - + response.getInterfaceName()); - } - - // set dns servers - if (response.getDnsAddresses().size() > 0) { - for (InetAddress dns : response.getDnsAddresses()) { - if (!dns.isAnyLocalAddress()) { - linkProperties.addDnsServer(dns); - } - } - } else if (okToUseSystemPropertyDns) { - for (String dnsAddr : dnsServers) { - dnsAddr = dnsAddr.trim(); - if (dnsAddr.isEmpty()) continue; - InetAddress ia; - try { - ia = InetAddresses.parseNumericAddress(dnsAddr); - } catch (IllegalArgumentException e) { - throw new UnknownHostException("Non-numeric dns addr=" + dnsAddr); - } - if (!ia.isAnyLocalAddress()) { - linkProperties.addDnsServer(ia); - } - } - } else { - throw new UnknownHostException("Empty dns response and no system default dns"); - } - - // set pcscf - if (response.getPcscfAddresses().size() > 0) { - for (InetAddress pcscf : response.getPcscfAddresses()) { - linkProperties.addPcscfServer(pcscf); - } - } - - for (InetAddress gateway : response.getGatewayAddresses()) { - int mtu = gateway instanceof java.net.Inet6Address ? response.getMtuV6() - : response.getMtuV4(); - // Allow 0.0.0.0 or :: as a gateway; - // this indicates a point-to-point interface. - linkProperties.addRoute(new RouteInfo(null, gateway, null, - RouteInfo.RTN_UNICAST, mtu)); - } - - // set interface MTU - // this may clobber the setting read from the APN db, but that's ok - // TODO: remove once LinkProperties#setMtu is deprecated - linkProperties.setMtu(response.getMtu()); - - result = SetupResult.SUCCESS; - } catch (UnknownHostException e) { - log("setLinkProperties: UnknownHostException " + e); - result = SetupResult.ERROR_INVALID_ARG; - } - } else { - result = SetupResult.ERROR_DATA_SERVICE_SPECIFIC_ERROR; - } - - // An error occurred so clear properties - if (result != SetupResult.SUCCESS) { - if (DBG) { - log("setLinkProperties: error clearing LinkProperties status=" - + response.getCause() + " result=" + result); - } - linkProperties.clear(); - } - - return result; - } - - /** - * Initialize connection, this will fail if the - * apnSettings are not compatible. - * - * @param cp the Connection parameters - * @return true if initialization was successful. - */ - private boolean initConnection(ConnectionParams cp) { - ApnContext apnContext = cp.mApnContext; - if (mApnSetting == null) { - // Only change apn setting if it isn't set, it will - // only NOT be set only if we're in DcInactiveState. - mApnSetting = apnContext.getApnSetting(); - } - if (mApnSetting == null || (!mApnSetting.canHandleType(apnContext.getApnTypeBitmask()) - && apnContext.getApnTypeBitmask() != ApnSetting.TYPE_ENTERPRISE)) { - if (DBG) { - log("initConnection: incompatible apnSetting in ConnectionParams cp=" + cp - + " dc=" + DataConnection.this); - } - return false; - } - mTag += 1; - mConnectionParams = cp; - mConnectionParams.mTag = mTag; - - // always update the ConnectionParams with the latest or the - // connectionGeneration gets stale - mApnContexts.put(apnContext, cp); - - if (DBG) { - log("initConnection: " - + " RefCount=" + mApnContexts.size() - + " mApnList=" + mApnContexts - + " mConnectionParams=" + mConnectionParams); - } - return true; - } - - /** - * The parent state for all other states. - */ - private class DcDefaultState extends State { - @Override - public void enter() { - if (DBG) log("DcDefaultState: enter"); - - // Register for DRS or RAT change - mPhone.getServiceStateTracker().registerForDataRegStateOrRatChanged( - mTransportType, getHandler(), - DataConnection.EVENT_DATA_CONNECTION_DRS_OR_RAT_CHANGED, null); - - mPhone.getServiceStateTracker().registerForDataRoamingOn(getHandler(), - DataConnection.EVENT_DATA_CONNECTION_ROAM_ON, null); - mPhone.getServiceStateTracker().registerForDataRoamingOff(getHandler(), - DataConnection.EVENT_DATA_CONNECTION_ROAM_OFF, null, true); - mPhone.getServiceStateTracker().registerForNrStateChanged(getHandler(), - DataConnection.EVENT_NR_STATE_CHANGED, null); - mPhone.getServiceStateTracker().registerForNrFrequencyChanged(getHandler(), - DataConnection.EVENT_NR_FREQUENCY_CHANGED, null); - mPhone.getServiceStateTracker().registerForCssIndicatorChanged(getHandler(), - DataConnection.EVENT_CSS_INDICATOR_CHANGED, null); - if (isBandwidthSourceKey(DctConstants.BANDWIDTH_SOURCE_BANDWIDTH_ESTIMATOR_KEY)) { - mPhone.getLinkBandwidthEstimator().registerForBandwidthChanged(getHandler(), - DataConnection.EVENT_LINK_BANDWIDTH_ESTIMATOR_UPDATE, null); - } - - // Add ourselves to the list of data connections - mDcController.addDc(DataConnection.this); - } - @Override - public void exit() { - if (DBG) log("DcDefaultState: exit"); - - // Unregister for DRS or RAT change. - mPhone.getServiceStateTracker().unregisterForDataRegStateOrRatChanged( - mTransportType, getHandler()); - - mPhone.getServiceStateTracker().unregisterForDataRoamingOn(getHandler()); - mPhone.getServiceStateTracker().unregisterForDataRoamingOff(getHandler()); - mPhone.getServiceStateTracker().unregisterForNrStateChanged(getHandler()); - mPhone.getServiceStateTracker().unregisterForNrFrequencyChanged(getHandler()); - mPhone.getServiceStateTracker().unregisterForCssIndicatorChanged(getHandler()); - if (isBandwidthSourceKey(DctConstants.BANDWIDTH_SOURCE_BANDWIDTH_ESTIMATOR_KEY)) { - mPhone.getLinkBandwidthEstimator().unregisterForBandwidthChanged(getHandler()); - } - - // Remove ourselves from the DC lists - mDcController.removeDc(DataConnection.this); - - if (mAc != null) { - mAc.disconnected(); - mAc = null; - } - mApnContexts.clear(); - mReconnectIntent = null; - mDct = null; - mApnSetting = null; - mPhone = null; - mDataServiceManager = null; - mLinkProperties = null; - mLastFailCause = DataFailCause.NONE; - mUserData = null; - mDcController = null; - mDcTesterFailBringUpAll = null; - } - - @Override - public boolean processMessage(Message msg) { - boolean retVal = HANDLED; - - if (VDBG) { - log("DcDefault msg=" + getWhatToString(msg.what) - + " RefCount=" + mApnContexts.size()); - } - switch (msg.what) { - case EVENT_RESET: - if (VDBG) log("DcDefaultState: msg.what=REQ_RESET"); - transitionTo(mInactiveState); - break; - case EVENT_CONNECT: - if (DBG) log("DcDefaultState: msg.what=EVENT_CONNECT, fail not expected"); - ConnectionParams cp = (ConnectionParams) msg.obj; - notifyConnectCompleted(cp, DataFailCause.UNKNOWN, - DataCallResponse.HANDOVER_FAILURE_MODE_UNKNOWN, false); - break; - - case EVENT_DISCONNECT: - case EVENT_DISCONNECT_ALL: - case EVENT_REEVALUATE_RESTRICTED_STATE: - if (DBG) { - log("DcDefaultState deferring msg.what=" + getWhatToString(msg.what) - + " RefCount=" + mApnContexts.size()); - } - deferMessage(msg); - break; - case EVENT_TEAR_DOWN_NOW: - if (DBG) log("DcDefaultState EVENT_TEAR_DOWN_NOW"); - mDataServiceManager.deactivateDataCall(mCid, DataService.REQUEST_REASON_NORMAL, - null); - mDataCallSessionStats.setDeactivateDataCallReason( - DataService.REQUEST_REASON_NORMAL); - break; - case EVENT_LOST_CONNECTION: - if (DBG) { - String s = "DcDefaultState ignore EVENT_LOST_CONNECTION" - + " tag=" + msg.arg1 + ":mTag=" + mTag; - logAndAddLogRec(s); - } - break; - case EVENT_DATA_CONNECTION_DRS_OR_RAT_CHANGED: - AsyncResult ar = (AsyncResult)msg.obj; - Pair<Integer, Integer> drsRatPair = (Pair<Integer, Integer>)ar.result; - mDataRegState = drsRatPair.first; - updateTcpBufferSizes(drsRatPair.second); - if (isBandwidthSourceKey(DctConstants.BANDWIDTH_SOURCE_CARRIER_CONFIG_KEY)) { - updateLinkBandwidthsFromCarrierConfig(drsRatPair.second); - } - mRilRat = drsRatPair.second; - if (DBG) { - log("DcDefaultState: EVENT_DATA_CONNECTION_DRS_OR_RAT_CHANGED" - + " regState=" + ServiceState.rilServiceStateToString(mDataRegState) - + " RAT=" + ServiceState.rilRadioTechnologyToString(mRilRat)); - } - mDataCallSessionStats.onDrsOrRatChanged( - ServiceState.rilRadioTechnologyToNetworkType(mRilRat)); - break; - - case EVENT_START_HANDOVER: //calls startHandover() - if (DBG) { - log("DcDefaultState: EVENT_START_HANDOVER not expected."); - } - Consumer<Integer> r = (Consumer<Integer>) msg.obj; - r.accept(DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE); - break; - case EVENT_START_HANDOVER_ON_TARGET: - if (DBG) { - log("DcDefaultState: EVENT_START_HANDOVER not expected, but will " - + "clean up, result code: " - + DataServiceCallback.resultCodeToString(msg.arg1)); - } - ((Consumer<Boolean>) msg.obj).accept(false /* is in correct state*/); - break; - case EVENT_CANCEL_HANDOVER: - // We don't need to do anything in this case - if (DBG) { - log("DcDefaultState: EVENT_CANCEL_HANDOVER resultCode=" - + DataServiceCallback.resultCodeToString(msg.arg1)); - } - break; - case EVENT_RELEASE_PDU_SESSION_ID: { - // We do the same thing in all state in order to preserve the existing workflow - final AsyncResult asyncResult = (AsyncResult) msg.obj; - if (asyncResult == null) { - loge("EVENT_RELEASE_PDU_SESSION_ID: asyncResult is null!"); - } else { - if (msg.obj != null) { - if (DBG) logd("EVENT_RELEASE_PDU_SESSION_ID: id released"); - Runnable runnable = (Runnable) asyncResult.userObj; - runnable.run(); - } else { - loge("EVENT_RELEASE_PDU_SESSION_ID: no runnable set"); - } - } - retVal = HANDLED; - break; - } - case EVENT_ALLOCATE_PDU_SESSION_ID: { - // We do the same thing in all state in order to preserve the existing workflow - final AsyncResult asyncResult = (AsyncResult) msg.obj; - if (asyncResult == null) { - loge("EVENT_ALLOCATE_PDU_SESSION_ID: asyncResult is null!"); - } else { - Consumer<Integer> onAllocated = (Consumer<Integer>) asyncResult.userObj; - if (asyncResult.exception != null) { - loge("EVENT_ALLOCATE_PDU_SESSION_ID: exception", - asyncResult.exception); - onAllocated.accept(PDU_SESSION_ID_NOT_SET); - } else if (asyncResult.result == null) { - loge("EVENT_ALLOCATE_PDU_SESSION_ID: result null, no id"); - onAllocated.accept(PDU_SESSION_ID_NOT_SET); - } else { - int psi = (int) asyncResult.result; - if (DBG) logd("EVENT_ALLOCATE_PDU_SESSION_ID: psi=" + psi); - onAllocated.accept(psi); - } - } - retVal = HANDLED; - break; - } - default: - if (DBG) { - log("DcDefaultState: ignore msg.what=" + getWhatToString(msg.what)); - } - break; - } - - return retVal; - } - } - - private void updateSuspendState() { - if (mNetworkAgent == null) { - Rlog.d(getName(), "Setting suspend state without a NetworkAgent"); - } - - 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 reflecting the latest suspended state. - if (isActive()) { - notifyDataConnectionState(); - } - } - } - - private void notifyDataConnectionState() { - // The receivers of this have no way to differentiate between default and enterprise - // connections. Do not notify for enterprise. - if (!isEnterpriseUse()) { - mPhone.notifyDataConnection(getPreciseDataConnectionState()); - } else { - log("notifyDataConnectionState: Skipping for enterprise; state=" + getState()); - } - } - - private DcDefaultState mDefaultState = new DcDefaultState(); - - private int getApnTypeBitmask() { - return isEnterpriseUse() ? ApnSetting.TYPE_ENTERPRISE : - mApnSetting != null ? mApnSetting.getApnTypeBitmask() : 0; - } - - private boolean canHandleDefault() { - return !isEnterpriseUse() && mApnSetting != null - ? mApnSetting.canHandleType(ApnSetting.TYPE_DEFAULT) : false; - } - - /** - * The state machine is inactive and expects a EVENT_CONNECT. - */ - private class DcInactiveState extends State { - // Inform all contexts we've failed connecting - public void setEnterNotificationParams(ConnectionParams cp, @DataFailureCause int cause, - @HandoverFailureMode int handoverFailureMode) { - if (VDBG) log("DcInactiveState: setEnterNotificationParams cp,cause"); - mConnectionParams = cp; - mDisconnectParams = null; - mDcFailCause = cause; - mHandoverFailureMode = handoverFailureMode; - } - - // Inform all contexts we've failed disconnected - public void setEnterNotificationParams(DisconnectParams dp) { - if (VDBG) log("DcInactiveState: setEnterNotificationParams dp"); - mConnectionParams = null; - mDisconnectParams = dp; - mDcFailCause = DataFailCause.NONE; - } - - // Inform all contexts of the failure cause - public void setEnterNotificationParams(@DataFailureCause int cause) { - mConnectionParams = null; - mDisconnectParams = null; - mDcFailCause = cause; - } - - @Override - public void enter() { - mTag += 1; - if (DBG) log("DcInactiveState: enter() mTag=" + mTag); - TelephonyStatsLog.write(TelephonyStatsLog.MOBILE_CONNECTION_STATE_CHANGED, - TelephonyStatsLog.MOBILE_CONNECTION_STATE_CHANGED__STATE__INACTIVE, - mPhone.getPhoneId(), mId, getApnTypeBitmask(), canHandleDefault()); - mDataCallSessionStats.onDataCallDisconnected(mDcFailCause); - if (mHandoverState == HANDOVER_STATE_BEING_TRANSFERRED) { - // This is from source data connection to set itself's state - setHandoverState(HANDOVER_STATE_COMPLETED); - } - - // Check for dangling agent. Ideally the handover source agent should be null if - // handover process is smooth. When it's not null, that means handover failed. The - // agent was not successfully transferred to the new data connection. We should - // gracefully notify connectivity service the network was disconnected. - if (mHandoverSourceNetworkAgent != null) { - DataConnection sourceDc = mHandoverSourceNetworkAgent.getDataConnection(); - if (sourceDc != null) { - // If the source data connection still owns this agent, then just reset the - // handover state back to idle because handover is already failed. - mHandoverLocalLog.log( - "Handover failed. Reset the source dc " + sourceDc.getName() - + " state to idle"); - sourceDc.cancelHandover(); - } else { - // The agent is now a dangling agent. No data connection owns this agent. - // Gracefully notify connectivity service disconnected. - mHandoverLocalLog.log( - "Handover failed and dangling agent found."); - mHandoverSourceNetworkAgent.acquireOwnership( - DataConnection.this, mTransportType); - log("Cleared dangling network agent. " + mHandoverSourceNetworkAgent); - mHandoverSourceNetworkAgent.unregister(DataConnection.this); - mHandoverSourceNetworkAgent.releaseOwnership(DataConnection.this); - } - mHandoverSourceNetworkAgent = null; - } - - if (mConnectionParams != null) { - if (DBG) { - log("DcInactiveState: enter notifyConnectCompleted +ALL failCause=" - + DataFailCause.toString(mDcFailCause)); - } - notifyConnectCompleted(mConnectionParams, mDcFailCause, mHandoverFailureMode, - true); - } - if (mDisconnectParams != null) { - if (DBG) { - log("DcInactiveState: enter notifyDisconnectCompleted +ALL failCause=" - + DataFailCause.toString(mDcFailCause)); - } - notifyDisconnectCompleted(mDisconnectParams, true); - } - if (mDisconnectParams == null && mConnectionParams == null - && mDcFailCause != DataFailCause.NONE) { - if (DBG) { - log("DcInactiveState: enter notifyAllDisconnectCompleted failCause=" - + DataFailCause.toString(mDcFailCause)); - } - notifyAllWithEvent(null, DctConstants.EVENT_DISCONNECT_DONE, - DataFailCause.toString(mDcFailCause)); - } - - // Remove ourselves from cid mapping, before clearSettings - mDcController.removeActiveDcByCid(DataConnection.this); - - // For the first time entering here (idle state before setup), do not notify - // disconnected state. Only notify data connection disconnected for data that is - // actually moving from disconnecting to disconnected, or setup failed. In both cases, - // APN setting will not be null. - if (mApnSetting != null) { - notifyDataConnectionState(); - } - clearSettings(); - } - - @Override - public void exit() { - } - - @Override - public boolean processMessage(Message msg) { - switch (msg.what) { - case EVENT_RESET: - case EVENT_REEVALUATE_RESTRICTED_STATE: - if (DBG) { - log("DcInactiveState: msg.what=" + getWhatToString(msg.what) - + ", ignore we're already done"); - } - return HANDLED; - case EVENT_CONNECT: - if (DBG) log("DcInactiveState: mag.what=EVENT_CONNECT"); - ConnectionParams cp = (ConnectionParams) msg.obj; - - if (!initConnection(cp)) { - log("DcInactiveState: msg.what=EVENT_CONNECT initConnection failed"); - notifyConnectCompleted(cp, DataFailCause.UNACCEPTABLE_NETWORK_PARAMETER, - DataCallResponse.HANDOVER_FAILURE_MODE_UNKNOWN, false); - transitionTo(mInactiveState); - return HANDLED; - } - - int cause = connect(cp); - if (cause != DataFailCause.NONE) { - log("DcInactiveState: msg.what=EVENT_CONNECT connect failed"); - notifyConnectCompleted(cp, cause, - DataCallResponse.HANDOVER_FAILURE_MODE_UNKNOWN, false); - transitionTo(mInactiveState); - return HANDLED; - } - - if (mSubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) { - mSubId = cp.mSubId; - } - - transitionTo(mActivatingState); - return HANDLED; - case EVENT_DISCONNECT: - if (DBG) log("DcInactiveState: msg.what=EVENT_DISCONNECT"); - notifyDisconnectCompleted((DisconnectParams)msg.obj, false); - return HANDLED; - case EVENT_DISCONNECT_ALL: - if (DBG) log("DcInactiveState: msg.what=EVENT_DISCONNECT_ALL"); - notifyDisconnectCompleted((DisconnectParams)msg.obj, false); - return HANDLED; - default: - if (VDBG) { - log("DcInactiveState not handled msg.what=" + getWhatToString(msg.what)); - } - return NOT_HANDLED; - } - } - } - private DcInactiveState mInactiveState = new DcInactiveState(); - - /** - * The state machine is activating a connection. - */ - private class DcActivatingState extends State { - @Override - public void enter() { - int apnTypeBitmask = getApnTypeBitmask(); - TelephonyStatsLog.write(TelephonyStatsLog.MOBILE_CONNECTION_STATE_CHANGED, - TelephonyStatsLog.MOBILE_CONNECTION_STATE_CHANGED__STATE__ACTIVATING, - mPhone.getPhoneId(), mId, apnTypeBitmask, canHandleDefault()); - setHandoverState(HANDOVER_STATE_IDLE); - // restricted evaluation depends on network requests from apnContext. The evaluation - // should happen once entering connecting state rather than active state because it's - // possible that restricted network request can be released during the connecting window - // and if we wait for connection established, then we might mistakenly - // consider it as un-restricted. ConnectivityService then will immediately - // tear down the connection through networkAgent unwanted callback if all requests for - // this connection are going away. - mRestrictedNetworkOverride = shouldRestrictNetwork(); - - CarrierPrivilegesTracker carrierPrivTracker = mPhone.getCarrierPrivilegesTracker(); - if (carrierPrivTracker != null) { - carrierPrivTracker.registerCarrierPrivilegesListener( - getHandler(), EVENT_CARRIER_PRIVILEGED_UIDS_CHANGED, null); - } - notifyDataConnectionState(); - mDataCallSessionStats.onSetupDataCall(apnTypeBitmask); - } - @Override - public boolean processMessage(Message msg) { - boolean retVal; - AsyncResult ar; - ConnectionParams cp; - - if (DBG) log("DcActivatingState: msg=" + msgToString(msg)); - switch (msg.what) { - case EVENT_DATA_CONNECTION_DRS_OR_RAT_CHANGED: - case EVENT_CONNECT: - // Activating can't process until we're done. - deferMessage(msg); - retVal = HANDLED; - break; - - case EVENT_SETUP_DATA_CONNECTION_DONE: - cp = (ConnectionParams) msg.obj; - - DataCallResponse dataCallResponse = - msg.getData().getParcelable(DataServiceManager.DATA_CALL_RESPONSE); - SetupResult result = onSetupConnectionCompleted(msg.arg1, dataCallResponse, cp); - if (result != SetupResult.ERROR_STALE) { - if (mConnectionParams != cp) { - loge("DcActivatingState: WEIRD mConnectionsParams:"+ mConnectionParams - + " != cp:" + cp); - } - } - if (DBG) { - log("DcActivatingState onSetupConnectionCompleted result=" + result - + " dc=" + DataConnection.this); - } - ApnContext.requestLog( - cp.mApnContext, "onSetupConnectionCompleted result=" + result); - switch (result) { - case SUCCESS: - // All is well - mDcFailCause = DataFailCause.NONE; - transitionTo(mActiveState); - break; - case ERROR_RADIO_NOT_AVAILABLE: - // Vendor ril rejected the command and didn't connect. - // Transition to inactive but send notifications after - // we've entered the mInactive state. - mInactiveState.setEnterNotificationParams(cp, result.mFailCause, - DataCallResponse.HANDOVER_FAILURE_MODE_UNKNOWN); - transitionTo(mInactiveState); - break; - case ERROR_DUPLICATE_CID: - // TODO (b/180988471): Properly handle the case when an existing cid is - // returned by tearing down the network agent if enterprise changed. - long retry = RetryManager.NO_SUGGESTED_RETRY_DELAY; - if (cp.mApnContext != null) { - retry = RetryManager.NO_RETRY; - mDct.getDataThrottler().setRetryTime( - cp.mApnContext.getApnTypeBitmask(), - retry, DcTracker.REQUEST_TYPE_NORMAL); - } - String logStr = "DcActivatingState: " - + DataFailCause.toString(result.mFailCause) - + " retry=" + retry; - if (DBG) log(logStr); - ApnContext.requestLog(cp.mApnContext, logStr); - mInactiveState.setEnterNotificationParams(cp, result.mFailCause, - DataCallResponse.HANDOVER_FAILURE_MODE_UNKNOWN); - transitionTo(mInactiveState); - break; - case ERROR_NO_DEFAULT_CONNECTION: - // TODO (b/180988471): Properly handle the case when a default data - // connection doesn't exist (tear down connection and retry). - // Currently, this just tears down the connection without retry. - if (DBG) log("DcActivatingState: NO_DEFAULT_DATA"); - case ERROR_INVALID_ARG: - // The addresses given from the RIL are bad - tearDownData(cp); - transitionTo(mDisconnectingErrorCreatingConnection); - break; - case ERROR_DATA_SERVICE_SPECIFIC_ERROR: - - // Retrieve the suggested retry delay from the modem and save it. - // If the modem want us to retry the current APN again, it will - // suggest a positive delay value (in milliseconds). Otherwise we'll get - // NO_SUGGESTED_RETRY_DELAY here. - - long delay = getSuggestedRetryDelay(dataCallResponse); - long retryTime = RetryManager.NO_SUGGESTED_RETRY_DELAY; - if (delay == RetryManager.NO_RETRY) { - retryTime = RetryManager.NO_RETRY; - } else if (delay >= 0) { - retryTime = SystemClock.elapsedRealtime() + delay; - } - int newRequestType = DcTracker.calculateNewRetryRequestType( - mHandoverFailureMode, cp.mRequestType, mDcFailCause); - mDct.getDataThrottler().setRetryTime(getApnTypeBitmask(), - retryTime, newRequestType); - - String str = "DcActivatingState: ERROR_DATA_SERVICE_SPECIFIC_ERROR " - + " delay=" + delay - + " result=" + result - + " result.isRadioRestartFailure=" - + DataFailCause.isRadioRestartFailure(mPhone.getContext(), - result.mFailCause, mPhone.getSubId()) - + " isPermanentFailure=" + - mDct.isPermanentFailure(result.mFailCause); - if (DBG) log(str); - ApnContext.requestLog(cp.mApnContext, str); - - // Save the cause. DcTracker.onDataSetupComplete will check this - // failure cause and determine if we need to retry this APN later - // or not. - mInactiveState.setEnterNotificationParams(cp, result.mFailCause, - dataCallResponse != null - ? dataCallResponse.getHandoverFailureMode() - : DataCallResponse.HANDOVER_FAILURE_MODE_UNKNOWN); - transitionTo(mInactiveState); - break; - case ERROR_STALE: - loge("DcActivatingState: stale EVENT_SETUP_DATA_CONNECTION_DONE" - + " tag:" + cp.mTag + " != mTag:" + mTag); - break; - default: - throw new RuntimeException("Unknown SetupResult, should not happen"); - } - retVal = HANDLED; - mDataCallSessionStats - .onSetupDataCallResponse(dataCallResponse, - ServiceState.rilRadioTechnologyToNetworkType(cp.mRilRat), - getApnTypeBitmask(), mApnSetting.getProtocol(), - result.mFailCause); - break; - case EVENT_CARRIER_PRIVILEGED_UIDS_CHANGED: - AsyncResult asyncResult = (AsyncResult) msg.obj; - int[] administratorUids = (int[]) asyncResult.result; - mAdministratorUids = Arrays.copyOf(administratorUids, administratorUids.length); - retVal = HANDLED; - break; - case EVENT_START_HANDOVER_ON_TARGET: - //called after startHandover on target transport - ((Consumer<Boolean>) msg.obj).accept(true /* is in correct state*/); - retVal = HANDLED; - break; - case EVENT_CANCEL_HANDOVER: - transitionTo(mInactiveState); - retVal = HANDLED; - break; - default: - if (VDBG) { - log("DcActivatingState not handled msg.what=" + - getWhatToString(msg.what) + " RefCount=" + mApnContexts.size()); - } - retVal = NOT_HANDLED; - break; - } - return retVal; - } - } - private DcActivatingState mActivatingState = new DcActivatingState(); - - /** - * The state machine is connected, expecting an EVENT_DISCONNECT. - */ - private class DcActiveState extends State { - - @Override public void enter() { - if (DBG) log("DcActiveState: enter dc=" + DataConnection.this); - TelephonyStatsLog.write(TelephonyStatsLog.MOBILE_CONNECTION_STATE_CHANGED, - TelephonyStatsLog.MOBILE_CONNECTION_STATE_CHANGED__STATE__ACTIVE, - mPhone.getPhoneId(), mId, getApnTypeBitmask(), canHandleDefault()); - // If we were retrying there maybe more than one, otherwise they'll only be one. - notifyAllWithEvent(null, DctConstants.EVENT_DATA_SETUP_COMPLETE, - Phone.REASON_CONNECTED); - - mPhone.getCallTracker().registerForVoiceCallStarted(getHandler(), - DataConnection.EVENT_DATA_CONNECTION_VOICE_CALL_STARTED, null); - mPhone.getCallTracker().registerForVoiceCallEnded(getHandler(), - DataConnection.EVENT_DATA_CONNECTION_VOICE_CALL_ENDED, null); - - // If the EVENT_CONNECT set the current max retry restore it here - // if it didn't then this is effectively a NOP. - mDcController.addActiveDcByCid(DataConnection.this); - - updateTcpBufferSizes(mRilRat); - updateLinkBandwidthsFromCarrierConfig(mRilRat); - - final NetworkAgentConfig.Builder configBuilder = new NetworkAgentConfig.Builder(); - configBuilder.setLegacyType(ConnectivityManager.TYPE_MOBILE); - configBuilder.setLegacyTypeName(NETWORK_TYPE); - int networkType = getNetworkType(); - configBuilder.setLegacySubType(networkType); - configBuilder.setLegacySubTypeName(TelephonyManager.getNetworkTypeName(networkType)); - configBuilder.setLegacyExtraInfo(mApnSetting.getApnName()); - final CarrierSignalAgent carrierSignalAgent = mPhone.getCarrierSignalAgent(); - if (carrierSignalAgent.hasRegisteredReceivers(TelephonyManager - .ACTION_CARRIER_SIGNAL_REDIRECTED)) { - // carrierSignal Receivers will place the carrier-specific provisioning notification - configBuilder.setProvisioningNotificationEnabled(false); - } - - final String subscriberId = mPhone.getSubscriberId(); - if (!TextUtils.isEmpty(subscriberId)) { - configBuilder.setSubscriberId(subscriberId); - } - - // set skip464xlat if it is not default otherwise - if (shouldSkip464Xlat()) { - configBuilder.setNat64DetectionEnabled(false); - } - - mUnmeteredUseOnly = isUnmeteredUseOnly(); - mMmsUseOnly = isMmsUseOnly(); - mEnterpriseUse = isEnterpriseUse(); - - if (DBG) { - log("mRestrictedNetworkOverride = " + mRestrictedNetworkOverride - + ", mUnmeteredUseOnly = " + mUnmeteredUseOnly - + ", mMmsUseOnly = " + mMmsUseOnly - + ", mEnterpriseUse = " + mEnterpriseUse); - } - - // Always register a VcnNetworkPolicyChangeListener, regardless of whether this is a - // handover - // or new Network. - mVcnManager.addVcnNetworkPolicyChangeListener( - new HandlerExecutor(getHandler()), mVcnPolicyChangeListener); - - if (mConnectionParams != null - && mConnectionParams.mRequestType == REQUEST_TYPE_HANDOVER) { - // If this is a data setup for handover, we need to reuse the existing network agent - // instead of creating a new one. This should be transparent to connectivity - // service. - DcTracker dcTracker = mPhone.getDcTracker(getHandoverSourceTransport()); - DataConnection dc = dcTracker.getDataConnectionByApnType( - mConnectionParams.mApnContext.getApnType()); - // It's possible that the source data connection has been disconnected by the modem - // already. If not, set its handover state to completed. - if (dc != null) { - // Transfer network agent from the original data connection as soon as the - // new handover data connection is connected. - dc.setHandoverState(HANDOVER_STATE_COMPLETED); - } - - if (mHandoverSourceNetworkAgent != null) { - String logStr = "Transfer network agent " + mHandoverSourceNetworkAgent.getTag() - + " successfully."; - log(logStr); - mHandoverLocalLog.log(logStr); - mNetworkAgent = mHandoverSourceNetworkAgent; - mNetworkAgent.acquireOwnership(DataConnection.this, mTransportType); - - // TODO: Should evaluate mDisabledApnTypeBitMask again after handover. We don't - // do it now because connectivity service does not support dynamically removing - // immutable capabilities. - - mNetworkAgent.updateLegacySubtype(DataConnection.this); - // Update the capability after handover - mNetworkAgent.sendNetworkCapabilities(getNetworkCapabilities(), - DataConnection.this); - mNetworkAgent.sendLinkProperties(mLinkProperties, DataConnection.this); - mHandoverSourceNetworkAgent = null; - } else { - String logStr = "Failed to get network agent from original data connection"; - loge(logStr); - mHandoverLocalLog.log(logStr); - return; - } - } else { - mScore = calculateScore(); - final NetworkFactory factory = PhoneFactory.getNetworkFactory( - mPhone.getPhoneId()); - final NetworkProvider provider = (null == factory) ? null : factory.getProvider(); - - mDisabledApnTypeBitMask |= getDisallowedApnTypes(); - updateLinkPropertiesHttpProxy(); - mNetworkAgent = new DcNetworkAgent(DataConnection.this, mPhone, mScore, - configBuilder.build(), provider, mTransportType); - - VcnNetworkPolicyResult policyResult = - mVcnManager.applyVcnNetworkPolicy( - getNetworkCapabilities(), getLinkProperties()); - if (policyResult.isTeardownRequested()) { - tearDownAll( - Phone.REASON_VCN_REQUESTED_TEARDOWN, - DcTracker.RELEASE_TYPE_DETACH, - null /* onCompletedMsg */); - } else { - // All network agents start out in CONNECTING mode, but DcNetworkAgents are - // created when the network is already connected. Hence, send the connected - // notification immediately. - mNetworkAgent.markConnected(); - } - - // The network agent is always created with NOT_SUSPENDED capability, but the - // network might be already out of service (or voice call is ongoing) just right - // before data connection is created. Connectivity service would not allow a network - // created with suspended state, so we create a non-suspended network first, and - // then immediately evaluate the suspended state. - sendMessage(obtainMessage(EVENT_UPDATE_SUSPENDED_STATE)); - } - - // The qos parameters are set when the call is connected - syncQosToNetworkAgent(); - - if (mTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) { - mPhone.mCi.registerForNattKeepaliveStatus( - getHandler(), DataConnection.EVENT_KEEPALIVE_STATUS, null); - mPhone.mCi.registerForLceInfo( - getHandler(), DataConnection.EVENT_LINK_CAPACITY_CHANGED, null); - } - notifyDataConnectionState(); - TelephonyMetrics.getInstance().writeRilDataCallEvent(mPhone.getPhoneId(), - mCid, getApnTypeBitmask(), RilDataCall.State.CONNECTED); - } - - @Override - public void exit() { - if (DBG) log("DcActiveState: exit dc=" + this); - mPhone.getCallTracker().unregisterForVoiceCallStarted(getHandler()); - mPhone.getCallTracker().unregisterForVoiceCallEnded(getHandler()); - - if (mTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) { - mPhone.mCi.unregisterForNattKeepaliveStatus(getHandler()); - mPhone.mCi.unregisterForLceInfo(getHandler()); - } - - // If we are still owning this agent, then we should inform connectivity service the - // data connection is disconnected. There is one exception that we shouldn't unregister, - // which is when IWLAN handover is ongoing. Instead of unregistering, the agent will - // be transferred to the new data connection on the other transport. - if (mNetworkAgent != null) { - syncQosToNetworkAgent(); - if (mHandoverState == HANDOVER_STATE_IDLE) { - mNetworkAgent.unregister(DataConnection.this); - } - mNetworkAgent.releaseOwnership(DataConnection.this); - } - mNetworkAgent = null; - - TelephonyMetrics.getInstance().writeRilDataCallEvent(mPhone.getPhoneId(), - mCid, getApnTypeBitmask(), RilDataCall.State.DISCONNECTED); - - mVcnManager.removeVcnNetworkPolicyChangeListener(mVcnPolicyChangeListener); - - CarrierPrivilegesTracker carrierPrivTracker = mPhone.getCarrierPrivilegesTracker(); - if (carrierPrivTracker != null) { - carrierPrivTracker.unregisterCarrierPrivilegesListener(getHandler()); - } - } - - @Override - public boolean processMessage(Message msg) { - boolean retVal; - - switch (msg.what) { - case EVENT_CONNECT: { - ConnectionParams cp = (ConnectionParams) msg.obj; - // either add this new apn context to our set or - // update the existing cp with the latest connection generation number - mApnContexts.put(cp.mApnContext, cp); - // TODO (b/118347948): evaluate if it's still needed after assigning - // different scores to different Cellular network. - mDisabledApnTypeBitMask &= ~cp.mApnContext.getApnTypeBitmask(); - mNetworkAgent.sendNetworkCapabilities(getNetworkCapabilities(), - DataConnection.this); - if (DBG) { - log("DcActiveState: EVENT_CONNECT cp=" + cp + " dc=" + DataConnection.this); - } - notifyConnectCompleted(cp, DataFailCause.NONE, - DataCallResponse.HANDOVER_FAILURE_MODE_UNKNOWN, false); - retVal = HANDLED; - break; - } - case EVENT_DISCONNECT: { - DisconnectParams dp = (DisconnectParams) msg.obj; - if (DBG) { - log("DcActiveState: EVENT_DISCONNECT dp=" + dp - + " dc=" + DataConnection.this); - } - if (mApnContexts.containsKey(dp.mApnContext)) { - if (DBG) { - log("DcActiveState msg.what=EVENT_DISCONNECT RefCount=" - + mApnContexts.size()); - } - - if (mApnContexts.size() == 1) { - mApnContexts.clear(); - mDisconnectParams = dp; - mConnectionParams = null; - dp.mTag = mTag; - tearDownData(dp); - transitionTo(mDisconnectingState); - } else { - mApnContexts.remove(dp.mApnContext); - // TODO (b/118347948): evaluate if it's still needed after assigning - // different scores to different Cellular network. - mDisabledApnTypeBitMask |= dp.mApnContext.getApnTypeBitmask(); - mNetworkAgent.sendNetworkCapabilities(getNetworkCapabilities(), - DataConnection.this); - notifyDisconnectCompleted(dp, false); - } - } else { - log("DcActiveState ERROR no such apnContext=" + dp.mApnContext - + " in this dc=" + DataConnection.this); - notifyDisconnectCompleted(dp, false); - } - retVal = HANDLED; - break; - } - case EVENT_DISCONNECT_ALL: { - if (DBG) { - log("DcActiveState EVENT_DISCONNECT clearing apn contexts," - + " dc=" + DataConnection.this); - } - DisconnectParams dp = (DisconnectParams) msg.obj; - mDisconnectParams = dp; - mConnectionParams = null; - dp.mTag = mTag; - tearDownData(dp); - transitionTo(mDisconnectingState); - retVal = HANDLED; - break; - } - case EVENT_LOST_CONNECTION: { - if (DBG) { - log("DcActiveState EVENT_LOST_CONNECTION dc=" + DataConnection.this); - } - - mInactiveState.setEnterNotificationParams(DataFailCause.LOST_CONNECTION); - transitionTo(mInactiveState); - retVal = HANDLED; - break; - } - case EVENT_DATA_CONNECTION_DRS_OR_RAT_CHANGED: { - AsyncResult ar = (AsyncResult) msg.obj; - Pair<Integer, Integer> drsRatPair = (Pair<Integer, Integer>) ar.result; - mDataRegState = drsRatPair.first; - updateTcpBufferSizes(drsRatPair.second); - if (isBandwidthSourceKey(DctConstants.BANDWIDTH_SOURCE_CARRIER_CONFIG_KEY)) { - updateLinkBandwidthsFromCarrierConfig(drsRatPair.second); - } - mRilRat = drsRatPair.second; - if (DBG) { - log("DcActiveState: EVENT_DATA_CONNECTION_DRS_OR_RAT_CHANGED" - + " drs=" + mDataRegState - + " mRilRat=" + mRilRat); - } - updateSuspendState(); - if (mNetworkAgent != null) { - mNetworkAgent.updateLegacySubtype(DataConnection.this); - // The new suspended state will be passed through connectivity service - // through NET_CAPABILITY_NOT_SUSPENDED. - mNetworkAgent.sendNetworkCapabilities(getNetworkCapabilities(), - DataConnection.this); - mNetworkAgent.sendLinkProperties(mLinkProperties, DataConnection.this); - } - retVal = HANDLED; - mDataCallSessionStats.onDrsOrRatChanged( - ServiceState.rilRadioTechnologyToNetworkType(mRilRat)); - break; - } - case EVENT_NR_FREQUENCY_CHANGED: - // fallthrough - case EVENT_CARRIER_CONFIG_LINK_BANDWIDTHS_CHANGED: - if (isBandwidthSourceKey(DctConstants.BANDWIDTH_SOURCE_CARRIER_CONFIG_KEY)) { - updateLinkBandwidthsFromCarrierConfig(mRilRat); - } - if (mNetworkAgent != null) { - mNetworkAgent.sendNetworkCapabilities(getNetworkCapabilities(), - DataConnection.this); - } - retVal = HANDLED; - break; - case EVENT_DATA_CONNECTION_METEREDNESS_CHANGED: - boolean isUnmetered = (boolean) msg.obj; - if (isUnmetered == mUnmeteredOverride) { - retVal = HANDLED; - break; - } - mUnmeteredOverride = isUnmetered; - if (mNetworkAgent != null) { - mNetworkAgent.updateLegacySubtype(DataConnection.this); - mNetworkAgent.sendNetworkCapabilities(getNetworkCapabilities(), - DataConnection.this); - } - retVal = HANDLED; - break; - case EVENT_DATA_CONNECTION_CONGESTEDNESS_CHANGED: - boolean isCongested = (boolean) msg.obj; - if (isCongested == mCongestedOverride) { - retVal = HANDLED; - break; - } - mCongestedOverride = isCongested; - if (mNetworkAgent != null) { - mNetworkAgent.updateLegacySubtype(DataConnection.this); - mNetworkAgent.sendNetworkCapabilities(getNetworkCapabilities(), - DataConnection.this); - } - retVal = HANDLED; - break; - case EVENT_DATA_CONNECTION_ROAM_ON: - case EVENT_DATA_CONNECTION_ROAM_OFF: { - if (mNetworkAgent != null) { - mNetworkAgent.updateLegacySubtype(DataConnection.this); - mNetworkAgent.sendNetworkCapabilities(getNetworkCapabilities(), - DataConnection.this); - } - retVal = HANDLED; - break; - } - case EVENT_DATA_CONNECTION_VOICE_CALL_STARTED: - case EVENT_DATA_CONNECTION_VOICE_CALL_ENDED: - case EVENT_CSS_INDICATOR_CHANGED: - case EVENT_UPDATE_SUSPENDED_STATE: { - updateSuspendState(); - if (mNetworkAgent != null) { - mNetworkAgent.sendNetworkCapabilities(getNetworkCapabilities(), - DataConnection.this); - } - retVal = HANDLED; - break; - } - case EVENT_BW_REFRESH_RESPONSE: { - AsyncResult ar = (AsyncResult)msg.obj; - if (ar.exception != null) { - log("EVENT_BW_REFRESH_RESPONSE: error ignoring, e=" + ar.exception); - } else { - if (isBandwidthSourceKey(DctConstants.BANDWIDTH_SOURCE_MODEM_KEY)) { - updateLinkBandwidthsFromModem((List<LinkCapacityEstimate>) ar.result); - } - } - retVal = HANDLED; - break; - } - case EVENT_KEEPALIVE_START_REQUEST: { - KeepalivePacketData pkt = (KeepalivePacketData) msg.obj; - int slotId = msg.arg1; - int intervalMillis = msg.arg2 * 1000; - if (mTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) { - mPhone.mCi.startNattKeepalive( - DataConnection.this.mCid, pkt, intervalMillis, - DataConnection.this.obtainMessage( - EVENT_KEEPALIVE_STARTED, slotId, 0, null)); - } else { - // We currently do not support NATT Keepalive requests using the - // DataService API, so unless the request is WWAN (always bound via - // the CommandsInterface), the request cannot be honored. - // - // TODO: b/72331356 to add support for Keepalive to the DataService - // so that keepalive requests can be handled (if supported) by the - // underlying transport. - if (mNetworkAgent != null) { - mNetworkAgent.sendSocketKeepaliveEvent( - msg.arg1, SocketKeepalive.ERROR_INVALID_NETWORK); - } - } - retVal = HANDLED; - break; - } - case EVENT_KEEPALIVE_STOP_REQUEST: { - int slotId = msg.arg1; - int handle = mNetworkAgent.keepaliveTracker.getHandleForSlot(slotId); - if (handle < 0) { - loge("No slot found for stopSocketKeepalive! " + slotId); - mNetworkAgent.sendSocketKeepaliveEvent( - slotId, SocketKeepalive.ERROR_NO_SUCH_SLOT); - retVal = HANDLED; - break; - } else { - logd("Stopping keepalive with handle: " + handle); - } - - mPhone.mCi.stopNattKeepalive( - handle, DataConnection.this.obtainMessage( - EVENT_KEEPALIVE_STOPPED, handle, slotId, null)); - retVal = HANDLED; - break; - } - case EVENT_KEEPALIVE_STARTED: { - AsyncResult ar = (AsyncResult) msg.obj; - final int slot = msg.arg1; - if (ar.exception != null || ar.result == null) { - loge("EVENT_KEEPALIVE_STARTED: error starting keepalive, e=" - + ar.exception); - mNetworkAgent.sendSocketKeepaliveEvent( - slot, SocketKeepalive.ERROR_HARDWARE_ERROR); - } else { - KeepaliveStatus ks = (KeepaliveStatus) ar.result; - if (ks == null) { - loge("Null KeepaliveStatus received!"); - } else { - mNetworkAgent.keepaliveTracker.handleKeepaliveStarted(slot, ks); - } - } - retVal = HANDLED; - break; - } - case EVENT_KEEPALIVE_STATUS: { - AsyncResult ar = (AsyncResult) msg.obj; - if (ar.exception != null) { - loge("EVENT_KEEPALIVE_STATUS: error in keepalive, e=" + ar.exception); - // We have no way to notify connectivity in this case. - } - if (ar.result != null) { - KeepaliveStatus ks = (KeepaliveStatus) ar.result; - mNetworkAgent.keepaliveTracker.handleKeepaliveStatus(ks); - } - - retVal = HANDLED; - break; - } - case EVENT_KEEPALIVE_STOPPED: { - AsyncResult ar = (AsyncResult) msg.obj; - final int handle = msg.arg1; - final int slotId = msg.arg2; - - if (ar.exception != null) { - loge("EVENT_KEEPALIVE_STOPPED: error stopping keepalive for handle=" - + handle + " e=" + ar.exception); - mNetworkAgent.keepaliveTracker.handleKeepaliveStatus( - new KeepaliveStatus(KeepaliveStatus.ERROR_UNKNOWN)); - } else { - log("Keepalive Stop Requested for handle=" + handle); - mNetworkAgent.keepaliveTracker.handleKeepaliveStatus( - new KeepaliveStatus( - handle, KeepaliveStatus.STATUS_INACTIVE)); - } - retVal = HANDLED; - break; - } - case EVENT_LINK_CAPACITY_CHANGED: { - AsyncResult ar = (AsyncResult) msg.obj; - if (ar.exception != null) { - loge("EVENT_LINK_CAPACITY_CHANGED e=" + ar.exception); - } else { - if (isBandwidthSourceKey(DctConstants.BANDWIDTH_SOURCE_MODEM_KEY)) { - updateLinkBandwidthsFromModem((List<LinkCapacityEstimate>) ar.result); - } - } - retVal = HANDLED; - break; - } - case EVENT_LINK_BANDWIDTH_ESTIMATOR_UPDATE: { - AsyncResult ar = (AsyncResult) msg.obj; - if (ar.exception != null) { - loge("EVENT_LINK_BANDWIDTH_ESTIMATOR_UPDATE e=" + ar.exception); - } else { - Pair<Integer, Integer> pair = (Pair<Integer, Integer>) ar.result; - if (isBandwidthSourceKey( - DctConstants.BANDWIDTH_SOURCE_BANDWIDTH_ESTIMATOR_KEY)) { - updateLinkBandwidthsFromBandwidthEstimator(pair.first, pair.second); - } - } - retVal = HANDLED; - break; - } - case EVENT_REEVALUATE_RESTRICTED_STATE: { - // If the network was restricted, and now it does not need to be restricted - // anymore, we should add the NET_CAPABILITY_NOT_RESTRICTED capability. - if (mRestrictedNetworkOverride && !shouldRestrictNetwork()) { - if (DBG) { - log("Data connection becomes not-restricted. dc=" + this); - } - // Note we only do this when network becomes non-restricted. When a - // non-restricted becomes restricted (e.g. users disable data, or turn off - // data roaming), DCT will explicitly tear down the networks (because - // connectivity service does not support force-close TCP connections today). - // Also note that NET_CAPABILITY_NOT_RESTRICTED is an immutable capability - // (see {@link NetworkCapabilities}) once we add it to the network, we can't - // remove it through the entire life cycle of the connection. - mRestrictedNetworkOverride = false; - mNetworkAgent.sendNetworkCapabilities(getNetworkCapabilities(), - DataConnection.this); - } - - // If the data does need to be unmetered use only (e.g. users turn on data, or - // device is not roaming anymore assuming data roaming is off), then we can - // dynamically add those metered APN type capabilities back. (But not the - // other way around because most of the APN-type capabilities are immutable - // capabilities.) - if (mUnmeteredUseOnly && !isUnmeteredUseOnly()) { - mUnmeteredUseOnly = false; - mNetworkAgent.sendNetworkCapabilities(getNetworkCapabilities(), - DataConnection.this); - } - - mMmsUseOnly = isMmsUseOnly(); - - retVal = HANDLED; - break; - } - case EVENT_REEVALUATE_DATA_CONNECTION_PROPERTIES: { - // Update other properties like link properties if needed in future. - updateScore(); - retVal = HANDLED; - break; - } - case EVENT_NR_STATE_CHANGED: { - updateTcpBufferSizes(mRilRat); - if (isBandwidthSourceKey(DctConstants.BANDWIDTH_SOURCE_CARRIER_CONFIG_KEY)) { - updateLinkBandwidthsFromCarrierConfig(mRilRat); - } - if (mNetworkAgent != null) { - mNetworkAgent.sendLinkProperties(mLinkProperties, DataConnection.this); - mNetworkAgent.sendNetworkCapabilities(getNetworkCapabilities(), - DataConnection.this); - } - retVal = HANDLED; - break; - } - case EVENT_CARRIER_PRIVILEGED_UIDS_CHANGED: - AsyncResult asyncResult = (AsyncResult) msg.obj; - int[] administratorUids = (int[]) asyncResult.result; - mAdministratorUids = Arrays.copyOf(administratorUids, administratorUids.length); - - // Administrator UIDs changed, so update NetworkAgent with new - // NetworkCapabilities - if (mNetworkAgent != null) { - mNetworkAgent.sendNetworkCapabilities( - getNetworkCapabilities(), DataConnection.this); - } - retVal = HANDLED; - break; - case EVENT_START_HANDOVER: //calls startHandover() - Consumer<Integer> r = (Consumer<Integer>) msg.obj; - r.accept(msg.arg1); - retVal = HANDLED; - break; - - default: - if (VDBG) { - log("DcActiveState not handled msg.what=" + getWhatToString(msg.what)); - } - retVal = NOT_HANDLED; - break; - } - return retVal; - } - } - private DcActiveState mActiveState = new DcActiveState(); - - /** - * The state machine is disconnecting. - */ - private class DcDisconnectingState extends State { - @Override - public void enter() { - TelephonyStatsLog.write(TelephonyStatsLog.MOBILE_CONNECTION_STATE_CHANGED, - TelephonyStatsLog.MOBILE_CONNECTION_STATE_CHANGED__STATE__DISCONNECTING, - mPhone.getPhoneId(), mId, getApnTypeBitmask(), canHandleDefault()); - notifyDataConnectionState(); - } - @Override - public boolean processMessage(Message msg) { - boolean retVal; - - switch (msg.what) { - case EVENT_CONNECT: - if (DBG) log("DcDisconnectingState msg.what=EVENT_CONNECT. Defer. RefCount = " - + mApnContexts.size()); - deferMessage(msg); - retVal = HANDLED; - break; - - case EVENT_DEACTIVATE_DONE: - DisconnectParams dp = (DisconnectParams) msg.obj; - - String str = "DcDisconnectingState msg.what=EVENT_DEACTIVATE_DONE RefCount=" - + mApnContexts.size(); - - if (DBG) log(str); - ApnContext.requestLog(dp.mApnContext, str); - - // Clear out existing qos sessions - updateQosParameters(null); - - if (dp.mTag == mTag) { - // Transition to inactive but send notifications after - // we've entered the mInactive state. - mInactiveState.setEnterNotificationParams(dp); - transitionTo(mInactiveState); - } else { - if (DBG) log("DcDisconnectState stale EVENT_DEACTIVATE_DONE" - + " dp.tag=" + dp.mTag + " mTag=" + mTag); - } - retVal = HANDLED; - break; - - default: - if (VDBG) { - log("DcDisconnectingState not handled msg.what=" - + getWhatToString(msg.what)); - } - retVal = NOT_HANDLED; - break; - } - return retVal; - } - } - private DcDisconnectingState mDisconnectingState = new DcDisconnectingState(); - - /** - * The state machine is disconnecting after an creating a connection. - */ - private class DcDisconnectionErrorCreatingConnection extends State { - @Override - public void enter() { - TelephonyStatsLog.write(TelephonyStatsLog.MOBILE_CONNECTION_STATE_CHANGED, - TelephonyStatsLog - .MOBILE_CONNECTION_STATE_CHANGED__STATE__DISCONNECTION_ERROR_CREATING_CONNECTION, - mPhone.getPhoneId(), mId, getApnTypeBitmask(), canHandleDefault()); - notifyDataConnectionState(); - } - @Override - public boolean processMessage(Message msg) { - boolean retVal; - - switch (msg.what) { - case EVENT_DEACTIVATE_DONE: - ConnectionParams cp = (ConnectionParams) msg.obj; - if (cp.mTag == mTag) { - String str = "DcDisconnectionErrorCreatingConnection" + - " msg.what=EVENT_DEACTIVATE_DONE"; - if (DBG) log(str); - ApnContext.requestLog(cp.mApnContext, str); - - // Transition to inactive but send notifications after - // we've entered the mInactive state. - mInactiveState.setEnterNotificationParams(cp, - DataFailCause.UNACCEPTABLE_NETWORK_PARAMETER, - DataCallResponse.HANDOVER_FAILURE_MODE_UNKNOWN); - transitionTo(mInactiveState); - } else { - if (DBG) { - log("DcDisconnectionErrorCreatingConnection stale EVENT_DEACTIVATE_DONE" - + " dp.tag=" + cp.mTag + ", mTag=" + mTag); - } - } - retVal = HANDLED; - break; - - default: - if (VDBG) { - log("DcDisconnectionErrorCreatingConnection not handled msg.what=" - + getWhatToString(msg.what)); - } - retVal = NOT_HANDLED; - break; - } - return retVal; - } - } - private DcDisconnectionErrorCreatingConnection mDisconnectingErrorCreatingConnection = - new DcDisconnectionErrorCreatingConnection(); - - /** - * Bring up a connection to the apn and return an AsyncResult in onCompletedMsg. - * Used for cellular networks that use Access Point Names (APN) such - * as GSM networks. - * - * @param apnContext is the Access Point Name to bring up a connection to - * @param profileId for the connection - * @param rilRadioTechnology Radio technology for the data connection - * @param onCompletedMsg is sent with its msg.obj as an AsyncResult object. - * With AsyncResult.userObj set to the original msg.obj, - * AsyncResult.result = FailCause and AsyncResult.exception = Exception(). - * @param connectionGeneration used to track a single connection request so disconnects can get - * ignored if obsolete. - * @param requestType Data request type - * @param subId the subscription id associated with this data connection. - * @param isApnPreferred whether or not the apn is preferred. - */ - public void bringUp(ApnContext apnContext, int profileId, int rilRadioTechnology, - Message onCompletedMsg, int connectionGeneration, - @RequestNetworkType int requestType, int subId, boolean isApnPreferred) { - if (DBG) { - log("bringUp: apnContext=" + apnContext + " onCompletedMsg=" + onCompletedMsg); - } - - if (mApnSetting == null) { - mApnSetting = apnContext.getApnSetting(); - } - - sendMessage(DataConnection.EVENT_CONNECT, - new ConnectionParams(apnContext, profileId, rilRadioTechnology, onCompletedMsg, - connectionGeneration, requestType, subId, isApnPreferred)); - } - - /** - * Tear down the connection through the apn on the network. - * - * @param apnContext APN context - * @param reason reason to tear down - * @param onCompletedMsg is sent with its msg.obj as an AsyncResult object. - * With AsyncResult.userObj set to the original msg.obj. - */ - public void tearDown(ApnContext apnContext, String reason, Message onCompletedMsg) { - if (DBG) { - log("tearDown: apnContext=" + apnContext + " reason=" + reason + " onCompletedMsg=" - + onCompletedMsg); - } - sendMessage(DataConnection.EVENT_DISCONNECT, - new DisconnectParams(apnContext, reason, DcTracker.RELEASE_TYPE_DETACH, - onCompletedMsg)); - } - - // ******* "public" interface - - /** - * Used for testing purposes. - */ - void tearDownNow() { - if (DBG) log("tearDownNow()"); - sendMessage(obtainMessage(EVENT_TEAR_DOWN_NOW)); - } - - /** - * Tear down the connection through the apn on the network. Ignores reference count and - * and always tears down. - * - * @param releaseType Data release type - * @param onCompletedMsg is sent with its msg.obj as an AsyncResult object. - * With AsyncResult.userObj set to the original msg.obj. - */ - public void tearDownAll(String reason, @ReleaseNetworkType int releaseType, - Message onCompletedMsg) { - if (DBG) { - log("tearDownAll: reason=" + reason + ", releaseType=" - + DcTracker.releaseTypeToString(releaseType)); - } - sendMessage(DataConnection.EVENT_DISCONNECT_ALL, - new DisconnectParams(null, reason, releaseType, onCompletedMsg)); - } - - /** - * Reset the data connection to inactive state. - */ - public void reset() { - sendMessage(EVENT_RESET); - if (DBG) log("reset"); - } - - /** - * Re-evaluate the restricted state. If the restricted data connection does not need to be - * restricted anymore, we need to dynamically change the network's capability. - */ - void reevaluateRestrictedState() { - sendMessage(EVENT_REEVALUATE_RESTRICTED_STATE); - if (DBG) log("reevaluate restricted state"); - } - - /** - * Re-evaluate the data connection properties. For example, it will recalculate data connection - * score and update through network agent it if changed. - */ - void reevaluateDataConnectionProperties() { - sendMessage(EVENT_REEVALUATE_DATA_CONNECTION_PROPERTIES); - if (DBG) log("reevaluate data connection properties"); - } - - /** - * @return The parameters used for initiating a data connection. - */ - public ConnectionParams getConnectionParams() { - return mConnectionParams; - } - - /** - * Update PCSCF addresses - * - * @param response - */ - public void updatePcscfAddr(DataCallResponse response) { - mPcscfAddr = response.getPcscfAddresses().stream() - .map(InetAddress::getHostAddress).toArray(String[]::new); - } - - /** - * @return The list of PCSCF addresses - */ - public String[] getPcscfAddresses() { - return mPcscfAddr; - } - - /** - * Using the result of the SETUP_DATA_CALL determine the retry delay. - * - * @param response The response from setup data call - * @return {@link RetryManager#NO_SUGGESTED_RETRY_DELAY} if not suggested. - * {@link RetryManager#NO_RETRY} if retry should not happen. Otherwise the delay in milliseconds - * to the next SETUP_DATA_CALL. - */ - private long getSuggestedRetryDelay(DataCallResponse response) { - /** According to ril.h - * The value < 0 means no value is suggested - * The value 0 means retry should be done ASAP. - * The value of Long.MAX_VALUE(0x7fffffffffffffff) means no retry. - */ - if (response == null) { - return RetryManager.NO_SUGGESTED_RETRY_DELAY; - } - - long suggestedRetryTime = response.getRetryDurationMillis(); - - // The value < 0 means no value is suggested - if (suggestedRetryTime < 0) { - if (DBG) log("No suggested retry delay."); - return RetryManager.NO_SUGGESTED_RETRY_DELAY; - } else if (mPhone.getHalVersion().greaterOrEqual(RIL.RADIO_HAL_VERSION_1_6) - && suggestedRetryTime == Long.MAX_VALUE) { - if (DBG) log("Network suggested not retrying."); - return RetryManager.NO_RETRY; - } else if (mPhone.getHalVersion().less(RIL.RADIO_HAL_VERSION_1_6) - && suggestedRetryTime == Integer.MAX_VALUE) { - if (DBG) log("Network suggested not retrying."); - return RetryManager.NO_RETRY; - } - - return suggestedRetryTime; - } - - public List<ApnContext> getApnContexts() { - return new ArrayList<>(mApnContexts.keySet()); - } - - /** - * Return whether there is an ApnContext for the given type in this DataConnection. - * @param type APN type to check - * @param exclusive true if the given APN type should be the only APN type that exists - * @return True if there is an ApnContext for the given type - */ - private boolean isApnContextAttached(@ApnType int type, boolean exclusive) { - boolean attached = mApnContexts.keySet().stream() - .map(ApnContext::getApnTypeBitmask) - .anyMatch(bitmask -> bitmask == type); - if (exclusive) { - attached &= mApnContexts.size() == 1; - } - return attached; - } - - /** Get the network agent of the data connection */ - @Nullable - DcNetworkAgent getNetworkAgent() { - return mNetworkAgent; - } - - void setHandoverState(@HandoverState int state) { - if (mHandoverState != state) { - String logStr = "State changed from " + handoverStateToString(mHandoverState) - + " to " + handoverStateToString(state); - mHandoverLocalLog.log(logStr); - logd(logStr); - mHandoverState = state; - } - } - - /** Sets the {@link DataCallSessionStats} mock for this data connection during unit testing. */ - @VisibleForTesting - public void setDataCallSessionStats(DataCallSessionStats dataCallSessionStats) { - mDataCallSessionStats = dataCallSessionStats; - } - - /** - * @return the string for msg.what as our info. - */ - @Override - protected String getWhatToString(int what) { - return cmdToString(what); - } - - private static String msgToString(Message msg) { - String retVal; - if (msg == null) { - retVal = "null"; - } else { - StringBuilder b = new StringBuilder(); - - b.append("{what="); - b.append(cmdToString(msg.what)); - - b.append(" when="); - TimeUtils.formatDuration(msg.getWhen() - SystemClock.uptimeMillis(), b); - - if (msg.arg1 != 0) { - b.append(" arg1="); - b.append(msg.arg1); - } - - if (msg.arg2 != 0) { - b.append(" arg2="); - b.append(msg.arg2); - } - - if (msg.obj != null) { - b.append(" obj="); - b.append(msg.obj); - } - - b.append(" target="); - b.append(msg.getTarget()); - - b.append(" replyTo="); - b.append(msg.replyTo); - - b.append("}"); - - retVal = b.toString(); - } - return retVal; - } - - static void slog(String s) { - Rlog.d("DC", s); - } - - /** - * Log with debug - * - * @param s is string log - */ - @Override - protected void log(String s) { - Rlog.d(getName(), s); - } - - /** - * Log with debug attribute - * - * @param s is string log - */ - @Override - protected void logd(String s) { - Rlog.d(getName(), s); - } - - /** - * Log with verbose attribute - * - * @param s is string log - */ - @Override - protected void logv(String s) { - Rlog.v(getName(), s); - } - - /** - * Log with info attribute - * - * @param s is string log - */ - @Override - protected void logi(String s) { - Rlog.i(getName(), s); - } - - /** - * Log with warning attribute - * - * @param s is string log - */ - @Override - protected void logw(String s) { - Rlog.w(getName(), s); - } - - /** - * Log with error attribute - * - * @param s is string log - */ - @Override - protected void loge(String s) { - Rlog.e(getName(), s); - } - - /** - * Log with error attribute - * - * @param s is string log - * @param e is a Throwable which logs additional information. - */ - @Override - protected void loge(String s, Throwable e) { - Rlog.e(getName(), s, e); - } - - /** Doesn't print mApnList of ApnContext's which would be recursive */ - public synchronized String toStringSimple() { - return getName() + ": State=" + getCurrentState().getName() - + " mApnSetting=" + mApnSetting + " RefCount=" + mApnContexts.size() - + " mCid=" + mCid + " mCreateTime=" + mCreateTime - + " mLastastFailTime=" + mLastFailTime - + " mLastFailCause=" + DataFailCause.toString(mLastFailCause) - + " mTag=" + mTag - + " mLinkProperties=" + mLinkProperties - + " linkCapabilities=" + getNetworkCapabilities() - + " mRestrictedNetworkOverride=" + mRestrictedNetworkOverride; - } - - @Override - public String toString() { - return "{" + toStringSimple() + " mApnContexts=" + mApnContexts + "}"; - } - - /** Check if the device is connected to NR 5G Non-Standalone network. */ - private boolean isNRConnected() { - return mPhone.getServiceState().getNrState() - == NetworkRegistrationInfo.NR_STATE_CONNECTED; - } - - /** - * @return The disallowed APN types bitmask - */ - private @ApnType int getDisallowedApnTypes() { - CarrierConfigManager configManager = (CarrierConfigManager) - mPhone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE); - int apnTypesBitmask = 0; - if (configManager != null) { - PersistableBundle bundle = configManager.getConfigForSubId(mSubId); - if (bundle != null) { - String key = (mTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) - ? CarrierConfigManager.KEY_CARRIER_WWAN_DISALLOWED_APN_TYPES_STRING_ARRAY - : CarrierConfigManager.KEY_CARRIER_WLAN_DISALLOWED_APN_TYPES_STRING_ARRAY; - if (bundle.getStringArray(key) != null) { - String disallowedApnTypesString = - TextUtils.join(",", bundle.getStringArray(key)); - if (!TextUtils.isEmpty(disallowedApnTypesString)) { - apnTypesBitmask = ApnSetting.getApnTypesBitmaskFromString( - disallowedApnTypesString); - } - } - } - } - - return apnTypesBitmask; - } - - private void dumpToLog() { - dump(null, new PrintWriter(new StringWriter(0)) { - @Override - public void println(String s) { - DataConnection.this.logd(s); - } - - @Override - public void flush() { - } - }, null); - } - - /** - * Re-calculate score and update through network agent if it changes. - */ - private void updateScore() { - int oldScore = mScore; - mScore = calculateScore(); - if (oldScore != mScore && mNetworkAgent != null) { - log("Updating score from " + oldScore + " to " + mScore); - mNetworkAgent.sendNetworkScore(mScore, this); - } - } - - private int calculateScore() { - int score = OTHER_CONNECTION_SCORE; - - // If it's serving a network request that asks NET_CAPABILITY_INTERNET and doesn't have - // specify a subId, this dataConnection is considered to be default Internet data - // connection. In this case we assign a slightly higher score of 50. The intention is - // it will not be replaced by other data connections accidentally in DSDS usecase. - for (ApnContext apnContext : mApnContexts.keySet()) { - for (NetworkRequest networkRequest : apnContext.getNetworkRequests()) { - if (networkRequest.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) - && networkRequest.getNetworkSpecifier() == null) { - score = DEFAULT_INTERNET_CONNECTION_SCORE; - break; - } - } - } - - return score; - } - - private String handoverStateToString(@HandoverState int state) { - switch (state) { - case HANDOVER_STATE_IDLE: return "IDLE"; - case HANDOVER_STATE_BEING_TRANSFERRED: return "BEING_TRANSFERRED"; - case HANDOVER_STATE_COMPLETED: return "COMPLETED"; - default: return "UNKNOWN"; - } - } - - private @DataState int getState() { - if (isInactive()) { - return TelephonyManager.DATA_DISCONNECTED; - } else if (isActivating()) { - return TelephonyManager.DATA_CONNECTING; - } else if (isActive()) { - // The data connection can only be suspended when it's in active state. - if (mIsSuspended) { - return TelephonyManager.DATA_SUSPENDED; - } - return TelephonyManager.DATA_CONNECTED; - } else if (isDisconnecting()) { - return TelephonyManager.DATA_DISCONNECTING; - } - - return TelephonyManager.DATA_UNKNOWN; - } - - /** - * Get precise data connection state - * - * @return The {@link PreciseDataConnectionState} - */ - public PreciseDataConnectionState getPreciseDataConnectionState() { - return new PreciseDataConnectionState.Builder() - .setTransportType(mTransportType) - .setId(mCid) - .setState(getState()) - .setApnSetting(mApnSetting) - .setLinkProperties(mLinkProperties) - .setNetworkType(getNetworkType()) - .setFailCause(mDcFailCause) - .build(); - } - - /** - * Dump the current state. - * - * @param fd - * @param printWriter - * @param args - */ - @Override - public void dump(FileDescriptor fd, PrintWriter printWriter, String[] args) { - IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, " "); - pw.print("DataConnection "); - super.dump(fd, pw, args); - pw.flush(); - pw.increaseIndent(); - pw.println("transport type=" - + AccessNetworkConstants.transportTypeToString(mTransportType)); - pw.println("mApnContexts.size=" + mApnContexts.size()); - pw.println("mApnContexts=" + mApnContexts); - pw.println("mApnSetting=" + mApnSetting); - pw.println("mTag=" + mTag); - pw.println("mCid=" + mCid); - pw.println("mConnectionParams=" + mConnectionParams); - pw.println("mDisconnectParams=" + mDisconnectParams); - pw.println("mDcFailCause=" + DataFailCause.toString(mDcFailCause)); - pw.println("mPhone=" + mPhone); - pw.println("mSubId=" + mSubId); - pw.println("mLinkProperties=" + mLinkProperties); - pw.flush(); - pw.println("mDataRegState=" + mDataRegState); - pw.println("mHandoverState=" + handoverStateToString(mHandoverState)); - pw.println("mRilRat=" + mRilRat); - pw.println("mNetworkCapabilities=" + getNetworkCapabilities()); - pw.println("mCreateTime=" + TimeUtils.logTimeOfDay(mCreateTime)); - pw.println("mLastFailTime=" + TimeUtils.logTimeOfDay(mLastFailTime)); - pw.println("mLastFailCause=" + DataFailCause.toString(mLastFailCause)); - pw.println("mUserData=" + mUserData); - pw.println("mRestrictedNetworkOverride=" + mRestrictedNetworkOverride); - pw.println("mUnmeteredUseOnly=" + mUnmeteredUseOnly); - pw.println("mMmsUseOnly=" + mMmsUseOnly); - pw.println("mEnterpriseUse=" + mEnterpriseUse); - pw.println("mUnmeteredOverride=" + mUnmeteredOverride); - pw.println("mCongestedOverride=" + mCongestedOverride); - pw.println("mDownlinkBandwidth" + mDownlinkBandwidth); - pw.println("mUplinkBandwidth=" + mUplinkBandwidth); - pw.println("mDefaultQos=" + mDefaultQos); - pw.println("mQosBearerSessions=" + mQosBearerSessions); - pw.println("disallowedApnTypes=" - + ApnSetting.getApnTypesStringFromBitmask(getDisallowedApnTypes())); - pw.println("mInstanceNumber=" + mInstanceNumber); - pw.println("mAc=" + mAc); - pw.println("mScore=" + mScore); - if (mNetworkAgent != null) { - mNetworkAgent.dump(fd, pw, args); - } - pw.println("handover local log:"); - pw.increaseIndent(); - mHandoverLocalLog.dump(fd, pw, args); - pw.decreaseIndent(); - pw.decreaseIndent(); - pw.println(); - pw.flush(); - } - - /** - * Class used to track VCN-defined Network policies for this DataConnection. - * - * <p>MUST be registered with the associated DataConnection's Handler. - */ - private class DataConnectionVcnNetworkPolicyChangeListener - implements VcnNetworkPolicyChangeListener { - @Override - public void onPolicyChanged() { - // Poll the current underlying Network policy from VcnManager and send to NetworkAgent. - final NetworkCapabilities networkCapabilities = getNetworkCapabilities(); - VcnNetworkPolicyResult policyResult = - mVcnManager.applyVcnNetworkPolicy( - networkCapabilities, getLinkProperties()); - if (policyResult.isTeardownRequested()) { - tearDownAll( - Phone.REASON_VCN_REQUESTED_TEARDOWN, - DcTracker.RELEASE_TYPE_DETACH, - null /* onCompletedMsg */); - } - - if (mNetworkAgent != null) { - mNetworkAgent.sendNetworkCapabilities(networkCapabilities, DataConnection.this); - } - } - } -} -
diff --git a/src/java/com/android/internal/telephony/dataconnection/DataConnectionReasons.java b/src/java/com/android/internal/telephony/dataconnection/DataConnectionReasons.java deleted file mode 100644 index 68f2ab3..0000000 --- a/src/java/com/android/internal/telephony/dataconnection/DataConnectionReasons.java +++ /dev/null
@@ -1,176 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.internal.telephony.dataconnection; - -import com.android.internal.annotations.VisibleForTesting; - -import java.util.HashSet; - -/** - * The class to describe the reasons of allowing or disallowing to establish a data connection. - */ -public class DataConnectionReasons { - private HashSet<DataDisallowedReasonType> mDataDisallowedReasonSet = new HashSet<>(); - private DataAllowedReasonType mDataAllowedReason = DataAllowedReasonType.NONE; - - public DataConnectionReasons() {} - - void add(DataDisallowedReasonType reason) { - // Adding a disallowed reason will clean up the allowed reason because they are - // mutual exclusive. - mDataAllowedReason = DataAllowedReasonType.NONE; - mDataDisallowedReasonSet.add(reason); - } - - void add(DataAllowedReasonType reason) { - // Adding an allowed reason will clean up the disallowed reasons because they are - // mutual exclusive. - mDataDisallowedReasonSet.clear(); - - // Only higher priority allowed reason can overwrite the old one. See - // DataAllowedReasonType for the oder. - if (reason.ordinal() > mDataAllowedReason.ordinal()) { - mDataAllowedReason = reason; - } - } - - @Override - public String toString() { - StringBuilder reasonStr = new StringBuilder(); - if (mDataDisallowedReasonSet.size() > 0) { - reasonStr.append("Data disallowed reasons:"); - for (DataDisallowedReasonType reason : mDataDisallowedReasonSet) { - reasonStr.append(" ").append(reason); - } - } else { - reasonStr.append("Data allowed reason:"); - reasonStr.append(" ").append(mDataAllowedReason); - } - return reasonStr.toString(); - } - - void copyFrom(DataConnectionReasons reasons) { - this.mDataDisallowedReasonSet = reasons.mDataDisallowedReasonSet; - this.mDataAllowedReason = reasons.mDataAllowedReason; - } - - boolean allowed() { - return mDataDisallowedReasonSet.size() == 0; - } - - /** - * Check if it contains a certain disallowed reason. - * - * @param reason The disallowed reason to check. - * @return {@code true} if the provided reason matches one of the disallowed reasons. - */ - @VisibleForTesting - public boolean contains(DataDisallowedReasonType reason) { - return mDataDisallowedReasonSet.contains(reason); - } - - /** - * Check if only one disallowed reason prevent data connection. - * - * @param reason The given reason to check - * @return True if the given reason is the only one that prevents data connection - */ - public boolean containsOnly(DataDisallowedReasonType reason) { - return mDataDisallowedReasonSet.size() == 1 && contains(reason); - } - - boolean contains(DataAllowedReasonType reason) { - return reason == mDataAllowedReason; - } - - boolean containsHardDisallowedReasons() { - for (DataDisallowedReasonType reason : mDataDisallowedReasonSet) { - if (reason.isHardReason()) { - return true; - } - } - return false; - } - - // Disallowed reasons. There could be multiple reasons if data connection is not allowed. - public enum DataDisallowedReasonType { - // Soft failure reasons. Normally the reasons from users or policy settings. - - // Data is disabled by the user or policy. - DATA_DISABLED(false), - // Data roaming is disabled by the user. - ROAMING_DISABLED(false), - // Default data not selected. - DEFAULT_DATA_UNSELECTED(false), - - // Belows are all hard failure reasons. - NOT_ATTACHED(true), - SIM_NOT_READY(true), - INVALID_PHONE_STATE(true), - CONCURRENT_VOICE_DATA_NOT_ALLOWED(true), - PS_RESTRICTED(true), - UNDESIRED_POWER_STATE(true), - INTERNAL_DATA_DISABLED(true), - RADIO_DISABLED_BY_CARRIER(true), - // Not in the right state for data call setup. - APN_NOT_CONNECTABLE(true), - // Data is in connecting state. No need to send another setup request. - DATA_IS_CONNECTING(true), - // Data is being disconnected. Telephony will retry after disconnected. - DATA_IS_DISCONNECTING(true), - // Data is already connected. No need to setup data again. - DATA_ALREADY_CONNECTED(true), - // certain APNs are not allowed on IWLAN in legacy mode. - ON_IWLAN(true), - // certain APNs are only allowed when the device is camped on NR. - NOT_ON_NR(true), - // Data is not allowed while device is in emergency callback mode. - IN_ECBM(true), - // The given APN type's preferred transport has switched. - ON_OTHER_TRANSPORT(true), - // Underlying data service is not bound. - DATA_SERVICE_NOT_READY(true), - // Qualified networks service does not allow certain types of APN brought up on either - // cellular or IWLAN. - DISABLED_BY_QNS(true), - // Data is throttled. The network explicitly requested device not to establish data - // connection for a certain period. - DATA_THROTTLED(true); - - private boolean mIsHardReason; - - boolean isHardReason() { - return mIsHardReason; - } - - DataDisallowedReasonType(boolean isHardReason) { - mIsHardReason = isHardReason; - } - } - - // Data allowed reasons. There will be only one reason if data is allowed. - enum DataAllowedReasonType { - // Note that unlike disallowed reasons, we only have one allowed reason every time - // when we check data is allowed or not. The order of these allowed reasons is very - // important. The lower ones take precedence over the upper ones. - NONE, - NORMAL, - UNMETERED_APN, - RESTRICTED_REQUEST, - EMERGENCY_APN, - } -}
diff --git a/src/java/com/android/internal/telephony/dataconnection/DataEnabledSettings.java b/src/java/com/android/internal/telephony/dataconnection/DataEnabledSettings.java deleted file mode 100644 index 305b4a8..0000000 --- a/src/java/com/android/internal/telephony/dataconnection/DataEnabledSettings.java +++ /dev/null
@@ -1,566 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.internal.telephony.dataconnection; - - -import static android.telephony.PhoneStateListener.LISTEN_CALL_STATE; -import static android.telephony.PhoneStateListener.LISTEN_NONE; - -import android.annotation.IntDef; -import android.content.ContentResolver; -import android.content.Context; -import android.os.Handler; -import android.os.RegistrantList; -import android.os.SystemProperties; -import android.provider.Settings; -import android.sysprop.TelephonyProperties; -import android.telephony.Annotation.CallState; -import android.telephony.CarrierConfigManager; -import android.telephony.PhoneStateListener; -import android.telephony.SubscriptionInfo; -import android.telephony.SubscriptionManager; -import android.telephony.TelephonyManager; -import android.telephony.data.ApnSetting; -import android.util.LocalLog; -import android.util.Pair; - -import com.android.internal.telephony.GlobalSettingsHelper; -import com.android.internal.telephony.MultiSimSettingController; -import com.android.internal.telephony.Phone; -import com.android.internal.telephony.SubscriptionController; -import com.android.internal.telephony.data.DataEnabledOverride; -import com.android.telephony.Rlog; - -import java.io.FileDescriptor; -import java.io.PrintWriter; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** - * The class to hold different data enabled/disabled settings. Also it allows clients to register - * for overall data enabled setting changed event. - * @hide - */ -public class DataEnabledSettings { - - private static final String LOG_TAG = "DataEnabledSettings"; - - @Retention(RetentionPolicy.SOURCE) - @IntDef(prefix = {"REASON_"}, - value = { - REASON_REGISTERED, - REASON_INTERNAL_DATA_ENABLED, - REASON_USER_DATA_ENABLED, - REASON_POLICY_DATA_ENABLED, - REASON_DATA_ENABLED_BY_CARRIER, - REASON_PROVISIONED_CHANGED, - REASON_PROVISIONING_DATA_ENABLED_CHANGED, - REASON_OVERRIDE_RULE_CHANGED, - REASON_OVERRIDE_CONDITION_CHANGED, - REASON_THERMAL_DATA_ENABLED - }) - public @interface DataEnabledChangedReason {} - - public static final int REASON_REGISTERED = 0; - - public static final int REASON_INTERNAL_DATA_ENABLED = 1; - - public static final int REASON_USER_DATA_ENABLED = 2; - - public static final int REASON_POLICY_DATA_ENABLED = 3; - - public static final int REASON_DATA_ENABLED_BY_CARRIER = 4; - - public static final int REASON_PROVISIONED_CHANGED = 5; - - public static final int REASON_PROVISIONING_DATA_ENABLED_CHANGED = 6; - - public static final int REASON_OVERRIDE_RULE_CHANGED = 7; - - public static final int REASON_OVERRIDE_CONDITION_CHANGED = 8; - - public static final int REASON_THERMAL_DATA_ENABLED = 9; - - /** - * responds to the setInternalDataEnabled call - used internally to turn off data. - * For example during emergency calls - */ - private boolean mInternalDataEnabled = true; - - /** - * Flag indicating data allowed by network policy manager or not. - */ - private boolean mPolicyDataEnabled = true; - - /** - * Indicate if metered APNs are enabled by the carrier. set false to block all the metered APNs - * from continuously sending requests, which causes undesired network load. - */ - private boolean mCarrierDataEnabled = true; - - /** - * Flag indicating data allowed by Thermal service or not. - */ - private boolean mThermalDataEnabled = true; - - /** - * Flag indicating whether data is allowed or not for the device. It can be disabled by - * user, carrier, policy or thermal - */ - private boolean mIsDataEnabled = false; - - private final Phone mPhone; - - private int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; - - private ContentResolver mResolver = null; - - private final RegistrantList mOverallDataEnabledChangedRegistrants = new RegistrantList(); - - // TODO: Merge this with mOverallDataEnabledChangedRegistrants. In the future, notifying data - // enabled changed with APN types bitmask - private final RegistrantList mOverallDataEnabledOverrideChangedRegistrants = - new RegistrantList(); - - private final LocalLog mSettingChangeLocalLog = new LocalLog(32); - - private DataEnabledOverride mDataEnabledOverride; - - private TelephonyManager mTelephonyManager; - - // for msim, user data enabled setting depends on subId. - private final SubscriptionManager.OnSubscriptionsChangedListener - mOnSubscriptionsChangeListener = - new SubscriptionManager.OnSubscriptionsChangedListener() { - @Override - public void onSubscriptionsChanged() { - synchronized (this) { - if (mSubId != mPhone.getSubId()) { - log("onSubscriptionsChanged subId: " + mSubId + " to: " - + mPhone.getSubId()); - mSubId = mPhone.getSubId(); - mDataEnabledOverride = getDataEnabledOverride(); - updatePhoneStateListener(); - updateDataEnabledAndNotify(REASON_USER_DATA_ENABLED); - mPhone.notifyUserMobileDataStateChanged(isUserDataEnabled()); - } - } - } - }; - - private void updatePhoneStateListener() { - mTelephonyManager.listen(mPhoneStateListener, LISTEN_NONE); - if (SubscriptionManager.isUsableSubscriptionId(mSubId)) { - mTelephonyManager = mTelephonyManager.createForSubscriptionId(mSubId); - } - mTelephonyManager.listen(mPhoneStateListener, LISTEN_CALL_STATE); - } - - private final PhoneStateListener mPhoneStateListener = new PhoneStateListener() { - @Override - public void onCallStateChanged(@CallState int state, String phoneNumber) { - updateDataEnabledAndNotify(REASON_OVERRIDE_CONDITION_CHANGED); - } - }; - - @Override - public String toString() { - return "[mInternalDataEnabled=" + mInternalDataEnabled - + ", isUserDataEnabled=" + isUserDataEnabled() - + ", isProvisioningDataEnabled=" + isProvisioningDataEnabled() - + ", mPolicyDataEnabled=" + mPolicyDataEnabled - + ", mCarrierDataEnabled=" + mCarrierDataEnabled - + ", mIsDataEnabled=" + mIsDataEnabled - + ", mThermalDataEnabled=" + mThermalDataEnabled - + ", " + mDataEnabledOverride - + "]"; - } - - public DataEnabledSettings(Phone phone) { - mPhone = phone; - mResolver = mPhone.getContext().getContentResolver(); - SubscriptionManager subscriptionManager = (SubscriptionManager) mPhone.getContext() - .getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE); - subscriptionManager.addOnSubscriptionsChangedListener(mOnSubscriptionsChangeListener); - mTelephonyManager = (TelephonyManager) mPhone.getContext() - .getSystemService(Context.TELEPHONY_SERVICE); - mDataEnabledOverride = getDataEnabledOverride(); - updateDataEnabled(); - } - - private DataEnabledOverride getDataEnabledOverride() { - return new DataEnabledOverride(SubscriptionController.getInstance() - .getDataEnabledOverrideRules(mPhone.getSubId())); - } - - public synchronized void setInternalDataEnabled(boolean enabled) { - if (mInternalDataEnabled != enabled) { - localLog("InternalDataEnabled", enabled); - mInternalDataEnabled = enabled; - updateDataEnabledAndNotify(REASON_INTERNAL_DATA_ENABLED); - } - } - public synchronized boolean isInternalDataEnabled() { - return mInternalDataEnabled; - } - - private synchronized void setUserDataEnabled(boolean enabled) { - // By default the change should propagate to the group. - setUserDataEnabled(enabled, true); - } - - /** - * @param notifyMultiSimSettingController if setUserDataEnabled is already from propagating - * from MultiSimSettingController, don't notify MultiSimSettingController again. - * For example, if sub1 and sub2 are in the same group and user enables data for sub - * 1, sub 2 will also be enabled but with propagateToGroup = false. - */ - public synchronized void setUserDataEnabled(boolean enabled, - boolean notifyMultiSimSettingController) { - // Can't disable data for stand alone opportunistic subscription. - if (isStandAloneOpportunistic(mPhone.getSubId(), mPhone.getContext()) && !enabled) return; - - boolean changed = GlobalSettingsHelper.setInt(mPhone.getContext(), - Settings.Global.MOBILE_DATA, mPhone.getSubId(), (enabled ? 1 : 0)); - if (changed) { - localLog("UserDataEnabled", enabled); - mPhone.notifyUserMobileDataStateChanged(enabled); - updateDataEnabledAndNotify(REASON_USER_DATA_ENABLED); - if (notifyMultiSimSettingController) { - MultiSimSettingController.getInstance().notifyUserDataEnabled( - mPhone.getSubId(), enabled); - } - } - } - - /** - * Policy control of data connection with reason - * @param reason the reason the data enable change is taking place - * @param enabled True if enabling the data, otherwise disabling. - */ - public synchronized void setDataEnabled(@TelephonyManager.DataEnabledReason int reason, - boolean enabled) { - switch (reason) { - case TelephonyManager.DATA_ENABLED_REASON_USER: - setUserDataEnabled(enabled); - break; - case TelephonyManager.DATA_ENABLED_REASON_CARRIER: - setCarrierDataEnabled(enabled); - break; - case TelephonyManager.DATA_ENABLED_REASON_POLICY: - setPolicyDataEnabled(enabled); - break; - case TelephonyManager.DATA_ENABLED_REASON_THERMAL: - setThermalDataEnabled(enabled); - break; - default: - log("Invalid data enable reason " + reason); - break; - } - } - - public synchronized boolean isUserDataEnabled() { - // User data should always be true for opportunistic subscription. - if (isStandAloneOpportunistic(mPhone.getSubId(), mPhone.getContext())) return true; - - boolean defaultVal = TelephonyProperties.mobile_data().orElse(true); - - return GlobalSettingsHelper.getBoolean(mPhone.getContext(), - Settings.Global.MOBILE_DATA, mPhone.getSubId(), defaultVal); - } - - /** - * Set whether always allowing MMS data connection. - * - * @param alwaysAllow {@code true} if MMS data is always allowed. - * - * @return {@code false} if the setting is changed. - */ - public synchronized boolean setAlwaysAllowMmsData(boolean alwaysAllow) { - localLog("setAlwaysAllowMmsData", alwaysAllow); - mDataEnabledOverride.setAlwaysAllowMms(alwaysAllow); - boolean changed = SubscriptionController.getInstance() - .setDataEnabledOverrideRules(mPhone.getSubId(), mDataEnabledOverride.getRules()); - if (changed) { - updateDataEnabledAndNotify(REASON_OVERRIDE_RULE_CHANGED); - notifyDataEnabledOverrideChanged(); - } - - return changed; - } - - /** - * Set allowing mobile data during voice call. This is used for allowing data on the non-default - * data SIM. When a voice call is placed on the non-default data SIM on DSDS devices, users will - * not be able to use mobile data. By calling this API, data will be temporarily enabled on the - * non-default data SIM during the life cycle of the voice call. - * - * @param allow {@code true} if allowing using data during voice call, {@code false} if - * disallowed - * - * @return {@code true} if operation is successful. otherwise {@code false}. - */ - public synchronized boolean setAllowDataDuringVoiceCall(boolean allow) { - localLog("setAllowDataDuringVoiceCall", allow); - if (allow == isDataAllowedInVoiceCall()) { - return true; - } - mDataEnabledOverride.setDataAllowedInVoiceCall(allow); - - boolean changed = SubscriptionController.getInstance() - .setDataEnabledOverrideRules(mPhone.getSubId(), mDataEnabledOverride.getRules()); - if (changed) { - updateDataEnabledAndNotify(REASON_OVERRIDE_RULE_CHANGED); - notifyDataEnabledOverrideChanged(); - } - - return changed; - } - - /** - * Check if data is allowed during voice call. - * - * @return {@code true} if data is allowed during voice call. - */ - public synchronized boolean isDataAllowedInVoiceCall() { - return mDataEnabledOverride.isDataAllowedInVoiceCall(); - } - - public synchronized boolean isMmsAlwaysAllowed() { - return mDataEnabledOverride.isMmsAlwaysAllowed(); - } - - private synchronized void setPolicyDataEnabled(boolean enabled) { - if (mPolicyDataEnabled != enabled) { - localLog("PolicyDataEnabled", enabled); - mPolicyDataEnabled = enabled; - updateDataEnabledAndNotify(REASON_POLICY_DATA_ENABLED); - } - } - - public synchronized boolean isPolicyDataEnabled() { - return mPolicyDataEnabled; - } - - private synchronized void setCarrierDataEnabled(boolean enabled) { - if (mCarrierDataEnabled != enabled) { - localLog("CarrierDataEnabled", enabled); - mCarrierDataEnabled = enabled; - updateDataEnabledAndNotify(REASON_DATA_ENABLED_BY_CARRIER); - } - } - - public synchronized boolean isCarrierDataEnabled() { - return mCarrierDataEnabled; - } - - private synchronized void setThermalDataEnabled(boolean enabled) { - if (mThermalDataEnabled != enabled) { - localLog("ThermalDataEnabled", enabled); - mThermalDataEnabled = enabled; - updateDataEnabledAndNotify(REASON_THERMAL_DATA_ENABLED); - } - } - - public synchronized boolean isThermalDataEnabled() { - return mThermalDataEnabled; - } - - public synchronized void updateProvisionedChanged() { - updateDataEnabledAndNotify(REASON_PROVISIONED_CHANGED); - } - - public synchronized void updateProvisioningDataEnabled() { - updateDataEnabledAndNotify(REASON_PROVISIONING_DATA_ENABLED_CHANGED); - } - - public synchronized boolean isDataEnabled() { - return mIsDataEnabled; - } - - /** - * Check if data is enabled for a specific reason {@@TelephonyManager.DataEnabledReason} - * - * @return {@code true} if the overall data is enabled; {@code false} if not. - */ - public synchronized boolean isDataEnabledForReason( - @TelephonyManager.DataEnabledReason int reason) { - switch (reason) { - case TelephonyManager.DATA_ENABLED_REASON_USER: - return isUserDataEnabled(); - case TelephonyManager.DATA_ENABLED_REASON_CARRIER: - return isCarrierDataEnabled(); - case TelephonyManager.DATA_ENABLED_REASON_POLICY: - return isPolicyDataEnabled(); - case TelephonyManager.DATA_ENABLED_REASON_THERMAL: - return isThermalDataEnabled(); - default: - return false; - } - } - - private synchronized void updateDataEnabledAndNotify(int reason) { - boolean prevDataEnabled = mIsDataEnabled; - - updateDataEnabled(); - - if (prevDataEnabled != mIsDataEnabled) { - notifyDataEnabledChanged(!prevDataEnabled, reason); - } - } - - private synchronized void updateDataEnabled() { - if (isProvisioning()) { - mIsDataEnabled = isProvisioningDataEnabled(); - } else { - mIsDataEnabled = mInternalDataEnabled && (isUserDataEnabled() || mDataEnabledOverride - .shouldOverrideDataEnabledSettings(mPhone, ApnSetting.TYPE_ALL)) - && mPolicyDataEnabled && mCarrierDataEnabled && mThermalDataEnabled; - } - } - - public boolean isProvisioning() { - return Settings.Global.getInt(mResolver, Settings.Global.DEVICE_PROVISIONED, 0) == 0; - } - /** - * In provisioning, we might want to have enable mobile data during provisioning. It depends - * on value of Settings.Global.DEVICE_PROVISIONING_MOBILE_DATA_ENABLED which is set by - * setupwizard. It only matters if it's in provisioning stage. - * @return whether we are enabling userData during provisioning stage. - */ - public boolean isProvisioningDataEnabled() { - final String prov_property = SystemProperties.get("ro.com.android.prov_mobiledata", - "false"); - boolean retVal = "true".equalsIgnoreCase(prov_property); - - final int prov_mobile_data = Settings.Global.getInt(mResolver, - Settings.Global.DEVICE_PROVISIONING_MOBILE_DATA_ENABLED, - retVal ? 1 : 0); - retVal = prov_mobile_data != 0; - log("getDataEnabled during provisioning retVal=" + retVal + " - (" + prov_property - + ", " + prov_mobile_data + ")"); - - return retVal; - } - - public synchronized void setDataRoamingEnabled(boolean enabled) { - // will trigger handleDataOnRoamingChange() through observer - boolean changed = GlobalSettingsHelper.setBoolean(mPhone.getContext(), - Settings.Global.DATA_ROAMING, mPhone.getSubId(), enabled); - - if (changed) { - localLog("setDataRoamingEnabled", enabled); - MultiSimSettingController.getInstance().notifyRoamingDataEnabled(mPhone.getSubId(), - enabled); - } - } - - /** - * Return current {@link android.provider.Settings.Global#DATA_ROAMING} value. - */ - public synchronized boolean getDataRoamingEnabled() { - return GlobalSettingsHelper.getBoolean(mPhone.getContext(), - Settings.Global.DATA_ROAMING, mPhone.getSubId(), getDefaultDataRoamingEnabled()); - } - - /** - * get default values for {@link Settings.Global#DATA_ROAMING} - * return {@code true} if either - * {@link CarrierConfigManager#KEY_CARRIER_DEFAULT_DATA_ROAMING_ENABLED_BOOL} or - * system property ro.com.android.dataroaming is set to true. otherwise return {@code false} - */ - public synchronized boolean getDefaultDataRoamingEnabled() { - final CarrierConfigManager configMgr = (CarrierConfigManager) - mPhone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE); - boolean isDataRoamingEnabled = "true".equalsIgnoreCase(SystemProperties.get( - "ro.com.android.dataroaming", "false")); - isDataRoamingEnabled |= configMgr.getConfigForSubId(mPhone.getSubId()).getBoolean( - CarrierConfigManager.KEY_CARRIER_DEFAULT_DATA_ROAMING_ENABLED_BOOL); - return isDataRoamingEnabled; - } - - private void notifyDataEnabledChanged(boolean enabled, int reason) { - mOverallDataEnabledChangedRegistrants.notifyResult(new Pair<>(enabled, reason)); - mPhone.notifyDataEnabled(enabled, reason); - } - - public void registerForDataEnabledChanged(Handler h, int what, Object obj) { - mOverallDataEnabledChangedRegistrants.addUnique(h, what, obj); - notifyDataEnabledChanged(isDataEnabled(), REASON_REGISTERED); - } - - public void unregisterForDataEnabledChanged(Handler h) { - mOverallDataEnabledChangedRegistrants.remove(h); - } - - private void notifyDataEnabledOverrideChanged() { - mOverallDataEnabledOverrideChangedRegistrants.notifyRegistrants(); - } - - /** - * Register for data enabled override changed event. - * - * @param h The handler - * @param what The event - */ - public void registerForDataEnabledOverrideChanged(Handler h, int what) { - mOverallDataEnabledOverrideChangedRegistrants.addUnique(h, what, null); - notifyDataEnabledOverrideChanged(); - } - - /** - * Unregistered for data enabled override changed event. - * - * @param h The handler - */ - public void unregisterForDataEnabledOverrideChanged(Handler h) { - mOverallDataEnabledOverrideChangedRegistrants.remove(h); - } - - private static boolean isStandAloneOpportunistic(int subId, Context context) { - SubscriptionInfo info = SubscriptionController.getInstance().getActiveSubscriptionInfo( - subId, context.getOpPackageName(), context.getAttributionTag()); - return (info != null) && info.isOpportunistic() && info.getGroupUuid() == null; - } - - public synchronized boolean isDataEnabled(int apnType) { - if (isProvisioning()) { - return isProvisioningDataEnabled(); - } else { - boolean userDataEnabled = isUserDataEnabled(); - // Check if we should temporarily enable data in certain conditions. - boolean isDataEnabledOverridden = mDataEnabledOverride - .shouldOverrideDataEnabledSettings(mPhone, apnType); - - return (mInternalDataEnabled && mPolicyDataEnabled && mCarrierDataEnabled - && mThermalDataEnabled && (userDataEnabled || isDataEnabledOverridden)); - } - } - - private void log(String s) { - Rlog.d(LOG_TAG, "[" + mPhone.getPhoneId() + "]" + s); - } - - private void localLog(String name, boolean value) { - mSettingChangeLocalLog.log(name + " change to " + value); - } - - protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - pw.println(" DataEnabledSettings="); - mSettingChangeLocalLog.dump(fd, pw, args); - } -}
diff --git a/src/java/com/android/internal/telephony/dataconnection/DataServiceManager.java b/src/java/com/android/internal/telephony/dataconnection/DataServiceManager.java deleted file mode 100644 index d006004..0000000 --- a/src/java/com/android/internal/telephony/dataconnection/DataServiceManager.java +++ /dev/null
@@ -1,978 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.internal.telephony.dataconnection; - -import static android.text.format.DateUtils.MINUTE_IN_MILLIS; -import static android.text.format.DateUtils.SECOND_IN_MILLIS; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.app.AppOpsManager; -import android.content.BroadcastReceiver; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.ServiceConnection; -import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; -import android.net.LinkProperties; -import android.os.AsyncResult; -import android.os.Handler; -import android.os.IBinder; -import android.os.Message; -import android.os.PersistableBundle; -import android.os.RegistrantList; -import android.os.RemoteException; -import android.os.UserHandle; -import android.permission.LegacyPermissionManager; -import android.telephony.AccessNetworkConstants; -import android.telephony.AccessNetworkConstants.TransportType; -import android.telephony.AnomalyReporter; -import android.telephony.CarrierConfigManager; -import android.telephony.SubscriptionManager; -import android.telephony.data.DataCallResponse; -import android.telephony.data.DataProfile; -import android.telephony.data.DataService; -import android.telephony.data.DataServiceCallback; -import android.telephony.data.IDataService; -import android.telephony.data.IDataServiceCallback; -import android.telephony.data.NetworkSliceInfo; -import android.telephony.data.TrafficDescriptor; -import android.text.TextUtils; - -import com.android.internal.telephony.Phone; -import com.android.internal.telephony.PhoneConfigurationManager; -import com.android.internal.telephony.util.TelephonyUtils; -import com.android.telephony.Rlog; - -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.UUID; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.CountDownLatch; - -/** - * Data service manager manages handling data requests and responses on data services (e.g. - * Cellular data service, IWLAN data service). - */ -public class DataServiceManager extends Handler { - private static final boolean DBG = true; - - static final String DATA_CALL_RESPONSE = "data_call_response"; - - private static final int EVENT_BIND_DATA_SERVICE = 1; - - private static final int EVENT_WATCHDOG_TIMEOUT = 2; - - private static final long REQUEST_UNRESPONDED_TIMEOUT = 10 * MINUTE_IN_MILLIS; // 10 mins - - private static final long CHANGE_PERMISSION_TIMEOUT_MS = 15 * SECOND_IN_MILLIS; // 15 secs - - private final Phone mPhone; - - private final String mTag; - - private final CarrierConfigManager mCarrierConfigManager; - private final AppOpsManager mAppOps; - private final LegacyPermissionManager mPermissionManager; - - private final int mTransportType; - - private boolean mBound; - - private IDataService mIDataService; - - private DataServiceManagerDeathRecipient mDeathRecipient; - - private final RegistrantList mServiceBindingChangedRegistrants = new RegistrantList(); - - private final Map<IBinder, Message> mMessageMap = new ConcurrentHashMap<>(); - - private final RegistrantList mDataCallListChangedRegistrants = new RegistrantList(); - - private final RegistrantList mApnUnthrottledRegistrants = new RegistrantList(); - - private String mTargetBindingPackageName; - - private CellularDataServiceConnection mServiceConnection; - - private final UUID mAnomalyUUID = UUID.fromString("fc1956de-c080-45de-8431-a1faab687110"); - private String mLastBoundPackageName; - - /** - * Helpful for logging - * @return the tag name - * - * @hide - */ - public String getTag() { - return mTag; - } - - private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - final String action = intent.getAction(); - if (CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED.equals(action) - && mPhone.getPhoneId() == intent.getIntExtra( - CarrierConfigManager.EXTRA_SLOT_INDEX, 0)) { - // We should wait for carrier config changed event because the target binding - // package name can come from the carrier config. Note that we still get this event - // even when SIM is absent. - if (DBG) log("Carrier config changed. Try to bind data service."); - sendEmptyMessage(EVENT_BIND_DATA_SERVICE); - } - } - }; - - private class DataServiceManagerDeathRecipient implements IBinder.DeathRecipient { - @Override - public void binderDied() { - // TODO: try to rebind the service. - String message = "Data service " + mLastBoundPackageName + " for transport type " - + AccessNetworkConstants.transportTypeToString(mTransportType) + " died."; - loge(message); - AnomalyReporter.reportAnomaly(mAnomalyUUID, message, mPhone.getCarrierId()); - } - } - - private void grantPermissionsToService(String packageName) { - final String[] pkgToGrant = {packageName}; - CountDownLatch latch = new CountDownLatch(1); - try { - mPermissionManager.grantDefaultPermissionsToEnabledTelephonyDataServices( - pkgToGrant, UserHandle.of(UserHandle.myUserId()), Runnable::run, - isSuccess -> { - if (isSuccess) { - latch.countDown(); - } else { - loge("Failed to grant permissions to service."); - } - }); - TelephonyUtils.waitUntilReady(latch, CHANGE_PERMISSION_TIMEOUT_MS); - mAppOps.setMode(AppOpsManager.OPSTR_MANAGE_IPSEC_TUNNELS, - UserHandle.myUserId(), pkgToGrant[0], AppOpsManager.MODE_ALLOWED); - mAppOps.setMode(AppOpsManager.OPSTR_FINE_LOCATION, - UserHandle.myUserId(), pkgToGrant[0], AppOpsManager.MODE_ALLOWED); - } catch (RuntimeException e) { - loge("Binder to package manager died, permission grant for DataService failed."); - throw e; - } - } - - /** - * Loop through all DataServices installed on the system and revoke permissions from any that - * are not currently the WWAN or WLAN data service. - */ - private void revokePermissionsFromUnusedDataServices() { - // Except the current data services from having their permissions removed. - Set<String> dataServices = getAllDataServicePackageNames(); - for (int transportType : mPhone.getAccessNetworksManager().getAvailableTransports()) { - dataServices.remove(getDataServicePackageName(transportType)); - } - - CountDownLatch latch = new CountDownLatch(1); - try { - String[] dataServicesArray = new String[dataServices.size()]; - dataServices.toArray(dataServicesArray); - mPermissionManager.revokeDefaultPermissionsFromDisabledTelephonyDataServices( - dataServicesArray, UserHandle.of(UserHandle.myUserId()), Runnable::run, - isSuccess -> { - if (isSuccess) { - latch.countDown(); - } else { - loge("Failed to revoke permissions from data services."); - } - }); - TelephonyUtils.waitUntilReady(latch, CHANGE_PERMISSION_TIMEOUT_MS); - for (String pkg : dataServices) { - mAppOps.setMode(AppOpsManager.OPSTR_MANAGE_IPSEC_TUNNELS, UserHandle.myUserId(), - pkg, AppOpsManager.MODE_ERRORED); - mAppOps.setMode(AppOpsManager.OPSTR_FINE_LOCATION, UserHandle.myUserId(), - pkg, AppOpsManager.MODE_ERRORED); - } - } catch (RuntimeException e) { - loge("Binder to package manager died; failed to revoke DataService permissions."); - throw e; - } - } - - private final class CellularDataServiceConnection implements ServiceConnection { - @Override - public void onServiceConnected(ComponentName name, IBinder service) { - if (DBG) log("onServiceConnected"); - mIDataService = IDataService.Stub.asInterface(service); - mDeathRecipient = new DataServiceManagerDeathRecipient(); - mBound = true; - mLastBoundPackageName = getDataServicePackageName(); - removeMessages(EVENT_WATCHDOG_TIMEOUT); - - try { - service.linkToDeath(mDeathRecipient, 0); - mIDataService.createDataServiceProvider(mPhone.getPhoneId()); - mIDataService.registerForDataCallListChanged(mPhone.getPhoneId(), - new CellularDataServiceCallback("dataCallListChanged")); - mIDataService.registerForUnthrottleApn(mPhone.getPhoneId(), - new CellularDataServiceCallback("unthrottleApn")); - } catch (RemoteException e) { - loge("Remote exception. " + e); - return; - } - mServiceBindingChangedRegistrants.notifyResult(true); - } - @Override - public void onServiceDisconnected(ComponentName name) { - if (DBG) log("onServiceDisconnected"); - removeMessages(EVENT_WATCHDOG_TIMEOUT); - mIDataService = null; - mBound = false; - mServiceBindingChangedRegistrants.notifyResult(false); - mTargetBindingPackageName = null; - } - } - - private final class CellularDataServiceCallback extends IDataServiceCallback.Stub { - - private final String mTag; - - CellularDataServiceCallback(String tag) { - mTag = tag; - } - - public String getTag() { - return mTag; - } - - @Override - public void onSetupDataCallComplete(@DataServiceCallback.ResultCode int resultCode, - DataCallResponse response) { - if (DBG) { - log("onSetupDataCallComplete. resultCode = " + resultCode + ", response = " - + response); - } - removeMessages(EVENT_WATCHDOG_TIMEOUT, CellularDataServiceCallback.this); - Message msg = mMessageMap.remove(asBinder()); - if (msg != null) { - msg.getData().putParcelable(DATA_CALL_RESPONSE, response); - sendCompleteMessage(msg, resultCode); - } else { - loge("Unable to find the message for setup call response."); - } - } - - @Override - public void onDeactivateDataCallComplete(@DataServiceCallback.ResultCode int resultCode) { - if (DBG) log("onDeactivateDataCallComplete. resultCode = " + resultCode); - removeMessages(EVENT_WATCHDOG_TIMEOUT, CellularDataServiceCallback.this); - Message msg = mMessageMap.remove(asBinder()); - sendCompleteMessage(msg, resultCode); - } - - @Override - public void onSetInitialAttachApnComplete(@DataServiceCallback.ResultCode int resultCode) { - if (DBG) log("onSetInitialAttachApnComplete. resultCode = " + resultCode); - Message msg = mMessageMap.remove(asBinder()); - sendCompleteMessage(msg, resultCode); - } - - @Override - public void onSetDataProfileComplete(@DataServiceCallback.ResultCode int resultCode) { - if (DBG) log("onSetDataProfileComplete. resultCode = " + resultCode); - Message msg = mMessageMap.remove(asBinder()); - sendCompleteMessage(msg, resultCode); - } - - @Override - public void onRequestDataCallListComplete(@DataServiceCallback.ResultCode int resultCode, - List<DataCallResponse> dataCallList) { - if (DBG) log("onRequestDataCallListComplete. resultCode = " + resultCode); - Message msg = mMessageMap.remove(asBinder()); - sendCompleteMessage(msg, resultCode); - } - - @Override - public void onDataCallListChanged(List<DataCallResponse> dataCallList) { - mDataCallListChangedRegistrants.notifyRegistrants( - new AsyncResult(null, dataCallList, null)); - } - - @Override - public void onHandoverStarted(@DataServiceCallback.ResultCode int resultCode) { - if (DBG) log("onHandoverStarted. resultCode = " + resultCode); - removeMessages(EVENT_WATCHDOG_TIMEOUT, CellularDataServiceCallback.this); - Message msg = mMessageMap.remove(asBinder()); - sendCompleteMessage(msg, resultCode); - } - - @Override - public void onHandoverCancelled(@DataServiceCallback.ResultCode int resultCode) { - if (DBG) log("onHandoverCancelled. resultCode = " + resultCode); - removeMessages(EVENT_WATCHDOG_TIMEOUT, CellularDataServiceCallback.this); - Message msg = mMessageMap.remove(asBinder()); - sendCompleteMessage(msg, resultCode); - } - - @Override - public void onApnUnthrottled(String apn) { - if (apn != null) { - mApnUnthrottledRegistrants.notifyRegistrants( - new AsyncResult(null, apn, null)); - } else { - loge("onApnUnthrottled: apn is null"); - } - } - - @Override - public void onDataProfileUnthrottled(DataProfile dataProfile) { - if (dataProfile != null) { - mApnUnthrottledRegistrants.notifyRegistrants( - new AsyncResult(null, dataProfile, null)); - } else { - loge("onDataProfileUnthrottled: dataProfile is null"); - } - } - } - - /** - * Constructor - * - * @param phone The phone object - * @param transportType The transport type - * @param tagSuffix Logging tag suffix - */ - public DataServiceManager(Phone phone, @TransportType int transportType, String tagSuffix) { - mPhone = phone; - mTag = "DSM" + tagSuffix; - mTransportType = transportType; - mBound = false; - mCarrierConfigManager = (CarrierConfigManager) phone.getContext().getSystemService( - Context.CARRIER_CONFIG_SERVICE); - // NOTE: Do NOT use AppGlobals to retrieve the permission manager; AppGlobals - // caches the service instance, but we need to explicitly request a new service - // so it can be mocked out for tests - mPermissionManager = (LegacyPermissionManager) phone.getContext().getSystemService( - Context.LEGACY_PERMISSION_SERVICE); - mAppOps = (AppOpsManager) phone.getContext().getSystemService(Context.APP_OPS_SERVICE); - - IntentFilter intentFilter = new IntentFilter(); - intentFilter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED); - try { - Context contextAsUser = phone.getContext().createPackageContextAsUser( - phone.getContext().getPackageName(), 0, UserHandle.ALL); - contextAsUser.registerReceiver(mBroadcastReceiver, intentFilter, - null /* broadcastPermission */, null); - } catch (PackageManager.NameNotFoundException e) { - loge("Package name not found: " + e.getMessage()); - } - - PhoneConfigurationManager.registerForMultiSimConfigChange( - this, EVENT_BIND_DATA_SERVICE, null); - - sendEmptyMessage(EVENT_BIND_DATA_SERVICE); - } - - /** - * Handle message events - * - * @param msg The message to handle - */ - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case EVENT_BIND_DATA_SERVICE: - rebindDataService(); - break; - case EVENT_WATCHDOG_TIMEOUT: - handleRequestUnresponded((CellularDataServiceCallback) msg.obj); - break; - default: - loge("Unhandled event " + msg.what); - } - } - - private void handleRequestUnresponded(CellularDataServiceCallback callback) { - String message = "Request " + callback.getTag() + " unresponded on transport " - + AccessNetworkConstants.transportTypeToString(mTransportType) + " in " - + REQUEST_UNRESPONDED_TIMEOUT / 1000 + " seconds."; - log(message); - // Using fixed UUID to avoid duplicate bugreport notification - AnomalyReporter.reportAnomaly( - UUID.fromString("f5d5cbe6-9bd6-4009-b764-42b1b649b1de"), - message, mPhone.getCarrierId()); - } - - private void unbindDataService() { - // Start by cleaning up all packages that *shouldn't* have permissions. - revokePermissionsFromUnusedDataServices(); - if (mIDataService != null && mIDataService.asBinder().isBinderAlive()) { - log("unbinding service"); - // Remove the network availability updater and then unbind the service. - try { - mIDataService.removeDataServiceProvider(mPhone.getPhoneId()); - } catch (RemoteException e) { - loge("Cannot remove data service provider. " + e); - } - } - - if (mServiceConnection != null) { - mPhone.getContext().unbindService(mServiceConnection); - } - mIDataService = null; - mServiceConnection = null; - mTargetBindingPackageName = null; - mBound = false; - } - - private void bindDataService(String packageName) { - if (mPhone == null || !SubscriptionManager.isValidPhoneId(mPhone.getPhoneId())) { - loge("can't bindDataService with invalid phone or phoneId."); - return; - } - - if (TextUtils.isEmpty(packageName)) { - loge("Can't find the binding package"); - return; - } - - Intent intent = null; - String className = getDataServiceClassName(); - if (TextUtils.isEmpty(className)) { - intent = new Intent(DataService.SERVICE_INTERFACE); - intent.setPackage(packageName); - } else { - ComponentName cm = new ComponentName(packageName, className); - intent = new Intent(DataService.SERVICE_INTERFACE).setComponent(cm); - } - - // Then pre-emptively grant the permissions to the package we will bind. - grantPermissionsToService(packageName); - - try { - mServiceConnection = new CellularDataServiceConnection(); - if (!mPhone.getContext().bindService( - intent, mServiceConnection, Context.BIND_AUTO_CREATE)) { - loge("Cannot bind to the data service."); - return; - } - mTargetBindingPackageName = packageName; - } catch (Exception e) { - loge("Cannot bind to the data service. Exception: " + e); - } - } - - private void rebindDataService() { - String packageName = getDataServicePackageName(); - // Do nothing if no need to rebind. - if (SubscriptionManager.isValidPhoneId(mPhone.getPhoneId()) - && TextUtils.equals(packageName, mTargetBindingPackageName)) { - if (DBG) log("Service " + packageName + " already bound or being bound."); - return; - } - - unbindDataService(); - bindDataService(packageName); - } - - @NonNull - private Set<String> getAllDataServicePackageNames() { - // Cowardly using the public PackageManager interface here. - // Note: This matches only packages that were installed on the system image. If we ever - // expand the permissions model to allow CarrierPrivileged packages, then this will need - // to be updated. - List<ResolveInfo> dataPackages = - mPhone.getContext().getPackageManager().queryIntentServices( - new Intent(DataService.SERVICE_INTERFACE), - PackageManager.MATCH_SYSTEM_ONLY); - HashSet<String> packageNames = new HashSet<>(); - for (ResolveInfo info : dataPackages) { - if (info.serviceInfo == null) continue; - packageNames.add(info.serviceInfo.packageName); - } - return packageNames; - } - - /** - * Get the data service package name for our current transport type. - * - * @return package name of the data service package for the the current transportType. - */ - public String getDataServicePackageName() { - return getDataServicePackageName(mTransportType); - } - - /** - * Get the data service package by transport type. - * - * When we bind to a DataService package, we need to revoke permissions from stale - * packages; we need to exclude data packages for all transport types, so we need to - * to be able to query by transport type. - * - * @param transportType The transport type - * @return package name of the data service package for the specified transportType. - */ - private String getDataServicePackageName(@TransportType int transportType) { - String packageName; - int resourceId; - String carrierConfig; - - switch (transportType) { - case AccessNetworkConstants.TRANSPORT_TYPE_WWAN: - resourceId = com.android.internal.R.string.config_wwan_data_service_package; - carrierConfig = CarrierConfigManager - .KEY_CARRIER_DATA_SERVICE_WWAN_PACKAGE_OVERRIDE_STRING; - break; - case AccessNetworkConstants.TRANSPORT_TYPE_WLAN: - resourceId = com.android.internal.R.string.config_wlan_data_service_package; - carrierConfig = CarrierConfigManager - .KEY_CARRIER_DATA_SERVICE_WLAN_PACKAGE_OVERRIDE_STRING; - break; - default: - throw new IllegalStateException("Transport type not WWAN or WLAN. type=" - + AccessNetworkConstants.transportTypeToString(mTransportType)); - } - - // Read package name from resource overlay - packageName = mPhone.getContext().getResources().getString(resourceId); - - PersistableBundle b = mCarrierConfigManager.getConfigForSubId(mPhone.getSubId()); - - if (b != null && !TextUtils.isEmpty(b.getString(carrierConfig))) { - // If carrier config overrides it, use the one from carrier config - packageName = b.getString(carrierConfig, packageName); - } - - return packageName; - } - - /** - * Get the data service class name for our current transport type. - * - * @return class name of the data service package for the the current transportType. - */ - private String getDataServiceClassName() { - return getDataServiceClassName(mTransportType); - } - - - /** - * Get the data service class by transport type. - * - * @param transportType either WWAN or WLAN - * @return class name of the data service package for the specified transportType. - */ - private String getDataServiceClassName(int transportType) { - String className; - int resourceId; - String carrierConfig; - switch (transportType) { - case AccessNetworkConstants.TRANSPORT_TYPE_WWAN: - resourceId = com.android.internal.R.string.config_wwan_data_service_class; - carrierConfig = CarrierConfigManager - .KEY_CARRIER_DATA_SERVICE_WWAN_CLASS_OVERRIDE_STRING; - break; - case AccessNetworkConstants.TRANSPORT_TYPE_WLAN: - resourceId = com.android.internal.R.string.config_wlan_data_service_class; - carrierConfig = CarrierConfigManager - .KEY_CARRIER_DATA_SERVICE_WLAN_CLASS_OVERRIDE_STRING; - break; - default: - throw new IllegalStateException("Transport type not WWAN or WLAN. type=" - + transportType); - } - - // Read package name from resource overlay - className = mPhone.getContext().getResources().getString(resourceId); - - PersistableBundle b = mCarrierConfigManager.getConfigForSubId(mPhone.getSubId()); - - if (b != null && !TextUtils.isEmpty(b.getString(carrierConfig))) { - // If carrier config overrides it, use the one from carrier config - className = b.getString(carrierConfig, className); - } - - return className; - } - - private void sendCompleteMessage(Message msg, @DataServiceCallback.ResultCode int code) { - if (msg != null) { - msg.arg1 = code; - msg.sendToTarget(); - } - } - - /** - * Setup a data connection. The data service provider must implement this method to support - * establishing a packet data connection. When completed or error, the service must invoke - * the provided callback to notify the platform. - * - * @param accessNetworkType Access network type that the data call will be established on. - * Must be one of {@link AccessNetworkConstants.AccessNetworkType}. - * @param dataProfile Data profile used for data call setup. See {@link DataProfile} - * @param isRoaming True if the device is data roaming. - * @param allowRoaming True if data roaming is allowed by the user. - * @param reason The reason for data setup. Must be {@link DataService#REQUEST_REASON_NORMAL} or - * {@link DataService#REQUEST_REASON_HANDOVER}. - * @param linkProperties If {@code reason} is {@link DataService#REQUEST_REASON_HANDOVER}, this - * is the link properties of the existing data connection, otherwise null. - * @param pduSessionId The pdu session id to be used for this data call. A value of -1 means - * no pdu session id was attached to this call. - * Reference: 3GPP TS 24.007 Section 11.2.3.1b - * @param sliceInfo The slice that represents S-NSSAI. - * Reference: 3GPP TS 24.501 - * @param trafficDescriptor The traffic descriptor for this data call, used for URSP matching. - * Reference: 3GPP TS TS 24.526 Section 5.2 - * @param matchAllRuleAllowed True if using the default match-all URSP rule for this request is - * allowed. - * @param onCompleteMessage The result message for this request. Null if the client does not - * care about the result. - */ - public void setupDataCall(int accessNetworkType, DataProfile dataProfile, boolean isRoaming, - boolean allowRoaming, int reason, LinkProperties linkProperties, int pduSessionId, - @Nullable NetworkSliceInfo sliceInfo, @Nullable TrafficDescriptor trafficDescriptor, - boolean matchAllRuleAllowed, Message onCompleteMessage) { - if (DBG) log("setupDataCall"); - if (!mBound) { - loge("setupDataCall: Data service not bound."); - sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE); - return; - } - - CellularDataServiceCallback callback = new CellularDataServiceCallback("setupDataCall"); - if (onCompleteMessage != null) { - mMessageMap.put(callback.asBinder(), onCompleteMessage); - } - try { - sendMessageDelayed(obtainMessage(EVENT_WATCHDOG_TIMEOUT, callback), - REQUEST_UNRESPONDED_TIMEOUT); - mIDataService.setupDataCall(mPhone.getPhoneId(), accessNetworkType, dataProfile, - isRoaming, allowRoaming, reason, linkProperties, pduSessionId, sliceInfo, - trafficDescriptor, matchAllRuleAllowed, callback); - } catch (RemoteException e) { - loge("setupDataCall: Cannot invoke setupDataCall on data service."); - mMessageMap.remove(callback.asBinder()); - sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE); - } - } - - /** - * Deactivate a data connection. The data service provider must implement this method to - * support data connection tear down. When completed or error, the service must invoke the - * provided callback to notify the platform. - * - * @param cid Call id returned in the callback of {@link #setupDataCall(int, DataProfile, - * boolean, boolean, int, LinkProperties, Message)} - * @param reason The reason for data deactivation. Must be - * {@link DataService#REQUEST_REASON_NORMAL}, {@link DataService#REQUEST_REASON_SHUTDOWN} - * or {@link DataService#REQUEST_REASON_HANDOVER}. - * @param onCompleteMessage The result message for this request. Null if the client does not - * care about the result. - */ - public void deactivateDataCall(int cid, int reason, Message onCompleteMessage) { - if (DBG) log("deactivateDataCall"); - if (!mBound) { - loge("Data service not bound."); - sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE); - return; - } - - CellularDataServiceCallback callback = - new CellularDataServiceCallback("deactivateDataCall"); - if (onCompleteMessage != null) { - mMessageMap.put(callback.asBinder(), onCompleteMessage); - } - try { - sendMessageDelayed(obtainMessage(EVENT_WATCHDOG_TIMEOUT, callback), - REQUEST_UNRESPONDED_TIMEOUT); - mIDataService.deactivateDataCall(mPhone.getPhoneId(), cid, reason, callback); - } catch (RemoteException e) { - loge("Cannot invoke deactivateDataCall on data service."); - mMessageMap.remove(callback.asBinder()); - sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE); - } - } - - /** - * Indicates that a handover has begun. This is called on the source transport. - * - * Any resources being transferred cannot be released while a - * handover is underway. - * - * If a handover was unsuccessful, then the framework calls DataServiceManager#cancelHandover. - * The target transport retains ownership over any of the resources being transferred. - * - * If a handover was successful, the framework calls DataServiceManager#deactivateDataCall with - * reason HANDOVER. The target transport now owns the transferred resources and is - * responsible for releasing them. - * - * @param cid The identifier of the data call which is provided in DataCallResponse - * @param onCompleteMessage The result callback for this request. - */ - public void startHandover(int cid, @NonNull Message onCompleteMessage) { - CellularDataServiceCallback callback = - setupCallbackHelper("startHandover", onCompleteMessage); - if (callback == null) { - loge("startHandover: callback == null"); - sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE); - return; - } - - try { - sendMessageDelayed(obtainMessage(EVENT_WATCHDOG_TIMEOUT, callback), - REQUEST_UNRESPONDED_TIMEOUT); - mIDataService.startHandover(mPhone.getPhoneId(), cid, callback); - } catch (RemoteException e) { - loge("Cannot invoke startHandover on data service."); - mMessageMap.remove(callback.asBinder()); - sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE); - } - } - - /** - * Indicates that a handover was cancelled after a call to DataServiceManager#startHandover. - * This is called on the source transport. - * - * Since the handover was unsuccessful, the source transport retains ownership over any of - * the resources being transferred and is still responsible for releasing them. - * - * @param cid The identifier of the data call which is provided in DataCallResponse - * @param onCompleteMessage The result callback for this request. - */ - public void cancelHandover(int cid, @NonNull Message onCompleteMessage) { - CellularDataServiceCallback callback = - setupCallbackHelper("cancelHandover", onCompleteMessage); - if (callback == null) { - sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE); - return; - } - - try { - sendMessageDelayed(obtainMessage(EVENT_WATCHDOG_TIMEOUT, callback), - REQUEST_UNRESPONDED_TIMEOUT); - mIDataService.cancelHandover(mPhone.getPhoneId(), cid, callback); - } catch (RemoteException e) { - loge("Cannot invoke cancelHandover on data service."); - mMessageMap.remove(callback.asBinder()); - sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE); - } - } - - @Nullable - private CellularDataServiceCallback setupCallbackHelper( - @NonNull final String operationName, @NonNull final Message onCompleteMessage) { - if (DBG) log(operationName); - if (!mBound) { - sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE); - return null; - } - - CellularDataServiceCallback callback = - new CellularDataServiceCallback(operationName); - if (onCompleteMessage != null) { - if (DBG) log(operationName + ": onCompleteMessage set"); - mMessageMap.put(callback.asBinder(), onCompleteMessage); - } else { - if (DBG) log(operationName + ": onCompleteMessage not set"); - } - return callback; - } - - /** - * Set an APN to initial attach network. - * - * @param dataProfile Data profile used for data call setup. See {@link DataProfile}. - * @param isRoaming True if the device is data roaming. - * @param onCompleteMessage The result message for this request. Null if the client does not - * care about the result. - */ - public void setInitialAttachApn(DataProfile dataProfile, boolean isRoaming, - Message onCompleteMessage) { - if (DBG) log("setInitialAttachApn"); - if (!mBound) { - loge("Data service not bound."); - sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE); - return; - } - - CellularDataServiceCallback callback = - new CellularDataServiceCallback("setInitialAttachApn"); - if (onCompleteMessage != null) { - mMessageMap.put(callback.asBinder(), onCompleteMessage); - } - try { - mIDataService.setInitialAttachApn(mPhone.getPhoneId(), dataProfile, isRoaming, - callback); - } catch (RemoteException e) { - loge("Cannot invoke setInitialAttachApn on data service."); - mMessageMap.remove(callback.asBinder()); - sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE); - } - } - - /** - * Send current carrier's data profiles to the data service for data call setup. This is - * only for CDMA carrier that can change the profile through OTA. The data service should - * always uses the latest data profile sent by the framework. - * - * @param dps A list of data profiles. - * @param isRoaming True if the device is data roaming. - * @param onCompleteMessage The result message for this request. Null if the client does not - * care about the result. - */ - public void setDataProfile(List<DataProfile> dps, boolean isRoaming, - Message onCompleteMessage) { - if (DBG) log("setDataProfile"); - if (!mBound) { - loge("Data service not bound."); - sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE); - return; - } - - CellularDataServiceCallback callback = new CellularDataServiceCallback("setDataProfile"); - if (onCompleteMessage != null) { - mMessageMap.put(callback.asBinder(), onCompleteMessage); - } - try { - mIDataService.setDataProfile(mPhone.getPhoneId(), dps, isRoaming, callback); - } catch (RemoteException e) { - loge("Cannot invoke setDataProfile on data service."); - mMessageMap.remove(callback.asBinder()); - sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE); - } - } - - /** - * Get the active data call list. - * - * @param onCompleteMessage The result message for this request. Null if the client does not - * care about the result. - */ - public void requestDataCallList(Message onCompleteMessage) { - if (DBG) log("requestDataCallList"); - if (!mBound) { - loge("Data service not bound."); - sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE); - return; - } - - CellularDataServiceCallback callback = - new CellularDataServiceCallback("requestDataCallList"); - if (onCompleteMessage != null) { - mMessageMap.put(callback.asBinder(), onCompleteMessage); - } - try { - mIDataService.requestDataCallList(mPhone.getPhoneId(), callback); - } catch (RemoteException e) { - loge("Cannot invoke requestDataCallList on data service."); - if (callback != null) { - mMessageMap.remove(callback.asBinder()); - } - sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE); - } - } - - /** - * Register for data call list changed event. - * - * @param h The target to post the event message to. - * @param what The event. - */ - public void registerForDataCallListChanged(Handler h, int what) { - if (h != null) { - mDataCallListChangedRegistrants.addUnique(h, what, null); - } - } - - /** - * Unregister for data call list changed event. - * - * @param h The handler - */ - public void unregisterForDataCallListChanged(Handler h) { - if (h != null) { - mDataCallListChangedRegistrants.remove(h); - } - } - - /** - * Register apn unthrottled event - * - * @param h The target to post the event message to. - * @param what The event. - */ - public void registerForApnUnthrottled(Handler h, int what) { - if (h != null) { - mApnUnthrottledRegistrants.addUnique(h, what, null); - } - } - - /** - * Unregister for apn unthrottled event - * - * @param h The handler - */ - public void unregisterForApnUnthrottled(Handler h) { - if (h != null) { - mApnUnthrottledRegistrants.remove(h); - } - } - - /** - * Register for data service binding status changed event. - * - * @param h The target to post the event message to. - * @param what The event. - * @param obj The user object. - */ - public void registerForServiceBindingChanged(Handler h, int what, Object obj) { - if (h != null) { - mServiceBindingChangedRegistrants.addUnique(h, what, obj); - } - - } - - /** - * Unregister for data service binding status changed event. - * - * @param h The handler - */ - public void unregisterForServiceBindingChanged(Handler h) { - if (h != null) { - mServiceBindingChangedRegistrants.remove(h); - } - } - - /** - * Get the transport type. Must be a {@link TransportType}. - * - * @return - */ - @TransportType - public int getTransportType() { - return mTransportType; - } - - private void log(String s) { - Rlog.d(mTag, s); - } - - private void loge(String s) { - Rlog.e(mTag, s); - } -}
diff --git a/src/java/com/android/internal/telephony/dataconnection/DataThrottler.java b/src/java/com/android/internal/telephony/dataconnection/DataThrottler.java deleted file mode 100644 index 4a465d2..0000000 --- a/src/java/com/android/internal/telephony/dataconnection/DataThrottler.java +++ /dev/null
@@ -1,366 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.internal.telephony.dataconnection; - -import android.annotation.ElapsedRealtimeLong; -import android.annotation.NonNull; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.os.AsyncResult; -import android.os.Handler; -import android.os.Message; -import android.os.PersistableBundle; -import android.telephony.AccessNetworkConstants; -import android.telephony.Annotation; -import android.telephony.Annotation.ApnType; -import android.telephony.CarrierConfigManager; -import android.telephony.SubscriptionManager; -import android.telephony.data.ApnSetting; -import android.telephony.data.ThrottleStatus; - -import com.android.internal.telephony.Phone; -import com.android.internal.telephony.RetryManager; -import com.android.telephony.Rlog; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -/** - * Data throttler tracks the throttling status of the data network and notifies registrants when - * there are changes. The throttler is per phone and per transport type. - */ -public class DataThrottler extends Handler { - private static final String TAG = DataThrottler.class.getSimpleName(); - - private static final int EVENT_SET_RETRY_TIME = 1; - private static final int EVENT_CARRIER_CONFIG_CHANGED = 2; - private static final int EVENT_RESET = 3; - private static final int EVENT_AIRPLANE_MODE_CHANGED = 4; - private static final int EVENT_TRACING_AREA_CODE_CHANGED = 5; - - private final Phone mPhone; - private final int mSlotIndex; - private final @AccessNetworkConstants.TransportType int mTransportType; - private boolean mResetWhenAreaCodeChanged = false; - - /** - * Callbacks that report the apn throttle status. - */ - private final List<DataThrottler.Callback> mCallbacks = new ArrayList<>(); - - /** - * Keeps track of detailed information of the throttle status that is meant to be - * reported to other components. - */ - private final Map<Integer, ThrottleStatus> mThrottleStatus = new ConcurrentHashMap<>(); - - private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - if (intent.getAction().equals(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)) { - if (mPhone.getPhoneId() == intent.getIntExtra(CarrierConfigManager.EXTRA_SLOT_INDEX, - SubscriptionManager.INVALID_SIM_SLOT_INDEX)) { - if (intent.getBooleanExtra( - CarrierConfigManager.EXTRA_REBROADCAST_ON_UNLOCK, false)) { - // Ignore the rebroadcast one to prevent multiple carrier config changed - // event during boot up. - return; - } - int subId = intent.getIntExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, - SubscriptionManager.INVALID_SUBSCRIPTION_ID); - if (SubscriptionManager.isValidSubscriptionId(subId)) { - sendEmptyMessage(EVENT_CARRIER_CONFIG_CHANGED); - } - } - } - } - }; - - public DataThrottler(Phone phone, int transportType) { - super(null, false); - mPhone = phone; - mSlotIndex = phone.getPhoneId(); - mTransportType = transportType; - - IntentFilter filter = new IntentFilter(); - filter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED); - mPhone.getContext().registerReceiver(mBroadcastReceiver, filter, null, mPhone); - - mPhone.getServiceStateTracker().registerForAirplaneModeChanged(this, - EVENT_AIRPLANE_MODE_CHANGED, null); - mPhone.getServiceStateTracker().registerForAreaCodeChanged(this, - EVENT_TRACING_AREA_CODE_CHANGED, null); - } - - @Override - public void handleMessage(Message msg) { - AsyncResult ar; - switch (msg.what) { - case EVENT_SET_RETRY_TIME: - int apnTypes = msg.arg1; - int newRequestType = msg.arg2; - long retryElapsedTime = (long) msg.obj; - setRetryTimeInternal(apnTypes, retryElapsedTime, newRequestType); - break; - case EVENT_CARRIER_CONFIG_CHANGED: - onCarrierConfigChanged(); - break; - case EVENT_RESET: - resetInternal(); - break; - case EVENT_AIRPLANE_MODE_CHANGED: - ar = (AsyncResult) msg.obj; - if (!(Boolean) ar.result) { - resetInternal(); - } - break; - case EVENT_TRACING_AREA_CODE_CHANGED: - if (mResetWhenAreaCodeChanged) { - resetInternal(); - } - break; - } - } - - @NonNull - private PersistableBundle getCarrierConfig() { - CarrierConfigManager configManager = (CarrierConfigManager) mPhone.getContext() - .getSystemService(Context.CARRIER_CONFIG_SERVICE); - if (configManager != null) { - // If an invalid subId is used, this bundle will contain default values. - PersistableBundle config = configManager.getConfigForSubId(mPhone.getSubId()); - if (config != null) { - return config; - } - } - // Return static default defined in CarrierConfigManager. - return CarrierConfigManager.getDefaultConfig(); - } - - private void onCarrierConfigChanged() { - PersistableBundle config = getCarrierConfig(); - mResetWhenAreaCodeChanged = config.getBoolean( - CarrierConfigManager.KEY_UNTHROTTLE_DATA_RETRY_WHEN_TAC_CHANGES_BOOL, false); - } - - /** - * Set the retry time and handover failure mode for the give APN types. - * - * @param apnTypes APN types - * @param retryElapsedTime The elapsed time that data connection for APN types should not be - * retried. {@link RetryManager#NO_SUGGESTED_RETRY_DELAY} indicates throttling does not exist. - * {@link RetryManager#NO_RETRY} indicates retry should never happen. - */ - public void setRetryTime(@ApnType int apnTypes, @ElapsedRealtimeLong long retryElapsedTime, - @DcTracker.RequestNetworkType int newRequestType) { - sendMessage(obtainMessage(EVENT_SET_RETRY_TIME, apnTypes, newRequestType, - retryElapsedTime)); - } - - /** - * Set the retry time and handover failure mode for the give APN types. This is running on the - * handler thread. - * - * @param apnTypes APN types - * @param retryElapsedTime The elapsed time that data connection for APN types should not be - * retried. {@link RetryManager#NO_SUGGESTED_RETRY_DELAY} indicates throttling does not exist. - * {@link RetryManager#NO_RETRY} indicates retry should never happen. - */ - private void setRetryTimeInternal(@ApnType int apnTypes, @ElapsedRealtimeLong - long retryElapsedTime, @DcTracker.RequestNetworkType int newRequestType) { - if (retryElapsedTime < 0) { - retryElapsedTime = RetryManager.NO_SUGGESTED_RETRY_DELAY; - } - - List<ThrottleStatus> changedStatuses = new ArrayList<>(); - while (apnTypes != 0) { - int apnType; - // Due to an API mistake of ApnSetting.TYPE_DEFAULT (which combines default and hipri - // bit), we need to do special handling here. - if ((apnTypes & ApnSetting.TYPE_DEFAULT) == ApnSetting.TYPE_DEFAULT) { - apnType = ApnSetting.TYPE_DEFAULT; - apnTypes &= ~ApnSetting.TYPE_DEFAULT; - } else { - //Extract the least significant bit. - apnType = apnTypes & -apnTypes; - //Remove the least significant bit. - apnTypes &= apnTypes - 1; - } - - //Update the apn throttle status - ThrottleStatus newStatus = createStatus(apnType, retryElapsedTime, newRequestType); - - ThrottleStatus oldStatus = mThrottleStatus.get(apnType); - - //Check to see if there is a change that needs to be applied - if (!newStatus.equals(oldStatus)) { - //Mark as changed status - changedStatuses.add(newStatus); - - //Put the new status in the temp space - mThrottleStatus.put(apnType, newStatus); - } - } - - if (changedStatuses.size() > 0) { - sendThrottleStatusChanged(changedStatuses); - } - } - - /** - * Get the earliest retry time for the given APN type. The time is the system's elapse time. - * - * Note the DataThrottler is running phone process's main thread, which is most of the telephony - * components running on. Calling this method from other threads might run into race conditions. - * - * @param apnType APN type - * @return The earliest retry time for APN type. The time is the system's elapse time. - * {@link RetryManager#NO_SUGGESTED_RETRY_DELAY} indicates there is no throttling for given APN - * type, {@link RetryManager#NO_RETRY} indicates retry should never happen. - */ - @ElapsedRealtimeLong - public long getRetryTime(@ApnType int apnType) { - ThrottleStatus status = mThrottleStatus.get(apnType); - if (status != null) { - if (status.getThrottleType() == ThrottleStatus.THROTTLE_TYPE_NONE) { - return RetryManager.NO_SUGGESTED_RETRY_DELAY; - } else { - return status.getThrottleExpiryTimeMillis(); - } - } - return RetryManager.NO_SUGGESTED_RETRY_DELAY; - } - - /** - * Resets retry times for all APNs to {@link RetryManager.NO_SUGGESTED_RETRY_DELAY}. - */ - public void reset() { - sendEmptyMessage(EVENT_RESET); - } - - /** - * Resets retry times for all APNs to {@link RetryManager.NO_SUGGESTED_RETRY_DELAY}. - */ - private void resetInternal() { - final List<Integer> apnTypes = new ArrayList<>(); - for (ThrottleStatus throttleStatus : mThrottleStatus.values()) { - apnTypes.add(throttleStatus.getApnType()); - } - - for (int apnType : apnTypes) { - setRetryTime(apnType, RetryManager.NO_SUGGESTED_RETRY_DELAY, - DcTracker.REQUEST_TYPE_NORMAL); - } - } - - private ThrottleStatus createStatus(@Annotation.ApnType int apnType, long retryElapsedTime, - @DcTracker.RequestNetworkType int newRequestType) { - ThrottleStatus.Builder builder = new ThrottleStatus.Builder(); - - if (retryElapsedTime == RetryManager.NO_SUGGESTED_RETRY_DELAY) { - builder - .setNoThrottle() - .setRetryType(getRetryType(newRequestType)); - } else if (retryElapsedTime == RetryManager.NO_RETRY) { - builder - .setThrottleExpiryTimeMillis(RetryManager.NO_RETRY) - .setRetryType(ThrottleStatus.RETRY_TYPE_NONE); - } else { - builder - .setThrottleExpiryTimeMillis(retryElapsedTime) - .setRetryType(getRetryType(newRequestType)); - } - return builder - .setSlotIndex(mSlotIndex) - .setTransportType(mTransportType) - .setApnType(apnType) - .build(); - } - - private static int getRetryType(@DcTracker.RequestNetworkType int newRequestType) { - if (newRequestType == DcTracker.REQUEST_TYPE_NORMAL) { - return ThrottleStatus.RETRY_TYPE_NEW_CONNECTION; - } - - if (newRequestType == DcTracker.REQUEST_TYPE_HANDOVER) { - return ThrottleStatus.RETRY_TYPE_HANDOVER; - } - - loge("createStatus: Unknown requestType=" + newRequestType); - return ThrottleStatus.RETRY_TYPE_NEW_CONNECTION; - } - - private void sendThrottleStatusChanged(List<ThrottleStatus> statuses) { - synchronized (mCallbacks) { - for (int i = 0; i < mCallbacks.size(); i++) { - mCallbacks.get(i).onThrottleStatusChanged(statuses); - } - } - } - - private static void loge(String s) { - Rlog.e(TAG, s); - } - - /** - * Reports changes to apn throttle statuses. - * - * Note: All statuses are sent when first registered. - * - * @param callback status changes callback - */ - public void registerForThrottleStatusChanges(DataThrottler.Callback callback) { - synchronized (mCallbacks) { - //Only add if it's not there already - if (!mCallbacks.contains(callback)) { - //Report everything the first time - List<ThrottleStatus> throttleStatuses = - new ArrayList<>(mThrottleStatus.values()); - callback.onThrottleStatusChanged(throttleStatuses); - mCallbacks.add(callback); - } - } - } - - /** - * Unregister the callback - * @param callback the callback to unregister - */ - public void unregisterForThrottleStatusChanges(DataThrottler.Callback callback) { - synchronized (mCallbacks) { - mCallbacks.remove(callback); - } - } - - /** - * Callback for when throttle statuses change - */ - public interface Callback { - /** - * Called whenever the throttle status of an APN has changed. - * - * Note: Called with all statuses when first registered. - * - * @param throttleStatuses the status changes - */ - void onThrottleStatusChanged(List<ThrottleStatus> throttleStatuses); - } -}
diff --git a/src/java/com/android/internal/telephony/dataconnection/DcController.java b/src/java/com/android/internal/telephony/dataconnection/DcController.java deleted file mode 100644 index c34157e..0000000 --- a/src/java/com/android/internal/telephony/dataconnection/DcController.java +++ /dev/null
@@ -1,487 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.internal.telephony.dataconnection; - -import android.hardware.radio.V1_4.DataConnActiveStatus; -import android.net.LinkAddress; -import android.os.AsyncResult; -import android.os.Handler; -import android.os.Looper; -import android.os.Message; -import android.os.RegistrantList; -import android.telephony.AccessNetworkConstants; -import android.telephony.CarrierConfigManager; -import android.telephony.DataFailCause; -import android.telephony.data.ApnSetting; -import android.telephony.data.DataCallResponse; -import android.telephony.data.TrafficDescriptor; - -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; -import com.android.internal.telephony.util.TelephonyUtils; -import com.android.net.module.util.LinkPropertiesUtils; -import com.android.net.module.util.LinkPropertiesUtils.CompareOrUpdateResult; -import com.android.net.module.util.NetUtils; -import com.android.telephony.Rlog; - -import java.io.FileDescriptor; -import java.io.PrintWriter; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Objects; - -/** - * Data Connection Controller which is a package visible class and controls - * multiple data connections. For instance listening for unsolicited messages - * and then demultiplexing them to the appropriate DC. - */ -public class DcController extends Handler { - private static final boolean DBG = true; - private static final boolean VDBG = false; - - private final Phone mPhone; - private final DcTracker mDct; - private final String mTag; - private final DataServiceManager mDataServiceManager; - private final DcTesterDeactivateAll mDcTesterDeactivateAll; - - // package as its used by Testing code - // @GuardedBy("mDcListAll") - final ArrayList<DataConnection> mDcListAll = new ArrayList<>(); - // @GuardedBy("mDcListAll") - private final HashMap<Integer, DataConnection> mDcListActiveByCid = new HashMap<>(); - // @GuardedBy("mTrafficDescriptorsByCid") - private final HashMap<Integer, List<TrafficDescriptor>> mTrafficDescriptorsByCid = - new HashMap<>(); - - /** - * Aggregated physical link status from all data connections. This reflects the device's RRC - * connection state. - * 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 @DataCallResponse.LinkStatus int mPhysicalLinkStatus = - DataCallResponse.LINK_STATUS_UNKNOWN; - - private RegistrantList mPhysicalLinkStatusChangedRegistrants = new RegistrantList(); - - /** - * Constructor. - * - * @param name to be used for the Controller - * @param phone the phone associated with Dcc and Dct - * @param dct the DataConnectionTracker associated with Dcc - * @param dataServiceManager the data service manager that manages data services - * @param looper looper for this handler - */ - private DcController(String name, Phone phone, DcTracker dct, - DataServiceManager dataServiceManager, Looper looper) { - super(looper); - mPhone = phone; - mDct = dct; - mTag = name; - mDataServiceManager = dataServiceManager; - - mDcTesterDeactivateAll = (TelephonyUtils.IS_DEBUGGABLE) - ? new DcTesterDeactivateAll(mPhone, DcController.this, this) - : null; - mDataServiceManager.registerForDataCallListChanged(this, - DataConnection.EVENT_DATA_STATE_CHANGED); - } - - public static DcController makeDcc(Phone phone, DcTracker dct, - DataServiceManager dataServiceManager, Looper looper, - String tagSuffix) { - return new DcController("Dcc" + tagSuffix, phone, dct, dataServiceManager, looper); - } - - void addDc(DataConnection dc) { - synchronized (mDcListAll) { - mDcListAll.add(dc); - } - } - - void removeDc(DataConnection dc) { - synchronized (mDcListAll) { - mDcListActiveByCid.remove(dc.mCid); - mDcListAll.remove(dc); - } - synchronized (mTrafficDescriptorsByCid) { - mTrafficDescriptorsByCid.remove(dc.mCid); - } - } - - public void addActiveDcByCid(DataConnection dc) { - if (DBG && dc.mCid < 0) { - log("addActiveDcByCid dc.mCid < 0 dc=" + dc); - } - synchronized (mDcListAll) { - mDcListActiveByCid.put(dc.mCid, dc); - } - updateTrafficDescriptorsForCid(dc.mCid, dc.getTrafficDescriptors()); - } - - DataConnection getActiveDcByCid(int cid) { - synchronized (mDcListAll) { - return mDcListActiveByCid.get(cid); - } - } - - void removeActiveDcByCid(DataConnection dc) { - synchronized (mDcListAll) { - DataConnection removedDc = mDcListActiveByCid.remove(dc.mCid); - if (DBG && removedDc == null) { - log("removeActiveDcByCid removedDc=null dc=" + dc); - } - } - synchronized (mTrafficDescriptorsByCid) { - mTrafficDescriptorsByCid.remove(dc.mCid); - } - } - - boolean isDefaultDataActive() { - synchronized (mDcListAll) { - return mDcListActiveByCid.values().stream() - .anyMatch(dc -> dc.getApnContexts().stream() - .anyMatch(apn -> apn.getApnTypeBitmask() == ApnSetting.TYPE_DEFAULT)); - } - } - - List<TrafficDescriptor> getTrafficDescriptorsForCid(int cid) { - synchronized (mTrafficDescriptorsByCid) { - return mTrafficDescriptorsByCid.get(cid); - } - } - - void updateTrafficDescriptorsForCid(int cid, List<TrafficDescriptor> tds) { - synchronized (mTrafficDescriptorsByCid) { - mTrafficDescriptorsByCid.put(cid, tds); - } - } - - @Override - public void handleMessage(Message msg) { - AsyncResult ar; - - switch (msg.what) { - case DataConnection.EVENT_DATA_STATE_CHANGED: - ar = (AsyncResult) msg.obj; - if (ar.exception == null) { - onDataStateChanged((ArrayList<DataCallResponse>) ar.result); - } else { - log("EVENT_DATA_STATE_CHANGED: exception; likely radio not available, ignore"); - } - break; - default: - loge("Unexpected event " + msg); - break; - } - } - - /** - * Process the new list of "known" Data Calls - * @param dcsList as sent by RIL_UNSOL_DATA_CALL_LIST_CHANGED - */ - private void onDataStateChanged(ArrayList<DataCallResponse> dcsList) { - final HashMap<Integer, DataConnection> dcListActiveByCid; - synchronized (mDcListAll) { - dcListActiveByCid = new HashMap<>(mDcListActiveByCid); - } - - if (DBG) { - log("onDataStateChanged: dcsList=" + dcsList - + " dcListActiveByCid=" + dcListActiveByCid); - } - - // Create hashmap of cid to DataCallResponse - HashMap<Integer, DataCallResponse> dataCallResponseListByCid = new HashMap<>(); - for (DataCallResponse dcs : dcsList) { - dataCallResponseListByCid.put(dcs.getId(), dcs); - } - - // Add a DC that is active but not in the dcsList to the list of DC's to retry - ArrayList<DataConnection> dcsToRetry = new ArrayList<>(); - for (DataConnection dc : dcListActiveByCid.values()) { - DataCallResponse response = dataCallResponseListByCid.get(dc.mCid); - if (response == null) { - if (DBG) log("onDataStateChanged: add to retry dc=" + dc); - dcsToRetry.add(dc); - } else { - List<TrafficDescriptor> oldTds = getTrafficDescriptorsForCid(dc.mCid); - List<TrafficDescriptor> newTds = response.getTrafficDescriptors(); - if (!oldTds.equals(newTds)) { - if (DBG) { - log("onDataStateChanged: add to retry due to TD changed dc=" + dc - + ", oldTds=" + oldTds + ", newTds=" + newTds); - } - updateTrafficDescriptorsForCid(dc.mCid, newTds); - dcsToRetry.add(dc); - } - } - } - if (DBG) log("onDataStateChanged: dcsToRetry=" + dcsToRetry); - - // Find which connections have changed state and send a notification or cleanup - // and any that are in active need to be retried. - ArrayList<ApnContext> apnsToCleanup = new ArrayList<ApnContext>(); - - boolean isAnyDataCallDormant = false; - boolean isAnyDataCallActive = false; - boolean isInternetDataCallActive = false; - - for (DataCallResponse newState : dcsList) { - - DataConnection dc = dcListActiveByCid.get(newState.getId()); - if (dc == null) { - // UNSOL_DATA_CALL_LIST_CHANGED arrived before SETUP_DATA_CALL completed. - loge("onDataStateChanged: no associated DC yet, ignore"); - continue; - } - - List<ApnContext> apnContexts = dc.getApnContexts(); - if (apnContexts.size() == 0) { - if (DBG) loge("onDataStateChanged: no connected apns, ignore"); - } else { - // Determine if the connection/apnContext should be cleaned up - // or just a notification should be sent out. - if (DBG) { - 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); - mDct.isCleanupRequired.set(false); - } else { - int failCause = DataFailCause.getFailCause(newState.getCause()); - if (DataFailCause.isRadioRestartFailure(mPhone.getContext(), failCause, - mPhone.getSubId())) { - if (DBG) { - log("onDataStateChanged: X restart radio, failCause=" - + failCause); - } - mDct.sendRestartRadio(); - } else if (mDct.isPermanentFailure(failCause)) { - if (DBG) { - log("onDataStateChanged: inactive, add to cleanup list. " - + "failCause=" + failCause); - } - apnsToCleanup.addAll(apnContexts); - } else { - if (DBG) { - log("onDataStateChanged: inactive, add to retry list. " - + "failCause=" + failCause); - } - dcsToRetry.add(dc); - } - } - } else { - // Update the pdu session id - dc.setPduSessionId(newState.getPduSessionId()); - - dc.updatePcscfAddr(newState); - - // Its active so update the DataConnections link properties - UpdateLinkPropertyResult result = dc.updateLinkProperty(newState); - dc.updateResponseFields(newState); - if (result.oldLp.equals(result.newLp)) { - if (DBG) log("onDataStateChanged: no change"); - } else { - if (LinkPropertiesUtils.isIdenticalInterfaceName( - result.oldLp, result.newLp)) { - if (!LinkPropertiesUtils.isIdenticalDnses( - result.oldLp, result.newLp) - || !LinkPropertiesUtils.isIdenticalRoutes( - result.oldLp, result.newLp) - || !LinkPropertiesUtils.isIdenticalHttpProxy( - result.oldLp, result.newLp) - || !LinkPropertiesUtils.isIdenticalAddresses( - result.oldLp, result.newLp)) { - // If the same address type was removed and - // added we need to cleanup - CompareOrUpdateResult<Integer, LinkAddress> car - = new CompareOrUpdateResult( - result.oldLp != null ? - result.oldLp.getLinkAddresses() : null, - result.newLp != null ? - result.newLp.getLinkAddresses() : null, - (la) -> Objects.hash(((LinkAddress)la).getAddress(), - ((LinkAddress)la).getPrefixLength(), - ((LinkAddress)la).getScope())); - if (DBG) { - log("onDataStateChanged: oldLp=" + result.oldLp - + " newLp=" + result.newLp + " car=" + car); - } - boolean needToClean = false; - for (LinkAddress added : car.added) { - for (LinkAddress removed : car.removed) { - if (NetUtils.addressTypeMatches( - removed.getAddress(), - added.getAddress())) { - needToClean = true; - break; - } - } - } - if (needToClean) { - if (DBG) { - log("onDataStateChanged: addr change," - + " cleanup apns=" + apnContexts - + " oldLp=" + result.oldLp - + " newLp=" + result.newLp); - } - apnsToCleanup.addAll(apnContexts); - } - } else { - if (DBG) { - log("onDataStateChanged: no changes"); - } - } - } else { - apnsToCleanup.addAll(apnContexts); - if (DBG) { - log("onDataStateChanged: interface change, cleanup apns=" - + apnContexts); - } - } - } - } - } - - if (newState.getLinkStatus() == DataConnActiveStatus.ACTIVE) { - isAnyDataCallActive = true; - } - if (newState.getLinkStatus() == DataConnActiveStatus.DORMANT) { - isAnyDataCallDormant = true; - } - } - - if (mDataServiceManager.getTransportType() - == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) { - boolean isPhysicalLinkStatusFocusingOnInternetData = - mDct.getLteEndcUsingUserDataForIdleDetection(); - int physicalLinkStatus = - (isPhysicalLinkStatusFocusingOnInternetData - ? isInternetDataCallActive : isAnyDataCallActive) - ? DataCallResponse.LINK_STATUS_ACTIVE - : DataCallResponse.LINK_STATUS_DORMANT; - if (mPhysicalLinkStatus != physicalLinkStatus) { - mPhysicalLinkStatus = physicalLinkStatus; - mPhysicalLinkStatusChangedRegistrants.notifyResult(mPhysicalLinkStatus); - } - if (isAnyDataCallDormant && !isAnyDataCallActive) { - // There is no way to indicate link activity per APN right now. So - // Link Activity will be considered dormant only when all data calls - // are dormant. - // If a single data call is in dormant state and none of the data - // calls are active broadcast overall link status as dormant. - if (DBG) { - log("onDataStateChanged: Data activity DORMANT. stopNetStatePoll"); - } - mDct.sendStopNetStatPoll(DctConstants.Activity.DORMANT); - } else { - if (DBG) { - log("onDataStateChanged: Data Activity updated to NONE. " - + "isAnyDataCallActive = " + isAnyDataCallActive - + " isAnyDataCallDormant = " + isAnyDataCallDormant); - } - if (isAnyDataCallActive) { - mDct.sendStartNetStatPoll(DctConstants.Activity.NONE); - } - } - } - - if (DBG) { - log("onDataStateChanged: dcsToRetry=" + dcsToRetry - + " apnsToCleanup=" + apnsToCleanup); - } - - // Cleanup connections that have changed - for (ApnContext apnContext : apnsToCleanup) { - mDct.cleanUpConnection(apnContext); - } - - // Retry connections that have disappeared - for (DataConnection dc : dcsToRetry) { - if (DBG) log("onDataStateChanged: send EVENT_LOST_CONNECTION dc.mTag=" + dc.mTag); - dc.sendMessage(DataConnection.EVENT_LOST_CONNECTION, dc.mTag); - } - - if (VDBG) log("onDataStateChanged: X"); - } - - /** - * Register for physical link status (i.e. RRC state) changed event. - * if {@link CarrierConfigManager#KEY_LTE_ENDC_USING_USER_DATA_FOR_RRC_DETECTION_BOOL} is true, - * then physical link status is focusing on "internet data connection" instead of RRC state. - * @param h The handler - * @param what The event - */ - @VisibleForTesting - public void registerForPhysicalLinkStatusChanged(Handler h, int what) { - mPhysicalLinkStatusChangedRegistrants.addUnique(h, what, null); - } - - /** - * Unregister from physical link status (i.e. RRC state) changed event. - * - * @param h The previously registered handler - */ - void unregisterForPhysicalLinkStatusChanged(Handler h) { - mPhysicalLinkStatusChangedRegistrants.remove(h); - } - - private void log(String s) { - Rlog.d(mTag, s); - } - - private void loge(String s) { - Rlog.e(mTag, s); - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - synchronized (mDcListAll) { - sb.append("mDcListAll=").append(mDcListAll) - .append(" mDcListActiveByCid=").append(mDcListActiveByCid); - } - synchronized (mTrafficDescriptorsByCid) { - sb.append("mTrafficDescriptorsByCid=").append(mTrafficDescriptorsByCid); - } - return sb.toString(); - } - - public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - pw.println(" mPhone=" + mPhone); - synchronized (mDcListAll) { - pw.println(" mDcListAll=" + mDcListAll); - pw.println(" mDcListActiveByCid=" + mDcListActiveByCid); - } - synchronized (mTrafficDescriptorsByCid) { - pw.println(" mTrafficDescriptorsByCid=" + mTrafficDescriptorsByCid); - } - } -}
diff --git a/src/java/com/android/internal/telephony/dataconnection/DcFailBringUp.java b/src/java/com/android/internal/telephony/dataconnection/DcFailBringUp.java deleted file mode 100644 index 3cdd209..0000000 --- a/src/java/com/android/internal/telephony/dataconnection/DcFailBringUp.java +++ /dev/null
@@ -1,85 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.internal.telephony.dataconnection; - -import android.content.Intent; -import android.telephony.Annotation.DataFailureCause; -import android.telephony.DataFailCause; - -import com.android.telephony.Rlog; - -/** - * A package visible class for supporting testing failing bringUp commands. This - * saves the parameters from a action_fail_bringup intent. See - * {@link DataConnection#doOnConnect} and {@see DcTesterFailBringUpAll} for more info. - */ -public class DcFailBringUp { - private static final String LOG_TAG = "DcFailBringUp"; - private static final boolean DBG = true; - - static final String INTENT_BASE = DataConnection.class.getPackage().getName(); - - static final String ACTION_FAIL_BRINGUP = "action_fail_bringup"; - - // counter with its --ei option name and default value - static final String COUNTER = "counter"; - static final int DEFAULT_COUNTER = 2; - int mCounter; - - // failCause with its --ei option name and default value - static final String FAIL_CAUSE = "fail_cause"; - static final int DEFAULT_FAIL_CAUSE = DataFailCause.ERROR_UNSPECIFIED; - @DataFailureCause - int mFailCause; - - // suggestedRetryTime with its --ei option name and default value - static final String SUGGESTED_RETRY_TIME = "suggested_retry_time"; - static final long DEFAULT_SUGGESTED_RETRY_TIME = -1; - long mSuggestedRetryTime; - - // Get the Extra Intent parameters - void saveParameters(Intent intent, String s) { - if (DBG) log(s + ".saveParameters: action=" + intent.getAction()); - mCounter = intent.getIntExtra(COUNTER, DEFAULT_COUNTER); - mFailCause = DataFailCause.getFailCause( - intent.getIntExtra(FAIL_CAUSE, DEFAULT_FAIL_CAUSE)); - mSuggestedRetryTime = - intent.getLongExtra(SUGGESTED_RETRY_TIME, DEFAULT_SUGGESTED_RETRY_TIME); - if (DBG) { - log(s + ".saveParameters: " + this); - } - } - - public void saveParameters(int counter, @DataFailureCause int failCause, - long suggestedRetryTime) { - mCounter = counter; - mFailCause = DataFailCause.getFailCause(failCause); - mSuggestedRetryTime = suggestedRetryTime; - } - - @Override - public String toString() { - return "{mCounter=" + mCounter + - " mFailCause=" + mFailCause + - " mSuggestedRetryTime=" + mSuggestedRetryTime + "}"; - - } - - private static void log(String s) { - Rlog.d(LOG_TAG, s); - } -}
diff --git a/src/java/com/android/internal/telephony/dataconnection/DcNetworkAgent.java b/src/java/com/android/internal/telephony/dataconnection/DcNetworkAgent.java deleted file mode 100644 index c6def34..0000000 --- a/src/java/com/android/internal/telephony/dataconnection/DcNetworkAgent.java +++ /dev/null
@@ -1,617 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.internal.telephony.dataconnection; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.net.KeepalivePacketData; -import android.net.LinkProperties; -import android.net.NattKeepalivePacketData; -import android.net.NetworkAgent; -import android.net.NetworkAgentConfig; -import android.net.NetworkCapabilities; -import android.net.NetworkProvider; -import android.net.QosFilter; -import android.net.QosSessionAttributes; -import android.net.SocketKeepalive; -import android.net.Uri; -import android.os.Handler; -import android.os.Message; -import android.telephony.AccessNetworkConstants; -import android.telephony.AccessNetworkConstants.TransportType; -import android.telephony.Annotation.NetworkType; -import android.telephony.AnomalyReporter; -import android.telephony.NetworkRegistrationInfo; -import android.telephony.ServiceState; -import android.telephony.TelephonyManager; -import android.telephony.data.QosBearerSession; -import android.util.LocalLog; -import android.util.SparseArray; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.telephony.DctConstants; -import com.android.internal.telephony.Phone; -import com.android.internal.telephony.RILConstants; -import com.android.internal.telephony.SlidingWindowEventCounter; -import com.android.internal.telephony.data.KeepaliveStatus; -import com.android.internal.telephony.data.NotifyQosSessionInterface; -import com.android.internal.telephony.data.QosCallbackTracker; -import com.android.internal.telephony.metrics.TelephonyMetrics; -import com.android.internal.util.IndentingPrintWriter; -import com.android.telephony.Rlog; - -import java.io.FileDescriptor; -import java.io.PrintWriter; -import java.net.InetAddress; -import java.time.Duration; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.UUID; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.Executor; -import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; - -/** - * This class represents a network agent which is communication channel between - * {@link DataConnection} and {@link com.android.server.ConnectivityService}. The agent is - * created when data connection enters {@link DataConnection.DcActiveState} until it exits that - * state. - * - * Note that in IWLAN handover scenario, this agent could be transferred to the new - * {@link DataConnection} so for a short window of time this object might be accessed by two - * different {@link DataConnection}. Thus each method in this class needs to be synchronized. - */ -public class DcNetworkAgent extends NetworkAgent implements NotifyQosSessionInterface { - private final String mTag; - - private final int mId; - - private final Phone mPhone; - - private final Handler mHandler; - - private int mTransportType; - - private NetworkCapabilities mNetworkCapabilities; - - public final DcKeepaliveTracker keepaliveTracker = new DcKeepaliveTracker(); - - private final QosCallbackTracker mQosCallbackTracker; - - private final Executor mQosCallbackExecutor = Executors.newSingleThreadExecutor(); - - private DataConnection mDataConnection; - - private final LocalLog mNetCapsLocalLog = new LocalLog(32); - - // For interface duplicate detection. Key is the net id, value is the interface name in string. - private static Map<Integer, String> sInterfaceNames = new ConcurrentHashMap<>(); - - private static final long NETWORK_UNWANTED_ANOMALY_WINDOW_MS = TimeUnit.MINUTES.toMillis(5); - private static final int NETWORK_UNWANTED_ANOMALY_NUM_OCCURRENCES = 12; - - private static final int EVENT_UNWANTED_TIMEOUT = 1; - - @VisibleForTesting - public DcNetworkAgent(DataConnection dc, Phone phone, int score, NetworkAgentConfig config, - NetworkProvider networkProvider, int transportType) { - super(phone.getContext(), dc.getHandler().getLooper(), "DcNetworkAgent", - dc.getNetworkCapabilities(), dc.getLinkProperties(), score, config, - networkProvider); - register(); - mId = getNetwork().getNetId(); - mTag = "DcNetworkAgent" + "-" + mId; - mPhone = phone; - mHandler = new Handler(dc.getHandler().getLooper()) { - @Override - public void handleMessage(Message msg) { - if (msg.what == EVENT_UNWANTED_TIMEOUT) { - loge("onNetworkUnwanted timed out. Perform silent de-register."); - logd("Unregister from connectivity service. " + sInterfaceNames.get(mId) - + " removed."); - sInterfaceNames.remove(mId); - DcNetworkAgent.this.unregister(); - } - } - }; - mNetworkCapabilities = dc.getNetworkCapabilities(); - mTransportType = transportType; - mDataConnection = dc; - if (dc.getLinkProperties() != null) { - checkDuplicateInterface(mId, dc.getLinkProperties().getInterfaceName()); - logd("created for data connection " + dc.getName() + ", " - + dc.getLinkProperties().getInterfaceName()); - } else { - loge("The connection does not have a valid link properties."); - } - mQosCallbackTracker = new QosCallbackTracker(this, mPhone); - } - - private @NetworkType int getNetworkType() { - ServiceState ss = mPhone.getServiceState(); - int networkType = TelephonyManager.NETWORK_TYPE_UNKNOWN; - - NetworkRegistrationInfo nri = ss.getNetworkRegistrationInfo( - NetworkRegistrationInfo.DOMAIN_PS, mTransportType); - if (nri != null) { - networkType = nri.getAccessNetworkTechnology(); - } - - return networkType; - } - - private void checkDuplicateInterface(int netId, @Nullable String interfaceName) { - for (Map.Entry<Integer, String> entry: sInterfaceNames.entrySet()) { - if (Objects.equals(interfaceName, entry.getValue())) { - String message = "Duplicate interface " + interfaceName - + " is detected. DcNetworkAgent-" + entry.getKey() - + " already used this interface name."; - loge(message); - // Using fixed UUID to avoid duplicate bugreport notification - AnomalyReporter.reportAnomaly( - UUID.fromString("02f3d3f6-4613-4415-b6cb-8d92c8a938a6"), - message, mPhone.getCarrierId()); - return; - } - } - sInterfaceNames.put(netId, interfaceName); - } - - /** - * @return The tag - */ - String getTag() { - return mTag; - } - - /** - * Set the data connection that owns this network agent. - * - * @param dc Data connection owning this network agent. - * @param transportType Transport that this data connection is on. - */ - public synchronized void acquireOwnership(@NonNull DataConnection dc, - @TransportType int transportType) { - mDataConnection = dc; - mTransportType = transportType; - logd(dc.getName() + " acquired the ownership of this agent."); - } - - /** - * Release the ownership of network agent. - */ - public synchronized void releaseOwnership(DataConnection dc) { - if (mDataConnection == null) { - loge("releaseOwnership called on no-owner DcNetworkAgent!"); - return; - } else if (mDataConnection != dc) { - loge("releaseOwnership: This agent belongs to " - + mDataConnection.getName() + ", ignored the request from " + dc.getName()); - return; - } - logd("Data connection " + mDataConnection.getName() + " released the ownership."); - mDataConnection = null; - } - - /** - * @return The data connection that owns this agent - */ - public synchronized DataConnection getDataConnection() { - return mDataConnection; - } - - private static final SlidingWindowEventCounter sNetworkUnwantedCounter = - new SlidingWindowEventCounter(NETWORK_UNWANTED_ANOMALY_WINDOW_MS, - NETWORK_UNWANTED_ANOMALY_NUM_OCCURRENCES); - - @Override - public synchronized void onNetworkUnwanted() { - mHandler.sendEmptyMessageDelayed(EVENT_UNWANTED_TIMEOUT, TimeUnit.SECONDS.toMillis(30)); - trackNetworkUnwanted(); - if (mDataConnection == null) { - loge("onNetworkUnwanted found called on no-owner DcNetworkAgent!"); - return; - } - - logd("onNetworkUnwanted called. Now tear down the data connection " - + mDataConnection.getName()); - mDataConnection.tearDownAll(Phone.REASON_RELEASED_BY_CONNECTIVITY_SERVICE, - DcTracker.RELEASE_TYPE_DETACH, null); - } - - /** - * There have been several bugs where a RECONNECT loop kicks off where a DataConnection - * connects to the Network, ConnectivityService indicates that the Network is unwanted, - * and then the DataConnection reconnects. By the time we get the bug report it's too late - * because there have already been hundreds of RECONNECTS. This is meant to capture the issue - * when it first starts. - * - * The unwanted counter is configured to only take an anomaly report in extreme cases. - * This is to avoid having the anomaly message show up on several devices. - * - * This is directly related to b/175845538. But, there have been several other occurrences of - * this issue. - */ - private void trackNetworkUnwanted() { - if (sNetworkUnwantedCounter.addOccurrence()) { - AnomalyReporter.reportAnomaly( - UUID.fromString("3f578b5c-64e9-11eb-ae93-0242ac130002"), - "Network Unwanted called 12 times in 5 minutes.", mPhone.getCarrierId()); - } - } - - @Override - public synchronized void onBandwidthUpdateRequested() { - if (mDataConnection == null) { - loge("onBandwidthUpdateRequested called on no-owner DcNetworkAgent!"); - return; - } - - if (mPhone.getLceStatus() == RILConstants.LCE_ACTIVE // active LCE service - && mTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) { - mPhone.mCi.pullLceData(mDataConnection.obtainMessage( - DataConnection.EVENT_BW_REFRESH_RESPONSE)); - } - } - - @Override - public synchronized void onValidationStatus(int status, Uri redirectUri) { - if (mDataConnection == null) { - loge("onValidationStatus called on no-owner DcNetworkAgent!"); - return; - } - - logd("validation status: " + status + " with redirection URL: " + redirectUri); - DcTracker dct = mPhone.getDcTracker(mTransportType); - if (dct != null) { - Message msg = dct.obtainMessage(DctConstants.EVENT_NETWORK_STATUS_CHANGED, - status, mDataConnection.getCid(), redirectUri.toString()); - msg.sendToTarget(); - } - } - - private synchronized boolean isOwned(DataConnection dc, String reason) { - if (mDataConnection == null) { - loge(reason + " called on no-owner DcNetworkAgent!"); - return false; - } else if (mDataConnection != dc) { - loge(reason + ": This agent belongs to " - + mDataConnection.getName() + ", ignored the request from " + dc.getName()); - return false; - } - return true; - } - - /** - * Update the legacy sub type (i.e. data network type). - * - * @param dc The data connection that invokes this method. - */ - public synchronized void updateLegacySubtype(DataConnection dc) { - if (!isOwned(dc, "updateLegacySubtype")) return; - - int networkType = getNetworkType(); - setLegacySubtype(networkType, TelephonyManager.getNetworkTypeName(networkType)); - } - - /** - * Set the network capabilities. - * - * @param networkCapabilities The network capabilities. - * @param dc The data connection that invokes this method. - */ - public synchronized void sendNetworkCapabilities(NetworkCapabilities networkCapabilities, - DataConnection dc) { - if (!isOwned(dc, "sendNetworkCapabilities")) return; - - if (!networkCapabilities.equals(mNetworkCapabilities)) { - String logStr = "Changed from " + mNetworkCapabilities + " to " - + networkCapabilities + ", Data RAT=" - + mPhone.getServiceState().getRilDataRadioTechnology() - + ", dc=" + mDataConnection.getName(); - logd(logStr); - mNetCapsLocalLog.log(logStr); - if (networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) { - // only log metrics for DataConnection with NET_CAPABILITY_INTERNET - if (mNetworkCapabilities == null - || networkCapabilities.hasCapability( - NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED) - != mNetworkCapabilities.hasCapability( - NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED)) { - TelephonyMetrics.getInstance().writeNetworkCapabilitiesChangedEvent( - mPhone.getPhoneId(), networkCapabilities); - } - } - mNetworkCapabilities = networkCapabilities; - } - sendNetworkCapabilities(networkCapabilities); - } - - /** - * Set the link properties - * - * @param linkProperties The link properties - * @param dc The data connection that invokes this method. - */ - public synchronized void sendLinkProperties(@NonNull LinkProperties linkProperties, - DataConnection dc) { - if (!isOwned(dc, "sendLinkProperties")) return; - - sInterfaceNames.put(mId, dc.getLinkProperties().getInterfaceName()); - sendLinkProperties(linkProperties); - } - - /** - * Set the network score. - * - * @param score The network score. - * @param dc The data connection that invokes this method. - */ - public synchronized void sendNetworkScore(int score, DataConnection dc) { - if (!isOwned(dc, "sendNetworkScore")) return; - sendNetworkScore(score); - } - - /** - * Unregister the network agent from connectivity service. - * - * @param dc The data connection that invokes this method. - */ - public synchronized void unregister(DataConnection dc) { - if (!isOwned(dc, "unregister")) return; - - mHandler.removeMessages(EVENT_UNWANTED_TIMEOUT); - logd("Unregister from connectivity service. " + sInterfaceNames.get(mId) + " removed."); - sInterfaceNames.remove(mId); - super.unregister(); - } - - @Override - public synchronized void onStartSocketKeepalive(int slot, @NonNull Duration interval, - @NonNull KeepalivePacketData packet) { - if (mDataConnection == null) { - loge("onStartSocketKeepalive called on no-owner DcNetworkAgent!"); - return; - } - - if (packet instanceof NattKeepalivePacketData) { - mDataConnection.obtainMessage(DataConnection.EVENT_KEEPALIVE_START_REQUEST, - slot, (int) interval.getSeconds(), packet).sendToTarget(); - } else { - sendSocketKeepaliveEvent(slot, SocketKeepalive.ERROR_UNSUPPORTED); - } - } - - @Override - public synchronized void onStopSocketKeepalive(int slot) { - if (mDataConnection == null) { - loge("onStopSocketKeepalive called on no-owner DcNetworkAgent!"); - return; - } - - mDataConnection.obtainMessage(DataConnection.EVENT_KEEPALIVE_STOP_REQUEST, slot) - .sendToTarget(); - } - - @Override - public void onQosCallbackRegistered(final int qosCallbackId, final @NonNull QosFilter filter) { - mQosCallbackExecutor.execute(() -> mQosCallbackTracker.addFilter(qosCallbackId, - new QosCallbackTracker.IFilter() { - @Override - public boolean matchesLocalAddress( - InetAddress address, int startPort, int endPort) { - return filter.matchesLocalAddress(address, startPort, endPort); - } - - @Override - public boolean matchesRemoteAddress( - InetAddress address, int startPort, int endPort) { - return filter.matchesRemoteAddress(address, startPort, endPort); - } - })); - } - - @Override - public void onQosCallbackUnregistered(final int qosCallbackId) { - mQosCallbackExecutor.execute(() -> mQosCallbackTracker.removeFilter(qosCallbackId)); - } - - void updateQosBearerSessions(final List<QosBearerSession> qosBearerSessions) { - mQosCallbackExecutor.execute(() -> mQosCallbackTracker.updateSessions(qosBearerSessions)); - } - - @Override - public void notifyQosSessionAvailable(final int qosCallbackId, final int sessionId, - @NonNull final QosSessionAttributes attributes) { - super.sendQosSessionAvailable(qosCallbackId, sessionId, attributes); - } - - @Override - public void notifyQosSessionLost(final int qosCallbackId, - final int sessionId, final int qosSessionType) { - super.sendQosSessionLost(qosCallbackId, sessionId, qosSessionType); - } - - @Override - public String toString() { - return "DcNetworkAgent-" - + mId - + " mDataConnection=" - + ((mDataConnection != null) ? mDataConnection.getName() : null) - + " mTransportType=" - + AccessNetworkConstants.transportTypeToString(mTransportType) - + " " + ((mDataConnection != null) ? mDataConnection.getLinkProperties() : null) - + " mNetworkCapabilities=" + mNetworkCapabilities; - } - - /** - * Dump the state of transport manager - * - * @param fd File descriptor - * @param printWriter Print writer - * @param args Arguments - */ - public void dump(FileDescriptor fd, PrintWriter printWriter, String[] args) { - IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, " "); - pw.println(toString()); - pw.increaseIndent(); - pw.println("Net caps logs:"); - mNetCapsLocalLog.dump(fd, pw, args); - pw.decreaseIndent(); - } - - /** - * Log with debug level - * - * @param s is string log - */ - private void logd(String s) { - Rlog.d(mTag, s); - } - - /** - * Log with error level - * - * @param s is string log - */ - private void loge(String s) { - Rlog.e(mTag, s); - } - - class DcKeepaliveTracker { - private class KeepaliveRecord { - public int slotId; - public int currentStatus; - - KeepaliveRecord(int slotId, int status) { - this.slotId = slotId; - this.currentStatus = status; - } - } - - private final SparseArray<KeepaliveRecord> mKeepalives = new SparseArray(); - - int getHandleForSlot(int slotId) { - for (int i = 0; i < mKeepalives.size(); i++) { - KeepaliveRecord kr = mKeepalives.valueAt(i); - if (kr.slotId == slotId) return mKeepalives.keyAt(i); - } - return -1; - } - - int keepaliveStatusErrorToPacketKeepaliveError(int error) { - switch(error) { - case KeepaliveStatus.ERROR_NONE: - return SocketKeepalive.SUCCESS; - case KeepaliveStatus.ERROR_UNSUPPORTED: - return SocketKeepalive.ERROR_UNSUPPORTED; - case KeepaliveStatus.ERROR_NO_RESOURCES: - return SocketKeepalive.ERROR_INSUFFICIENT_RESOURCES; - case KeepaliveStatus.ERROR_UNKNOWN: - default: - return SocketKeepalive.ERROR_HARDWARE_ERROR; - } - } - - void handleKeepaliveStarted(final int slot, KeepaliveStatus ks) { - switch (ks.statusCode) { - case KeepaliveStatus.STATUS_INACTIVE: - DcNetworkAgent.this.sendSocketKeepaliveEvent(slot, - keepaliveStatusErrorToPacketKeepaliveError(ks.errorCode)); - break; - case KeepaliveStatus.STATUS_ACTIVE: - DcNetworkAgent.this.sendSocketKeepaliveEvent( - slot, SocketKeepalive.SUCCESS); - // fall through to add record - case KeepaliveStatus.STATUS_PENDING: - logd("Adding keepalive handle=" - + ks.sessionHandle + " slot = " + slot); - mKeepalives.put(ks.sessionHandle, - new KeepaliveRecord( - slot, ks.statusCode)); - break; - default: - logd("Invalid KeepaliveStatus Code: " + ks.statusCode); - break; - } - } - - void handleKeepaliveStatus(KeepaliveStatus ks) { - final KeepaliveRecord kr; - kr = mKeepalives.get(ks.sessionHandle); - - if (kr == null) { - // If there is no slot for the session handle, we received an event - // for a different data connection. This is not an error because the - // keepalive session events are broadcast to all listeners. - loge("Discarding keepalive event for different data connection:" + ks); - return; - } - // Switch on the current state, to see what we do with the status update - switch (kr.currentStatus) { - case KeepaliveStatus.STATUS_INACTIVE: - logd("Inactive Keepalive received status!"); - DcNetworkAgent.this.sendSocketKeepaliveEvent( - kr.slotId, SocketKeepalive.ERROR_HARDWARE_ERROR); - break; - case KeepaliveStatus.STATUS_PENDING: - switch (ks.statusCode) { - case KeepaliveStatus.STATUS_INACTIVE: - DcNetworkAgent.this.sendSocketKeepaliveEvent(kr.slotId, - keepaliveStatusErrorToPacketKeepaliveError(ks.errorCode)); - kr.currentStatus = KeepaliveStatus.STATUS_INACTIVE; - mKeepalives.remove(ks.sessionHandle); - break; - case KeepaliveStatus.STATUS_ACTIVE: - logd("Pending Keepalive received active status!"); - kr.currentStatus = KeepaliveStatus.STATUS_ACTIVE; - DcNetworkAgent.this.sendSocketKeepaliveEvent( - kr.slotId, SocketKeepalive.SUCCESS); - break; - case KeepaliveStatus.STATUS_PENDING: - loge("Invalid unsolicied Keepalive Pending Status!"); - break; - default: - loge("Invalid Keepalive Status received, " + ks.statusCode); - } - break; - case KeepaliveStatus.STATUS_ACTIVE: - switch (ks.statusCode) { - case KeepaliveStatus.STATUS_INACTIVE: - logd("Keepalive received stopped status!"); - DcNetworkAgent.this.sendSocketKeepaliveEvent( - kr.slotId, SocketKeepalive.SUCCESS); - - kr.currentStatus = KeepaliveStatus.STATUS_INACTIVE; - mKeepalives.remove(ks.sessionHandle); - break; - case KeepaliveStatus.STATUS_PENDING: - case KeepaliveStatus.STATUS_ACTIVE: - loge("Active Keepalive received invalid status!"); - break; - default: - loge("Invalid Keepalive Status received, " + ks.statusCode); - } - break; - default: - loge("Invalid Keepalive Status received, " + kr.currentStatus); - } - } - } -}
diff --git a/src/java/com/android/internal/telephony/dataconnection/DcRequest.java b/src/java/com/android/internal/telephony/dataconnection/DcRequest.java deleted file mode 100644 index da775e8..0000000 --- a/src/java/com/android/internal/telephony/dataconnection/DcRequest.java +++ /dev/null
@@ -1,102 +0,0 @@ -/* - * Copyright (C) 2006 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.internal.telephony.dataconnection; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.net.NetworkRequest; -import android.net.NetworkSpecifier; -import android.net.TelephonyNetworkSpecifier; -import android.telephony.Annotation.ApnType; - -import com.android.telephony.Rlog; - -/** - * Wraps cellular network requests to configured apn types. - */ -public class DcRequest implements Comparable<DcRequest> { - private static final String LOG_TAG = "DcRequest"; - - @NonNull - public final NetworkRequest networkRequest; - public final int priority; - public final @ApnType int apnType; - - private DcRequest(@NonNull final NetworkRequest nr, @ApnType final int type, - int apnPriority) { - networkRequest = nr; - priority = apnPriority; - apnType = type; - } - - /** - * Create a DcRequest based off of the network request. If the network request is not cellular, - * then null is returned and a warning is generated. - * @param networkRequest sets the type of dc request - * @param apnConfigTypeRepository apn config types to match on the network request - * @return corresponding DcRequest - * - */ - @Nullable - public static DcRequest create(@NonNull final NetworkRequest networkRequest, - @NonNull final ApnConfigTypeRepository apnConfigTypeRepository) { - final int apnType = ApnContext.getApnTypeFromNetworkRequest(networkRequest); - final ApnConfigType apnConfigType = apnConfigTypeRepository.getByType(apnType); - if (apnConfigType == null) { - Rlog.d(LOG_TAG, "Non cellular request ignored: " + networkRequest.toString()); - checkForAnomalousNetworkRequest(networkRequest); - return null; - } else { - Rlog.d(LOG_TAG, "Cellular request confirmed: " + networkRequest.toString()); - return new DcRequest(networkRequest, apnType, apnConfigType.getPriority()); - } - } - - private static void checkForAnomalousNetworkRequest(NetworkRequest networkRequest) { - NetworkSpecifier specifier = networkRequest.getNetworkSpecifier(); - if (specifier != null) { - if (specifier instanceof TelephonyNetworkSpecifier) { - reportAnomalousNetworkRequest(networkRequest); - } - } - } - - private static void reportAnomalousNetworkRequest(NetworkRequest networkRequest) { - //TODO: Report anomaly if this happens - Rlog.w(LOG_TAG, "A TelephonyNetworkSpecifier for a non-cellular request is invalid: " - + networkRequest.toString()); - - } - - public String toString() { - return networkRequest.toString() + ", priority=" + priority + ", apnType=" + apnType; - } - - public int hashCode() { - return networkRequest.hashCode(); - } - - public boolean equals(Object o) { - if (o instanceof DcRequest) { - return networkRequest.equals(((DcRequest)o).networkRequest); - } - return false; - } - - public int compareTo(DcRequest o) { - return o.priority - priority; - } -}
diff --git a/src/java/com/android/internal/telephony/dataconnection/DcTesterDeactivateAll.java b/src/java/com/android/internal/telephony/dataconnection/DcTesterDeactivateAll.java deleted file mode 100644 index 11a0ae6..0000000 --- a/src/java/com/android/internal/telephony/dataconnection/DcTesterDeactivateAll.java +++ /dev/null
@@ -1,95 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.internal.telephony.dataconnection; - -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.os.Handler; - -import com.android.internal.telephony.Phone; -import com.android.internal.telephony.util.TelephonyUtils; -import com.android.telephony.Rlog; - -/** - * To bring down all DC's send the following intent: - * - * adb shell am broadcast -a com.android.internal.telephony.dataconnection.action_deactivate_all - */ -public class DcTesterDeactivateAll { - private static final String LOG_TAG = "DcTesterDeacativateAll"; - private static final boolean DBG = true; - - private Phone mPhone; - private DcController mDcc; - - public static String sActionDcTesterDeactivateAll = - "com.android.internal.telephony.dataconnection.action_deactivate_all"; - - - // The static intent receiver one for all instances and we assume this - // is running on the same thread as Dcc. - protected BroadcastReceiver sIntentReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - String action = intent.getAction(); - if (DBG) log("sIntentReceiver.onReceive: action=" + action); - if (action.equals(sActionDcTesterDeactivateAll) - || action.equals(mPhone.getActionDetached())) { - log("Send DEACTIVATE to all Dcc's"); - if (mDcc != null) { - for (DataConnection dc : mDcc.mDcListAll) { - dc.tearDownNow(); - } - } else { - if (DBG) log("onReceive: mDcc is null, ignoring"); - } - } else { - if (DBG) log("onReceive: unknown action=" + action); - } - } - }; - - DcTesterDeactivateAll(Phone phone, DcController dcc, Handler handler) { - mPhone = phone; - mDcc = dcc; - - if (TelephonyUtils.IS_DEBUGGABLE) { - IntentFilter filter = new IntentFilter(); - - filter.addAction(sActionDcTesterDeactivateAll); - log("register for intent action=" + sActionDcTesterDeactivateAll); - - filter.addAction(mPhone.getActionDetached()); - log("register for intent action=" + mPhone.getActionDetached()); - - phone.getContext().registerReceiver(sIntentReceiver, filter, null, handler, - Context.RECEIVER_EXPORTED); - } - } - - void dispose() { - if (TelephonyUtils.IS_DEBUGGABLE) { - mPhone.getContext().unregisterReceiver(sIntentReceiver); - } - } - - private static void log(String s) { - Rlog.d(LOG_TAG, s); - } -}
diff --git a/src/java/com/android/internal/telephony/dataconnection/DcTesterFailBringUpAll.java b/src/java/com/android/internal/telephony/dataconnection/DcTesterFailBringUpAll.java deleted file mode 100644 index 788da29..0000000 --- a/src/java/com/android/internal/telephony/dataconnection/DcTesterFailBringUpAll.java +++ /dev/null
@@ -1,110 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.internal.telephony.dataconnection; - -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.os.Handler; -import android.telephony.DataFailCause; - -import com.android.internal.telephony.Phone; -import com.android.internal.telephony.util.TelephonyUtils; -import com.android.telephony.Rlog; - -/** - * A package level call that causes all DataConnection bringUp calls to fail a specific - * number of times. Here is an example that sets counter to 2 and cause to -3 for all instances: - * adb shell am broadcast -a com.android.internal.telephony.dataconnection.action_fail_bringup \ - * --ei counter 2 --ei fail_cause -3 - * - * Also you can add a suggested retry time if desired: - * --ei suggested_retry_time 5000 - * - * The fail_cause is one of {@link DataFailCause} - */ -public class DcTesterFailBringUpAll { - private static final String LOG_TAG = "DcTesterFailBrinupAll"; - private static final boolean DBG = true; - - private Phone mPhone; - - private String mActionFailBringUp = DcFailBringUp.INTENT_BASE + "." - + DcFailBringUp.ACTION_FAIL_BRINGUP; - - // The saved FailBringUp data from the intent - private DcFailBringUp mFailBringUp = new DcFailBringUp(); - - // The static intent receiver one for all instances. - private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - String action = intent.getAction(); - if (DBG) log("sIntentReceiver.onReceive: action=" + action); - if (action.equals(mActionFailBringUp)) { - mFailBringUp.saveParameters(intent, "sFailBringUp"); - } else if (action.equals(mPhone.getActionDetached())) { - // Counter is MAX, bringUp/retry will always fail - log("simulate detaching"); - mFailBringUp.saveParameters(Integer.MAX_VALUE, - DataFailCause.LOST_CONNECTION, - DcFailBringUp.DEFAULT_SUGGESTED_RETRY_TIME); - } else if (action.equals(mPhone.getActionAttached())) { - // Counter is 0 next bringUp/retry will succeed - log("simulate attaching"); - mFailBringUp.saveParameters(0, DataFailCause.NONE, - DcFailBringUp.DEFAULT_SUGGESTED_RETRY_TIME); - } else { - if (DBG) log("onReceive: unknown action=" + action); - } - } - }; - - DcTesterFailBringUpAll(Phone phone, Handler handler) { - mPhone = phone; - if (TelephonyUtils.IS_DEBUGGABLE) { - IntentFilter filter = new IntentFilter(); - - filter.addAction(mActionFailBringUp); - log("register for intent action=" + mActionFailBringUp); - - filter.addAction(mPhone.getActionDetached()); - log("register for intent action=" + mPhone.getActionDetached()); - - filter.addAction(mPhone.getActionAttached()); - log("register for intent action=" + mPhone.getActionAttached()); - - phone.getContext().registerReceiver(mIntentReceiver, filter, null, handler, - Context.RECEIVER_EXPORTED); - } - } - - void dispose() { - if (TelephonyUtils.IS_DEBUGGABLE) { - mPhone.getContext().unregisterReceiver(mIntentReceiver); - } - } - - public DcFailBringUp getDcFailBringUp() { - return mFailBringUp; - } - - private void log(String s) { - Rlog.d(LOG_TAG, s); - } -}
diff --git a/src/java/com/android/internal/telephony/dataconnection/DcTracker.java b/src/java/com/android/internal/telephony/dataconnection/DcTracker.java deleted file mode 100755 index 28f69dc..0000000 --- a/src/java/com/android/internal/telephony/dataconnection/DcTracker.java +++ /dev/null
@@ -1,5665 +0,0 @@ -/* - * Copyright (C) 2006 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.internal.telephony.dataconnection; - -import static android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE; -import static android.net.NetworkPolicyManager.SUBSCRIPTION_OVERRIDE_CONGESTED; -import static android.net.NetworkPolicyManager.SUBSCRIPTION_OVERRIDE_UNMETERED; -import static android.telephony.TelephonyManager.NETWORK_TYPE_LTE; -import static android.telephony.TelephonyManager.NETWORK_TYPE_NR; -import static android.telephony.data.DataCallResponse.HANDOVER_FAILURE_MODE_DO_FALLBACK; -import static android.telephony.data.DataCallResponse.HANDOVER_FAILURE_MODE_LEGACY; -import static android.telephony.data.DataCallResponse.HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_SETUP_NORMAL; - -import static com.android.internal.telephony.RILConstants.DATA_PROFILE_DEFAULT; -import static com.android.internal.telephony.RILConstants.DATA_PROFILE_INVALID; - -import android.annotation.IntDef; -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.app.AlarmManager; -import android.app.Notification; -import android.app.NotificationManager; -import android.app.PendingIntent; -import android.app.ProgressDialog; -import android.content.ActivityNotFoundException; -import android.content.BroadcastReceiver; -import android.content.ContentResolver; -import android.content.ContentValues; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.SharedPreferences; -import android.content.res.Resources; -import android.database.ContentObserver; -import android.database.Cursor; -import android.net.ConnectivityManager; -import android.net.LinkProperties; -import android.net.NetworkAgent; -import android.net.NetworkCapabilities; -import android.net.NetworkPolicyManager; -import android.net.NetworkRequest; -import android.net.TrafficStats; -import android.net.Uri; -import android.os.AsyncResult; -import android.os.Bundle; -import android.os.Handler; -import android.os.HandlerThread; -import android.os.Message; -import android.os.PersistableBundle; -import android.os.RegistrantList; -import android.os.SystemClock; -import android.os.SystemProperties; -import android.os.UserHandle; -import android.preference.PreferenceManager; -import android.provider.Settings; -import android.provider.Settings.SettingNotFoundException; -import android.provider.Telephony; -import android.telephony.AccessNetworkConstants; -import android.telephony.AccessNetworkConstants.TransportType; -import android.telephony.Annotation.ApnType; -import android.telephony.Annotation.DataFailureCause; -import android.telephony.Annotation.NetworkType; -import android.telephony.CarrierConfigManager; -import android.telephony.CellLocation; -import android.telephony.DataFailCause; -import android.telephony.NetworkRegistrationInfo; -import android.telephony.PcoData; -import android.telephony.PreciseDataConnectionState; -import android.telephony.ServiceState; -import android.telephony.ServiceState.RilRadioTechnology; -import android.telephony.SubscriptionManager; -import android.telephony.SubscriptionPlan; -import android.telephony.TelephonyDisplayInfo; -import android.telephony.TelephonyFrameworkInitializer; -import android.telephony.TelephonyManager; -import android.telephony.TelephonyManager.SimState; -import android.telephony.cdma.CdmaCellLocation; -import android.telephony.data.ApnSetting; -import android.telephony.data.DataCallResponse; -import android.telephony.data.DataCallResponse.HandoverFailureMode; -import android.telephony.data.DataProfile; -import android.telephony.data.ThrottleStatus; -import android.telephony.gsm.GsmCellLocation; -import android.text.TextUtils; -import android.util.EventLog; -import android.util.LocalLog; -import android.util.Log; -import android.util.Pair; -import android.util.SparseArray; -import android.view.WindowManager; - -import com.android.internal.R; -import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.telephony.DctConstants; -import com.android.internal.telephony.EventLogTags; -import com.android.internal.telephony.GsmCdmaPhone; -import com.android.internal.telephony.ITelephony; -import com.android.internal.telephony.Phone; -import com.android.internal.telephony.PhoneConstants; -import com.android.internal.telephony.PhoneFactory; -import com.android.internal.telephony.RILConstants; -import com.android.internal.telephony.RetryManager; -import com.android.internal.telephony.SettingsObserver; -import com.android.internal.telephony.SubscriptionInfoUpdater; -import com.android.internal.telephony.data.DataConfigManager; -import com.android.internal.telephony.data.PhoneSwitcher; -import com.android.internal.telephony.dataconnection.DataConnectionReasons.DataAllowedReasonType; -import com.android.internal.telephony.dataconnection.DataConnectionReasons.DataDisallowedReasonType; -import com.android.internal.telephony.dataconnection.DataEnabledSettings.DataEnabledChangedReason; -import com.android.internal.telephony.metrics.DataStallRecoveryStats; -import com.android.internal.telephony.metrics.TelephonyMetrics; -import com.android.internal.telephony.util.ArrayUtils; -import com.android.internal.telephony.util.NotificationChannelController; -import com.android.internal.telephony.util.TelephonyUtils; -import com.android.internal.util.AsyncChannel; -import com.android.telephony.Rlog; - -import java.io.FileDescriptor; -import java.io.PrintWriter; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.stream.Collectors; - -/** - * {@hide} - */ -public class DcTracker extends Handler { - protected static final boolean DBG = true; - private static final boolean VDBG = false; // STOPSHIP if true - private static final boolean VDBG_STALL = false; // STOPSHIP if true - private static final boolean RADIO_TESTS = false; - private static final String NOTIFICATION_TAG = DcTracker.class.getSimpleName(); - - @IntDef(value = { - REQUEST_TYPE_NORMAL, - REQUEST_TYPE_HANDOVER, - }) - @Retention(RetentionPolicy.SOURCE) - public @interface RequestNetworkType {} - - /** - * Normal request for {@link #requestNetwork(NetworkRequest, int, Message)}. For request - * network, this adds the request to the {@link ApnContext}. If there were no network request - * attached to the {@link ApnContext} earlier, this request setups a data connection. - */ - public static final int REQUEST_TYPE_NORMAL = 1; - - /** - * Handover request for {@link #requestNetwork(NetworkRequest, int, Message)} or - * {@link #releaseNetwork(NetworkRequest, int)}. For request network, this - * initiates the handover data setup process. The existing data connection will be seamlessly - * handover to the new network. For release network, this performs a data connection softly - * clean up at the underlying layer (versus normal data release). - */ - public static final int REQUEST_TYPE_HANDOVER = 2; - - @IntDef(value = { - RELEASE_TYPE_NORMAL, - RELEASE_TYPE_DETACH, - RELEASE_TYPE_HANDOVER, - }) - @Retention(RetentionPolicy.SOURCE) - public @interface ReleaseNetworkType {} - - /** - * For release network, this is just removing the network request from the {@link ApnContext}. - * Note this does not tear down the physical data connection. Normally the data connection is - * torn down by connectivity service directly calling {@link NetworkAgent#unwanted()}. - */ - public static final int RELEASE_TYPE_NORMAL = 1; - - /** - * Detach request for {@link #releaseNetwork(NetworkRequest, int)} only. This - * forces the APN context detach from the data connection. If this {@link ApnContext} is the - * last one attached to the data connection, the data connection will be torn down, otherwise - * the data connection remains active. - */ - public static final int RELEASE_TYPE_DETACH = 2; - - /** - * Handover request for {@link #releaseNetwork(NetworkRequest, int)}. For release - * network, this performs a data connection softly clean up at the underlying layer (versus - * normal data release). - */ - public static final int RELEASE_TYPE_HANDOVER = 3; - - /** The extras for handover completion message */ - public static final String DATA_COMPLETE_MSG_EXTRA_NETWORK_REQUEST = "extra_network_request"; - public static final String DATA_COMPLETE_MSG_EXTRA_TRANSPORT_TYPE = "extra_transport_type"; - public static final String DATA_COMPLETE_MSG_EXTRA_SUCCESS = "extra_success"; - /** - * The flag indicates whether after handover failure, the data connection should remain on the - * original transport. - */ - public static final String DATA_COMPLETE_MSG_EXTRA_HANDOVER_FAILURE_FALLBACK = - "extra_handover_failure_fallback"; - - private final String mLogTag; - - public AtomicBoolean isCleanupRequired = new AtomicBoolean(false); - - private final TelephonyManager mTelephonyManager; - - private final AlarmManager mAlarmManager; - - /* Currently requested APN type (TODO: This should probably be a parameter not a member) */ - private int mRequestedApnType = ApnSetting.TYPE_DEFAULT; - - // All data enabling/disabling related settings - private final DataEnabledSettings mDataEnabledSettings; - - /** - * After detecting a potential connection problem, this is the max number - * of subsequent polls before attempting recovery. - */ - // 1 sec. default polling interval when screen is on. - private static final int POLL_NETSTAT_MILLIS = 1000; - // 10 min. default polling interval when screen is off. - private static final int POLL_NETSTAT_SCREEN_OFF_MILLIS = 1000*60*10; - // Default sent packets without ack which triggers initial recovery steps - private static final int NUMBER_SENT_PACKETS_OF_HANG = 10; - - // Default for the data stall alarm while non-aggressive stall detection - private static final int DATA_STALL_ALARM_NON_AGGRESSIVE_DELAY_IN_MS_DEFAULT = 1000 * 60 * 6; - // Default for the data stall alarm for aggressive stall detection - private static final int DATA_STALL_ALARM_AGGRESSIVE_DELAY_IN_MS_DEFAULT = 1000 * 60; - - private static final boolean DATA_STALL_SUSPECTED = true; - protected static final boolean DATA_STALL_NOT_SUSPECTED = false; - - private static final String INTENT_DATA_STALL_ALARM = - "com.android.internal.telephony.data-stall"; - // Tag for tracking stale alarms - private static final String INTENT_DATA_STALL_ALARM_EXTRA_TAG = "data_stall_alarm_extra_tag"; - private static final String INTENT_DATA_STALL_ALARM_EXTRA_TRANSPORT_TYPE = - "data_stall_alarm_extra_transport_type"; - - // Unique id for no data notification on setup data permanently failed. - private static final int NO_DATA_NOTIFICATION = 1001; - - /** The higher index has higher priority. */ - private static final DctConstants.State[] DATA_CONNECTION_STATE_PRIORITIES = { - DctConstants.State.IDLE, - DctConstants.State.DISCONNECTING, - DctConstants.State.CONNECTING, - DctConstants.State.CONNECTED, - }; - - private DcTesterFailBringUpAll mDcTesterFailBringUpAll; - private DcController mDcc; - - /** kept in sync with mApnContexts - * Higher numbers are higher priority and sorted so highest priority is first */ - private ArrayList<ApnContext> mPrioritySortedApnContexts = new ArrayList<>(); - - /** all APN settings applicable to the current carrier */ - private ArrayList<ApnSetting> mAllApnSettings = new ArrayList<>(); - - /** preferred apn */ - private ApnSetting mPreferredApn = null; - - /** Is packet service restricted by network */ - private boolean mIsPsRestricted = false; - - /** emergency apn Setting*/ - private ApnSetting mEmergencyApn = null; - - /* Once disposed dont handle any messages */ - private boolean mIsDisposed = false; - - private ContentResolver mResolver; - - /* Set to true with CMD_ENABLE_MOBILE_PROVISIONING */ - private boolean mIsProvisioning = false; - - /* The Url passed as object parameter in CMD_ENABLE_MOBILE_PROVISIONING */ - private String mProvisioningUrl = null; - - /* Indicating data service is bound or not */ - private boolean mDataServiceBound = false; - - /* Intent to hide/show the sign-in error notification for provisioning */ - private static final String INTENT_PROVISION = "com.android.internal.telephony.PROVISION"; - - /** - * Extra containing the phone ID for INTENT_PROVISION - * Must be kept consistent with NetworkNotificationManager#setProvNotificationVisible. - * TODO: refactor the deprecated API to prevent hardcoding values. - */ - private static final String EXTRA_PROVISION_PHONE_ID = "provision.phone.id"; - - /* Intent for the provisioning apn alarm */ - private static final String INTENT_PROVISIONING_APN_ALARM = - "com.android.internal.telephony.provisioning_apn_alarm"; - - /* Tag for tracking stale alarms */ - private static final String PROVISIONING_APN_ALARM_TAG_EXTRA = "provisioning.apn.alarm.tag"; - - /* Debug property for overriding the PROVISIONING_APN_ALARM_DELAY_IN_MS */ - private static final String DEBUG_PROV_APN_ALARM = "persist.debug.prov_apn_alarm"; - - /* Default for the provisioning apn alarm timeout */ - private static final int PROVISIONING_APN_ALARM_DELAY_IN_MS_DEFAULT = 1000 * 60 * 15; - - /* The provision apn alarm intent used to disable the provisioning apn */ - private PendingIntent mProvisioningApnAlarmIntent = null; - - /* Used to track stale provisioning apn alarms */ - private int mProvisioningApnAlarmTag = (int) SystemClock.elapsedRealtime(); - - private AsyncChannel mReplyAc = new AsyncChannel(); - - private final LocalLog mDataRoamingLeakageLog = new LocalLog(32); - private final LocalLog mApnSettingsInitializationLog = new LocalLog(32); - - /* 5G connection reevaluation watchdog alarm constants */ - private long mWatchdogTimeMs = 1000 * 60 * 60; - private boolean mWatchdog = false; - - /* Default for whether 5G frequencies are considered unmetered */ - private boolean mNrNsaAllUnmetered = false; - private boolean mNrNsaMmwaveUnmetered = false; - private boolean mNrNsaSub6Unmetered = false; - private boolean mNrSaAllUnmetered = false; - private boolean mNrSaMmwaveUnmetered = false; - private boolean mNrSaSub6Unmetered = false; - private boolean mNrNsaRoamingUnmetered = false; - - // it effect the PhysicalLinkStatusChanged - private boolean mLteEndcUsingUserDataForRrcDetection = false; - - /* List of SubscriptionPlans, updated when initialized and when plans are changed. */ - private List<SubscriptionPlan> mSubscriptionPlans = new ArrayList<>(); - /* List of network types an unmetered override applies to, set by onSubscriptionOverride - * and cleared when the device is rebooted or the override expires. */ - private List<Integer> mUnmeteredNetworkTypes = null; - /* List of network types a congested override applies to, set by onSubscriptionOverride - * and cleared when the device is rebooted or the override expires. */ - private List<Integer> mCongestedNetworkTypes = null; - /* Whether an unmetered override is currently active. */ - private boolean mUnmeteredOverride = false; - /* Whether a congested override is currently active. */ - private boolean mCongestedOverride = false; - - @SimState - private int mSimState = TelephonyManager.SIM_STATE_UNKNOWN; - - private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver () { - @Override - public void onReceive(Context context, Intent intent) { - String action = intent.getAction(); - - if (action.equals(Intent.ACTION_SCREEN_ON)) { - // TODO: Evaluate hooking this up with DeviceStateMonitor - if (DBG) log("screen on"); - mIsScreenOn = true; - stopNetStatPoll(); - startNetStatPoll(); - restartDataStallAlarm(); - } else if (action.equals(Intent.ACTION_SCREEN_OFF)) { - if (DBG) log("screen off"); - mIsScreenOn = false; - stopNetStatPoll(); - startNetStatPoll(); - restartDataStallAlarm(); - } else if (action.equals(INTENT_DATA_STALL_ALARM)) { - onActionIntentDataStallAlarm(intent); - } else if (action.equals(INTENT_PROVISIONING_APN_ALARM)) { - if (DBG) log("Provisioning apn alarm"); - onActionIntentProvisioningApnAlarm(intent); - } else if (action.equals(TelephonyManager.ACTION_SIM_CARD_STATE_CHANGED) - || action.equals(TelephonyManager.ACTION_SIM_APPLICATION_STATE_CHANGED)) { - if (mPhone.getPhoneId() == intent.getIntExtra(SubscriptionManager.EXTRA_SLOT_INDEX, - SubscriptionManager.INVALID_SIM_SLOT_INDEX)) { - int simState = intent.getIntExtra(TelephonyManager.EXTRA_SIM_STATE, - TelephonyManager.SIM_STATE_UNKNOWN); - sendMessage(obtainMessage(DctConstants.EVENT_SIM_STATE_UPDATED, simState, 0)); - } - } else if (action.equals(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)) { - if (mPhone.getPhoneId() == intent.getIntExtra(CarrierConfigManager.EXTRA_SLOT_INDEX, - SubscriptionManager.INVALID_SIM_SLOT_INDEX)) { - if (intent.getBooleanExtra( - CarrierConfigManager.EXTRA_REBROADCAST_ON_UNLOCK, false)) { - // Ignore the rebroadcast one to prevent multiple carrier config changed - // event during boot up. - return; - } - int subId = intent.getIntExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, - SubscriptionManager.INVALID_SUBSCRIPTION_ID); - if (SubscriptionManager.isValidSubscriptionId(subId)) { - sendEmptyMessage(DctConstants.EVENT_CARRIER_CONFIG_CHANGED); - } - } - } else { - if (DBG) log("onReceive: Unknown action=" + action); - } - } - }; - - private final Runnable mPollNetStat = new Runnable() { - @Override - public void run() { - updateDataActivity(); - - if (mIsScreenOn) { - mNetStatPollPeriod = Settings.Global.getInt(mResolver, - Settings.Global.PDP_WATCHDOG_POLL_INTERVAL_MS, POLL_NETSTAT_MILLIS); - } else { - mNetStatPollPeriod = Settings.Global.getInt(mResolver, - Settings.Global.PDP_WATCHDOG_LONG_POLL_INTERVAL_MS, - POLL_NETSTAT_SCREEN_OFF_MILLIS); - } - - if (mNetStatPollEnabled) { - mDataConnectionTracker.postDelayed(this, mNetStatPollPeriod); - } - } - }; - - private class ThrottleStatusChangedCallback implements DataThrottler.Callback { - @Override - public void onThrottleStatusChanged(List<ThrottleStatus> throttleStatuses) { - for (ThrottleStatus status : throttleStatuses) { - if (status.getThrottleType() == ThrottleStatus.THROTTLE_TYPE_NONE) { - setupDataOnConnectableApn(mApnContextsByType.get(status.getApnType()), - Phone.REASON_DATA_UNTHROTTLED, - RetryFailures.ALWAYS); - } - } - } - } - - private NetworkPolicyManager mNetworkPolicyManager; - private final NetworkPolicyManager.SubscriptionCallback mSubscriptionCallback = - new NetworkPolicyManager.SubscriptionCallback() { - @Override - public void onSubscriptionOverride(int subId, int overrideMask, int overrideValue, - int[] networkTypes) { - if (mPhone == null || mPhone.getSubId() != subId) return; - - List<Integer> tempList = new ArrayList<>(); - for (int networkType : networkTypes) { - tempList.add(networkType); - } - - log("Subscription override: overrideMask=" + overrideMask - + ", overrideValue=" + overrideValue + ", networkTypes=" + tempList); - - if (overrideMask == SUBSCRIPTION_OVERRIDE_UNMETERED) { - mUnmeteredNetworkTypes = tempList; - mUnmeteredOverride = overrideValue != 0; - reevaluateUnmeteredConnections(); - } else if (overrideMask == SUBSCRIPTION_OVERRIDE_CONGESTED) { - mCongestedNetworkTypes = tempList; - mCongestedOverride = overrideValue != 0; - reevaluateCongestedConnections(); - } - } - - @Override - public void onSubscriptionPlansChanged(int subId, SubscriptionPlan[] plans) { - if (mPhone == null || mPhone.getSubId() != subId) return; - - mSubscriptionPlans = Arrays.asList(plans); - if (DBG) log("SubscriptionPlans changed: " + mSubscriptionPlans); - reevaluateUnmeteredConnections(); - } - }; - - private final SettingsObserver mSettingsObserver; - - private void registerSettingsObserver() { - mSettingsObserver.unobserve(); - String simSuffix = ""; - if (TelephonyManager.getDefault().getSimCount() > 1) { - simSuffix = Integer.toString(mPhone.getSubId()); - } - - mSettingsObserver.observe( - Settings.Global.getUriFor(Settings.Global.DATA_ROAMING + simSuffix), - DctConstants.EVENT_ROAMING_SETTING_CHANGE); - mSettingsObserver.observe( - Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED), - DctConstants.EVENT_DEVICE_PROVISIONED_CHANGE); - } - - /** - * Maintain the sum of transmit and receive packets. - * - * The packet counts are initialized and reset to -1 and - * remain -1 until they can be updated. - */ - public static class TxRxSum { - public long txPkts; - public long rxPkts; - - public TxRxSum() { - reset(); - } - - public TxRxSum(long txPkts, long rxPkts) { - this.txPkts = txPkts; - this.rxPkts = rxPkts; - } - - public TxRxSum(TxRxSum sum) { - txPkts = sum.txPkts; - rxPkts = sum.rxPkts; - } - - public void reset() { - txPkts = -1; - rxPkts = -1; - } - - @Override - public String toString() { - return "{txSum=" + txPkts + " rxSum=" + rxPkts + "}"; - } - - /** - * Get total Tx/Rx packet count from TrafficStats - */ - public void updateTotalTxRxSum() { - this.txPkts = TrafficStats.getMobileTxPackets(); - this.rxPkts = TrafficStats.getMobileRxPackets(); - } - } - - private void onDataReconnect(ApnContext apnContextforRetry, int subId, - @RequestNetworkType int requestType) { - int phoneSubId = mPhone.getSubId(); - String apnType = apnContextforRetry.getApnType(); - String reason = apnContextforRetry.getReason(); - - if (!SubscriptionManager.isValidSubscriptionId(subId) || (subId != phoneSubId)) { - log("onDataReconnect: invalid subId"); - return; - } - - ApnContext apnContext = mApnContexts.get(apnType); - - if (DBG) { - log("onDataReconnect: mState=" + mState + " reason=" + reason + " apnType=" + apnType - + " apnContext=" + apnContext); - } - - if ((apnContext != null) && (apnContext.isEnabled())) { - apnContext.setReason(reason); - DctConstants.State apnContextState = apnContext.getState(); - if (DBG) { - log("onDataReconnect: apnContext state=" + apnContextState); - } - if ((apnContextState == DctConstants.State.FAILED) - || (apnContextState == DctConstants.State.IDLE)) { - if (DBG) { - log("onDataReconnect: state is FAILED|IDLE, disassociate"); - } - apnContext.releaseDataConnection(""); - } else { - if (DBG) log("onDataReconnect: keep associated"); - } - // TODO: IF already associated should we send the EVENT_TRY_SETUP_DATA??? - sendMessage(obtainMessage(DctConstants.EVENT_TRY_SETUP_DATA, requestType, - 0, apnContext)); - } - } - - private void onActionIntentDataStallAlarm(Intent intent) { - if (VDBG_STALL) log("onActionIntentDataStallAlarm: action=" + intent.getAction()); - - int subId = intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY, - SubscriptionManager.INVALID_SUBSCRIPTION_ID); - if (!SubscriptionManager.isValidSubscriptionId(subId) || (subId != mPhone.getSubId())) { - return; - } - - int transportType = intent.getIntExtra(INTENT_DATA_STALL_ALARM_EXTRA_TRANSPORT_TYPE, 0); - if (transportType != mTransportType) { - return; - } - - Message msg = obtainMessage(DctConstants.EVENT_DATA_STALL_ALARM, - intent.getAction()); - msg.arg1 = intent.getIntExtra(INTENT_DATA_STALL_ALARM_EXTRA_TAG, 0); - sendMessage(msg); - } - - private RegistrantList mAllDataDisconnectedRegistrants = new RegistrantList(); - - // member variables - protected final Phone mPhone; - private DctConstants.Activity mActivity = DctConstants.Activity.NONE; - private DctConstants.State mState = DctConstants.State.IDLE; - private final Handler mDataConnectionTracker; - - private long mTxPkts; - private long mRxPkts; - private int mNetStatPollPeriod; - private boolean mNetStatPollEnabled = false; - - private TxRxSum mDataStallTxRxSum = new TxRxSum(0, 0); - // Used to track stale data stall alarms. - private int mDataStallAlarmTag = (int) SystemClock.elapsedRealtime(); - // The current data stall alarm intent - private PendingIntent mDataStallAlarmIntent = null; - // Number of packets sent since the last received packet - private long mSentSinceLastRecv; - // Controls when a simple recovery attempt it to be tried - private int mNoRecvPollCount = 0; - // Reference counter for enabling fail fast - private static int sEnableFailFastRefCounter = 0; - // True if data stall detection is enabled - private volatile boolean mDataStallNoRxEnabled = true; - - protected volatile boolean mFailFast = false; - - // True when in voice call - protected boolean mInVoiceCall = false; - - /** Intent sent when the reconnect alarm fires. */ - private PendingIntent mReconnectIntent = null; - - // When false we will not auto attach and manually attaching is required. - protected boolean mAutoAttachOnCreationConfig = false; - private AtomicBoolean mAutoAttachEnabled = new AtomicBoolean(false); - - // State of screen - // (TODO: Reconsider tying directly to screen, maybe this is - // really a lower power mode") - private boolean mIsScreenOn = true; - - /** Allows the generation of unique Id's for DataConnection objects */ - private AtomicInteger mUniqueIdGenerator = new AtomicInteger(0); - - /** The data connections. */ - private HashMap<Integer, DataConnection> mDataConnections = - new HashMap<Integer, DataConnection>(); - - /** Convert an ApnType string to Id (TODO: Use "enumeration" instead of String for ApnType) */ - private HashMap<String, Integer> mApnToDataConnectionId = new HashMap<String, Integer>(); - - /** Phone.APN_TYPE_* ===> ApnContext */ - protected ConcurrentHashMap<String, ApnContext> mApnContexts = - new ConcurrentHashMap<String, ApnContext>(); - - private SparseArray<ApnContext> mApnContextsByType = new SparseArray<ApnContext>(); - - private ArrayList<DataProfile> mLastDataProfileList = new ArrayList<>(); - - /** RAT name ===> (downstream, upstream) bandwidth values from carrier config. */ - private ConcurrentHashMap<String, Pair<Integer, Integer>> mBandwidths = - new ConcurrentHashMap<>(); - - private boolean mConfigReady = false; - - /** - * Handles changes to the APN db. - */ - private class ApnChangeObserver extends ContentObserver { - public ApnChangeObserver () { - super(mDataConnectionTracker); - } - - @Override - public void onChange(boolean selfChange) { - sendMessage(obtainMessage(DctConstants.EVENT_APN_CHANGED)); - } - } - - //***** Instance Variables - - private boolean mReregisterOnReconnectFailure = false; - - - //***** Constants - - private static final int PROVISIONING_SPINNER_TIMEOUT_MILLIS = 120 * 1000; - - static final Uri PREFERAPN_NO_UPDATE_URI_USING_SUBID = - Uri.parse("content://telephony/carriers/preferapn_no_update/subId/"); - static final String APN_ID = "apn_id"; - - private boolean mCanSetPreferApn = false; - - private AtomicBoolean mAttached = new AtomicBoolean(false); - - /** Watches for changes to the APN db. */ - private ApnChangeObserver mApnObserver; - - private BroadcastReceiver mProvisionBroadcastReceiver; - private ProgressDialog mProvisioningSpinner; - - private final DataServiceManager mDataServiceManager; - - @AccessNetworkConstants.TransportType - private final int mTransportType; - - private DataStallRecoveryHandler mDsRecoveryHandler; - private HandlerThread mHandlerThread; - - private final DataThrottler mDataThrottler; - - private final ThrottleStatusChangedCallback mThrottleStatusCallback; - - /** - * Request network completion message map. Key is the APN type, value is the list of completion - * messages to be sent. Using a list because there might be multiple network requests for - * the same APN type. - */ - private final Map<Integer, List<Message>> mHandoverCompletionMsgs = new HashMap<>(); - - //***** Constructor - public DcTracker(Phone phone, @TransportType int transportType) { - super(); - mPhone = phone; - if (DBG) log("DCT.constructor"); - mTelephonyManager = TelephonyManager.from(phone.getContext()) - .createForSubscriptionId(phone.getSubId()); - // The 'C' in tag indicates cellular, and 'I' indicates IWLAN. This is to distinguish - // between two DcTrackers, one for each. - String tagSuffix = "-" + ((transportType == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) - ? "C" : "I"); - tagSuffix += "-" + mPhone.getPhoneId(); - mLogTag = "DCT" + tagSuffix; - - mTransportType = transportType; - mDataServiceManager = new DataServiceManager(phone, transportType, tagSuffix); - mDataThrottler = new DataThrottler(mPhone, transportType); - - mResolver = mPhone.getContext().getContentResolver(); - mAlarmManager = - (AlarmManager) mPhone.getContext().getSystemService(Context.ALARM_SERVICE); - - mDsRecoveryHandler = new DataStallRecoveryHandler(); - - IntentFilter filter = new IntentFilter(); - filter.addAction(Intent.ACTION_SCREEN_ON); - filter.addAction(Intent.ACTION_SCREEN_OFF); - filter.addAction(INTENT_DATA_STALL_ALARM); - filter.addAction(INTENT_PROVISIONING_APN_ALARM); - filter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED); - filter.addAction(TelephonyManager.ACTION_SIM_CARD_STATE_CHANGED); - filter.addAction(TelephonyManager.ACTION_SIM_APPLICATION_STATE_CHANGED); - - mDataEnabledSettings = mPhone.getDataEnabledSettings(); - - mDataEnabledSettings.registerForDataEnabledChanged(this, - DctConstants.EVENT_DATA_ENABLED_CHANGED, null); - mDataEnabledSettings.registerForDataEnabledOverrideChanged(this, - DctConstants.EVENT_DATA_ENABLED_OVERRIDE_RULES_CHANGED); - - mPhone.getContext().registerReceiver(mIntentReceiver, filter, null, mPhone); - - SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mPhone.getContext()); - mAutoAttachEnabled.set(sp.getBoolean(Phone.DATA_DISABLED_ON_BOOT_KEY, false)); - - mNetworkPolicyManager = (NetworkPolicyManager) mPhone.getContext() - .getSystemService(Context.NETWORK_POLICY_SERVICE); - mNetworkPolicyManager.registerSubscriptionCallback(mSubscriptionCallback); - - mHandlerThread = new HandlerThread("DcHandlerThread"); - mHandlerThread.start(); - Handler dcHandler = new Handler(mHandlerThread.getLooper()); - mDcc = DcController.makeDcc(mPhone, this, mDataServiceManager, dcHandler.getLooper(), - tagSuffix); - mDcTesterFailBringUpAll = new DcTesterFailBringUpAll(mPhone, dcHandler); - - mDataConnectionTracker = this; - registerForAllEvents(); - mApnObserver = new ApnChangeObserver(); - phone.getContext().getContentResolver().registerContentObserver( - Telephony.Carriers.CONTENT_URI, true, mApnObserver); - - initApnContexts(); - - addDefaultApnSettingsAsNeeded(); - - mSettingsObserver = new SettingsObserver(mPhone.getContext(), this); - registerSettingsObserver(); - - mThrottleStatusCallback = new ThrottleStatusChangedCallback(); - mDataThrottler.registerForThrottleStatusChanges(mThrottleStatusCallback); - } - - @VisibleForTesting - public DcTracker() { - mLogTag = "DCT"; - mTelephonyManager = null; - mAlarmManager = null; - mPhone = null; - mDataConnectionTracker = null; - mSettingsObserver = new SettingsObserver(null, this); - mDataEnabledSettings = null; - mTransportType = 0; - mDataServiceManager = null; - mDataThrottler = null; - mThrottleStatusCallback = null; - } - - public void registerServiceStateTrackerEvents() { - mPhone.getServiceStateTracker().registerForDataConnectionAttached(mTransportType, this, - DctConstants.EVENT_DATA_CONNECTION_ATTACHED, null); - mPhone.getServiceStateTracker().registerForDataConnectionDetached(mTransportType, this, - DctConstants.EVENT_DATA_CONNECTION_DETACHED, null); - mPhone.getServiceStateTracker().registerForDataRoamingOn(this, - DctConstants.EVENT_ROAMING_ON, null); - mPhone.getServiceStateTracker().registerForDataRoamingOff(this, - DctConstants.EVENT_ROAMING_OFF, null, true); - mPhone.getServiceStateTracker().registerForPsRestrictedEnabled(this, - DctConstants.EVENT_PS_RESTRICT_ENABLED, null); - mPhone.getServiceStateTracker().registerForPsRestrictedDisabled(this, - DctConstants.EVENT_PS_RESTRICT_DISABLED, null); - mPhone.getServiceStateTracker().registerForDataRegStateOrRatChanged(mTransportType, this, - DctConstants.EVENT_DATA_RAT_CHANGED, null); - } - - public void unregisterServiceStateTrackerEvents() { - mPhone.getServiceStateTracker().unregisterForDataConnectionAttached(mTransportType, this); - mPhone.getServiceStateTracker().unregisterForDataConnectionDetached(mTransportType, this); - mPhone.getServiceStateTracker().unregisterForDataRoamingOn(this); - mPhone.getServiceStateTracker().unregisterForDataRoamingOff(this); - mPhone.getServiceStateTracker().unregisterForPsRestrictedEnabled(this); - mPhone.getServiceStateTracker().unregisterForPsRestrictedDisabled(this); - mPhone.getServiceStateTracker().unregisterForDataRegStateOrRatChanged(mTransportType, this); - mPhone.getServiceStateTracker().unregisterForAirplaneModeChanged(this); - } - - private void registerForAllEvents() { - if (mTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) { - mPhone.mCi.registerForAvailable(this, DctConstants.EVENT_RADIO_AVAILABLE, null); - mPhone.mCi.registerForOffOrNotAvailable(this, - DctConstants.EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null); - mPhone.mCi.registerForPcoData(this, DctConstants.EVENT_PCO_DATA_RECEIVED, null); - } - - // Note, this is fragile - the Phone is now presenting a merged picture - // of PS (volte) & CS and by diving into its internals you're just seeing - // the CS data. This works well for the purposes this is currently used for - // but that may not always be the case. Should probably be redesigned to - // accurately reflect what we're really interested in (registerForCSVoiceCallEnded). - mPhone.getCallTracker().registerForVoiceCallEnded(this, - DctConstants.EVENT_VOICE_CALL_ENDED, null); - mPhone.getCallTracker().registerForVoiceCallStarted(this, - DctConstants.EVENT_VOICE_CALL_STARTED, null); - mPhone.getDisplayInfoController().registerForTelephonyDisplayInfoChanged(this, - DctConstants.EVENT_TELEPHONY_DISPLAY_INFO_CHANGED, null); - registerServiceStateTrackerEvents(); - mDataServiceManager.registerForServiceBindingChanged(this, - DctConstants.EVENT_DATA_SERVICE_BINDING_CHANGED, null); - mDataServiceManager.registerForApnUnthrottled(this, DctConstants.EVENT_APN_UNTHROTTLED); - } - - public void dispose() { - if (DBG) log("DCT.dispose"); - - if (mProvisionBroadcastReceiver != null) { - mPhone.getContext().unregisterReceiver(mProvisionBroadcastReceiver); - mProvisionBroadcastReceiver = null; - } - if (mProvisioningSpinner != null) { - mProvisioningSpinner.dismiss(); - mProvisioningSpinner = null; - } - - cleanUpAllConnectionsInternal(true, null); - - mIsDisposed = true; - mPhone.getContext().unregisterReceiver(mIntentReceiver); - mSettingsObserver.unobserve(); - - mNetworkPolicyManager.unregisterSubscriptionCallback(mSubscriptionCallback); - mDcTesterFailBringUpAll.dispose(); - - mPhone.getContext().getContentResolver().unregisterContentObserver(mApnObserver); - mApnContexts.clear(); - mApnContextsByType.clear(); - mPrioritySortedApnContexts.clear(); - unregisterForAllEvents(); - - destroyDataConnections(); - } - - /** - * Stop the internal handler thread - * - * TESTING ONLY - */ - @VisibleForTesting - public void stopHandlerThread() { - mHandlerThread.quit(); - } - - private void unregisterForAllEvents() { - //Unregister for all events - if (mTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) { - mPhone.mCi.unregisterForAvailable(this); - mPhone.mCi.unregisterForOffOrNotAvailable(this); - mPhone.mCi.unregisterForPcoData(this); - } - - mPhone.getCallTracker().unregisterForVoiceCallEnded(this); - mPhone.getCallTracker().unregisterForVoiceCallStarted(this); - mPhone.getDisplayInfoController().unregisterForTelephonyDisplayInfoChanged(this); - unregisterServiceStateTrackerEvents(); - mDataServiceManager.unregisterForServiceBindingChanged(this); - mDataEnabledSettings.unregisterForDataEnabledChanged(this); - mDataEnabledSettings.unregisterForDataEnabledOverrideChanged(this); - mDataServiceManager.unregisterForApnUnthrottled(this); - } - - /** - * Reevaluate existing data connections when conditions change. - * - * For example, handle reverting restricted networks back to unrestricted. If we're changing - * user data to enabled and this makes data truly enabled (not disabled by other factors) we - * need to reevaluate and possibly add NET_CAPABILITY_NOT_RESTRICTED capability to the data - * connection. This allows non-privilege apps to use the network. - * - * Or when we brought up a unmetered data connection while data is off, we only limit this - * data connection for unmetered use only. When data is turned back on, we need to tear that - * down so a full capable data connection can be re-established. - */ - private void reevaluateDataConnections() { - for (DataConnection dataConnection : mDataConnections.values()) { - dataConnection.reevaluateRestrictedState(); - } - } - - public long getSubId() { - return mPhone.getSubId(); - } - - public DctConstants.Activity getActivity() { - return mActivity; - } - - private void setActivity(DctConstants.Activity activity) { - log("setActivity = " + activity); - mActivity = activity; - mPhone.notifyDataActivity(); - } - - /** - * Request a network - * - * @param networkRequest Network request from clients - * @param type The request type - * @param onHandoverCompleteMsg When request type is handover, this message will be sent when - * handover is completed. For normal request, this should be null. - */ - public void requestNetwork(NetworkRequest networkRequest, @RequestNetworkType int type, - Message onHandoverCompleteMsg) { - if (type != REQUEST_TYPE_HANDOVER && onHandoverCompleteMsg != null) { - throw new RuntimeException("request network with normal type request type but passing " - + "handover complete message."); - } - final int apnType = ApnContext.getApnTypeFromNetworkRequest(networkRequest); - final ApnContext apnContext = mApnContextsByType.get(apnType); - if (apnContext != null) { - apnContext.requestNetwork(networkRequest, type, onHandoverCompleteMsg); - } - } - - public void releaseNetwork(NetworkRequest networkRequest, @ReleaseNetworkType int type) { - final int apnType = ApnContext.getApnTypeFromNetworkRequest(networkRequest); - final ApnContext apnContext = mApnContextsByType.get(apnType); - if (apnContext != null) { - apnContext.releaseNetwork(networkRequest, type); - } - } - - // Turn telephony radio on or off. - private void setRadio(boolean on) { - final ITelephony phone = ITelephony.Stub.asInterface( - TelephonyFrameworkInitializer - .getTelephonyServiceManager() - .getTelephonyServiceRegisterer() - .get()); - try { - phone.setRadio(on); - } catch (Exception e) { - // Ignore. - } - } - - // Class to handle Intent dispatched with user selects the "Sign-in to network" - // notification. - private class ProvisionNotificationBroadcastReceiver extends BroadcastReceiver { - private final String mNetworkOperator; - // Mobile provisioning URL. Valid while provisioning notification is up. - // Set prior to notification being posted as URL contains ICCID which - // disappears when radio is off (which is the case when notification is up). - private final String mProvisionUrl; - - public ProvisionNotificationBroadcastReceiver(String provisionUrl, String networkOperator) { - mNetworkOperator = networkOperator; - mProvisionUrl = provisionUrl; - } - - private void setEnableFailFastMobileData(int enabled) { - sendMessage(obtainMessage(DctConstants.CMD_SET_ENABLE_FAIL_FAST_MOBILE_DATA, enabled, 0)); - } - - private void enableMobileProvisioning() { - final Message msg = obtainMessage(DctConstants.CMD_ENABLE_MOBILE_PROVISIONING); - Bundle bundle = new Bundle(1); - bundle.putString(DctConstants.PROVISIONING_URL_KEY, mProvisionUrl); - msg.setData(bundle); - sendMessage(msg); - } - - @Override - public void onReceive(Context context, Intent intent) { - if (mPhone.getPhoneId() != intent.getIntExtra(EXTRA_PROVISION_PHONE_ID, - SubscriptionManager.INVALID_PHONE_INDEX)) { - return; - } - // Turning back on the radio can take time on the order of a minute, so show user a - // spinner so they know something is going on. - log("onReceive : ProvisionNotificationBroadcastReceiver"); - mProvisioningSpinner = new ProgressDialog(context); - mProvisioningSpinner.setTitle(mNetworkOperator); - mProvisioningSpinner.setMessage( - // TODO: Don't borrow "Connecting..." i18n string; give Telephony a version. - context.getText(com.android.internal.R.string.media_route_status_connecting)); - mProvisioningSpinner.setIndeterminate(true); - mProvisioningSpinner.setCancelable(true); - // Allow non-Activity Service Context to create a View. - mProvisioningSpinner.getWindow().setType( - WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); - mProvisioningSpinner.show(); - // After timeout, hide spinner so user can at least use their device. - // TODO: Indicate to user that it is taking an unusually long time to connect? - sendMessageDelayed(obtainMessage(DctConstants.CMD_CLEAR_PROVISIONING_SPINNER, - mProvisioningSpinner), PROVISIONING_SPINNER_TIMEOUT_MILLIS); - // This code is almost identical to the old - // ConnectivityService.handleMobileProvisioningAction code. - setRadio(true); - setEnableFailFastMobileData(DctConstants.ENABLED); - enableMobileProvisioning(); - } - } - - @Override - protected void finalize() { - if(DBG && mPhone != null) log("finalize"); - } - - private void initApnContexts() { - PersistableBundle carrierConfig; - CarrierConfigManager configManager = (CarrierConfigManager) mPhone.getContext() - .getSystemService(Context.CARRIER_CONFIG_SERVICE); - if (configManager != null) { - carrierConfig = configManager.getConfigForSubId(mPhone.getSubId()); - } else { - carrierConfig = null; - } - initApnContexts(carrierConfig); - } - - //Blows away any existing apncontexts that may exist, only use in ctor. - private void initApnContexts(PersistableBundle carrierConfig) { - if (!mTelephonyManager.isDataCapable()) { - log("initApnContexts: isDataCapable == false. No Apn Contexts loaded"); - return; - } - - log("initApnContexts: E"); - // Load device network attributes from resources - final Collection<ApnConfigType> types = - new ApnConfigTypeRepository(carrierConfig).getTypes(); - - for (ApnConfigType apnConfigType : types) { - ApnContext apnContext = new ApnContext(mPhone, apnConfigType.getType(), mLogTag, this, - apnConfigType.getPriority()); - int bitmask = ApnSetting.getApnTypesBitmaskFromString(apnContext.getApnType()); - mPrioritySortedApnContexts.add(apnContext); - mApnContexts.put(apnContext.getApnType(), apnContext); - mApnContextsByType.put(bitmask, apnContext); - // Notify listeners that all data is disconnected when DCT is initialized. - // Once connections are established, DC will then notify that data is connected. - // This is to prevent the case where the phone process crashed but we don't notify - // listeners that data was disconnected, so they may be stuck in a connected state. - mPhone.notifyDataConnection(new PreciseDataConnectionState.Builder() - .setTransportType(mTransportType) - .setState(TelephonyManager.DATA_DISCONNECTED) - .setApnSetting(new ApnSetting.Builder() - .setApnTypeBitmask(bitmask).buildWithoutCheck()) - .setNetworkType(getDataRat()) - .build()); - log("initApnContexts: apnContext=" + ApnSetting.getApnTypeString( - apnConfigType.getType())); - } - mPrioritySortedApnContexts.sort((c1, c2) -> c2.getPriority() - c1.getPriority()); - logSortedApnContexts(); - } - - private void sortApnContextByPriority() { - if (!mTelephonyManager.isDataCapable()) { - log("sortApnContextByPriority: isDataCapable == false. No Apn Contexts loaded"); - return; - } - - PersistableBundle carrierConfig; - CarrierConfigManager configManager = (CarrierConfigManager) mPhone.getContext() - .getSystemService(Context.CARRIER_CONFIG_SERVICE); - if (configManager != null) { - carrierConfig = configManager.getConfigForSubId(mPhone.getSubId()); - } else { - carrierConfig = null; - } - - log("sortApnContextByPriority: E"); - // Load device network attributes from resources - final Collection<ApnConfigType> types = - new ApnConfigTypeRepository(carrierConfig).getTypes(); - for (ApnConfigType apnConfigType : types) { - if (mApnContextsByType.contains(apnConfigType.getType())) { - ApnContext apnContext = mApnContextsByType.get(apnConfigType.getType()); - apnContext.setPriority(apnConfigType.getPriority()); - } - } - - //Doing sorted in a different list to keep thread safety - ArrayList<ApnContext> prioritySortedApnContexts = - new ArrayList<>(mPrioritySortedApnContexts); - prioritySortedApnContexts.sort((c1, c2) -> c2.getPriority() - c1.getPriority()); - mPrioritySortedApnContexts = prioritySortedApnContexts; - logSortedApnContexts(); - } - - public LinkProperties getLinkProperties(String apnType) { - ApnContext apnContext = mApnContexts.get(apnType); - if (apnContext != null) { - DataConnection dataConnection = apnContext.getDataConnection(); - if (dataConnection != null) { - if (DBG) log("return link properties for " + apnType); - return dataConnection.getLinkProperties(); - } - } - if (DBG) log("return new LinkProperties"); - return new LinkProperties(); - } - - public NetworkCapabilities getNetworkCapabilities(String apnType) { - ApnContext apnContext = mApnContexts.get(apnType); - if (apnContext!=null) { - DataConnection dataConnection = apnContext.getDataConnection(); - if (dataConnection != null) { - if (DBG) { - log("get active pdp is not null, return NetworkCapabilities for " + apnType); - } - return dataConnection.getNetworkCapabilities(); - } - } - if (DBG) log("return new NetworkCapabilities"); - return new NetworkCapabilities(); - } - - // Return all active apn types - public String[] getActiveApnTypes() { - if (DBG) log("get all active apn types"); - ArrayList<String> result = new ArrayList<String>(); - - for (ApnContext apnContext : mApnContexts.values()) { - if (mAttached.get() && apnContext.isReady()) { - result.add(apnContext.getApnType()); - } - } - - return result.toArray(new String[0]); - } - - /** - * Get ApnTypes with connected data connections. This is different than getActiveApnTypes() - * which returns apn types that with active apn contexts. - * @return apn types - */ - public String[] getConnectedApnTypes() { - return mApnContexts.values().stream() - .filter(ac -> ac.getState() == DctConstants.State.CONNECTED) - .map(ApnContext::getApnType) - .toArray(String[]::new); - } - - @VisibleForTesting - public Collection<ApnContext> getApnContexts() { - return mPrioritySortedApnContexts; - } - - /** Return active ApnSetting of a specific apnType */ - public ApnSetting getActiveApnSetting(String apnType) { - if (VDBG) log("get active ApnSetting for type:" + apnType); - ApnContext apnContext = mApnContexts.get(apnType); - return (apnContext != null) ? apnContext.getApnSetting() : null; - } - - // Return active apn of specific apn type - public String getActiveApnString(String apnType) { - if (VDBG) log( "get active apn string for type:" + apnType); - ApnSetting setting = getActiveApnSetting(apnType); - return (setting != null) ? setting.getApnName() : null; - } - - /** - * Returns {@link DctConstants.State} based on the state of the {@link DataConnection} that - * contains a {@link ApnSetting} that supported the given apn type {@code anpType}. - * - * <p> - * Assumes there is less than one {@link ApnSetting} can support the given apn type. - */ - // TODO: for enterprise this always returns IDLE, which is ok for now since it is never called - // for enterprise - public DctConstants.State getState(String apnType) { - DctConstants.State state = DctConstants.State.IDLE; - final int apnTypeBitmask = ApnSetting.getApnTypesBitmaskFromString(apnType); - for (DataConnection dc : mDataConnections.values()) { - ApnSetting apnSetting = dc.getApnSetting(); - if (apnSetting != null && apnSetting.canHandleType(apnTypeBitmask)) { - if (dc.isActive()) { - state = getBetterConnectionState(state, DctConstants.State.CONNECTED); - } else if (dc.isActivating()) { - state = getBetterConnectionState(state, DctConstants.State.CONNECTING); - } else if (dc.isInactive()) { - state = getBetterConnectionState(state, DctConstants.State.IDLE); - } else if (dc.isDisconnecting()) { - state = getBetterConnectionState(state, DctConstants.State.DISCONNECTING); - } - } - } - return state; - } - - /** - * Return a better connection state between {@code stateA} and {@code stateB}. Check - * {@link #DATA_CONNECTION_STATE_PRIORITIES} for the details. - * @return the better connection state between {@code stateA} and {@code stateB}. - */ - private static DctConstants.State getBetterConnectionState( - DctConstants.State stateA, DctConstants.State stateB) { - int idxA = ArrayUtils.indexOf(DATA_CONNECTION_STATE_PRIORITIES, stateA); - int idxB = ArrayUtils.indexOf(DATA_CONNECTION_STATE_PRIORITIES, stateB); - return idxA >= idxB ? stateA : stateB; - } - - // Return if apn type is a provisioning apn. - private boolean isProvisioningApn(String apnType) { - ApnContext apnContext = mApnContexts.get(apnType); - if (apnContext != null) { - return apnContext.isProvisioningApn(); - } - return false; - } - - //****** Called from ServiceStateTracker - /** - * Invoked when ServiceStateTracker observes a transition from GPRS - * attach to detach. - */ - private void onDataConnectionDetached() { - /* - * We presently believe it is unnecessary to tear down the PDP context - * when GPRS detaches, but we should stop the network polling. - */ - if (DBG) log ("onDataConnectionDetached: stop polling and notify detached"); - stopNetStatPoll(); - stopDataStallAlarm(); - mAttached.set(false); - } - - private void onDataConnectionAttached() { - if (DBG) log("onDataConnectionAttached"); - mAttached.set(true); - if (isAnyDataConnected()) { - if (DBG) log("onDataConnectionAttached: start polling notify attached"); - startNetStatPoll(); - startDataStallAlarm(DATA_STALL_NOT_SUSPECTED); - } - if (mAutoAttachOnCreationConfig) { - mAutoAttachEnabled.set(true); - } - setupDataOnAllConnectableApns(Phone.REASON_DATA_ATTACHED, RetryFailures.ALWAYS); - } - - /** - * Check if it is allowed to make a data connection (without checking APN context specific - * conditions). - * - * @param dataConnectionReasons Data connection allowed or disallowed reasons as the output - * param. It's okay to pass null here and no reasons will be - * provided. - * @return True if data connection is allowed, otherwise false. - */ - public boolean isDataAllowed(DataConnectionReasons dataConnectionReasons) { - return isDataAllowed(null, REQUEST_TYPE_NORMAL, dataConnectionReasons); - } - - /** - * Check if it is allowed to make a data connection for a given APN type. - * - * @param apnContext APN context. If passing null, then will only check general but not APN - * specific conditions (e.g. APN state, metered/unmetered APN). - * @param requestType Setup data request type. - * @param dataConnectionReasons Data connection allowed or disallowed reasons as the output - * param. It's okay to pass null here and no reasons will be - * provided. - * @return True if data connection is allowed, otherwise false. - */ - public boolean isDataAllowed(ApnContext apnContext, @RequestNetworkType int requestType, - DataConnectionReasons dataConnectionReasons) { - // Step 1: Get all environment conditions. - // Step 2: Special handling for emergency APN. - // Step 3. Build disallowed reasons. - // Step 4: Determine if data should be allowed in some special conditions. - - DataConnectionReasons reasons = new DataConnectionReasons(); - - int requestApnType = 0; - if (apnContext != null) { - requestApnType = apnContext.getApnTypeBitmask(); - } - - // Step 1: Get all environment conditions. - final boolean internalDataEnabled = mDataEnabledSettings.isInternalDataEnabled(); - boolean attachedState = mAttached.get(); - boolean desiredPowerState = mPhone.getServiceStateTracker().getDesiredPowerState(); - boolean radioStateFromCarrier = mPhone.getServiceStateTracker().getPowerStateFromCarrier(); - // TODO: Remove this hack added by ag/641832. - int dataRat = getDataRat(); - if (dataRat == ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN) { - desiredPowerState = true; - radioStateFromCarrier = true; - } - - boolean defaultDataSelected = SubscriptionManager.isValidSubscriptionId( - SubscriptionManager.getDefaultDataSubscriptionId()); - - boolean isMeteredApnType = apnContext == null - || ApnSettingUtils.isMeteredApnType(requestApnType, mPhone); - - PhoneConstants.State phoneState = PhoneConstants.State.IDLE; - // Note this is explicitly not using mPhone.getState. See b/19090488. - // mPhone.getState reports the merge of CS and PS (volte) voice call state - // but we only care about CS calls here for data/voice concurrency issues. - // Calling getCallTracker currently gives you just the CS side where the - // ImsCallTracker is held internally where applicable. - // This should be redesigned to ask explicitly what we want: - // voiceCallStateAllowDataCall, or dataCallAllowed or something similar. - if (mPhone.getCallTracker() != null) { - phoneState = mPhone.getCallTracker().getState(); - } - - // Step 2: Special handling for emergency APN. - if (apnContext != null - && requestApnType == ApnSetting.TYPE_EMERGENCY - && apnContext.isConnectable()) { - // If this is an emergency APN, as long as the APN is connectable, we - // should allow it. - if (dataConnectionReasons != null) { - dataConnectionReasons.add(DataAllowedReasonType.EMERGENCY_APN); - } - // Bail out without further checks. - return true; - } - - // Step 3. Build disallowed reasons. - if (apnContext != null && !apnContext.isConnectable()) { - DctConstants.State state = apnContext.getState(); - if (state == DctConstants.State.CONNECTED) { - reasons.add(DataDisallowedReasonType.DATA_ALREADY_CONNECTED); - } else if (state == DctConstants.State.DISCONNECTING) { - reasons.add(DataDisallowedReasonType.DATA_IS_DISCONNECTING); - } else if (state == DctConstants.State.CONNECTING) { - reasons.add(DataDisallowedReasonType.DATA_IS_CONNECTING); - } else { - reasons.add(DataDisallowedReasonType.APN_NOT_CONNECTABLE); - } - } - - // In legacy mode, if RAT is IWLAN then don't allow default/IA PDP at all. - // Rest of APN types can be evaluated for remaining conditions. - if ((apnContext != null && requestApnType == ApnSetting.TYPE_DEFAULT - || requestApnType == ApnSetting.TYPE_ENTERPRISE - || requestApnType == ApnSetting.TYPE_IA) - && mPhone.getAccessNetworksManager().isInLegacyMode() - && dataRat == ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN) { - reasons.add(DataDisallowedReasonType.ON_IWLAN); - } - - // If device is not on NR, don't allow enterprise - if (apnContext != null && requestApnType == ApnSetting.TYPE_ENTERPRISE - && dataRat != ServiceState.RIL_RADIO_TECHNOLOGY_NR) { - reasons.add(DataDisallowedReasonType.NOT_ON_NR); - } - - if (shouldRestrictDataForEcbm() || mPhone.isInEmergencyCall()) { - reasons.add(DataDisallowedReasonType.IN_ECBM); - } - - if (!attachedState && !shouldAutoAttach() && requestType != REQUEST_TYPE_HANDOVER) { - reasons.add(DataDisallowedReasonType.NOT_ATTACHED); - } - if (mPhone.getSubId() == SubscriptionManager.INVALID_SUBSCRIPTION_ID) { - reasons.add(DataDisallowedReasonType.SIM_NOT_READY); - } - if (phoneState != PhoneConstants.State.IDLE - && !mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) { - reasons.add(DataDisallowedReasonType.INVALID_PHONE_STATE); - reasons.add(DataDisallowedReasonType.CONCURRENT_VOICE_DATA_NOT_ALLOWED); - } - if (!internalDataEnabled) { - reasons.add(DataDisallowedReasonType.INTERNAL_DATA_DISABLED); - } - if (!defaultDataSelected) { - reasons.add(DataDisallowedReasonType.DEFAULT_DATA_UNSELECTED); - } - if (mPhone.getServiceState().getDataRoaming() && !getDataRoamingEnabled()) { - reasons.add(DataDisallowedReasonType.ROAMING_DISABLED); - } - if (mIsPsRestricted) { - reasons.add(DataDisallowedReasonType.PS_RESTRICTED); - } - if (!desiredPowerState) { - reasons.add(DataDisallowedReasonType.UNDESIRED_POWER_STATE); - } - if (!radioStateFromCarrier) { - reasons.add(DataDisallowedReasonType.RADIO_DISABLED_BY_CARRIER); - } - if (!mDataServiceBound) { - reasons.add(DataDisallowedReasonType.DATA_SERVICE_NOT_READY); - } - - if (apnContext != null) { - if (mPhone.getAccessNetworksManager().getPreferredTransport( - apnContext.getApnTypeBitmask()) - == AccessNetworkConstants.TRANSPORT_TYPE_INVALID) { - // If QNS explicitly specified this APN type is not allowed on either cellular or - // IWLAN, we should not allow data setup. - reasons.add(DataDisallowedReasonType.DISABLED_BY_QNS); - } else if (mTransportType != mPhone.getAccessNetworksManager().getPreferredTransport( - apnContext.getApnTypeBitmask())) { - // If the latest preference has already switched to other transport, we should not - // allow data setup. - reasons.add(DataDisallowedReasonType.ON_OTHER_TRANSPORT); - } - - // If the transport has been already switched to the other transport, we should not - // allow the data setup. The only exception is the handover case, where we setup - // handover data connection before switching the transport. - if (mTransportType != mPhone.getAccessNetworksManager().getCurrentTransport( - apnContext.getApnTypeBitmask()) && requestType != REQUEST_TYPE_HANDOVER) { - reasons.add(DataDisallowedReasonType.ON_OTHER_TRANSPORT); - } - - // Check if the device is under data throttling. - long retryTime = mDataThrottler.getRetryTime(apnContext.getApnTypeBitmask()); - if (retryTime > SystemClock.elapsedRealtime()) { - reasons.add(DataDisallowedReasonType.DATA_THROTTLED); - } - } - - boolean isDataEnabled = apnContext == null ? mDataEnabledSettings.isDataEnabled() - : mDataEnabledSettings.isDataEnabled(requestApnType); - - if (!isDataEnabled) { - reasons.add(DataDisallowedReasonType.DATA_DISABLED); - } - - // If there are hard disallowed reasons, we should not allow data connection no matter what. - if (reasons.containsHardDisallowedReasons()) { - if (dataConnectionReasons != null) { - dataConnectionReasons.copyFrom(reasons); - } - return false; - } - - // Step 4: Determine if data should be allowed in some special conditions. - - // At this point, if data is not allowed, it must be because of the soft reasons. We - // should start to check some special conditions that data will be allowed. - if (!reasons.allowed()) { - // Check if the transport is WLAN ie wifi (for AP-assisted mode devices) - if (mTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WLAN) { - reasons.add(DataAllowedReasonType.UNMETERED_APN); - // Or if the data is on cellular, and the APN type is determined unmetered by the - // configuration. - } else if (mTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WWAN - && !isMeteredApnType && requestApnType != ApnSetting.TYPE_DEFAULT - && requestApnType != ApnSetting.TYPE_ENTERPRISE) { - reasons.add(DataAllowedReasonType.UNMETERED_APN); - } - - // If the request is restricted and there are only soft disallowed reasons (e.g. data - // disabled, data roaming disabled) existing, we should allow the data. ENTERPRISE is - // an exception and should not be treated as restricted for this purpose; it should be - // treated same as DEFAULT. - if (apnContext != null - && apnContext.hasRestrictedRequests(true) - && !apnContext.getApnType().equals(ApnSetting.TYPE_ENTERPRISE_STRING) - && !reasons.allowed()) { - reasons.add(DataAllowedReasonType.RESTRICTED_REQUEST); - } - } else { - // If there is no disallowed reasons, then we should allow the data request with - // normal reason. - reasons.add(DataAllowedReasonType.NORMAL); - } - - if (dataConnectionReasons != null) { - dataConnectionReasons.copyFrom(reasons); - } - - return reasons.allowed(); - } - - // arg for setupDataOnAllConnectableApns - protected enum RetryFailures { - // retry failed networks always (the old default) - ALWAYS, - // retry only when a substantial change has occurred. Either: - // 1) we were restricted by voice/data concurrency and aren't anymore - // 2) our apn list has change - ONLY_ON_CHANGE - }; - - protected void setupDataOnAllConnectableApns(String reason, RetryFailures retryFailures) { - if (VDBG) log("setupDataOnAllConnectableApns: " + reason); - - if (DBG && !VDBG) { - StringBuilder sb = new StringBuilder(120); - for (ApnContext apnContext : mPrioritySortedApnContexts) { - sb.append(apnContext.getApnType()); - sb.append(":[state="); - sb.append(apnContext.getState()); - sb.append(",enabled="); - sb.append(apnContext.isEnabled()); - sb.append("] "); - } - log("setupDataOnAllConnectableApns: " + reason + " " + sb); - } - - for (ApnContext apnContext : mPrioritySortedApnContexts) { - setupDataOnConnectableApn(apnContext, reason, retryFailures); - } - } - - protected void setupDataOnConnectableApn(ApnContext apnContext, String reason, - RetryFailures retryFailures) { - if (VDBG) log("setupDataOnAllConnectableApns: apnContext " + apnContext); - - if (apnContext.getState() == DctConstants.State.FAILED - || apnContext.getState() == DctConstants.State.RETRYING) { - if (retryFailures == RetryFailures.ALWAYS) { - apnContext.releaseDataConnection(reason); - } else if (!apnContext.isConcurrentVoiceAndDataAllowed() - && mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) { - // RetryFailures.ONLY_ON_CHANGE - check if voice concurrency has changed - apnContext.releaseDataConnection(reason); - } - } - if (apnContext.isConnectable()) { - log("isConnectable() call trySetupData"); - apnContext.setReason(reason); - trySetupData(apnContext, REQUEST_TYPE_NORMAL, null); - } - } - - private boolean shouldRestrictDataForEcbm() { - boolean isInEcm = mPhone.isInEcm(); - boolean isInImsEcm = mPhone.getImsPhone() != null && mPhone.getImsPhone().isInImsEcm(); - log("shouldRestrictDataForEcbm: isInEcm=" + isInEcm + " isInImsEcm=" + isInImsEcm); - return isInEcm && !isInImsEcm; - } - - private boolean isHandoverPending(@ApnType int apnType) { - List<Message> messageList = mHandoverCompletionMsgs.get(apnType); - return messageList != null && messageList.size() > 0; - } - - private void trySetupData(ApnContext apnContext, @RequestNetworkType int requestType, - @Nullable Message onHandoverCompleteMsg) { - if (onHandoverCompleteMsg != null) { - addHandoverCompleteMsg(onHandoverCompleteMsg, apnContext.getApnTypeBitmask()); - } - - if (mPhone.getSimulatedRadioControl() != null) { - // Assume data is connected on the simulator - log("trySetupData: X We're on the simulator; assuming connected retValue=true"); - return; - } - - DataConnectionReasons dataConnectionReasons = new DataConnectionReasons(); - boolean isDataAllowed = isDataAllowed(apnContext, requestType, dataConnectionReasons); - String logStr = "trySetupData for APN type " + apnContext.getApnType() + ", reason: " - + apnContext.getReason() + ", requestType=" + requestTypeToString(requestType) - + ". " + dataConnectionReasons.toString(); - if (dataConnectionReasons.contains(DataDisallowedReasonType.DISABLED_BY_QNS) - || dataConnectionReasons.contains(DataDisallowedReasonType.ON_OTHER_TRANSPORT)) { - logStr += ", current transport=" + AccessNetworkConstants.transportTypeToString( - mPhone.getAccessNetworksManager().getCurrentTransport( - apnContext.getApnTypeBitmask())); - logStr += ", preferred transport=" + AccessNetworkConstants.transportTypeToString( - mPhone.getAccessNetworksManager().getPreferredTransport( - apnContext.getApnTypeBitmask())); - } - if (DBG) log(logStr); - ApnContext.requestLog(apnContext, logStr); - if (!isDataAllowed) { - StringBuilder str = new StringBuilder(); - - str.append("trySetupData failed. apnContext = [type=" + apnContext.getApnType() - + ", mState=" + apnContext.getState() + ", apnEnabled=" - + apnContext.isEnabled() + ", mDependencyMet=" - + apnContext.isDependencyMet() + "] "); - - if (!mDataEnabledSettings.isDataEnabled()) { - str.append("isDataEnabled() = false. " + mDataEnabledSettings); - } - - // Check if it fails because of the existing data is still disconnecting. - if (dataConnectionReasons.contains(DataDisallowedReasonType.DATA_IS_DISCONNECTING) - && isHandoverPending(apnContext.getApnTypeBitmask())) { - // Normally we don't retry when isDataAllow() returns false, because that's consider - // pre-condition not met, for example, data not enabled by the user, or airplane - // mode is on. If we retry in those cases, there will be significant power impact. - // DATA_IS_DISCONNECTING is a special case we want to retry, and for the handover - // case only. - log("Data is disconnecting. Will retry handover later."); - return; - } - - // If this is a data retry, we should set the APN state to FAILED so it won't stay - // in RETRYING forever. - if (apnContext.getState() == DctConstants.State.RETRYING) { - apnContext.setState(DctConstants.State.FAILED); - str.append(" Stop retrying."); - } - - if (DBG) log(str.toString()); - ApnContext.requestLog(apnContext, str.toString()); - if (requestType == REQUEST_TYPE_HANDOVER) { - // 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; - } - - if (apnContext.getState() == DctConstants.State.FAILED) { - String str = "trySetupData: make a FAILED ApnContext IDLE so its reusable"; - if (DBG) log(str); - ApnContext.requestLog(apnContext, str); - apnContext.setState(DctConstants.State.IDLE); - } - int radioTech = getDataRat(); - if (radioTech == ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN && mPhone.getServiceState() - .getState() == ServiceState.STATE_IN_SERVICE) { - radioTech = getVoiceRat(); - } - log("service state=" + mPhone.getServiceState()); - apnContext.setConcurrentVoiceAndDataAllowed(mPhone.getServiceStateTracker() - .isConcurrentVoiceAndDataAllowed()); - if (apnContext.getState() == DctConstants.State.IDLE) { - ArrayList<ApnSetting> waitingApns = - buildWaitingApns(apnContext.getApnType(), radioTech); - if (waitingApns.isEmpty()) { - String str = "trySetupData: X No APN found retValue=false"; - if (DBG) log(str); - ApnContext.requestLog(apnContext, str); - if (requestType == REQUEST_TYPE_HANDOVER) { - sendHandoverCompleteMessages(apnContext.getApnTypeBitmask(), false, - false); - } - return; - } else { - apnContext.setWaitingApns(waitingApns); - if (DBG) { - log("trySetupData: Create from mAllApnSettings : " - + apnListToString(mAllApnSettings)); - } - } - } - - if (!setupData(apnContext, radioTech, requestType) - && requestType == REQUEST_TYPE_HANDOVER) { - sendHandoverCompleteMessages(apnContext.getApnTypeBitmask(), false, false); - } - } - - /** - * Clean up all data connections. Note this is just detach the APN context from the data - * connection. After all APN contexts are detached from the data connection, the data - * connection will be torn down. - * - * @param reason Reason for the clean up. - */ - public void cleanUpAllConnections(String reason) { - log("cleanUpAllConnections"); - Message msg = obtainMessage(DctConstants.EVENT_CLEAN_UP_ALL_CONNECTIONS); - msg.obj = reason; - sendMessage(msg); - } - - /** - * Clean up all data connections by detaching the APN contexts from the data connections, which - * eventually tearing down all data connections after all APN contexts are detached from the - * data connections. - * - * @param detach {@code true} if detaching APN context from the underlying data connection (when - * no other APN context is attached to the data connection, the data connection will be torn - * down.) {@code false} to only reset the data connection's state machine. - * - * @param reason reason for the clean up. - * @return boolean - true if we did cleanup any connections, false if they - * were already all disconnected. - */ - private boolean cleanUpAllConnectionsInternal(boolean detach, String reason) { - if (DBG) log("cleanUpAllConnectionsInternal: detach=" + detach + " reason=" + reason); - boolean didDisconnect = false; - boolean disableMeteredOnly = false; - - // reasons that only metered apn will be torn down - if (!TextUtils.isEmpty(reason)) { - disableMeteredOnly = reason.equals(Phone.REASON_DATA_SPECIFIC_DISABLED) || - reason.equals(Phone.REASON_ROAMING_ON) || - reason.equals(Phone.REASON_CARRIER_ACTION_DISABLE_METERED_APN); - } - - for (ApnContext apnContext : mApnContexts.values()) { - // Exclude the IMS APN from single data connection case. - if (reason.equals(Phone.REASON_SINGLE_PDN_ARBITRATION) - && apnContext.getApnType().equals(ApnSetting.TYPE_IMS_STRING)) { - continue; - } - - if (shouldCleanUpConnection(apnContext, disableMeteredOnly, - reason.equals(Phone.REASON_SINGLE_PDN_ARBITRATION))) { - // TODO - only do cleanup if not disconnected - if (apnContext.isDisconnected() == false) didDisconnect = true; - apnContext.setReason(reason); - cleanUpConnectionInternal(detach, RELEASE_TYPE_DETACH, apnContext); - } else if (DBG) { - log("cleanUpAllConnectionsInternal: APN type " + apnContext.getApnType() - + " shouldn't be cleaned up."); - } - } - - stopNetStatPoll(); - stopDataStallAlarm(); - - // TODO: Do we need mRequestedApnType? - mRequestedApnType = ApnSetting.TYPE_DEFAULT; - - if (areAllDataDisconnected()) { - notifyAllDataDisconnected(); - } - - return didDisconnect; - } - - boolean shouldCleanUpConnection(ApnContext apnContext, boolean disableMeteredOnly, - boolean singlePdn) { - if (apnContext == null) return false; - - // If APN setting is not null and the reason is single PDN arbitration, clean up connection. - ApnSetting apnSetting = apnContext.getApnSetting(); - if (apnSetting != null && singlePdn) return true; - - // If meteredOnly is false, clean up all connections. - if (!disableMeteredOnly) return true; - - // If meteredOnly is true, and apnSetting is null or it's un-metered, no need to clean up. - if (apnSetting == null || !ApnSettingUtils.isMetered(apnSetting, mPhone)) return false; - - boolean isRoaming = mPhone.getServiceState().getDataRoaming(); - boolean isDataRoamingDisabled = !getDataRoamingEnabled(); - boolean isDataDisabled = !mDataEnabledSettings.isDataEnabled( - apnSetting.getApnTypeBitmask()); - - // Should clean up if its data is disabled, or data roaming is disabled while roaming. - return isDataDisabled || (isRoaming && isDataRoamingDisabled); - } - - /** - * Detach the APN context from the associated data connection. This data connection might be - * torn down if no other APN context is attached to it. - * - * @param apnContext The APN context to be detached - */ - void cleanUpConnection(ApnContext apnContext) { - if (DBG) log("cleanUpConnection: apnContext=" + apnContext); - Message msg = obtainMessage(DctConstants.EVENT_CLEAN_UP_CONNECTION); - msg.arg2 = 0; - msg.obj = apnContext; - sendMessage(msg); - } - - /** - * Detach the APN context from the associated data connection. This data connection will be - * torn down if no other APN context is attached to it. - * - * @param detach {@code true} if detaching APN context from the underlying data connection (when - * no other APN context is attached to the data connection, the data connection will be torn - * down.) {@code false} to only reset the data connection's state machine. - * @param releaseType Data release type. - * @param apnContext The APN context to be detached. - */ - private void cleanUpConnectionInternal(boolean detach, @ReleaseNetworkType int releaseType, - ApnContext apnContext) { - if (apnContext == null) { - if (DBG) log("cleanUpConnectionInternal: apn context is null"); - return; - } - - DataConnection dataConnection = apnContext.getDataConnection(); - String str = "cleanUpConnectionInternal: detach=" + detach + " reason=" - + apnContext.getReason(); - if (VDBG) log(str + " apnContext=" + apnContext); - ApnContext.requestLog(apnContext, str); - if (detach) { - if (apnContext.isDisconnected()) { - // The request is detach and but ApnContext is not connected. - // If apnContext is not enabled anymore, break the linkage to the data connection. - apnContext.releaseDataConnection(""); - } else { - // Connection is still there. Try to clean up. - if (dataConnection != null) { - if (apnContext.getState() != DctConstants.State.DISCONNECTING) { - boolean disconnectAll = false; - if (ApnSetting.TYPE_DUN_STRING.equals(apnContext.getApnType()) - && ServiceState.isCdma(getDataRat())) { - if (DBG) { - log("cleanUpConnectionInternal: disconnectAll DUN connection"); - } - // For CDMA DUN, we need to tear it down immediately. A new data - // connection will be reestablished with correct profile id. - disconnectAll = true; - } - final int generation = apnContext.getConnectionGeneration(); - str = "cleanUpConnectionInternal: tearing down" - + (disconnectAll ? " all" : "") + " using gen#" + generation; - if (DBG) log(str + "apnContext=" + apnContext); - ApnContext.requestLog(apnContext, str); - Pair<ApnContext, Integer> pair = new Pair<>(apnContext, generation); - Message msg = obtainMessage(DctConstants.EVENT_DISCONNECT_DONE, pair); - - if (disconnectAll || releaseType == RELEASE_TYPE_HANDOVER) { - dataConnection.tearDownAll(apnContext.getReason(), releaseType, msg); - } else { - dataConnection.tearDown(apnContext, apnContext.getReason(), msg); - } - - apnContext.setState(DctConstants.State.DISCONNECTING); - } - } else { - // apn is connected but no reference to the data connection. - // Should not be happen, but reset the state in case. - apnContext.setState(DctConstants.State.IDLE); - ApnContext.requestLog( - apnContext, "cleanUpConnectionInternal: connected, bug no dc"); - } - } - } else { - // force clean up the data connection. - if (dataConnection != null) dataConnection.reset(); - apnContext.setState(DctConstants.State.IDLE); - apnContext.setDataConnection(null); - } - - // If there is any outstanding handover request, we need to respond it. - sendHandoverCompleteMessages(apnContext.getApnTypeBitmask(), false, false); - - // Make sure reconnection alarm is cleaned up if there is no ApnContext - // associated to the connection. - if (dataConnection != null) { - cancelReconnect(apnContext); - } - str = "cleanUpConnectionInternal: X detach=" + detach + " reason=" - + apnContext.getReason(); - if (DBG) log(str + " apnContext=" + apnContext + " dc=" + apnContext.getDataConnection()); - } - - private Cursor getPreferredApnCursor(int subId) { - Cursor cursor = null; - if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) { - cursor = mPhone.getContext().getContentResolver().query( - Uri.withAppendedPath(PREFERAPN_NO_UPDATE_URI_USING_SUBID, - String.valueOf(subId)), null, null, null, - Telephony.Carriers.DEFAULT_SORT_ORDER); - } - return cursor; - } - - private ApnSetting getPreferredApnFromDB() { - ApnSetting preferredApn = null; - Cursor cursor = getPreferredApnCursor(mPhone.getSubId()); - if (cursor != null) { - if (cursor.getCount() > 0) { - cursor.moveToFirst(); - preferredApn = ApnSetting.makeApnSetting(cursor); - } - cursor.close(); - } - if (VDBG) log("getPreferredApnFromDB: preferredApn=" + preferredApn); - return preferredApn; - } - - private void setDefaultPreferredApnIfNeeded() { - ApnSetting defaultPreferredApn = null; - PersistableBundle bundle = getCarrierConfig(); - String defaultPreferredApnName = bundle.getString(CarrierConfigManager - .KEY_DEFAULT_PREFERRED_APN_NAME_STRING); - - if (TextUtils.isEmpty(defaultPreferredApnName) || getPreferredApnFromDB() != null) { - return; - } - - String selection = Telephony.Carriers.APN + " = \"" + defaultPreferredApnName + "\" AND " - + Telephony.Carriers.EDITED_STATUS + " = " + Telephony.Carriers.UNEDITED; - Cursor cursor = mPhone.getContext().getContentResolver().query( - Uri.withAppendedPath(Telephony.Carriers.SIM_APN_URI, - "filtered/subId/" + mPhone.getSubId()), - null, selection, null, Telephony.Carriers._ID); - - if (cursor != null) { - if (cursor.getCount() > 0) { - if (cursor.moveToFirst()) { - defaultPreferredApn = ApnSetting.makeApnSetting(cursor); - } - } - cursor.close(); - } - - if (defaultPreferredApn != null - && defaultPreferredApn.canHandleType(mRequestedApnType)) { - log("setDefaultPreferredApnIfNeeded: For APN type " - + ApnSetting.getApnTypeString(mRequestedApnType) - + " found default apnSetting " - + defaultPreferredApn); - - setPreferredApn(defaultPreferredApn.getId(), true); - } - - return; - } - - /** - * Check if preferred apn is allowed to edit by user. - * @return {@code true} if it is allowed to edit. - */ - @VisibleForTesting - public boolean isPreferredApnUserEdited() { - boolean isUserEdited = false; - Cursor cursor = getPreferredApnCursor(mPhone.getSubId()); - if (cursor != null) { - if (cursor.getCount() > 0) { - if (cursor.moveToFirst()) { - isUserEdited = cursor.getInt( - cursor.getColumnIndexOrThrow(Telephony.Carriers.EDITED_STATUS)) - == Telephony.Carriers.USER_EDITED; - } - } - cursor.close(); - } - if (VDBG) log("isPreferredApnUserEdited: isUserEdited=" + isUserEdited); - return isUserEdited; - } - - /** - * Fetch the DUN apns - * @return a list of DUN ApnSetting objects - */ - @VisibleForTesting - public @NonNull ArrayList<ApnSetting> fetchDunApns() { - if (mPhone.getServiceState().getRoaming() && !isPreferredApnUserEdited() - && getCarrierConfig().getBoolean(CarrierConfigManager - .KEY_DISABLE_DUN_APN_WHILE_ROAMING_WITH_PRESET_APN_BOOL)) { - if (VDBG) log("fetchDunApns: Dun apn is not used in roaming network"); - return new ArrayList<ApnSetting>(0); - } - - int bearer = getDataRat(); - ArrayList<ApnSetting> dunCandidates = new ArrayList<ApnSetting>(); - ArrayList<ApnSetting> retDunSettings = new ArrayList<ApnSetting>(); - - if (dunCandidates.isEmpty()) { - if (!ArrayUtils.isEmpty(mAllApnSettings)) { - for (ApnSetting apn : mAllApnSettings) { - if (apn.canHandleType(ApnSetting.TYPE_DUN)) { - dunCandidates.add(apn); - } - } - if (VDBG) log("fetchDunApns: dunCandidates from database: " + dunCandidates); - } - } - - int preferredApnSetId = getPreferredApnSetId(); - ApnSetting preferredApn = getPreferredApnFromDB(); - for (ApnSetting dunSetting : dunCandidates) { - if (dunSetting.canSupportNetworkType( - ServiceState.rilRadioTechnologyToNetworkType(bearer))) { - if (preferredApnSetId == dunSetting.getApnSetId()) { - if (preferredApn != null && preferredApn.equals(dunSetting)) { - // If there is a preferred APN can handled DUN type, prepend it to list to - // use it preferred. - retDunSettings.add(0, dunSetting); - } else { - retDunSettings.add(dunSetting); - } - } - } - } - - if (VDBG) log("fetchDunApns: dunSettings=" + retDunSettings); - return retDunSettings; - } - - private int getPreferredApnSetId() { - // preferapnset uri returns all APNs for the current carrier which have an apn_set_id - // equal to the preferred APN (if no preferred APN, or if the preferred APN has no set id, - // the query will return null) - Cursor c = mPhone.getContext().getContentResolver() - .query(Uri.withAppendedPath(Telephony.Carriers.CONTENT_URI, - "preferapnset/subId/" + mPhone.getSubId()), - new String[] {Telephony.Carriers.APN_SET_ID}, null, null, null); - if (c == null) { - loge("getPreferredApnSetId: cursor is null"); - return Telephony.Carriers.NO_APN_SET_ID; - } - - int setId; - if (c.getCount() < 1) { - loge("getPreferredApnSetId: no APNs found"); - setId = Telephony.Carriers.NO_APN_SET_ID; - } else { - c.moveToFirst(); - setId = c.getInt(0 /* index of Telephony.Carriers.APN_SET_ID */); - } - - if (!c.isClosed()) { - c.close(); - } - return setId; - } - - public boolean hasMatchedTetherApnSetting() { - ArrayList<ApnSetting> matches = fetchDunApns(); - log("hasMatchedTetherApnSetting: APNs=" + matches); - return matches.size() > 0; - } - - /** - * @return the {@link DataConnection} with the given context id {@code cid}. - */ - public DataConnection getDataConnectionByContextId(int cid) { - return mDcc.getActiveDcByCid(cid); - } - - /** - * @return the {@link DataConnection} with the given APN context. Null if no data connection - * is found. - */ - public @Nullable DataConnection getDataConnectionByApnType(String apnType) { - // TODO: Clean up all APN type in string usage - ApnContext apnContext = mApnContexts.get(apnType); - if (apnContext != null) { - return apnContext.getDataConnection(); - } - return null; - } - - /** - * Check if the data fail cause is a permanent failure (i.e. Frameworks will not retry data - * setup). - * - * @param dcFailCause The data fail cause - * @return {@code true} if the data fail cause is a permanent failure. - */ - @VisibleForTesting - public boolean isPermanentFailure(@DataFailureCause int dcFailCause) { - return (DataFailCause.isPermanentFailure(mPhone.getContext(), dcFailCause, - mPhone.getSubId()) - && (mAttached.get() == false || dcFailCause != DataFailCause.SIGNAL_LOST)); - } - - private DataConnection findFreeDataConnection() { - for (DataConnection dataConnection : mDataConnections.values()) { - boolean inUse = false; - for (ApnContext apnContext : mApnContexts.values()) { - if (apnContext.getDataConnection() == dataConnection) { - inUse = true; - break; - } - } - if (!inUse) { - if (DBG) { - log("findFreeDataConnection: found free DataConnection=" + dataConnection); - } - return dataConnection; - } - } - log("findFreeDataConnection: NO free DataConnection"); - return null; - } - - /** - * Setup a data connection based on given APN type. - * - * @param apnContext APN context - * @param radioTech RAT of the data connection - * @param requestType Data request type - * @return True if successful, otherwise false. - */ - private boolean setupData(ApnContext apnContext, int radioTech, - @RequestNetworkType int requestType) { - if (DBG) { - log("setupData: apnContext=" + apnContext + ", requestType=" - + requestTypeToString(requestType)); - } - ApnContext.requestLog( - apnContext, "setupData. requestType=" + requestTypeToString(requestType)); - ApnSetting apnSetting; - DataConnection dataConnection = null; - - apnSetting = apnContext.getNextApnSetting(); - - if (apnSetting == null) { - if (DBG) log("setupData: return for no apn found!"); - return false; - } - - // profile id is only meaningful when the profile is persistent on the modem. - int profileId = DATA_PROFILE_INVALID; - if (apnSetting.isPersistent()) { - profileId = apnSetting.getProfileId(); - if (profileId == DATA_PROFILE_DEFAULT) { - profileId = getApnProfileID(apnContext.getApnType()); - } - } - - // On CDMA, if we're explicitly asking for DUN, we need have - // a dun-profiled connection so we can't share an existing one - // On GSM/LTE we can share existing apn connections provided they support - // this type. - // If asking for ENTERPRISE, there are no compatible data connections, so skip this check - if ((apnContext.getApnTypeBitmask() != ApnSetting.TYPE_DUN - || ServiceState.isGsm(getDataRat())) - && apnContext.getApnTypeBitmask() != ApnSetting.TYPE_ENTERPRISE) { - dataConnection = checkForCompatibleDataConnection(apnContext, apnSetting); - if (dataConnection != null) { - // Get the apn setting used by the data connection - ApnSetting dataConnectionApnSetting = dataConnection.getApnSetting(); - if (dataConnectionApnSetting != null) { - // Setting is good, so use it. - apnSetting = dataConnectionApnSetting; - } - } - } - if (dataConnection == null) { - if (isOnlySingleDcAllowed(radioTech)) { - if (isHigherPriorityApnContextActive(apnContext)) { - if (DBG) { - log("setupData: Higher priority ApnContext active. Ignoring call"); - } - return false; - } - - // Should not start cleanUp if the setupData is for IMS APN - // or retry of same APN(State==RETRYING). - if (!apnContext.getApnType().equals(ApnSetting.TYPE_IMS_STRING) - && (apnContext.getState() != DctConstants.State.RETRYING)) { - // Only lower priority calls left. Disconnect them all in this single PDP case - // so that we can bring up the requested higher priority call (once we receive - // response for deactivate request for the calls we are about to disconnect - if (cleanUpAllConnectionsInternal(true, Phone.REASON_SINGLE_PDN_ARBITRATION)) { - // If any call actually requested to be disconnected, means we can't - // bring up this connection yet as we need to wait for those data calls - // to be disconnected. - if (DBG) log("setupData: Some calls are disconnecting first." - + " Wait and retry"); - return false; - } - } - - // No other calls are active, so proceed - if (DBG) log("setupData: Single pdp. Continue setting up data call."); - } - - dataConnection = findFreeDataConnection(); - - if (dataConnection == null) { - dataConnection = createDataConnection(); - } - - if (dataConnection == null) { - if (DBG) log("setupData: No free DataConnection and couldn't create one, WEIRD"); - return false; - } - } - final int generation = apnContext.incAndGetConnectionGeneration(); - if (DBG) { - log("setupData: dc=" + dataConnection + " apnSetting=" + apnSetting + " gen#=" - + generation); - } - - apnContext.setDataConnection(dataConnection); - apnContext.setApnSetting(apnSetting); - apnContext.setState(DctConstants.State.CONNECTING); - - Message msg = obtainMessage(); - msg.what = DctConstants.EVENT_DATA_SETUP_COMPLETE; - msg.obj = new Pair<ApnContext, Integer>(apnContext, generation); - - ApnSetting preferredApn = getPreferredApn(); - boolean isPreferredApn = apnSetting.equals(preferredApn); - dataConnection.bringUp(apnContext, profileId, radioTech, msg, generation, requestType, - mPhone.getSubId(), isPreferredApn); - - if (DBG) { - if (isPreferredApn) { - log("setupData: initing! isPreferredApn=" + isPreferredApn - + ", apnSetting={" + apnSetting.toString() + "}"); - } else { - String preferredApnStr = preferredApn == null ? "null" : preferredApn.toString(); - log("setupData: initing! isPreferredApn=" + isPreferredApn - + ", apnSetting={" + apnSetting + "}" - + ", preferredApn={" + preferredApnStr + "}"); - } - } - return true; - } - - // Get the allowed APN types for initial attach. The order in the returned list represent - // the order of APN types that should be used for initial attach. - private @NonNull @ApnType List<Integer> getAllowedInitialAttachApnTypes() { - PersistableBundle bundle = getCarrierConfig(); - if (bundle != null) { - String[] apnTypesArray = bundle.getStringArray( - CarrierConfigManager.KEY_ALLOWED_INITIAL_ATTACH_APN_TYPES_STRING_ARRAY); - if (apnTypesArray != null) { - return Arrays.stream(apnTypesArray) - .map(ApnSetting::getApnTypesBitmaskFromString) - .collect(Collectors.toList()); - } - } - - return Collections.emptyList(); - } - - protected void setInitialAttachApn() { - ApnSetting apnSetting = null; - int preferredApnSetId = getPreferredApnSetId(); - ArrayList<ApnSetting> allApnSettings = new ArrayList<>(); - if (mPreferredApn != null) { - // Put the preferred apn at the beginning of the list. It's okay to have a duplicate - // when later on mAllApnSettings get added. That would not change the selection result. - allApnSettings.add(mPreferredApn); - } - allApnSettings.addAll(mAllApnSettings); - - // Get the allowed APN types for initial attach. Note that if none of the APNs has the - // allowed APN types, then the initial attach will not be performed. - List<Integer> allowedApnTypes = getAllowedInitialAttachApnTypes(); - for (int allowedApnType : allowedApnTypes) { - apnSetting = allApnSettings.stream() - .filter(apn -> apn.canHandleType(allowedApnType)) - .filter(apn -> (apn.getApnSetId() == preferredApnSetId - || apn.getApnSetId() == Telephony.Carriers.MATCH_ALL_APN_SET_ID)) - .findFirst() - .orElse(null); - if (apnSetting != null) break; - } - - if (DBG) { - log("setInitialAttachApn: Allowed APN types=" + allowedApnTypes.stream() - .map(ApnSetting::getApnTypeString) - .collect(Collectors.joining(","))); - } - - if (apnSetting == null) { - if (DBG) log("setInitialAttachApn: X There in no available apn."); - } else { - if (DBG) log("setInitialAttachApn: X selected APN=" + apnSetting); - mDataServiceManager.setInitialAttachApn(new DataProfile.Builder() - .setApnSetting(apnSetting) - .setPreferred(apnSetting.equals(getPreferredApn())) - .build(), - mPhone.getServiceState().getDataRoamingFromRegistration(), null); - } - } - - /** - * Handles changes to the APN database. - */ - private void onApnChanged() { - if (mPhone instanceof GsmCdmaPhone) { - // The "current" may no longer be valid. MMS depends on this to send properly. TBD - ((GsmCdmaPhone)mPhone).updateCurrentCarrierInProvider(); - } - - // 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(); - setInitialAttachApn(); - cleanUpConnectionsOnUpdatedApns(isAnyDataConnected(), Phone.REASON_APN_CHANGED); - - // FIXME: See bug 17426028 maybe no conditional is needed. - if (mPhone.getSubId() == SubscriptionManager.getDefaultDataSubscriptionId()) { - setupDataOnAllConnectableApns(Phone.REASON_APN_CHANGED, RetryFailures.ALWAYS); - } - } - - /** - * "Active" here means ApnContext isEnabled() and not in FAILED state - * @param apnContext to compare with - * @return true if higher priority active apn found - */ - private boolean isHigherPriorityApnContextActive(ApnContext apnContext) { - if (apnContext.getApnType().equals(ApnSetting.TYPE_IMS_STRING)) { - return false; - } - - for (ApnContext otherContext : mPrioritySortedApnContexts) { - if (otherContext.getApnType().equals(ApnSetting.TYPE_IMS_STRING)) { - continue; - } - if (apnContext.getApnType().equalsIgnoreCase(otherContext.getApnType())) return false; - if (otherContext.isEnabled() && otherContext.getState() != DctConstants.State.FAILED) { - return true; - } - } - return false; - } - - /** - * Reports if we support multiple connections or not. - * This is a combination of factors, based on carrier and RAT. - * @param rilRadioTech the RIL Radio Tech currently in use - * @return true if only single DataConnection is allowed - */ - private boolean isOnlySingleDcAllowed(int rilRadioTech) { - int networkType = ServiceState.rilRadioTechnologyToNetworkType(rilRadioTech); - // Default single dc rats with no knowledge of carrier - int[] singleDcRats = null; - // get the carrier specific value, if it exists, from CarrierConfigManager. - // generally configManager and bundle should not be null, but if they are it should be okay - // to leave singleDcRats null as well - CarrierConfigManager configManager = (CarrierConfigManager) - mPhone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE); - if (configManager != null) { - PersistableBundle bundle = configManager.getConfigForSubId(mPhone.getSubId()); - if (bundle != null) { - singleDcRats = bundle.getIntArray( - CarrierConfigManager.KEY_ONLY_SINGLE_DC_ALLOWED_INT_ARRAY); - } - } - boolean onlySingleDcAllowed = false; - if (TelephonyUtils.IS_DEBUGGABLE - && SystemProperties.getBoolean("persist.telephony.test.singleDc", false)) { - onlySingleDcAllowed = true; - } - if (singleDcRats != null) { - for (int i = 0; i < singleDcRats.length && !onlySingleDcAllowed; i++) { - if (networkType == singleDcRats[i]) { - onlySingleDcAllowed = true; - } - } - } - - if (DBG) { - log("isOnlySingleDcAllowed(" + TelephonyManager.getNetworkTypeName(networkType) + "): " - + onlySingleDcAllowed); - } - return onlySingleDcAllowed; - } - - void sendRestartRadio() { - if (DBG)log("sendRestartRadio:"); - Message msg = obtainMessage(DctConstants.EVENT_RESTART_RADIO); - sendMessage(msg); - } - - private void restartRadio() { - if (DBG) log("restartRadio: ************TURN OFF RADIO**************"); - cleanUpAllConnectionsInternal(true, Phone.REASON_RADIO_TURNED_OFF); - mPhone.getServiceStateTracker().powerOffRadioSafely(); - /* Note: no need to call setRadioPower(true). Assuming the desired - * radio power state is still ON (as tracked by ServiceStateTracker), - * ServiceStateTracker will call setRadioPower when it receives the - * RADIO_STATE_CHANGED notification for the power off. And if the - * desired power state has changed in the interim, we don't want to - * override it with an unconditional power on. - */ - } - - /** - * Return true if data connection need to be setup after disconnected due to - * reason. - * - * @param apnContext APN context - * @return true if try setup data connection is need for this reason - */ - private boolean retryAfterDisconnected(ApnContext apnContext) { - boolean retry = true; - String reason = apnContext.getReason(); - - if (Phone.REASON_RADIO_TURNED_OFF.equals(reason) || (isOnlySingleDcAllowed(getDataRat()) - && isHigherPriorityApnContextActive(apnContext))) { - retry = false; - } - return retry; - } - - protected void startReconnect(long delay, ApnContext apnContext, - @RequestNetworkType int requestType) { - apnContext.setState(DctConstants.State.RETRYING); - Message msg = obtainMessage(DctConstants.EVENT_DATA_RECONNECT, - mPhone.getSubId(), requestType, apnContext); - cancelReconnect(apnContext); - - // Wait a bit before trying the next APN, so that - // we're not tying up the RIL command channel - sendMessageDelayed(msg, delay); - - if (DBG) { - log("startReconnect: delay=" + delay + ", apn=" - + apnContext + ", reason=" + apnContext.getReason() - + ", subId=" + mPhone.getSubId() + ", request type=" - + requestTypeToString(requestType)); - } - } - - /** - * Cancels the alarm associated with apnContext. - * - * @param apnContext on which the alarm should be stopped. - */ - protected void cancelReconnect(ApnContext apnContext) { - if (apnContext == null) return; - - if (DBG) { - log("cancelReconnect: apn=" + apnContext); - } - removeMessages(DctConstants.EVENT_DATA_RECONNECT, apnContext); - } - - /** - * Read configuration. Note this must be called after carrier config is ready. - */ - private void readConfiguration() { - log("readConfiguration"); - if (mTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) { - // Auto attach is for cellular only. - mAutoAttachOnCreationConfig = mPhone.getContext().getResources() - .getBoolean(com.android.internal.R.bool.config_auto_attach_data_on_creation); - } - - mAutoAttachEnabled.set(false); - setDefaultPreferredApnIfNeeded(); - read5GConfiguration(); - registerSettingsObserver(); - SubscriptionPlan[] plans = mNetworkPolicyManager.getSubscriptionPlans( - mPhone.getSubId(), mPhone.getContext().getOpPackageName()); - mSubscriptionPlans = plans == null ? Collections.emptyList() : Arrays.asList(plans); - if (DBG) log("SubscriptionPlans initialized: " + mSubscriptionPlans); - reevaluateUnmeteredConnections(); - mConfigReady = true; - } - - /** - * @return {@code true} if carrier config has been applied. - */ - private boolean isCarrierConfigApplied() { - CarrierConfigManager configManager = (CarrierConfigManager) mPhone.getContext() - .getSystemService(Context.CARRIER_CONFIG_SERVICE); - if (configManager != null) { - PersistableBundle b = configManager.getConfigForSubId(mPhone.getSubId()); - if (b != null) { - return CarrierConfigManager.isConfigForIdentifiedCarrier(b); - } - } - return false; - } - - private void onCarrierConfigChanged() { - if (DBG) log("onCarrierConfigChanged"); - - if (!isCarrierConfigApplied()) { - log("onCarrierConfigChanged: Carrier config is not ready yet."); - return; - } - - readConfiguration(); - - if (mSimState == TelephonyManager.SIM_STATE_LOADED) { - setDefaultDataRoamingEnabled(); - createAllApnList(); - setDataProfilesAsNeeded(); - setInitialAttachApn(); - sortApnContextByPriority(); - cleanUpConnectionsOnUpdatedApns(true, Phone.REASON_CARRIER_CHANGE); - setupDataOnAllConnectableApns(Phone.REASON_CARRIER_CHANGE, RetryFailures.ALWAYS); - } else { - log("onCarrierConfigChanged: SIM is not loaded yet."); - } - } - - private void onSimAbsent() { - if (DBG) log("onSimAbsent"); - - mConfigReady = false; - cleanUpAllConnectionsInternal(true, Phone.REASON_SIM_NOT_READY); - mAllApnSettings.clear(); - mAutoAttachOnCreationConfig = false; - // Clear auto attach as modem is expected to do a new attach once SIM is ready - mAutoAttachEnabled.set(false); - // In no-sim case, we should still send the emergency APN to the modem, if there is any. - createAllApnList(); - setDataProfilesAsNeeded(); - } - - private void onSimStateUpdated(@SimState int simState) { - mSimState = simState; - - if (DBG) { - log("onSimStateUpdated: state=" + SubscriptionInfoUpdater.simStateString(mSimState)); - } - - if (mSimState == TelephonyManager.SIM_STATE_ABSENT) { - onSimAbsent(); - } else if (mSimState == TelephonyManager.SIM_STATE_LOADED) { - mDataThrottler.reset(); - if (mConfigReady) { - createAllApnList(); - setDataProfilesAsNeeded(); - setInitialAttachApn(); - setupDataOnAllConnectableApns(Phone.REASON_SIM_LOADED, RetryFailures.ALWAYS); - } else { - log("onSimStateUpdated: config not ready yet."); - } - } - } - - private void onApnUnthrottled(String apn) { - if (apn != null) { - ApnSetting apnSetting = mAllApnSettings.stream() - .filter(as -> apn.equals(as.getApnName())) - .findFirst() - .orElse(null); - if (apnSetting != null) { - @ApnType int apnTypes = apnSetting.getApnTypeBitmask(); - mDataThrottler.setRetryTime(apnTypes, RetryManager.NO_SUGGESTED_RETRY_DELAY, - REQUEST_TYPE_NORMAL); - } else { - loge("EVENT_APN_UNTHROTTLED: Invalid APN passed: " + apn); - } - } else { - loge("EVENT_APN_UNTHROTTLED: apn is null"); - } - } - - private void onTrafficDescriptorsUpdated() { - for (ApnContext apnContext : mPrioritySortedApnContexts) { - if (apnContext.getApnTypeBitmask() == ApnSetting.TYPE_ENTERPRISE - && apnContext.getApnSetting().getPermanentFailed()) { - setupDataOnConnectableApn( - apnContext, Phone.REASON_TRAFFIC_DESCRIPTORS_UPDATED, RetryFailures.ALWAYS); - } - } - } - - private DataConnection checkForCompatibleDataConnection(ApnContext apnContext, - ApnSetting nextApn) { - int apnType = apnContext.getApnTypeBitmask(); - ArrayList<ApnSetting> dunSettings = null; - - if (ApnSetting.TYPE_DUN == apnType) { - dunSettings = fetchDunApns(); - } - if (DBG) { - log("checkForCompatibleDataConnection: apnContext=" + apnContext); - } - - DataConnection potentialDc = null; - for (DataConnection curDc : mDataConnections.values()) { - if (curDc != null) { - ApnSetting apnSetting = curDc.getApnSetting(); - log("apnSetting: " + apnSetting); - if (dunSettings != null && dunSettings.size() > 0) { - for (ApnSetting dunSetting : dunSettings) { - //This ignore network type as a check which is ok because that's checked - //when calculating dun candidates. - if (areCompatible(dunSetting, apnSetting)) { - if (curDc.isActive()) { - if (DBG) { - log("checkForCompatibleDataConnection:" - + " found dun conn=" + curDc); - } - return curDc; - } else if (curDc.isActivating()) { - potentialDc = curDc; - } - } - } - } else if (isApnSettingCompatible(curDc, apnType)) { - if (curDc.isActive()) { - if (DBG) { - log("checkForCompatibleDataConnection:" - + " found canHandle conn=" + curDc); - } - return curDc; - } else if (curDc.isActivating() - || (apnSetting != null && apnSetting.equals(nextApn))) { - potentialDc = curDc; - } - } - } - } - - if (DBG) { - log("checkForCompatibleDataConnection: potential dc=" + potentialDc); - } - return potentialDc; - } - - private boolean isApnSettingCompatible(DataConnection dc, int apnType) { - ApnSetting apnSetting = dc.getApnSetting(); - if (apnSetting == null) return false; - - // Nothing can be compatible with type ENTERPRISE - for (ApnContext apnContext : dc.getApnContexts()) { - if (apnContext.getApnTypeBitmask() == ApnSetting.TYPE_ENTERPRISE) { - return false; - } - } - - return apnSetting.canHandleType(apnType); - } - - private void addHandoverCompleteMsg(Message onCompleteMsg, - @ApnType int apnType) { - if (onCompleteMsg != null) { - List<Message> messageList = mHandoverCompletionMsgs.get(apnType); - if (messageList == null) messageList = new ArrayList<>(); - messageList.add(onCompleteMsg); - mHandoverCompletionMsgs.put(apnType, messageList); - } - } - - private void sendHandoverCompleteMessages(@ApnType int apnType, boolean success, - boolean fallbackOnFailedHandover) { - List<Message> messageList = mHandoverCompletionMsgs.get(apnType); - if (messageList != null) { - for (Message msg : messageList) { - sendHandoverCompleteMsg(msg, success, mTransportType, fallbackOnFailedHandover); - } - messageList.clear(); - } - } - - private void sendHandoverCompleteMsg(Message message, boolean success, - @TransportType int transport, boolean doFallbackOnFailedHandover) { - if (message == null) return; - - Bundle b = message.getData(); - b.putBoolean(DATA_COMPLETE_MSG_EXTRA_SUCCESS, success); - b.putInt(DATA_COMPLETE_MSG_EXTRA_TRANSPORT_TYPE, transport); - b.putBoolean(DATA_COMPLETE_MSG_EXTRA_HANDOVER_FAILURE_FALLBACK, doFallbackOnFailedHandover); - message.sendToTarget(); - } - - private static boolean shouldFallbackOnFailedHandover( - @HandoverFailureMode int handoverFailureMode, - @RequestNetworkType int requestType, - @DataFailureCause int cause) { - if (requestType != REQUEST_TYPE_HANDOVER) { - //The fallback is only relevant if the request is a handover - return false; - } else if (handoverFailureMode == HANDOVER_FAILURE_MODE_DO_FALLBACK) { - return true; - } else if (handoverFailureMode == HANDOVER_FAILURE_MODE_LEGACY) { - return cause == DataFailCause.HANDOFF_PREFERENCE_CHANGED; - } else { - return false; - } - } - - /** - * Calculates the new request type that will be used the next time a data connection retries - * after a failed data call attempt. - */ - @RequestNetworkType - public static int calculateNewRetryRequestType(@HandoverFailureMode int handoverFailureMode, - @RequestNetworkType int requestType, - @DataFailureCause int cause) { - boolean fallbackOnFailedHandover = - shouldFallbackOnFailedHandover(handoverFailureMode, requestType, cause); - if (requestType != REQUEST_TYPE_HANDOVER) { - //The fallback is only relevant if the request is a handover - return requestType; - } - - if (fallbackOnFailedHandover) { - // Since fallback is happening, the request type is really "NONE". - return REQUEST_TYPE_NORMAL; - } - - if (handoverFailureMode == HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_SETUP_NORMAL) { - return REQUEST_TYPE_NORMAL; - } - - return REQUEST_TYPE_HANDOVER; - } - - public void enableApn(@ApnType int apnType, @RequestNetworkType int requestType, - Message onHandoverCompleteMsg) { - sendMessage(obtainMessage(DctConstants.EVENT_ENABLE_APN, apnType, requestType, - onHandoverCompleteMsg)); - } - - private void onEnableApn(@ApnType int apnType, @RequestNetworkType int requestType, - Message onHandoverCompleteMsg) { - ApnContext apnContext = mApnContextsByType.get(apnType); - if (apnContext == null) { - loge("onEnableApn(" + apnType + "): NO ApnContext"); - if (onHandoverCompleteMsg != null) { - sendHandoverCompleteMsg(onHandoverCompleteMsg, false, mTransportType, false); - } - return; - } - - String str = "onEnableApn: apnType=" + ApnSetting.getApnTypeString(apnType) - + ", request type=" + requestTypeToString(requestType); - if (DBG) log(str); - ApnContext.requestLog(apnContext, str); - - if (!apnContext.isDependencyMet()) { - apnContext.setReason(Phone.REASON_DATA_DEPENDENCY_UNMET); - apnContext.setEnabled(true); - str = "onEnableApn: dependency is not met."; - if (DBG) log(str); - ApnContext.requestLog(apnContext, str); - if (onHandoverCompleteMsg != null) { - sendHandoverCompleteMsg(onHandoverCompleteMsg, false, mTransportType, false); - } - return; - } - - if (apnContext.isReady()) { - DctConstants.State state = apnContext.getState(); - switch(state) { - case CONNECTING: - if (onHandoverCompleteMsg != null) { - if (DBG) { - log("onEnableApn: already in CONNECTING state. Handover request " - + "will be responded after connected."); - } - addHandoverCompleteMsg(onHandoverCompleteMsg, apnType); - } else { - if (DBG) log("onEnableApn: in CONNECTING state. Exit now."); - } - return; - case CONNECTED: - if (onHandoverCompleteMsg != null) { - sendHandoverCompleteMsg(onHandoverCompleteMsg, true, mTransportType, - false); - if (DBG) { - log("onEnableApn: already in CONNECTED state. Consider as handover " - + "succeeded"); - } - } else { - if (DBG) log("onEnableApn: APN in CONNECTED state. Exit now."); - } - return; - case IDLE: - case FAILED: - case RETRYING: - // We're "READY" but not active so disconnect (cleanup = true) and - // connect (trySetup = true) to be sure we retry the connection. - apnContext.setReason(Phone.REASON_DATA_ENABLED); - break; - } - } else { - if (apnContext.isEnabled()) { - apnContext.setReason(Phone.REASON_DATA_DEPENDENCY_MET); - } else { - apnContext.setReason(Phone.REASON_DATA_ENABLED); - } - if (apnContext.getState() == DctConstants.State.FAILED) { - apnContext.setState(DctConstants.State.IDLE); - } - } - apnContext.setEnabled(true); - apnContext.resetErrorCodeRetries(); - - if (mConfigReady || apnContext.getApnTypeBitmask() == ApnSetting.TYPE_EMERGENCY) { - trySetupData(apnContext, requestType, onHandoverCompleteMsg); - } else { - log("onEnableApn: config not ready yet."); - } - } - - public void disableApn(@ApnType int apnType, @ReleaseNetworkType int releaseType) { - sendMessage(obtainMessage(DctConstants.EVENT_DISABLE_APN, apnType, releaseType)); - } - - private void onDisableApn(@ApnType int apnType, - @ReleaseNetworkType int releaseType) { - ApnContext apnContext = mApnContextsByType.get(apnType); - if (apnContext == null) { - loge("disableApn(" + apnType + "): NO ApnContext"); - return; - } - - boolean cleanup = false; - String str = "onDisableApn: apnType=" + ApnSetting.getApnTypeString(apnType) - + ", release type=" + releaseTypeToString(releaseType); - if (DBG) log(str); - ApnContext.requestLog(apnContext, str); - - if (apnContext.isReady()) { - cleanup = (releaseType == RELEASE_TYPE_DETACH - || releaseType == RELEASE_TYPE_HANDOVER); - if (apnContext.isDependencyMet()) { - apnContext.setReason(Phone.REASON_DATA_DISABLED_INTERNAL); - // If ConnectivityService has disabled this network, stop trying to bring - // it up, but do not tear it down - ConnectivityService will do that - // directly by talking with the DataConnection. - // - // This doesn't apply to DUN. When the user disable tethering, we would like to - // detach the APN context from the data connection so the data connection can be - // torn down if no other APN context attached to it. - if (ApnSetting.TYPE_DUN_STRING.equals(apnContext.getApnType()) - || apnContext.getState() != DctConstants.State.CONNECTED) { - str = "Clean up the connection. Apn type = " + apnContext.getApnType() - + ", state = " + apnContext.getState(); - if (DBG) log(str); - ApnContext.requestLog(apnContext, str); - cleanup = true; - } - } else { - apnContext.setReason(Phone.REASON_DATA_DEPENDENCY_UNMET); - } - } - - apnContext.setEnabled(false); - if (cleanup) { - cleanUpConnectionInternal(true, releaseType, apnContext); - } - - if (isOnlySingleDcAllowed(getDataRat()) && !isHigherPriorityApnContextActive(apnContext)) { - if (DBG) log("disableApn:isOnlySingleDcAllowed true & higher priority APN disabled"); - // If the highest priority APN is disabled and only single - // data call is allowed, try to setup data call on other connectable APN. - setupDataOnAllConnectableApns(Phone.REASON_SINGLE_PDN_ARBITRATION, - RetryFailures.ALWAYS); - } - } - - /** - * Modify {@link android.provider.Settings.Global#DATA_ROAMING} value for user modification only - */ - public void setDataRoamingEnabledByUser(boolean enabled) { - mDataEnabledSettings.setDataRoamingEnabled(enabled); - setDataRoamingFromUserAction(true); - if (DBG) { - log("setDataRoamingEnabledByUser: set phoneSubId=" + mPhone.getSubId() - + " isRoaming=" + enabled); - } - } - - /** - * Return current {@link android.provider.Settings.Global#DATA_ROAMING} value. - */ - public boolean getDataRoamingEnabled() { - boolean isDataRoamingEnabled = mDataEnabledSettings.getDataRoamingEnabled(); - - if (VDBG) { - log("getDataRoamingEnabled: phoneSubId=" + mPhone.getSubId() - + " isDataRoamingEnabled=" + isDataRoamingEnabled); - } - return isDataRoamingEnabled; - } - - /** - * Set default value for {@link android.provider.Settings.Global#DATA_ROAMING} - * if the setting is not from user actions. default value is based on carrier config and system - * properties. - */ - private void setDefaultDataRoamingEnabled() { - // For single SIM phones, this is a per phone property. - String setting = Settings.Global.DATA_ROAMING; - boolean useCarrierSpecificDefault = false; - if (mTelephonyManager.getSimCount() != 1) { - setting = setting + mPhone.getSubId(); - try { - Settings.Global.getInt(mResolver, setting); - } catch (SettingNotFoundException ex) { - // For msim, update to carrier default if uninitialized. - useCarrierSpecificDefault = true; - } - } else if (!isDataRoamingFromUserAction()) { - // for single sim device, update to carrier default if user action is not set - useCarrierSpecificDefault = true; - } - log("setDefaultDataRoamingEnabled: useCarrierSpecificDefault " - + useCarrierSpecificDefault); - if (useCarrierSpecificDefault) { - boolean defaultVal = mDataEnabledSettings.getDefaultDataRoamingEnabled(); - mDataEnabledSettings.setDataRoamingEnabled(defaultVal); - } - } - - private boolean isDataRoamingFromUserAction() { - final SharedPreferences sp = PreferenceManager - .getDefaultSharedPreferences(mPhone.getContext()); - // since we don't want to unset user preference from system update, pass true as the default - // value if shared pref does not exist and set shared pref to false explicitly from factory - // reset. - if (!sp.contains(Phone.DATA_ROAMING_IS_USER_SETTING_KEY)) { - sp.edit().putBoolean(Phone.DATA_ROAMING_IS_USER_SETTING_KEY, false).commit(); - } - return sp.getBoolean(Phone.DATA_ROAMING_IS_USER_SETTING_KEY, true); - } - - private void setDataRoamingFromUserAction(boolean isUserAction) { - final SharedPreferences.Editor sp = PreferenceManager - .getDefaultSharedPreferences(mPhone.getContext()).edit(); - sp.putBoolean(Phone.DATA_ROAMING_IS_USER_SETTING_KEY, isUserAction).commit(); - } - - // When the data roaming status changes from roaming to non-roaming. - private void onDataRoamingOff() { - if (DBG) log("onDataRoamingOff"); - - reevaluateDataConnections(); - - if (!getDataRoamingEnabled()) { - // TODO: Remove this once all old vendor RILs are gone. We don't need to set initial apn - // attach and send the data profile again as the modem should have both roaming and - // non-roaming protocol in place. Modem should choose the right protocol based on the - // roaming condition. - setDataProfilesAsNeeded(); - setInitialAttachApn(); - - // If the user did not enable data roaming, now when we transit from roaming to - // non-roaming, we should try to reestablish the data connection. - - setupDataOnAllConnectableApns(Phone.REASON_ROAMING_OFF, RetryFailures.ALWAYS); - } - } - - // This method is called - // 1. When the data roaming status changes from non-roaming to roaming. - // 2. When allowed data roaming settings is changed by the user. - private void onDataRoamingOnOrSettingsChanged(int messageType) { - if (DBG) log("onDataRoamingOnOrSettingsChanged"); - // Used to differentiate data roaming turned on vs settings changed. - boolean settingChanged = (messageType == DctConstants.EVENT_ROAMING_SETTING_CHANGE); - - // Check if the device is actually data roaming - if (!mPhone.getServiceState().getDataRoaming()) { - if (DBG) log("device is not roaming. ignored the request."); - return; - } - - checkDataRoamingStatus(settingChanged); - - if (getDataRoamingEnabled()) { - // If the restricted data was brought up when data roaming is disabled, and now users - // enable data roaming, we need to re-evaluate the conditions and possibly change the - // network's capability. - if (settingChanged) { - reevaluateDataConnections(); - } - - if (DBG) log("onDataRoamingOnOrSettingsChanged: setup data on roaming"); - - setupDataOnAllConnectableApns(Phone.REASON_ROAMING_ON, RetryFailures.ALWAYS); - } else { - // If the user does not turn on data roaming, when we transit from non-roaming to - // roaming, we need to tear down the data connection otherwise the user might be - // charged for data roaming usage. - if (DBG) log("onDataRoamingOnOrSettingsChanged: Tear down data connection on roaming."); - cleanUpAllConnectionsInternal(true, Phone.REASON_ROAMING_ON); - } - } - - // We want to track possible roaming data leakage. Which is, if roaming setting - // is disabled, yet we still setup a roaming data connection or have a connected ApnContext - // switched to roaming. When this happens, we log it in a local log. - private void checkDataRoamingStatus(boolean settingChanged) { - if (!settingChanged && !getDataRoamingEnabled() - && mPhone.getServiceState().getDataRoaming()) { - for (ApnContext apnContext : mApnContexts.values()) { - if (apnContext.getState() == DctConstants.State.CONNECTED) { - mDataRoamingLeakageLog.log("PossibleRoamingLeakage " - + " connection params: " + (apnContext.getDataConnection() != null - ? apnContext.getDataConnection().getConnectionParams() : "")); - } - } - } - } - - private void onRadioAvailable() { - if (DBG) log("onRadioAvailable"); - if (!areAllDataDisconnected()) { - cleanUpConnectionInternal(true, RELEASE_TYPE_DETACH, null); - } - } - - private void onRadioOffOrNotAvailable() { - // Make sure our reconnect delay starts at the initial value - // next time the radio comes on - - mReregisterOnReconnectFailure = false; - - // Clear auto attach as modem is expected to do a new attach - mAutoAttachEnabled.set(false); - - if (mPhone.getSimulatedRadioControl() != null) { - // Assume data is connected on the simulator - // FIXME this can be improved - log("We're on the simulator; assuming radio off is meaningless"); - } else { - if (DBG) log("onRadioOffOrNotAvailable: is off and clean up all connections"); - cleanUpAllConnectionsInternal(false, Phone.REASON_RADIO_TURNED_OFF); - } - } - - private void completeConnection(ApnContext apnContext, @RequestNetworkType int type) { - - if (DBG) log("completeConnection: successful, notify the world apnContext=" + apnContext); - - if (mIsProvisioning && !TextUtils.isEmpty(mProvisioningUrl)) { - if (DBG) { - log("completeConnection: MOBILE_PROVISIONING_ACTION url=" - + mProvisioningUrl); - } - Intent newIntent = Intent.makeMainSelectorActivity(Intent.ACTION_MAIN, - Intent.CATEGORY_APP_BROWSER); - newIntent.setData(Uri.parse(mProvisioningUrl)); - newIntent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT | - Intent.FLAG_ACTIVITY_NEW_TASK); - try { - mPhone.getContext().startActivity(newIntent); - } catch (ActivityNotFoundException e) { - loge("completeConnection: startActivityAsUser failed" + e); - } - } - mIsProvisioning = false; - mProvisioningUrl = null; - if (mProvisioningSpinner != null) { - sendMessage(obtainMessage(DctConstants.CMD_CLEAR_PROVISIONING_SPINNER, - mProvisioningSpinner)); - } - - startNetStatPoll(); - startDataStallAlarm(DATA_STALL_NOT_SUSPECTED); - - PersistableBundle b = getCarrierConfig(); - if (apnContext.getApnTypeBitmask() == ApnSetting.TYPE_DEFAULT - && b.getBoolean(CarrierConfigManager - .KEY_DISPLAY_NO_DATA_NOTIFICATION_ON_PERMANENT_FAILURE_BOOL)) { - NotificationManager notificationManager = (NotificationManager) - mPhone.getContext().getSystemService(Context.NOTIFICATION_SERVICE); - notificationManager.cancel(Integer.toString(mPhone.getSubId()), - NO_DATA_NOTIFICATION); - } - } - - /** - * A SETUP (aka bringUp) has completed, possibly with an error. If - * there is an error this method will call {@link #onDataSetupCompleteError}. - */ - protected void onDataSetupComplete(ApnContext apnContext, boolean success, - @DataFailureCause int cause, @RequestNetworkType int requestType, - @HandoverFailureMode int handoverFailureMode) { - boolean fallbackOnFailedHandover = shouldFallbackOnFailedHandover( - handoverFailureMode, requestType, cause); - - if (success && (handoverFailureMode != DataCallResponse.HANDOVER_FAILURE_MODE_UNKNOWN - && handoverFailureMode != DataCallResponse.HANDOVER_FAILURE_MODE_LEGACY)) { - Log.wtf(mLogTag, "bad failure mode: " - + DataCallResponse.failureModeToString(handoverFailureMode)); - } else if (handoverFailureMode - != DataCallResponse.HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_HANDOVER - && cause != DataFailCause.SERVICE_TEMPORARILY_UNAVAILABLE) { - sendHandoverCompleteMessages(apnContext.getApnTypeBitmask(), success, - fallbackOnFailedHandover); - } - - if (success) { - DataConnection dataConnection = apnContext.getDataConnection(); - - if (RADIO_TESTS) { - // Note: To change radio.test.onDSC.null.dcac from command line you need to - // adb root and adb remount and from the command line you can only change the - // value to 1 once. To change it a second time you can reboot or execute - // adb shell stop and then adb shell start. The command line to set the value is: - // adb shell sqlite3 /data/data/com.android.providers.settings/databases/settings.db "insert into system (name,value) values ('radio.test.onDSC.null.dcac', '1');" - ContentResolver cr = mPhone.getContext().getContentResolver(); - String radioTestProperty = "radio.test.onDSC.null.dcac"; - if (Settings.System.getInt(cr, radioTestProperty, 0) == 1) { - log("onDataSetupComplete: " + radioTestProperty + - " is true, set dcac to null and reset property to false"); - dataConnection = null; - Settings.System.putInt(cr, radioTestProperty, 0); - log("onDataSetupComplete: " + radioTestProperty + "=" + - Settings.System.getInt(mPhone.getContext().getContentResolver(), - radioTestProperty, -1)); - } - } - if (dataConnection == null) { - log("onDataSetupComplete: no connection to DC, handle as error"); - onDataSetupCompleteError(apnContext, requestType, false); - } else { - ApnSetting apn = apnContext.getApnSetting(); - if (DBG) { - log("onDataSetupComplete: success apn=" + (apn == null ? "unknown" - : apn.getApnName())); - } - - // everything is setup - if (TextUtils.equals(apnContext.getApnType(), ApnSetting.TYPE_DEFAULT_STRING) - && mCanSetPreferApn && mPreferredApn == null) { - if (DBG) log("onDataSetupComplete: PREFERRED APN is null"); - mPreferredApn = apn; - if (mPreferredApn != null) { - setPreferredApn(mPreferredApn.getId()); - } - } - - // A connection is setup - apnContext.setState(DctConstants.State.CONNECTED); - - checkDataRoamingStatus(false); - - boolean isProvApn = apnContext.isProvisioningApn(); - final ConnectivityManager cm = (ConnectivityManager) mPhone.getContext() - .getSystemService(Context.CONNECTIVITY_SERVICE); - if (mProvisionBroadcastReceiver != null) { - mPhone.getContext().unregisterReceiver(mProvisionBroadcastReceiver); - mProvisionBroadcastReceiver = null; - } - - if ((!isProvApn) || mIsProvisioning) { - if (mIsProvisioning) { - // Hide any notification that was showing previously - hideProvisioningNotification(); - } - - // Complete the connection normally notifying the world we're connected. - // We do this if this isn't a special provisioning apn or if we've been - // told its time to provision. - completeConnection(apnContext, requestType); - } else { - // This is a provisioning APN that we're reporting as connected. Later - // when the user desires to upgrade this to a "default" connection, - // mIsProvisioning == true, we'll go through the code path above. - // mIsProvisioning becomes true when CMD_ENABLE_MOBILE_PROVISIONING - // is sent to the DCT. - if (DBG) { - log("onDataSetupComplete: successful, BUT send connected to prov apn as" - + " mIsProvisioning:" + mIsProvisioning + " == false" - + " && (isProvisioningApn:" + isProvApn + " == true"); - } - - // While radio is up, grab provisioning URL. The URL contains ICCID which - // disappears when radio is off. - mProvisionBroadcastReceiver = new ProvisionNotificationBroadcastReceiver( - mPhone.getMobileProvisioningUrl(), - mTelephonyManager.getNetworkOperatorName()); - mPhone.getContext().registerReceiver(mProvisionBroadcastReceiver, - new IntentFilter(INTENT_PROVISION)); - - // Put up user notification that sign-in is required. - showProvisioningNotification(); - - // Turn off radio to save battery and avoid wasting carrier resources. - // The network isn't usable and network validation will just fail anyhow. - setRadio(false); - } - if (DBG) { - log("onDataSetupComplete: SETUP complete type=" + apnContext.getApnType()); - } - if (TelephonyUtils.IS_DEBUGGABLE) { - // adb shell setprop persist.radio.test.pco [pco_val] - String radioTestProperty = "persist.radio.test.pco"; - int pcoVal = SystemProperties.getInt(radioTestProperty, -1); - if (pcoVal != -1) { - log("PCO testing: read pco value from persist.radio.test.pco " + pcoVal); - final byte[] value = new byte[1]; - value[0] = (byte) pcoVal; - final Intent intent = - new Intent(TelephonyManager.ACTION_CARRIER_SIGNAL_PCO_VALUE); - intent.putExtra(TelephonyManager.EXTRA_APN_TYPE, ApnSetting.TYPE_DEFAULT); - intent.putExtra(TelephonyManager.EXTRA_APN_PROTOCOL, - ApnSetting.PROTOCOL_IPV4V6); - intent.putExtra(TelephonyManager.EXTRA_PCO_ID, 0xFF00); - intent.putExtra(TelephonyManager.EXTRA_PCO_VALUE, value); - mPhone.getCarrierSignalAgent().notifyCarrierSignalReceivers(intent); - } - } - } - } else { - if (DBG) { - ApnSetting apn = apnContext.getApnSetting(); - log("onDataSetupComplete: error apn=" + apn.getApnName() + ", cause=" - + DataFailCause.toString(cause) + ", requestType=" - + requestTypeToString(requestType)); - } - if (DataFailCause.isEventLoggable(cause)) { - // Log this failure to the Event Logs. - int cid = getCellLocationId(); - EventLog.writeEvent(EventLogTags.PDP_SETUP_FAIL, - cause, cid, mTelephonyManager.getNetworkType()); - } - ApnSetting apn = apnContext.getApnSetting(); - - // Compose broadcast intent send to the specific carrier signaling receivers - Intent intent = new Intent(TelephonyManager - .ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED); - intent.putExtra(TelephonyManager.EXTRA_DATA_FAIL_CAUSE, cause); - intent.putExtra(TelephonyManager.EXTRA_APN_TYPE, - ApnSetting.getApnTypesBitmaskFromString(apnContext.getApnType())); - mPhone.getCarrierSignalAgent().notifyCarrierSignalReceivers(intent); - - if (DataFailCause.isRadioRestartFailure(mPhone.getContext(), cause, mPhone.getSubId()) - || apnContext.restartOnError(cause)) { - if (DBG) log("Modem restarted."); - sendRestartRadio(); - } - - // If the data call failure cause is a permanent failure, we mark the APN as permanent - // failed. - if (isPermanentFailure(cause)) { - log("cause=" + DataFailCause.toString(cause) - + ", mark apn as permanent failed. apn = " + apn); - apnContext.markApnPermanentFailed(apn); - - PersistableBundle b = getCarrierConfig(); - if (apnContext.getApnTypeBitmask() == ApnSetting.TYPE_DEFAULT - && b.getBoolean(CarrierConfigManager - .KEY_DISPLAY_NO_DATA_NOTIFICATION_ON_PERMANENT_FAILURE_BOOL)) { - NotificationManager notificationManager = (NotificationManager) - mPhone.getContext().getSystemService(Context.NOTIFICATION_SERVICE); - - CharSequence title = mPhone.getContext().getText( - com.android.internal.R.string.RestrictedOnDataTitle); - CharSequence details = mPhone.getContext().getText( - com.android.internal.R.string.RestrictedStateContent); - - Notification notification = new Notification.Builder(mPhone.getContext(), - NotificationChannelController.CHANNEL_ID_MOBILE_DATA_STATUS) - .setWhen(System.currentTimeMillis()) - .setAutoCancel(true) - .setSmallIcon(com.android.internal.R.drawable.stat_sys_warning) - .setTicker(title) - .setColor(mPhone.getContext().getResources().getColor( - com.android.internal.R.color.system_notification_accent_color)) - .setContentTitle(title) - .setStyle(new Notification.BigTextStyle().bigText(details)) - .setContentText(details) - .build(); - notificationManager.notify(Integer.toString(mPhone.getSubId()), - NO_DATA_NOTIFICATION, notification); - } - } - - int newRequestType = calculateNewRetryRequestType(handoverFailureMode, requestType, - cause); - onDataSetupCompleteError(apnContext, newRequestType, fallbackOnFailedHandover); - } - } - - - - /** - * Error has occurred during the SETUP {aka bringUP} request and the DCT - * should either try the next waiting APN or start over from the - * beginning if the list is empty. Between each SETUP request there will - * be a delay defined by {@link ApnContext#getDelayForNextApn(boolean)}. - */ - protected void onDataSetupCompleteError(ApnContext apnContext, - @RequestNetworkType int requestType, boolean fallbackOnFailedHandover) { - long delay = apnContext.getDelayForNextApn(mFailFast); - // Check if we need to retry or not. - if (delay >= 0 && delay != RetryManager.NO_RETRY && !fallbackOnFailedHandover) { - if (DBG) { - log("onDataSetupCompleteError: APN type=" + apnContext.getApnType() - + ". Request type=" + requestTypeToString(requestType) + ", Retry in " - + delay + "ms."); - } - startReconnect(delay, apnContext, requestType); - } else { - // If we are not going to retry any APN, set this APN context to failed state. - // This would be the final state of a data connection. - apnContext.setState(DctConstants.State.FAILED); - apnContext.setDataConnection(null); - log("onDataSetupCompleteError: Stop retrying APNs. delay=" + delay - + ", requestType=" + requestTypeToString(requestType)); - //send request network complete messages as needed - sendHandoverCompleteMessages(apnContext.getApnTypeBitmask(), false, - fallbackOnFailedHandover); - } - } - - /** - * Called when EVENT_NETWORK_STATUS_CHANGED is received. - * - * @param status One of {@code NetworkAgent.VALID_NETWORK} or - * {@code NetworkAgent.INVALID_NETWORK}. - * @param cid context id {@code cid} - * @param redirectUrl If the Internet probe was redirected, this - * is the destination it was redirected to, otherwise {@code null} - */ - private void onNetworkStatusChanged(int status, int cid, String redirectUrl) { - if (!TextUtils.isEmpty(redirectUrl)) { - Intent intent = new Intent(TelephonyManager.ACTION_CARRIER_SIGNAL_REDIRECTED); - intent.putExtra(TelephonyManager.EXTRA_REDIRECTION_URL, redirectUrl); - mPhone.getCarrierSignalAgent().notifyCarrierSignalReceivers(intent); - log("Notify carrier signal receivers with redirectUrl: " + redirectUrl); - } else { - final boolean isValid = status == NetworkAgent.VALIDATION_STATUS_VALID; - final DataConnection dc = getDataConnectionByContextId(cid); - if (!mDsRecoveryHandler.isRecoveryOnBadNetworkEnabled()) { - if (DBG) log("Skip data stall recovery on network status change with in threshold"); - return; - } - if (mTransportType != AccessNetworkConstants.TRANSPORT_TYPE_WWAN) { - if (DBG) log("Skip data stall recovery on non WWAN"); - return; - } - if (dc != null && dc.isValidationRequired()) { - mDsRecoveryHandler.processNetworkStatusChanged(isValid); - } - } - } - - /** - * Called when EVENT_DISCONNECT_DONE is received. - */ - private void onDisconnectDone(ApnContext apnContext) { - if(DBG) log("onDisconnectDone: EVENT_DISCONNECT_DONE apnContext=" + apnContext); - apnContext.setState(DctConstants.State.IDLE); - // If all data connection are gone, check whether Airplane mode request was pending. - if (areAllDataDisconnected() - && mPhone.getServiceStateTracker().processPendingRadioPowerOffAfterDataOff()) { - if (DBG) log("onDisconnectDone: radio will be turned off, no retries"); - // Radio will be turned off. No need to retry data setup - apnContext.setApnSetting(null); - apnContext.setDataConnection(null); - - // Need to notify disconnect as well, in the case of switching Airplane mode. - // Otherwise, it would cause 30s delayed to turn on Airplane mode. - notifyAllDataDisconnected(); - return; - } - // If APN is still enabled, try to bring it back up automatically - if (mAttached.get() && apnContext.isReady() && retryAfterDisconnected(apnContext)) { - // Wait a bit before trying the next APN, so that - // we're not tying up the RIL command channel. - // This also helps in any external dependency to turn off the context. - if (DBG) log("onDisconnectDone: attached, ready and retry after disconnect"); - - // See if there are still handover request pending that we need to retry handover - // after previous data gets disconnected. - if (isHandoverPending(apnContext.getApnTypeBitmask())) { - if (DBG) log("Handover request pending. Retry handover immediately."); - startReconnect(0, apnContext, REQUEST_TYPE_HANDOVER); - } else { - long delay = apnContext.getRetryAfterDisconnectDelay(); - if (delay > 0) { - // Data connection is in IDLE state, so when we reconnect later, we'll rebuild - // the waiting APN list, which will also reset/reconfigure the retry manager. - startReconnect(delay, apnContext, REQUEST_TYPE_NORMAL); - } - } - } else { - boolean restartRadioAfterProvisioning = mPhone.getContext().getResources().getBoolean( - com.android.internal.R.bool.config_restartRadioAfterProvisioning); - - if (apnContext.isProvisioningApn() && restartRadioAfterProvisioning) { - log("onDisconnectDone: restartRadio after provisioning"); - restartRadio(); - } - apnContext.setApnSetting(null); - apnContext.setDataConnection(null); - if (isOnlySingleDcAllowed(getDataRat())) { - if(DBG) log("onDisconnectDone: isOnlySigneDcAllowed true so setup single apn"); - setupDataOnAllConnectableApns(Phone.REASON_SINGLE_PDN_ARBITRATION, - RetryFailures.ALWAYS); - } else { - if(DBG) log("onDisconnectDone: not retrying"); - } - } - - if (areAllDataDisconnected()) { - apnContext.setConcurrentVoiceAndDataAllowed( - mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()); - notifyAllDataDisconnected(); - } - - } - - private void onVoiceCallStarted() { - if (DBG) log("onVoiceCallStarted"); - mInVoiceCall = true; - if (isAnyDataConnected() - && !mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) { - if (DBG) log("onVoiceCallStarted stop polling"); - stopNetStatPoll(); - stopDataStallAlarm(); - } - } - - protected void onVoiceCallEnded() { - if (DBG) log("onVoiceCallEnded"); - mInVoiceCall = false; - if (isAnyDataConnected()) { - if (!mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) { - startNetStatPoll(); - startDataStallAlarm(DATA_STALL_NOT_SUSPECTED); - } else { - // clean slate after call end. - resetPollStats(); - } - } - // reset reconnect timer - setupDataOnAllConnectableApns(Phone.REASON_VOICE_CALL_ENDED, RetryFailures.ALWAYS); - } - /** - * @return {@code true} if there is any data in connected state. - */ - @VisibleForTesting - public boolean isAnyDataConnected() { - for (DataConnection dc : mDataConnections.values()) { - if (dc.isActive()) { - return true; - } - } - return false; - } - - /** - * @return {@code true} if all data connections are in disconnected state. - */ - public boolean areAllDataDisconnected() { - for (DataConnection dc : mDataConnections.values()) { - if (!dc.isInactive()) { - if (DBG) log("areAllDataDisconnected false due to DC: " + dc.getName()); - return false; - } - } - return true; - } - - protected void setDataProfilesAsNeeded() { - if (DBG) log("setDataProfilesAsNeeded"); - - ArrayList<DataProfile> dataProfileList = new ArrayList<>(); - - int preferredApnSetId = getPreferredApnSetId(); - for (ApnSetting apn : mAllApnSettings) { - if (apn.getApnSetId() == Telephony.Carriers.MATCH_ALL_APN_SET_ID - || preferredApnSetId == apn.getApnSetId()) { - DataProfile dp = new DataProfile.Builder() - .setApnSetting(apn) - .setPreferred(apn.equals(getPreferredApn())) - .build(); - if (!dataProfileList.contains(dp)) { - dataProfileList.add(dp); - } - } else { - if (VDBG) { - log("setDataProfilesAsNeeded: APN set id " + apn.getApnSetId() - + " does not match the preferred set id " + preferredApnSetId); - } - } - } - - // Check if the data profiles we are sending are same as we did last time. We don't want to - // send the redundant profiles to the modem. Also if there the list is empty, we don't - // send it to the modem. - if (!dataProfileList.isEmpty() - && (dataProfileList.size() != mLastDataProfileList.size() - || !mLastDataProfileList.containsAll(dataProfileList))) { - mDataServiceManager.setDataProfile(dataProfileList, - mPhone.getServiceState().getDataRoamingFromRegistration(), null); - } - } - - /** - * Based on the sim operator numeric, create a list for all possible - * Data Connections and setup the preferredApn. - */ - protected void createAllApnList() { - mAllApnSettings.clear(); - String operator = mPhone.getOperatorNumeric(); - - // ORDER BY Telephony.Carriers._ID ("_id") - Cursor cursor = mPhone.getContext().getContentResolver().query( - Uri.withAppendedPath(Telephony.Carriers.SIM_APN_URI, "filtered/subId/" - + mPhone.getSubId()), null, null, null, Telephony.Carriers._ID); - - if (cursor != null) { - while (cursor.moveToNext()) { - ApnSetting apn = ApnSetting.makeApnSetting(cursor); - if (apn == null) { - continue; - } - mAllApnSettings.add(apn); - } - cursor.close(); - } else { - if (DBG) log("createAllApnList: cursor is null"); - mApnSettingsInitializationLog.log("cursor is null for carrier, operator: " - + operator); - } - - dedupeApnSettings(); - - if (mAllApnSettings.isEmpty()) { - log("createAllApnList: No APN found for carrier, operator: " + operator); - mApnSettingsInitializationLog.log("no APN found for carrier, operator: " - + operator); - mPreferredApn = null; - } else { - mPreferredApn = getPreferredApn(); - if (DBG) log("createAllApnList: mPreferredApn=" + mPreferredApn); - } - - addDefaultApnSettingsAsNeeded(); - if (DBG) log("createAllApnList: X mAllApnSettings=" + mAllApnSettings); - } - - private void dedupeApnSettings() { - ArrayList<ApnSetting> resultApns = new ArrayList<ApnSetting>(); - - // coalesce APNs if they are similar enough to prevent - // us from bringing up two data calls with the same interface - int i = 0; - while (i < mAllApnSettings.size() - 1) { - ApnSetting first = mAllApnSettings.get(i); - ApnSetting second = null; - int j = i + 1; - while (j < mAllApnSettings.size()) { - second = mAllApnSettings.get(j); - if (first.similar(second)) { - ApnSetting newApn = mergeApns(first, second); - mAllApnSettings.set(i, newApn); - first = newApn; - mAllApnSettings.remove(j); - } else { - j++; - } - } - i++; - } - } - - private ApnSetting mergeApns(ApnSetting dest, ApnSetting src) { - int id = dest.getId(); - if ((src.getApnTypeBitmask() & ApnSetting.TYPE_DEFAULT) == ApnSetting.TYPE_DEFAULT) { - id = src.getId(); - } - final int resultApnType = src.getApnTypeBitmask() | dest.getApnTypeBitmask(); - Uri mmsc = (dest.getMmsc() == null ? src.getMmsc() : dest.getMmsc()); - String mmsProxy = TextUtils.isEmpty(dest.getMmsProxyAddressAsString()) - ? src.getMmsProxyAddressAsString() : dest.getMmsProxyAddressAsString(); - int mmsPort = dest.getMmsProxyPort() == -1 ? src.getMmsProxyPort() : dest.getMmsProxyPort(); - String proxy = TextUtils.isEmpty(dest.getProxyAddressAsString()) - ? src.getProxyAddressAsString() : dest.getProxyAddressAsString(); - int port = dest.getProxyPort() == -1 ? src.getProxyPort() : dest.getProxyPort(); - int protocol = src.getProtocol() == ApnSetting.PROTOCOL_IPV4V6 ? src.getProtocol() - : dest.getProtocol(); - int roamingProtocol = src.getRoamingProtocol() == ApnSetting.PROTOCOL_IPV4V6 - ? src.getRoamingProtocol() : dest.getRoamingProtocol(); - int networkTypeBitmask = (dest.getNetworkTypeBitmask() == 0 - || src.getNetworkTypeBitmask() == 0) - ? 0 : (dest.getNetworkTypeBitmask() | src.getNetworkTypeBitmask()); - return new ApnSetting.Builder() - .setId(id) - .setOperatorNumeric(dest.getOperatorNumeric()) - .setEntryName(dest.getEntryName()) - .setApnName(dest.getApnName()) - .setProxyAddress(proxy) - .setProxyPort(port) - .setMmsc(mmsc) - .setMmsProxyAddress(mmsProxy) - .setMmsProxyPort(mmsPort) - .setUser(dest.getUser()) - .setPassword(dest.getPassword()) - .setAuthType(dest.getAuthType()) - .setApnTypeBitmask(resultApnType) - .setProtocol(protocol) - .setRoamingProtocol(roamingProtocol) - .setCarrierEnabled(dest.isEnabled()) - .setNetworkTypeBitmask(networkTypeBitmask) - .setProfileId(dest.getProfileId()) - .setModemCognitive(dest.isPersistent() || src.isPersistent()) - .setMaxConns(dest.getMaxConns()) - .setWaitTime(dest.getWaitTime()) - .setMaxConnsTime(dest.getMaxConnsTime()) - .setMtuV4(dest.getMtuV4()) - .setMtuV6(dest.getMtuV6()) - .setMvnoType(dest.getMvnoType()) - .setMvnoMatchData(dest.getMvnoMatchData()) - .setApnSetId(dest.getApnSetId()) - .setCarrierId(dest.getCarrierId()) - .setSkip464Xlat(dest.getSkip464Xlat()) - .build(); - } - - private DataConnection createDataConnection() { - if (DBG) log("createDataConnection E"); - - int id = mUniqueIdGenerator.getAndIncrement(); - DataConnection dataConnection = DataConnection.makeDataConnection(mPhone, id, this, - mDataServiceManager, mDcTesterFailBringUpAll, mDcc); - mDataConnections.put(id, dataConnection); - if (DBG) log("createDataConnection() X id=" + id + " dc=" + dataConnection); - return dataConnection; - } - - private void destroyDataConnections() { - if(mDataConnections != null) { - if (DBG) log("destroyDataConnections: clear mDataConnectionList"); - mDataConnections.clear(); - } else { - if (DBG) log("destroyDataConnections: mDataConnecitonList is empty, ignore"); - } - } - - /** - * Build a list of APNs to be used to create PDP's. - * - * @param requestedApnType - * @return waitingApns list to be used to create PDP - * error when waitingApns.isEmpty() - */ - private @NonNull ArrayList<ApnSetting> buildWaitingApns(String requestedApnType, - int radioTech) { - if (DBG) log("buildWaitingApns: E requestedApnType=" + requestedApnType); - ArrayList<ApnSetting> apnList = new ArrayList<ApnSetting>(); - - int requestedApnTypeBitmask = ApnSetting.getApnTypesBitmaskFromString(requestedApnType); - if (requestedApnTypeBitmask == ApnSetting.TYPE_ENTERPRISE) { - requestedApnTypeBitmask = ApnSetting.TYPE_DEFAULT; - } - if (requestedApnTypeBitmask == ApnSetting.TYPE_DUN) { - ArrayList<ApnSetting> dunApns = fetchDunApns(); - if (dunApns.size() > 0) { - for (ApnSetting dun : dunApns) { - apnList.add(dun); - if (DBG) log("buildWaitingApns: X added APN_TYPE_DUN apnList=" + apnList); - } - return apnList; - } - } - - String operator = mPhone.getOperatorNumeric(); - - // This is a workaround for a bug (7305641) where we don't failover to other - // suitable APNs if our preferred APN fails. On prepaid ATT sims we need to - // failover to a provisioning APN, but once we've used their default data - // connection we are locked to it for life. This change allows ATT devices - // to say they don't want to use preferred at all. - boolean usePreferred = true; - try { - usePreferred = !mPhone.getContext().getResources().getBoolean(com.android - .internal.R.bool.config_dontPreferApn); - } catch (Resources.NotFoundException e) { - if (DBG) log("buildWaitingApns: usePreferred NotFoundException set to true"); - usePreferred = true; - } - if (usePreferred) { - mPreferredApn = getPreferredApn(); - } - if (DBG) { - log("buildWaitingApns: usePreferred=" + usePreferred - + " canSetPreferApn=" + mCanSetPreferApn - + " mPreferredApn=" + mPreferredApn - + " operator=" + operator + " radioTech=" + radioTech); - } - - if (usePreferred && mCanSetPreferApn && mPreferredApn != null && - mPreferredApn.canHandleType(requestedApnTypeBitmask)) { - if (DBG) { - log("buildWaitingApns: Preferred APN:" + operator + ":" - + mPreferredApn.getOperatorNumeric() + ":" + mPreferredApn); - } - - if (TextUtils.equals(mPreferredApn.getOperatorNumeric(), operator) - || mPreferredApn.getCarrierId() == mPhone.getCarrierId()) { - if (mPreferredApn.canSupportNetworkType( - ServiceState.rilRadioTechnologyToNetworkType(radioTech))) { - // 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; - } - } - if (DBG) log("buildWaitingApns: no preferred APN"); - setPreferredApn(-1); - mPreferredApn = null; - } - - if (DBG) log("buildWaitingApns: mAllApnSettings=" + mAllApnSettings); - int preferredApnSetId = getPreferredApnSetId(); - for (ApnSetting apn : mAllApnSettings) { - if (apn.canHandleType(requestedApnTypeBitmask)) { - if (apn.canSupportNetworkType( - ServiceState.rilRadioTechnologyToNetworkType(radioTech))) { - if (apn.getApnSetId() == Telephony.Carriers.MATCH_ALL_APN_SET_ID - || preferredApnSetId == apn.getApnSetId()) { - if (VDBG) log("buildWaitingApns: adding apn=" + 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); - } - } else { - if (DBG) { - log("buildWaitingApns: networkTypeBitmask:" - + apn.getNetworkTypeBitmask() - + " does not include radioTech:" - + ServiceState.rilRadioTechnologyToString(radioTech)); - } - } - } else if (VDBG) { - log("buildWaitingApns: couldn't handle requested ApnType=" - + requestedApnType); - } - } - - if (DBG) log("buildWaitingApns: " + apnList.size() + " APNs in the list: " + apnList); - return apnList; - } - - private String apnListToString (ArrayList<ApnSetting> apns) { - StringBuilder result = new StringBuilder(); - for (int i = 0, size = apns.size(); i < size; i++) { - result.append('[') - .append(apns.get(i).toString()) - .append(']'); - } - return result.toString(); - } - - private void setPreferredApn(int pos) { - setPreferredApn(pos, false); - } - - private void setPreferredApn(int pos, boolean force) { - if (!force && !mCanSetPreferApn) { - log("setPreferredApn: X !canSEtPreferApn"); - return; - } - - String subId = Long.toString(mPhone.getSubId()); - Uri uri = Uri.withAppendedPath(PREFERAPN_NO_UPDATE_URI_USING_SUBID, subId); - log("setPreferredApn: delete"); - ContentResolver resolver = mPhone.getContext().getContentResolver(); - resolver.delete(uri, null, null); - - if (pos >= 0) { - log("setPreferredApn: insert"); - ContentValues values = new ContentValues(); - values.put(APN_ID, pos); - resolver.insert(uri, values); - } - } - - @Nullable - ApnSetting getPreferredApn() { - //Only call this method from main thread - if (mAllApnSettings == null || mAllApnSettings.isEmpty()) { - log("getPreferredApn: mAllApnSettings is empty"); - return null; - } - - String subId = Long.toString(mPhone.getSubId()); - Uri uri = Uri.withAppendedPath(PREFERAPN_NO_UPDATE_URI_USING_SUBID, subId); - Cursor cursor = mPhone.getContext().getContentResolver().query( - uri, new String[] { "_id", "name", "apn" }, - null, null, Telephony.Carriers.DEFAULT_SORT_ORDER); - - if (cursor != null) { - mCanSetPreferApn = true; - } else { - mCanSetPreferApn = false; - } - - if (VDBG) { - log("getPreferredApn: mRequestedApnType=" + mRequestedApnType + " cursor=" + cursor - + " cursor.count=" + ((cursor != null) ? cursor.getCount() : 0)); - } - - if (mCanSetPreferApn && cursor.getCount() > 0) { - int pos; - cursor.moveToFirst(); - pos = cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers._ID)); - for(ApnSetting p : mAllApnSettings) { - if (p.getId() == pos && p.canHandleType(mRequestedApnType)) { - log("getPreferredApn: For APN type " - + ApnSetting.getApnTypeString(mRequestedApnType) - + " found apnSetting " + p); - cursor.close(); - return p; - } - } - } - - if (cursor != null) { - cursor.close(); - } - - log("getPreferredApn: X not found"); - return null; - } - - @Override - public void handleMessage (Message msg) { - if (VDBG) log("handleMessage msg=" + msg); - - AsyncResult ar; - Pair<ApnContext, Integer> pair; - ApnContext apnContext; - int generation; - int requestType; - int handoverFailureMode; - switch (msg.what) { - case DctConstants.EVENT_DATA_CONNECTION_DETACHED: - onDataConnectionDetached(); - break; - - case DctConstants.EVENT_DATA_CONNECTION_ATTACHED: - onDataConnectionAttached(); - break; - - case DctConstants.EVENT_DO_RECOVERY: - mDsRecoveryHandler.doRecovery(); - break; - - case DctConstants.EVENT_APN_CHANGED: - onApnChanged(); - break; - - case DctConstants.EVENT_PS_RESTRICT_ENABLED: - /** - * We don't need to explicitly to tear down the PDP context - * when PS restricted is enabled. The base band will deactive - * PDP context and notify us with PDP_CONTEXT_CHANGED. - * But we should stop the network polling and prevent reset PDP. - */ - if (DBG) log("EVENT_PS_RESTRICT_ENABLED " + mIsPsRestricted); - stopNetStatPoll(); - stopDataStallAlarm(); - mIsPsRestricted = true; - break; - - case DctConstants.EVENT_PS_RESTRICT_DISABLED: - /** - * When PS restrict is removed, we need setup PDP connection if - * PDP connection is down. - */ - if (DBG) log("EVENT_PS_RESTRICT_DISABLED " + mIsPsRestricted); - mIsPsRestricted = false; - if (isAnyDataConnected()) { - startNetStatPoll(); - startDataStallAlarm(DATA_STALL_NOT_SUSPECTED); - } else { - // TODO: Should all PDN states be checked to fail? - if (mState == DctConstants.State.FAILED) { - cleanUpAllConnectionsInternal(false, Phone.REASON_PS_RESTRICT_ENABLED); - mReregisterOnReconnectFailure = false; - } - apnContext = mApnContextsByType.get(ApnSetting.TYPE_DEFAULT); - if (apnContext != null) { - apnContext.setReason(Phone.REASON_PS_RESTRICT_ENABLED); - trySetupData(apnContext, REQUEST_TYPE_NORMAL, null); - } else { - loge("**** Default ApnContext not found ****"); - if (TelephonyUtils.IS_DEBUGGABLE) { - throw new RuntimeException("Default ApnContext not found"); - } - } - } - break; - - case DctConstants.EVENT_TRY_SETUP_DATA: - apnContext = (ApnContext) msg.obj; - requestType = msg.arg1; - trySetupData(apnContext, requestType, null); - break; - case DctConstants.EVENT_CLEAN_UP_CONNECTION: - if (DBG) log("EVENT_CLEAN_UP_CONNECTION"); - cleanUpConnectionInternal(true, RELEASE_TYPE_DETACH, (ApnContext) msg.obj); - break; - case DctConstants.EVENT_CLEAN_UP_ALL_CONNECTIONS: - if ((msg.obj != null) && (msg.obj instanceof String == false)) { - msg.obj = null; - } - cleanUpAllConnectionsInternal(true, (String) msg.obj); - break; - - case DctConstants.EVENT_DATA_RAT_CHANGED: - if (getDataRat() == ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN) { - // unknown rat is an exception for data rat change. It's only received when out - // of service and is not applicable for apn bearer bitmask. We should bypass the - // check of waiting apn list and keep the data connection on, and no need to - // setup a new one. - break; - } - cleanUpConnectionsOnUpdatedApns(false, Phone.REASON_NW_TYPE_CHANGED); - //May new Network allow setupData, so try it here - setupDataOnAllConnectableApns(Phone.REASON_NW_TYPE_CHANGED, - RetryFailures.ONLY_ON_CHANGE); - break; - - case DctConstants.CMD_CLEAR_PROVISIONING_SPINNER: - // Check message sender intended to clear the current spinner. - if (mProvisioningSpinner == msg.obj) { - mProvisioningSpinner.dismiss(); - mProvisioningSpinner = null; - } - break; - - case DctConstants.EVENT_ENABLE_APN: - onEnableApn(msg.arg1, msg.arg2, (Message) msg.obj); - break; - - case DctConstants.EVENT_DISABLE_APN: - onDisableApn(msg.arg1, msg.arg2); - break; - - case DctConstants.EVENT_DATA_STALL_ALARM: - onDataStallAlarm(msg.arg1); - break; - - case DctConstants.EVENT_ROAMING_OFF: - onDataRoamingOff(); - break; - - case DctConstants.EVENT_ROAMING_ON: - case DctConstants.EVENT_ROAMING_SETTING_CHANGE: - onDataRoamingOnOrSettingsChanged(msg.what); - break; - - case DctConstants.EVENT_DEVICE_PROVISIONED_CHANGE: - // Update sharedPreference to false when exits new device provisioning, indicating - // no users modifications on the settings for new devices. Thus carrier specific - // default roaming settings can be applied for new devices till user modification. - final SharedPreferences sp = PreferenceManager - .getDefaultSharedPreferences(mPhone.getContext()); - if (!sp.contains(Phone.DATA_ROAMING_IS_USER_SETTING_KEY)) { - sp.edit().putBoolean(Phone.DATA_ROAMING_IS_USER_SETTING_KEY, false).commit(); - } - break; - - case DctConstants.EVENT_NETWORK_STATUS_CHANGED: - int status = msg.arg1; - int cid = msg.arg2; - String url = (String) msg.obj; - onNetworkStatusChanged(status, cid, url); - break; - - case DctConstants.EVENT_RADIO_AVAILABLE: - onRadioAvailable(); - break; - - case DctConstants.EVENT_RADIO_OFF_OR_NOT_AVAILABLE: - onRadioOffOrNotAvailable(); - break; - - case DctConstants.EVENT_DATA_SETUP_COMPLETE: - ar = (AsyncResult) msg.obj; - pair = (Pair<ApnContext, Integer>) ar.userObj; - apnContext = pair.first; - generation = pair.second; - requestType = msg.arg1; - handoverFailureMode = msg.arg2; - if (apnContext.getConnectionGeneration() == generation) { - boolean success = true; - int cause = DataFailCause.UNKNOWN; - if (ar.exception != null) { - success = false; - cause = (int) ar.result; - } - onDataSetupComplete(apnContext, success, cause, requestType, - handoverFailureMode); - } else { - loge("EVENT_DATA_SETUP_COMPLETE: Dropped the event because generation " - + "did not match."); - } - break; - - case DctConstants.EVENT_DATA_SETUP_COMPLETE_ERROR: - ar = (AsyncResult) msg.obj; - pair = (Pair<ApnContext, Integer>) ar.userObj; - apnContext = pair.first; - generation = pair.second; - handoverFailureMode = msg.arg2; - if (apnContext.getConnectionGeneration() == generation) { - onDataSetupCompleteError(apnContext, handoverFailureMode, false); - } else { - loge("EVENT_DATA_SETUP_COMPLETE_ERROR: Dropped the event because generation " - + "did not match."); - } - break; - - case DctConstants.EVENT_DISCONNECT_DONE: - log("EVENT_DISCONNECT_DONE msg=" + msg); - ar = (AsyncResult) msg.obj; - pair = (Pair<ApnContext, Integer>) ar.userObj; - apnContext = pair.first; - generation = pair.second; - if (apnContext.getConnectionGeneration() == generation) { - onDisconnectDone(apnContext); - } else { - loge("EVENT_DISCONNECT_DONE: Dropped the event because generation " - + "did not match."); - } - break; - - case DctConstants.EVENT_VOICE_CALL_STARTED: - onVoiceCallStarted(); - break; - - case DctConstants.EVENT_VOICE_CALL_ENDED: - onVoiceCallEnded(); - break; - case DctConstants.CMD_SET_ENABLE_FAIL_FAST_MOBILE_DATA: { - sEnableFailFastRefCounter += (msg.arg1 == DctConstants.ENABLED) ? 1 : -1; - if (DBG) { - log("CMD_SET_ENABLE_FAIL_FAST_MOBILE_DATA: " - + " sEnableFailFastRefCounter=" + sEnableFailFastRefCounter); - } - if (sEnableFailFastRefCounter < 0) { - final String s = "CMD_SET_ENABLE_FAIL_FAST_MOBILE_DATA: " - + "sEnableFailFastRefCounter:" + sEnableFailFastRefCounter + " < 0"; - loge(s); - sEnableFailFastRefCounter = 0; - } - final boolean enabled = sEnableFailFastRefCounter > 0; - if (DBG) { - log("CMD_SET_ENABLE_FAIL_FAST_MOBILE_DATA: enabled=" + enabled - + " sEnableFailFastRefCounter=" + sEnableFailFastRefCounter); - } - if (mFailFast != enabled) { - mFailFast = enabled; - - mDataStallNoRxEnabled = !enabled; - if (mDsRecoveryHandler.isNoRxDataStallDetectionEnabled() - && isAnyDataConnected() - && (!mInVoiceCall || - mPhone.getServiceStateTracker() - .isConcurrentVoiceAndDataAllowed())) { - if (DBG) log("CMD_SET_ENABLE_FAIL_FAST_MOBILE_DATA: start data stall"); - stopDataStallAlarm(); - startDataStallAlarm(DATA_STALL_NOT_SUSPECTED); - } else { - if (DBG) log("CMD_SET_ENABLE_FAIL_FAST_MOBILE_DATA: stop data stall"); - stopDataStallAlarm(); - } - } - - break; - } - case DctConstants.CMD_ENABLE_MOBILE_PROVISIONING: { - Bundle bundle = msg.getData(); - if (bundle != null) { - try { - mProvisioningUrl = (String)bundle.get(DctConstants.PROVISIONING_URL_KEY); - } catch(ClassCastException e) { - loge("CMD_ENABLE_MOBILE_PROVISIONING: provisioning url not a string" + e); - mProvisioningUrl = null; - } - } - if (TextUtils.isEmpty(mProvisioningUrl)) { - loge("CMD_ENABLE_MOBILE_PROVISIONING: provisioning url is empty, ignoring"); - mIsProvisioning = false; - mProvisioningUrl = null; - } else { - loge("CMD_ENABLE_MOBILE_PROVISIONING: provisioningUrl=" + mProvisioningUrl); - mIsProvisioning = true; - startProvisioningApnAlarm(); - } - break; - } - case DctConstants.EVENT_PROVISIONING_APN_ALARM: { - if (DBG) log("EVENT_PROVISIONING_APN_ALARM"); - ApnContext apnCtx = mApnContextsByType.get(ApnSetting.TYPE_DEFAULT); - if (apnCtx.isProvisioningApn() && apnCtx.isConnectedOrConnecting()) { - if (mProvisioningApnAlarmTag == msg.arg1) { - if (DBG) log("EVENT_PROVISIONING_APN_ALARM: Disconnecting"); - mIsProvisioning = false; - mProvisioningUrl = null; - stopProvisioningApnAlarm(); - cleanUpConnectionInternal(true, RELEASE_TYPE_DETACH, apnCtx); - } else { - if (DBG) { - log("EVENT_PROVISIONING_APN_ALARM: ignore stale tag," - + " mProvisioningApnAlarmTag:" + mProvisioningApnAlarmTag - + " != arg1:" + msg.arg1); - } - } - } else { - if (DBG) log("EVENT_PROVISIONING_APN_ALARM: Not connected ignore"); - } - break; - } - case DctConstants.CMD_IS_PROVISIONING_APN: { - if (DBG) log("CMD_IS_PROVISIONING_APN"); - boolean isProvApn; - try { - String apnType = null; - Bundle bundle = msg.getData(); - if (bundle != null) { - apnType = (String)bundle.get(DctConstants.APN_TYPE_KEY); - } - if (TextUtils.isEmpty(apnType)) { - loge("CMD_IS_PROVISIONING_APN: apnType is empty"); - isProvApn = false; - } else { - isProvApn = isProvisioningApn(apnType); - } - } catch (ClassCastException e) { - loge("CMD_IS_PROVISIONING_APN: NO provisioning url ignoring"); - isProvApn = false; - } - if (DBG) log("CMD_IS_PROVISIONING_APN: ret=" + isProvApn); - mReplyAc.replyToMessage(msg, DctConstants.CMD_IS_PROVISIONING_APN, - isProvApn ? DctConstants.ENABLED : DctConstants.DISABLED); - break; - } - case DctConstants.EVENT_RESTART_RADIO: { - restartRadio(); - break; - } - case DctConstants.CMD_NET_STAT_POLL: { - if (msg.arg1 == DctConstants.ENABLED) { - handleStartNetStatPoll((DctConstants.Activity)msg.obj); - } else if (msg.arg1 == DctConstants.DISABLED) { - handleStopNetStatPoll((DctConstants.Activity)msg.obj); - } - break; - } - case DctConstants.EVENT_PCO_DATA_RECEIVED: { - handlePcoData((AsyncResult)msg.obj); - break; - } - case DctConstants.EVENT_DATA_RECONNECT: - if (DBG) { - log("EVENT_DATA_RECONNECT: subId=" + msg.arg1 + ", type=" - + requestTypeToString(msg.arg2)); - } - onDataReconnect((ApnContext) msg.obj, msg.arg1, msg.arg2); - break; - case DctConstants.EVENT_DATA_SERVICE_BINDING_CHANGED: - onDataServiceBindingChanged((Boolean) ((AsyncResult) msg.obj).result); - break; - case DctConstants.EVENT_DATA_ENABLED_CHANGED: - ar = (AsyncResult) msg.obj; - if (ar.result instanceof Pair) { - Pair<Boolean, Integer> p = (Pair<Boolean, Integer>) ar.result; - boolean enabled = p.first; - int reason = p.second; - onDataEnabledChanged(enabled, reason); - } - break; - case DctConstants.EVENT_DATA_ENABLED_OVERRIDE_RULES_CHANGED: - onDataEnabledOverrideRulesChanged(); - break; - case DctConstants.EVENT_NR_TIMER_WATCHDOG: - mWatchdog = false; - reevaluateUnmeteredConnections(); - break; - case DctConstants.EVENT_TELEPHONY_DISPLAY_INFO_CHANGED: - reevaluateCongestedConnections(); - reevaluateUnmeteredConnections(); - break; - case DctConstants.EVENT_CARRIER_CONFIG_CHANGED: - onCarrierConfigChanged(); - break; - case DctConstants.EVENT_SIM_STATE_UPDATED: - int simState = msg.arg1; - onSimStateUpdated(simState); - break; - case DctConstants.EVENT_APN_UNTHROTTLED: - ar = (AsyncResult) msg.obj; - String apn = (String) ar.result; - onApnUnthrottled(apn); - break; - case DctConstants.EVENT_TRAFFIC_DESCRIPTORS_UPDATED: - onTrafficDescriptorsUpdated(); - break; - default: - Rlog.e("DcTracker", "Unhandled event=" + msg); - break; - - } - } - - private int getApnProfileID(String apnType) { - if (TextUtils.equals(apnType, ApnSetting.TYPE_IMS_STRING)) { - return RILConstants.DATA_PROFILE_IMS; - } else if (TextUtils.equals(apnType, ApnSetting.TYPE_FOTA_STRING)) { - return RILConstants.DATA_PROFILE_FOTA; - } else if (TextUtils.equals(apnType, ApnSetting.TYPE_CBS_STRING)) { - return RILConstants.DATA_PROFILE_CBS; - } else if (TextUtils.equals(apnType, ApnSetting.TYPE_IA_STRING)) { - return RILConstants.DATA_PROFILE_DEFAULT; // DEFAULT for now - } else if (TextUtils.equals(apnType, ApnSetting.TYPE_DUN_STRING)) { - return RILConstants.DATA_PROFILE_TETHERED; - } else { - return RILConstants.DATA_PROFILE_DEFAULT; - } - } - - private int getCellLocationId() { - int cid = -1; - CellLocation loc = mPhone.getCurrentCellIdentity().asCellLocation(); - - if (loc != null) { - if (loc instanceof GsmCellLocation) { - cid = ((GsmCellLocation)loc).getCid(); - } else if (loc instanceof CdmaCellLocation) { - cid = ((CdmaCellLocation)loc).getBaseStationId(); - } - } - return cid; - } - - /** - * Update link bandwidth estimate default values from carrier config. - * @param bandwidths String array of "RAT:upstream,downstream" for each RAT - * @param useLte For NR NSA, whether to use LTE value for upstream or not - */ - private void updateLinkBandwidths(String[] bandwidths, boolean useLte) { - ConcurrentHashMap<String, Pair<Integer, Integer>> temp = new ConcurrentHashMap<>(); - for (String config : bandwidths) { - int downstream = 14; - int upstream = 14; - String[] kv = config.split(":"); - if (kv.length == 2) { - String[] split = kv[1].split(","); - if (split.length == 2) { - try { - downstream = Integer.parseInt(split[0]); - upstream = Integer.parseInt(split[1]); - } catch (NumberFormatException ignored) { - } - } - temp.put(kv[0], new Pair<>(downstream, upstream)); - } - } - if (useLte) { - Pair<Integer, Integer> ltePair = - temp.get(DataConfigManager.DATA_CONFIG_NETWORK_TYPE_LTE); - if (ltePair != null) { - if (temp.containsKey(DataConfigManager.DATA_CONFIG_NETWORK_TYPE_NR_NSA)) { - temp.put(DataConfigManager.DATA_CONFIG_NETWORK_TYPE_NR_NSA, new Pair<>( - temp.get(DataConfigManager.DATA_CONFIG_NETWORK_TYPE_NR_NSA).first, - ltePair.second)); - } - if (temp.containsKey(DataConfigManager.DATA_CONFIG_NETWORK_TYPE_NR_NSA_MMWAVE)) { - temp.put(DataConfigManager.DATA_CONFIG_NETWORK_TYPE_NR_NSA_MMWAVE, new Pair<>( - temp.get(DataConfigManager.DATA_CONFIG_NETWORK_TYPE_NR_NSA_MMWAVE) - .first, ltePair.second)); - } - } - } - mBandwidths = temp; - for (DataConnection dc : mDataConnections.values()) { - dc.sendMessage(DataConnection.EVENT_CARRIER_CONFIG_LINK_BANDWIDTHS_CHANGED); - } - } - - /** - * Return the link upstream/downstream values from CarrierConfig for the given RAT name. - * @param ratName RAT name from ServiceState#rilRadioTechnologyToString. - * @return pair of downstream/upstream values (kbps), or null if the config is not defined. - */ - public Pair<Integer, Integer> getLinkBandwidthsFromCarrierConfig(String ratName) { - return mBandwidths.get(ratName); - } - - @VisibleForTesting - public boolean shouldAutoAttach() { - if (mAutoAttachEnabled.get()) return true; - - PhoneSwitcher phoneSwitcher = PhoneSwitcher.getInstance(); - ServiceState serviceState = mPhone.getServiceState(); - - if (phoneSwitcher == null || serviceState == null) return false; - - // If voice is also not in service, don't auto attach. - if (serviceState.getState() != ServiceState.STATE_IN_SERVICE) return false; - - // If voice is on LTE or NR, don't auto attach as for LTE / NR data would be attached. - if (serviceState.getVoiceNetworkType() == NETWORK_TYPE_LTE - || serviceState.getVoiceNetworkType() == NETWORK_TYPE_NR) return false; - - // If phone is non default phone, modem may have detached from data for optimization. - // If phone is in voice call, for DSDS case DDS switch may be limited so we do try our - // best to setup data connection and allow auto-attach. - return (mPhone.getPhoneId() != phoneSwitcher.getPreferredDataPhoneId() - || mPhone.getState() != PhoneConstants.State.IDLE); - } - - private void notifyAllDataDisconnected() { - sEnableFailFastRefCounter = 0; - mFailFast = false; - log("notify all data disconnected"); - mAllDataDisconnectedRegistrants.notifyRegistrants(); - } - - public void registerForAllDataDisconnected(Handler h, int what) { - mAllDataDisconnectedRegistrants.addUnique(h, what, null); - - if (areAllDataDisconnected()) { - notifyAllDataDisconnected(); - } - } - - public void unregisterForAllDataDisconnected(Handler h) { - mAllDataDisconnectedRegistrants.remove(h); - } - - private void onDataEnabledChanged(boolean enable, - @DataEnabledChangedReason int enabledChangedReason) { - if (DBG) { - log("onDataEnabledChanged: enable=" + enable + ", enabledChangedReason=" - + enabledChangedReason); - } - - if (enable) { - reevaluateDataConnections(); - setupDataOnAllConnectableApns(Phone.REASON_DATA_ENABLED, RetryFailures.ALWAYS); - } else { - String cleanupReason; - switch (enabledChangedReason) { - case DataEnabledSettings.REASON_INTERNAL_DATA_ENABLED: - cleanupReason = Phone.REASON_DATA_DISABLED_INTERNAL; - break; - case DataEnabledSettings.REASON_DATA_ENABLED_BY_CARRIER: - cleanupReason = Phone.REASON_CARRIER_ACTION_DISABLE_METERED_APN; - break; - case DataEnabledSettings.REASON_USER_DATA_ENABLED: - case DataEnabledSettings.REASON_POLICY_DATA_ENABLED: - case DataEnabledSettings.REASON_PROVISIONED_CHANGED: - case DataEnabledSettings.REASON_PROVISIONING_DATA_ENABLED_CHANGED: - default: - cleanupReason = Phone.REASON_DATA_SPECIFIC_DISABLED; - break; - - } - cleanUpAllConnectionsInternal(true, cleanupReason); - } - } - - private void reevaluateCongestedConnections() { - log("reevaluateCongestedConnections"); - int rat = mPhone.getDisplayInfoController().getTelephonyDisplayInfo().getNetworkType(); - // congested override and either network is specified or unknown and all networks specified - boolean isCongested = mCongestedOverride && (mCongestedNetworkTypes.contains(rat) - || mCongestedNetworkTypes.containsAll(Arrays.stream( - TelephonyManager.getAllNetworkTypes()).boxed().collect(Collectors.toSet()))); - for (DataConnection dataConnection : mDataConnections.values()) { - dataConnection.onCongestednessChanged(isCongested); - } - } - - private void reevaluateUnmeteredConnections() { - log("reevaluateUnmeteredConnections"); - int rat = mPhone.getDisplayInfoController().getTelephonyDisplayInfo().getNetworkType(); - if (isNrUnmetered() && (!mPhone.getServiceState().getRoaming() || mNrNsaRoamingUnmetered)) { - setDataConnectionUnmetered(true); - if (!mWatchdog) { - startWatchdogAlarm(); - } - } else { - stopWatchdogAlarm(); - setDataConnectionUnmetered(isNetworkTypeUnmetered(rat)); - } - } - - private void setDataConnectionUnmetered(boolean isUnmetered) { - if (!isUnmetered || isTempNotMeteredSupportedByCarrier()) { - for (DataConnection dataConnection : mDataConnections.values()) { - dataConnection.onMeterednessChanged(isUnmetered); - } - } - } - - private boolean isNetworkTypeUnmetered(@NetworkType int networkType) { - boolean isUnmetered; - if (mUnmeteredNetworkTypes == null || !mUnmeteredOverride) { - // check SubscriptionPlans if override is not defined - isUnmetered = isNetworkTypeUnmeteredViaSubscriptionPlan(networkType); - log("isNetworkTypeUnmeteredViaSubscriptionPlan: networkType=" + networkType - + ", isUnmetered=" + isUnmetered); - return isUnmetered; - } - // unmetered override and either network is specified or unknown and all networks specified - isUnmetered = mUnmeteredNetworkTypes.contains(networkType) - || mUnmeteredNetworkTypes.containsAll(Arrays.stream( - TelephonyManager.getAllNetworkTypes()).boxed().collect(Collectors.toSet())); - if (DBG) { - log("isNetworkTypeUnmetered: networkType=" + networkType - + ", isUnmetered=" + isUnmetered); - } - return isUnmetered; - } - - private boolean isNetworkTypeUnmeteredViaSubscriptionPlan(@NetworkType int networkType) { - if (mSubscriptionPlans.isEmpty()) { - // safe return false if unable to get subscription plans or plans don't exist - return false; - } - - boolean isGeneralUnmetered = true; - Set<Integer> allNetworkTypes = Arrays.stream(TelephonyManager.getAllNetworkTypes()) - .boxed().collect(Collectors.toSet()); - for (SubscriptionPlan plan : mSubscriptionPlans) { - // check plan is general (applies to all network types) or specific - if (Arrays.stream(plan.getNetworkTypes()).boxed().collect(Collectors.toSet()) - .containsAll(allNetworkTypes)) { - if (!isPlanUnmetered(plan)) { - // metered takes precedence over unmetered for safety - isGeneralUnmetered = false; - } - } else { - // check plan applies to given network type - if (networkType != TelephonyManager.NETWORK_TYPE_UNKNOWN) { - for (int planNetworkType : plan.getNetworkTypes()) { - if (planNetworkType == networkType) { - return isPlanUnmetered(plan); - } - } - } - } - } - return isGeneralUnmetered; - } - - private boolean isPlanUnmetered(SubscriptionPlan plan) { - return plan.getDataLimitBytes() == SubscriptionPlan.BYTES_UNLIMITED; - } - - private boolean isNrUnmetered() { - int rat = mPhone.getDisplayInfoController().getTelephonyDisplayInfo().getNetworkType(); - int override = mPhone.getDisplayInfoController().getTelephonyDisplayInfo() - .getOverrideNetworkType(); - - if (isNetworkTypeUnmetered(NETWORK_TYPE_NR)) { - if (mNrNsaMmwaveUnmetered) { - if (override == TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED) { - if (DBG) log("NR unmetered for mmwave only"); - return true; - } - return false; - } else if (mNrNsaSub6Unmetered) { - if (override == TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA) { - if (DBG) log("NR unmetered for sub6 only"); - return true; - } - return false; - } - if (override == TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED - || override == TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA - || rat == NETWORK_TYPE_NR) { - if (DBG) log("NR unmetered for all frequencies"); - return true; - } - return false; - } - - if (mNrNsaAllUnmetered) { - if (mNrNsaMmwaveUnmetered) { - if (override == TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED) { - if (DBG) log("NR NSA unmetered for mmwave only via carrier configs"); - return true; - } - return false; - } else if (mNrNsaSub6Unmetered) { - if (override == TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA) { - if (DBG) log("NR NSA unmetered for sub6 only via carrier configs"); - return true; - } - return false; - } - if (override == TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED - || override == TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA) { - if (DBG) log("NR NSA unmetered for all frequencies via carrier configs"); - return true; - } - return false; - } - - if (mNrSaAllUnmetered) { - // TODO: add logic for mNrSaMmwaveUnmetered and mNrSaSub6Unmetered once it's defined - // in TelephonyDisplayInfo - if (rat == NETWORK_TYPE_NR) { - if (DBG) log("NR SA unmetered for all frequencies via carrier configs"); - return true; - } - return false; - } - - return false; - } - - private boolean isTempNotMeteredSupportedByCarrier() { - CarrierConfigManager configManager = - mPhone.getContext().getSystemService(CarrierConfigManager.class); - if (configManager != null) { - PersistableBundle bundle = configManager.getConfigForSubId(mPhone.getSubId()); - if (bundle != null) { - return bundle.getBoolean( - CarrierConfigManager.KEY_NETWORK_TEMP_NOT_METERED_SUPPORTED_BOOL); - } - } - - return false; - } - - protected void log(String s) { - Rlog.d(mLogTag, s); - } - - private void loge(String s) { - Rlog.e(mLogTag, s); - } - - private void logSortedApnContexts() { - if (VDBG) { - log("initApnContexts: X mApnContexts=" + mApnContexts); - - StringBuilder sb = new StringBuilder(); - sb.append("sorted apncontexts -> ["); - for (ApnContext apnContext : mPrioritySortedApnContexts) { - sb.append(apnContext); - sb.append(", "); - - log("sorted list"); - } - sb.append("]"); - log(sb.toString()); - } - } - - public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - pw.println("DcTracker:"); - pw.println(" RADIO_TESTS=" + RADIO_TESTS); - pw.println(" mDataEnabledSettings=" + mDataEnabledSettings); - pw.println(" isDataAllowed=" + isDataAllowed(null)); - pw.flush(); - pw.println(" mRequestedApnType=" + mRequestedApnType); - pw.println(" mPhone=" + mPhone.getPhoneName()); - pw.println(" mConfigReady=" + mConfigReady); - pw.println(" mSimState=" + SubscriptionInfoUpdater.simStateString(mSimState)); - pw.println(" mActivity=" + mActivity); - pw.println(" mState=" + mState); - pw.println(" mTxPkts=" + mTxPkts); - pw.println(" mRxPkts=" + mRxPkts); - pw.println(" mNetStatPollPeriod=" + mNetStatPollPeriod); - pw.println(" mNetStatPollEnabled=" + mNetStatPollEnabled); - pw.println(" mDataStallTxRxSum=" + mDataStallTxRxSum); - pw.println(" mDataStallAlarmTag=" + mDataStallAlarmTag); - pw.println(" mDataStallNoRxEnabled=" + mDataStallNoRxEnabled); - pw.println(" mEmergencyApn=" + mEmergencyApn); - pw.println(" mSentSinceLastRecv=" + mSentSinceLastRecv); - pw.println(" mNoRecvPollCount=" + mNoRecvPollCount); - pw.println(" mResolver=" + mResolver); - pw.println(" mReconnectIntent=" + mReconnectIntent); - pw.println(" mAutoAttachEnabled=" + mAutoAttachEnabled.get()); - pw.println(" mIsScreenOn=" + mIsScreenOn); - pw.println(" mUniqueIdGenerator=" + mUniqueIdGenerator); - pw.println(" mDataServiceBound=" + mDataServiceBound); - pw.println(" mDataRoamingLeakageLog= "); - mDataRoamingLeakageLog.dump(fd, pw, args); - pw.println(" mApnSettingsInitializationLog= "); - mApnSettingsInitializationLog.dump(fd, pw, args); - pw.flush(); - pw.println(" ***************************************"); - DcController dcc = mDcc; - if (dcc != null) { - if (mDataServiceBound) { - dcc.dump(fd, pw, args); - } else { - pw.println(" Can't dump mDcc because data service is not bound."); - } - } else { - pw.println(" mDcc=null"); - } - pw.println(" ***************************************"); - HashMap<Integer, DataConnection> dcs = mDataConnections; - if (dcs != null) { - Set<Entry<Integer, DataConnection> > mDcSet = mDataConnections.entrySet(); - pw.println(" mDataConnections: count=" + mDcSet.size()); - for (Entry<Integer, DataConnection> entry : mDcSet) { - pw.printf(" *** mDataConnection[%d] \n", entry.getKey()); - entry.getValue().dump(fd, pw, args); - } - } else { - pw.println("mDataConnections=null"); - } - pw.println(" ***************************************"); - pw.flush(); - HashMap<String, Integer> apnToDcId = mApnToDataConnectionId; - if (apnToDcId != null) { - Set<Entry<String, Integer>> apnToDcIdSet = apnToDcId.entrySet(); - pw.println(" mApnToDataConnectonId size=" + apnToDcIdSet.size()); - for (Entry<String, Integer> entry : apnToDcIdSet) { - pw.printf(" mApnToDataConnectonId[%s]=%d\n", entry.getKey(), entry.getValue()); - } - } else { - pw.println("mApnToDataConnectionId=null"); - } - pw.println(" ***************************************"); - pw.flush(); - ConcurrentHashMap<String, ApnContext> apnCtxs = mApnContexts; - if (apnCtxs != null) { - Set<Entry<String, ApnContext>> apnCtxsSet = apnCtxs.entrySet(); - pw.println(" mApnContexts size=" + apnCtxsSet.size()); - for (Entry<String, ApnContext> entry : apnCtxsSet) { - entry.getValue().dump(fd, pw, args); - } - ApnContext.dumpLocalLog(fd, pw, args); - pw.println(" ***************************************"); - } else { - pw.println(" mApnContexts=null"); - } - pw.flush(); - - pw.println(" mAllApnSettings size=" + mAllApnSettings.size()); - for (int i = 0; i < mAllApnSettings.size(); i++) { - pw.printf(" mAllApnSettings[%d]: %s\n", i, mAllApnSettings.get(i)); - } - pw.flush(); - - pw.println(" mPreferredApn=" + mPreferredApn); - pw.println(" mIsPsRestricted=" + mIsPsRestricted); - pw.println(" mIsDisposed=" + mIsDisposed); - pw.println(" mIntentReceiver=" + mIntentReceiver); - pw.println(" mReregisterOnReconnectFailure=" + mReregisterOnReconnectFailure); - pw.println(" canSetPreferApn=" + mCanSetPreferApn); - pw.println(" mApnObserver=" + mApnObserver); - pw.println(" isAnyDataConnected=" + isAnyDataConnected()); - pw.println(" mAttached=" + mAttached.get()); - mDataEnabledSettings.dump(fd, pw, args); - pw.flush(); - } - - public String[] getPcscfAddress(String apnType) { - log("getPcscfAddress()"); - ApnContext apnContext = null; - - if(apnType == null){ - log("apnType is null, return null"); - return null; - } - - if (TextUtils.equals(apnType, ApnSetting.TYPE_EMERGENCY_STRING)) { - apnContext = mApnContextsByType.get(ApnSetting.TYPE_EMERGENCY); - } else if (TextUtils.equals(apnType, ApnSetting.TYPE_IMS_STRING)) { - apnContext = mApnContextsByType.get(ApnSetting.TYPE_IMS); - } else { - log("apnType is invalid, return null"); - return null; - } - - if (apnContext == null) { - log("apnContext is null, return null"); - return null; - } - - DataConnection dataConnection = apnContext.getDataConnection(); - String[] result = null; - - if (dataConnection != null) { - result = dataConnection.getPcscfAddresses(); - - if (result != null) { - for (int i = 0; i < result.length; i++) { - log("Pcscf[" + i + "]: " + result[i]); - } - } - return result; - } - return null; - } - - /** - * Create default apn settings for the apn type like emergency, and ims - */ - private ApnSetting buildDefaultApnSetting(@NonNull String entry, - @NonNull String apn, @ApnType int apnTypeBitmask) { - return new ApnSetting.Builder() - .setEntryName(entry) - .setProtocol(ApnSetting.PROTOCOL_IPV4V6) - .setRoamingProtocol(ApnSetting.PROTOCOL_IPV4V6) - .setApnName(apn) - .setApnTypeBitmask(apnTypeBitmask) - .setCarrierEnabled(true) - .setApnSetId(Telephony.Carriers.MATCH_ALL_APN_SET_ID) - .build(); - } - - /** - * Add default APN settings to APN settings list as needed - */ - private void addDefaultApnSettingsAsNeeded() { - boolean isEmergencyApnConfigured = false; - boolean isImsApnConfigured = false; - - for (ApnSetting apn : mAllApnSettings) { - if (apn.canHandleType(ApnSetting.TYPE_EMERGENCY)) { - isEmergencyApnConfigured = true; - } - if (apn.canHandleType(ApnSetting.TYPE_IMS)) { - isImsApnConfigured = true; - } - if (isEmergencyApnConfigured && isImsApnConfigured) { - log("Both emergency and ims apn setting are already present"); - return; - } - } - - // Add default apn setting for emergency service if it is not present - if (!isEmergencyApnConfigured) { - mAllApnSettings.add(buildDefaultApnSetting( - "DEFAULT EIMS", "sos", ApnSetting.TYPE_EMERGENCY)); - log("default emergency apn is created"); - } - - // Only add default apn setting for ims when it is not present and sim is loaded - if (!isImsApnConfigured && mSimState == TelephonyManager.SIM_STATE_LOADED) { - mAllApnSettings.add(buildDefaultApnSetting( - "DEFAULT IMS", "ims", ApnSetting.TYPE_IMS)); - log("default ims apn is created"); - } - } - - private void cleanUpConnectionsOnUpdatedApns(boolean detach, String reason) { - if (DBG) log("cleanUpConnectionsOnUpdatedApns: detach=" + detach); - if (mAllApnSettings.isEmpty()) { - cleanUpAllConnectionsInternal(detach, Phone.REASON_APN_CHANGED); - } else { - if (getDataRat() == ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN) { - // unknown rat is an exception for data rat change. Its only received when out of - // service and is not applicable for apn bearer bitmask. We should bypass the check - // of waiting apn list and keep the data connection on. - return; - } - for (ApnContext apnContext : mApnContexts.values()) { - boolean cleanupRequired = true; - if (!apnContext.isDisconnected()) { - ArrayList<ApnSetting> waitingApns = buildWaitingApns( - apnContext.getApnType(), getDataRat()); - if (apnContext.getWaitingApns().size() != waitingApns.size() - || !apnContext.getWaitingApns().containsAll(waitingApns)) { - apnContext.setWaitingApns(waitingApns); - } - for (ApnSetting apnSetting : waitingApns) { - if (areCompatible(apnSetting, apnContext.getApnSetting())) { - cleanupRequired = false; - break; - } - } - - if (cleanupRequired) { - if (DBG) { - log("cleanUpConnectionsOnUpdatedApns: APN type " - + apnContext.getApnType() + " clean up is required. The new " - + "waiting APN list " + waitingApns + " does not cover " - + apnContext.getApnSetting()); - } - apnContext.setReason(reason); - cleanUpConnectionInternal(true, RELEASE_TYPE_DETACH, apnContext); - } - } - } - } - - if (!isAnyDataConnected()) { - stopNetStatPoll(); - stopDataStallAlarm(); - } - - mRequestedApnType = ApnSetting.TYPE_DEFAULT; - - if (areAllDataDisconnected()) { - notifyAllDataDisconnected(); - } - } - - /** - * Polling stuff - */ - protected void resetPollStats() { - mTxPkts = -1; - mRxPkts = -1; - mNetStatPollPeriod = POLL_NETSTAT_MILLIS; - } - - protected void startNetStatPoll() { - if (isAnyDataConnected() && !mNetStatPollEnabled) { - if (DBG) { - log("startNetStatPoll"); - } - resetPollStats(); - mNetStatPollEnabled = true; - mPollNetStat.run(); - } - if (mPhone != null) { - mPhone.notifyDataActivity(); - } - } - - protected void stopNetStatPoll() { - mNetStatPollEnabled = false; - removeCallbacks(mPollNetStat); - if (DBG) { - log("stopNetStatPoll"); - } - - // To sync data activity icon in the case of switching data connection to send MMS. - if (mPhone != null) { - mPhone.notifyDataActivity(); - } - } - - public void sendStartNetStatPoll(DctConstants.Activity activity) { - Message msg = obtainMessage(DctConstants.CMD_NET_STAT_POLL); - msg.arg1 = DctConstants.ENABLED; - msg.obj = activity; - sendMessage(msg); - } - - private void handleStartNetStatPoll(DctConstants.Activity activity) { - startNetStatPoll(); - startDataStallAlarm(DATA_STALL_NOT_SUSPECTED); - setActivity(activity); - } - - public void sendStopNetStatPoll(DctConstants.Activity activity) { - Message msg = obtainMessage(DctConstants.CMD_NET_STAT_POLL); - msg.arg1 = DctConstants.DISABLED; - msg.obj = activity; - sendMessage(msg); - } - - private void handleStopNetStatPoll(DctConstants.Activity activity) { - stopNetStatPoll(); - stopDataStallAlarm(); - setActivity(activity); - } - - private void onDataEnabledOverrideRulesChanged() { - if (DBG) { - log("onDataEnabledOverrideRulesChanged"); - } - - for (ApnContext apnContext : mPrioritySortedApnContexts) { - if (isDataAllowed(apnContext, REQUEST_TYPE_NORMAL, null)) { - if (apnContext.getDataConnection() != null) { - apnContext.getDataConnection().reevaluateRestrictedState(); - } - setupDataOnConnectableApn(apnContext, Phone.REASON_DATA_ENABLED_OVERRIDE, - RetryFailures.ALWAYS); - } else if (shouldCleanUpConnection(apnContext, true, false)) { - apnContext.setReason(Phone.REASON_DATA_ENABLED_OVERRIDE); - cleanUpConnectionInternal(true, RELEASE_TYPE_DETACH, apnContext); - } - } - } - - private void updateDataActivity() { - long sent, received; - - DctConstants.Activity newActivity; - - TxRxSum preTxRxSum = new TxRxSum(mTxPkts, mRxPkts); - TxRxSum curTxRxSum = new TxRxSum(); - curTxRxSum.updateTotalTxRxSum(); - mTxPkts = curTxRxSum.txPkts; - mRxPkts = curTxRxSum.rxPkts; - - if (VDBG) { - log("updateDataActivity: curTxRxSum=" + curTxRxSum + " preTxRxSum=" + preTxRxSum); - } - - if (mNetStatPollEnabled && (preTxRxSum.txPkts > 0 || preTxRxSum.rxPkts > 0)) { - sent = mTxPkts - preTxRxSum.txPkts; - received = mRxPkts - preTxRxSum.rxPkts; - - if (VDBG) - log("updateDataActivity: sent=" + sent + " received=" + received); - if (sent > 0 && received > 0) { - newActivity = DctConstants.Activity.DATAINANDOUT; - } else if (sent > 0 && received == 0) { - newActivity = DctConstants.Activity.DATAOUT; - } else if (sent == 0 && received > 0) { - newActivity = DctConstants.Activity.DATAIN; - } else { - newActivity = (mActivity == DctConstants.Activity.DORMANT) ? - mActivity : DctConstants.Activity.NONE; - } - - if (mActivity != newActivity && mIsScreenOn) { - if (VDBG) - log("updateDataActivity: newActivity=" + newActivity); - mActivity = newActivity; - mPhone.notifyDataActivity(); - } - } - } - - private void handlePcoData(AsyncResult ar) { - if (ar.exception != null) { - loge("PCO_DATA exception: " + ar.exception); - return; - } - PcoData pcoData = (PcoData)(ar.result); - ArrayList<DataConnection> dcList = new ArrayList<>(); - DataConnection temp = mDcc.getActiveDcByCid(pcoData.cid); - if (temp != null) { - dcList.add(temp); - } - if (dcList.size() == 0) { - loge("PCO_DATA for unknown cid: " + pcoData.cid + ", inferring"); - for (DataConnection dc : mDataConnections.values()) { - final int cid = dc.getCid(); - if (cid == pcoData.cid) { - if (VDBG) log(" found " + dc); - dcList.clear(); - dcList.add(dc); - break; - } - // check if this dc is still connecting - if (cid == -1) { - for (ApnContext apnContext : dc.getApnContexts()) { - if (apnContext.getState() == DctConstants.State.CONNECTING) { - if (VDBG) log(" found potential " + dc); - dcList.add(dc); - break; - } - } - } - } - } - if (dcList.size() == 0) { - loge("PCO_DATA - couldn't infer cid"); - return; - } - for (DataConnection dc : dcList) { - List<ApnContext> apnContextList = dc.getApnContexts(); - if (apnContextList.size() == 0) { - break; - } - // send one out for each apn type in play - for (ApnContext apnContext : apnContextList) { - String apnType = apnContext.getApnType(); - - final Intent intent = new Intent(TelephonyManager.ACTION_CARRIER_SIGNAL_PCO_VALUE); - intent.putExtra(TelephonyManager.EXTRA_APN_TYPE, - ApnSetting.getApnTypesBitmaskFromString(apnType)); - intent.putExtra(TelephonyManager.EXTRA_APN_PROTOCOL, - ApnSetting.getProtocolIntFromString(pcoData.bearerProto)); - intent.putExtra(TelephonyManager.EXTRA_PCO_ID, pcoData.pcoId); - intent.putExtra(TelephonyManager.EXTRA_PCO_VALUE, pcoData.contents); - mPhone.getCarrierSignalAgent().notifyCarrierSignalReceivers(intent); - } - } - } - - /** - * Data-Stall - */ - - // Recovery action taken in case of data stall - @IntDef( - value = { - RECOVERY_ACTION_GET_DATA_CALL_LIST, - RECOVERY_ACTION_CLEANUP, - RECOVERY_ACTION_REREGISTER, - RECOVERY_ACTION_RADIO_RESTART - }) - @Retention(RetentionPolicy.SOURCE) - public @interface RecoveryAction {}; - private static final int RECOVERY_ACTION_GET_DATA_CALL_LIST = 0; - private static final int RECOVERY_ACTION_CLEANUP = 1; - private static final int RECOVERY_ACTION_REREGISTER = 2; - private static final int RECOVERY_ACTION_RADIO_RESTART = 3; - - // Recovery handler class for cellular data stall - private class DataStallRecoveryHandler { - // Default minimum duration between each recovery steps - private static final int - DEFAULT_MIN_DURATION_BETWEEN_RECOVERY_STEPS_IN_MS = (3 * 60 * 1000); // 3 mins - - // The elapsed real time of last recovery attempted - private long mTimeLastRecoveryStartMs; - // Whether current network good or not - private boolean mIsValidNetwork; - // Whether data stall happened or not. - private boolean mWasDataStall; - // Whether the result of last action(RADIO_RESTART) reported. - private boolean mLastActionReported; - // The real time for data stall start. - private long mDataStallStartMs; - // Last data stall action. - private @RecoveryAction int mLastAction; - - public DataStallRecoveryHandler() { - reset(); - } - - public void reset() { - mTimeLastRecoveryStartMs = 0; - putRecoveryAction(RECOVERY_ACTION_GET_DATA_CALL_LIST); - } - - private void setNetworkValidationState(boolean isValid) { - // Validation status is true and was not data stall. - if (isValid && !mWasDataStall) { - return; - } - - if (!mWasDataStall) { - mWasDataStall = true; - mDataStallStartMs = SystemClock.elapsedRealtime(); - if (DBG) log("data stall: start time = " + mDataStallStartMs); - return; - } - - if (!mLastActionReported) { - int timeDuration = (int) (SystemClock.elapsedRealtime() - mDataStallStartMs); - if (DBG) { - log("data stall: lastaction = " + mLastAction + ", isRecovered = " - + isValid + ", mTimeDuration = " + timeDuration); - } - DataStallRecoveryStats.onDataStallEvent(mLastAction, mPhone, isValid, - timeDuration); - mLastActionReported = true; - } - - if (isValid) { - mLastActionReported = false; - mWasDataStall = false; - } - } - - public boolean isAggressiveRecovery() { - @RecoveryAction int action = getRecoveryAction(); - - return ((action == RECOVERY_ACTION_CLEANUP) - || (action == RECOVERY_ACTION_REREGISTER) - || (action == RECOVERY_ACTION_RADIO_RESTART)); - } - - private long getMinDurationBetweenRecovery() { - return Settings.Global.getLong(mResolver, - Settings.Global.MIN_DURATION_BETWEEN_RECOVERY_STEPS_IN_MS, - DEFAULT_MIN_DURATION_BETWEEN_RECOVERY_STEPS_IN_MS); - } - - private long getElapsedTimeSinceRecoveryMs() { - return (SystemClock.elapsedRealtime() - mTimeLastRecoveryStartMs); - } - - @RecoveryAction - private int getRecoveryAction() { - @RecoveryAction int action = Settings.System.getInt(mResolver, - "radio.data.stall.recovery.action", RECOVERY_ACTION_GET_DATA_CALL_LIST); - if (VDBG_STALL) log("getRecoveryAction: " + action); - return action; - } - - private void putRecoveryAction(@RecoveryAction int action) { - Settings.System.putInt(mResolver, "radio.data.stall.recovery.action", action); - if (VDBG_STALL) log("putRecoveryAction: " + action); - } - - private void broadcastDataStallDetected(@RecoveryAction int recoveryAction) { - Intent intent = new Intent(TelephonyManager.ACTION_DATA_STALL_DETECTED); - SubscriptionManager.putPhoneIdAndSubIdExtra(intent, mPhone.getPhoneId()); - intent.putExtra(TelephonyManager.EXTRA_RECOVERY_ACTION, recoveryAction); - mPhone.getContext().sendBroadcast(intent, READ_PRIVILEGED_PHONE_STATE); - } - - private boolean isRecoveryAlreadyStarted() { - return getRecoveryAction() != RECOVERY_ACTION_GET_DATA_CALL_LIST; - } - - private boolean checkRecovery() { - // To avoid back to back recovery wait for a grace period - if (getElapsedTimeSinceRecoveryMs() < getMinDurationBetweenRecovery()) { - if (VDBG_STALL) log("skip back to back data stall recovery"); - return false; - } - - // Skip recovery if it can cause a call to drop - if (mPhone.getState() != PhoneConstants.State.IDLE - && getRecoveryAction() > RECOVERY_ACTION_CLEANUP) { - if (VDBG_STALL) log("skip data stall recovery as there is an active call"); - return false; - } - - // Allow recovery if data is expected to work - return mAttached.get() && isDataAllowed(null); - } - - private void triggerRecovery() { - // Updating the recovery start time early to avoid race when - // the message is being processed in the Queue - mTimeLastRecoveryStartMs = SystemClock.elapsedRealtime(); - sendMessage(obtainMessage(DctConstants.EVENT_DO_RECOVERY)); - } - - public void doRecovery() { - if (isAnyDataConnected()) { - // Go through a series of recovery steps, each action transitions to the next action - @RecoveryAction final int recoveryAction = getRecoveryAction(); - final int signalStrength = mPhone.getSignalStrength().getLevel(); - TelephonyMetrics.getInstance().writeSignalStrengthEvent( - mPhone.getPhoneId(), signalStrength); - TelephonyMetrics.getInstance().writeDataStallEvent( - mPhone.getPhoneId(), recoveryAction); - mLastAction = recoveryAction; - mLastActionReported = false; - broadcastDataStallDetected(recoveryAction); - - switch (recoveryAction) { - case RECOVERY_ACTION_GET_DATA_CALL_LIST: - EventLog.writeEvent(EventLogTags.DATA_STALL_RECOVERY_GET_DATA_CALL_LIST, - mSentSinceLastRecv); - if (DBG) log("doRecovery() get data call list"); - mDataServiceManager.requestDataCallList(obtainMessage()); - putRecoveryAction(RECOVERY_ACTION_CLEANUP); - break; - case RECOVERY_ACTION_CLEANUP: - EventLog.writeEvent(EventLogTags.DATA_STALL_RECOVERY_CLEANUP, - mSentSinceLastRecv); - if (DBG) log("doRecovery() cleanup all connections"); - cleanUpConnection(mApnContexts.get(ApnSetting.getApnTypeString( - ApnSetting.TYPE_DEFAULT))); - cleanUpConnection(mApnContexts.get(ApnSetting.getApnTypeString( - ApnSetting.TYPE_ENTERPRISE))); - putRecoveryAction(RECOVERY_ACTION_REREGISTER); - break; - case RECOVERY_ACTION_REREGISTER: - EventLog.writeEvent(EventLogTags.DATA_STALL_RECOVERY_REREGISTER, - mSentSinceLastRecv); - if (DBG) log("doRecovery() re-register"); - mPhone.getServiceStateTracker().reRegisterNetwork(null); - putRecoveryAction(RECOVERY_ACTION_RADIO_RESTART); - break; - case RECOVERY_ACTION_RADIO_RESTART: - EventLog.writeEvent(EventLogTags.DATA_STALL_RECOVERY_RADIO_RESTART, - mSentSinceLastRecv); - if (DBG) log("restarting radio"); - restartRadio(); - reset(); - break; - default: - throw new RuntimeException("doRecovery: Invalid recoveryAction=" - + recoveryAction); - } - mSentSinceLastRecv = 0; - } - } - - public void processNetworkStatusChanged(boolean isValid) { - setNetworkValidationState(isValid); - if (isValid) { - mIsValidNetwork = true; - reset(); - } else { - if (mIsValidNetwork || isRecoveryAlreadyStarted()) { - mIsValidNetwork = false; - // Check and trigger a recovery if network switched from good - // to bad or recovery is already started before. - if (checkRecovery()) { - if (DBG) log("trigger data stall recovery"); - triggerRecovery(); - } - } - } - } - - public boolean isRecoveryOnBadNetworkEnabled() { - return Settings.Global.getInt(mResolver, - Settings.Global.DATA_STALL_RECOVERY_ON_BAD_NETWORK, 1) == 1; - } - - public boolean isNoRxDataStallDetectionEnabled() { - return mDataStallNoRxEnabled && !isRecoveryOnBadNetworkEnabled(); - } - } - - private void updateDataStallInfo() { - long sent, received; - - TxRxSum preTxRxSum = new TxRxSum(mDataStallTxRxSum); - mDataStallTxRxSum.updateTotalTxRxSum(); - - if (VDBG_STALL) { - log("updateDataStallInfo: mDataStallTxRxSum=" + mDataStallTxRxSum + - " preTxRxSum=" + preTxRxSum); - } - - sent = mDataStallTxRxSum.txPkts - preTxRxSum.txPkts; - received = mDataStallTxRxSum.rxPkts - preTxRxSum.rxPkts; - - if (RADIO_TESTS) { - if (SystemProperties.getBoolean("radio.test.data.stall", false)) { - log("updateDataStallInfo: radio.test.data.stall true received = 0;"); - received = 0; - } - } - if ( sent > 0 && received > 0 ) { - if (VDBG_STALL) log("updateDataStallInfo: IN/OUT"); - mSentSinceLastRecv = 0; - mDsRecoveryHandler.reset(); - } else if (sent > 0 && received == 0) { - if (isPhoneStateIdle()) { - mSentSinceLastRecv += sent; - } else { - mSentSinceLastRecv = 0; - } - if (DBG) { - log("updateDataStallInfo: OUT sent=" + sent + - " mSentSinceLastRecv=" + mSentSinceLastRecv); - } - } else if (sent == 0 && received > 0) { - if (VDBG_STALL) log("updateDataStallInfo: IN"); - mSentSinceLastRecv = 0; - mDsRecoveryHandler.reset(); - } else { - if (VDBG_STALL) log("updateDataStallInfo: NONE"); - } - } - - private boolean isPhoneStateIdle() { - for (int i = 0; i < mTelephonyManager.getPhoneCount(); i++) { - Phone phone = PhoneFactory.getPhone(i); - if (phone != null && phone.getState() != PhoneConstants.State.IDLE) { - log("isPhoneStateIdle false: Voice call active on phone " + i); - return false; - } - } - return true; - } - - private void onDataStallAlarm(int tag) { - if (mDataStallAlarmTag != tag) { - if (DBG) { - log("onDataStallAlarm: ignore, tag=" + tag + " expecting " + mDataStallAlarmTag); - } - return; - } - - if (DBG) log("Data stall alarm"); - updateDataStallInfo(); - - int hangWatchdogTrigger = Settings.Global.getInt(mResolver, - Settings.Global.PDP_WATCHDOG_TRIGGER_PACKET_COUNT, - NUMBER_SENT_PACKETS_OF_HANG); - - boolean suspectedStall = DATA_STALL_NOT_SUSPECTED; - if (mSentSinceLastRecv >= hangWatchdogTrigger) { - if (DBG) { - log("onDataStallAlarm: tag=" + tag + " do recovery action=" - + mDsRecoveryHandler.getRecoveryAction()); - } - suspectedStall = DATA_STALL_SUSPECTED; - sendMessage(obtainMessage(DctConstants.EVENT_DO_RECOVERY)); - } else { - if (VDBG_STALL) { - log("onDataStallAlarm: tag=" + tag + " Sent " + String.valueOf(mSentSinceLastRecv) + - " pkts since last received, < watchdogTrigger=" + hangWatchdogTrigger); - } - } - startDataStallAlarm(suspectedStall); - } - - protected void startDataStallAlarm(boolean suspectedStall) { - int delayInMs; - - if (mDsRecoveryHandler.isNoRxDataStallDetectionEnabled() && isAnyDataConnected()) { - // If screen is on or data stall is currently suspected, set the alarm - // with an aggressive timeout. - if (mIsScreenOn || suspectedStall || mDsRecoveryHandler.isAggressiveRecovery()) { - delayInMs = Settings.Global.getInt(mResolver, - Settings.Global.DATA_STALL_ALARM_AGGRESSIVE_DELAY_IN_MS, - DATA_STALL_ALARM_AGGRESSIVE_DELAY_IN_MS_DEFAULT); - } else { - delayInMs = Settings.Global.getInt(mResolver, - Settings.Global.DATA_STALL_ALARM_NON_AGGRESSIVE_DELAY_IN_MS, - DATA_STALL_ALARM_NON_AGGRESSIVE_DELAY_IN_MS_DEFAULT); - } - - mDataStallAlarmTag += 1; - if (VDBG_STALL) { - log("startDataStallAlarm: tag=" + mDataStallAlarmTag + - " delay=" + (delayInMs / 1000) + "s"); - } - Intent intent = new Intent(INTENT_DATA_STALL_ALARM); - intent.putExtra(INTENT_DATA_STALL_ALARM_EXTRA_TAG, mDataStallAlarmTag); - intent.putExtra(INTENT_DATA_STALL_ALARM_EXTRA_TRANSPORT_TYPE, mTransportType); - SubscriptionManager.putPhoneIdAndSubIdExtra(intent, mPhone.getPhoneId()); - mDataStallAlarmIntent = PendingIntent.getBroadcast(mPhone.getContext(), 0, intent, - PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); - mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, - SystemClock.elapsedRealtime() + delayInMs, mDataStallAlarmIntent); - } else { - if (VDBG_STALL) { - log("startDataStallAlarm: NOT started, no connection tag=" + mDataStallAlarmTag); - } - } - } - - private void stopDataStallAlarm() { - if (VDBG_STALL) { - log("stopDataStallAlarm: current tag=" + mDataStallAlarmTag + - " mDataStallAlarmIntent=" + mDataStallAlarmIntent); - } - mDataStallAlarmTag += 1; - if (mDataStallAlarmIntent != null) { - mAlarmManager.cancel(mDataStallAlarmIntent); - mDataStallAlarmIntent = null; - } - } - - private void restartDataStallAlarm() { - if (!isAnyDataConnected()) return; - // To be called on screen status change. - // Do not cancel the alarm if it is set with aggressive timeout. - if (mDsRecoveryHandler.isAggressiveRecovery()) { - if (DBG) log("restartDataStallAlarm: action is pending. not resetting the alarm."); - return; - } - if (VDBG_STALL) log("restartDataStallAlarm: stop then start."); - stopDataStallAlarm(); - startDataStallAlarm(DATA_STALL_NOT_SUSPECTED); - } - - /** - * Provisioning APN - */ - private void onActionIntentProvisioningApnAlarm(Intent intent) { - if (DBG) log("onActionIntentProvisioningApnAlarm: action=" + intent.getAction()); - Message msg = obtainMessage(DctConstants.EVENT_PROVISIONING_APN_ALARM, - intent.getAction()); - msg.arg1 = intent.getIntExtra(PROVISIONING_APN_ALARM_TAG_EXTRA, 0); - sendMessage(msg); - } - - private void startProvisioningApnAlarm() { - int delayInMs = Settings.Global.getInt(mResolver, - Settings.Global.PROVISIONING_APN_ALARM_DELAY_IN_MS, - PROVISIONING_APN_ALARM_DELAY_IN_MS_DEFAULT); - if (TelephonyUtils.IS_DEBUGGABLE) { - // Allow debug code to use a system property to provide another value - String delayInMsStrg = Integer.toString(delayInMs); - delayInMsStrg = System.getProperty(DEBUG_PROV_APN_ALARM, delayInMsStrg); - try { - delayInMs = Integer.parseInt(delayInMsStrg); - } catch (NumberFormatException e) { - loge("startProvisioningApnAlarm: e=" + e); - } - } - mProvisioningApnAlarmTag += 1; - if (DBG) { - log("startProvisioningApnAlarm: tag=" + mProvisioningApnAlarmTag + - " delay=" + (delayInMs / 1000) + "s"); - } - Intent intent = new Intent(INTENT_PROVISIONING_APN_ALARM); - intent.putExtra(PROVISIONING_APN_ALARM_TAG_EXTRA, mProvisioningApnAlarmTag); - mProvisioningApnAlarmIntent = PendingIntent.getBroadcast(mPhone.getContext(), 0, intent, - PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); - mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, - SystemClock.elapsedRealtime() + delayInMs, mProvisioningApnAlarmIntent); - } - - private void stopProvisioningApnAlarm() { - if (DBG) { - log("stopProvisioningApnAlarm: current tag=" + mProvisioningApnAlarmTag + - " mProvsioningApnAlarmIntent=" + mProvisioningApnAlarmIntent); - } - mProvisioningApnAlarmTag += 1; - if (mProvisioningApnAlarmIntent != null) { - mAlarmManager.cancel(mProvisioningApnAlarmIntent); - mProvisioningApnAlarmIntent = null; - } - } - - /** - * 5G connection reevaluation alarm - */ - private void startWatchdogAlarm() { - sendMessageDelayed(obtainMessage(DctConstants.EVENT_NR_TIMER_WATCHDOG), mWatchdogTimeMs); - mWatchdog = true; - } - - private void stopWatchdogAlarm() { - removeMessages(DctConstants.EVENT_NR_TIMER_WATCHDOG); - mWatchdog = false; - } - - private void onDataServiceBindingChanged(boolean bound) { - if (!bound) { - if (mTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WLAN) { - boolean connPersistenceOnRestart = mPhone.getContext().getResources() - .getBoolean(com.android - .internal.R.bool.config_wlan_data_service_conn_persistence_on_restart); - if (!connPersistenceOnRestart) { - cleanUpAllConnectionsInternal(false, Phone.REASON_IWLAN_DATA_SERVICE_DIED); - } - } - } else { - //reset throttling after binding to data service - mDataThrottler.reset(); - } - mDataServiceBound = bound; - } - - public static String requestTypeToString(@RequestNetworkType int type) { - switch (type) { - case REQUEST_TYPE_NORMAL: return "NORMAL"; - case REQUEST_TYPE_HANDOVER: return "HANDOVER"; - } - return "UNKNOWN"; - } - - public static String releaseTypeToString(@ReleaseNetworkType int type) { - switch (type) { - case RELEASE_TYPE_NORMAL: return "NORMAL"; - case RELEASE_TYPE_DETACH: return "DETACH"; - case RELEASE_TYPE_HANDOVER: return "HANDOVER"; - } - return "UNKNOWN"; - } - - @RilRadioTechnology - protected int getDataRat() { - ServiceState ss = mPhone.getServiceState(); - NetworkRegistrationInfo nrs = ss.getNetworkRegistrationInfo( - NetworkRegistrationInfo.DOMAIN_PS, mTransportType); - if (nrs != null) { - return ServiceState.networkTypeToRilRadioTechnology(nrs.getAccessNetworkTechnology()); - } - return ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN; - } - - @RilRadioTechnology - private int getVoiceRat() { - ServiceState ss = mPhone.getServiceState(); - NetworkRegistrationInfo nrs = ss.getNetworkRegistrationInfo( - NetworkRegistrationInfo.DOMAIN_CS, mTransportType); - if (nrs != null) { - return ServiceState.networkTypeToRilRadioTechnology(nrs.getAccessNetworkTechnology()); - } - return ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN; - } - - private void read5GConfiguration() { - if (DBG) log("read5GConfiguration"); - String[] bandwidths = CarrierConfigManager.getDefaultConfig().getStringArray( - CarrierConfigManager.KEY_BANDWIDTH_STRING_ARRAY); - boolean useLte = false; - CarrierConfigManager configManager = (CarrierConfigManager) mPhone.getContext() - .getSystemService(Context.CARRIER_CONFIG_SERVICE); - if (configManager != null) { - PersistableBundle b = configManager.getConfigForSubId(mPhone.getSubId()); - if (b != null) { - if (b.getStringArray(CarrierConfigManager.KEY_BANDWIDTH_STRING_ARRAY) != null) { - bandwidths = b.getStringArray(CarrierConfigManager.KEY_BANDWIDTH_STRING_ARRAY); - } - useLte = b.getBoolean(CarrierConfigManager - .KEY_BANDWIDTH_NR_NSA_USE_LTE_VALUE_FOR_UPLINK_BOOL); - mWatchdogTimeMs = b.getLong(CarrierConfigManager.KEY_5G_WATCHDOG_TIME_MS_LONG); - mNrNsaAllUnmetered = b.getBoolean(CarrierConfigManager.KEY_UNMETERED_NR_NSA_BOOL); - mNrNsaMmwaveUnmetered = b.getBoolean( - CarrierConfigManager.KEY_UNMETERED_NR_NSA_MMWAVE_BOOL); - mNrNsaSub6Unmetered = b.getBoolean( - CarrierConfigManager.KEY_UNMETERED_NR_NSA_SUB6_BOOL); - mNrSaAllUnmetered = b.getBoolean(CarrierConfigManager.KEY_UNMETERED_NR_SA_BOOL); - mNrSaMmwaveUnmetered = b.getBoolean( - CarrierConfigManager.KEY_UNMETERED_NR_SA_MMWAVE_BOOL); - mNrSaSub6Unmetered = b.getBoolean( - 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 status (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 - */ - public void registerForPhysicalLinkStatusChanged(Handler h, int what) { - mDcc.registerForPhysicalLinkStatusChanged(h, what); - } - - /** - * Unregister from physical link status (i.e. RRC state) changed event. - * - * @param h The previously registered handler - */ - public void unregisterForPhysicalLinkStatusChanged(Handler h) { - mDcc.unregisterForPhysicalLinkStatusChanged(h); - } - - // We use a specialized equals function in Apn setting when checking if an active - // data connection is still legitimate to use against a different apn setting. - // This method is extracted to a function to ensure that any future changes to this check will - // be applied to both cleanUpConnectionsOnUpdatedApns and checkForCompatibleDataConnection. - // Fix for b/158908392. - private boolean areCompatible(ApnSetting apnSetting1, ApnSetting apnSetting2) { - return apnSetting1.equals(apnSetting2, - mPhone.getServiceState().getDataRoamingFromRegistration()); - } - - @NonNull - private PersistableBundle getCarrierConfig() { - CarrierConfigManager configManager = (CarrierConfigManager) mPhone.getContext() - .getSystemService(Context.CARRIER_CONFIG_SERVICE); - if (configManager != null) { - // If an invalid subId is used, this bundle will contain default values. - PersistableBundle config = configManager.getConfigForSubId(mPhone.getSubId()); - if (config != null) { - return config; - } - } - // Return static default defined in CarrierConfigManager. - return CarrierConfigManager.getDefaultConfig(); - } - - /** - * @return The data service manager. - */ - public @NonNull DataServiceManager getDataServiceManager() { - return mDataServiceManager; - } - - /** - * @return The data throttler - */ - public @NonNull DataThrottler getDataThrottler() { - return mDataThrottler; - } - - private void showProvisioningNotification() { - final Intent intent = new Intent(DcTracker.INTENT_PROVISION); - intent.putExtra(DcTracker.EXTRA_PROVISION_PHONE_ID, mPhone.getPhoneId()); - final PendingIntent pendingIntent = PendingIntent.getBroadcast( - mPhone.getContext(), 0 /* requestCode */, intent, PendingIntent.FLAG_IMMUTABLE); - - final Resources r = mPhone.getContext().getResources(); - final String title = r.getString(R.string.network_available_sign_in, 0); - final String details = mTelephonyManager.getNetworkOperator(mPhone.getSubId()); - final Notification.Builder builder = new Notification.Builder(mPhone.getContext()) - .setWhen(System.currentTimeMillis()) - .setSmallIcon(R.drawable.stat_notify_rssi_in_range) - .setChannelId(NotificationChannelController.CHANNEL_ID_MOBILE_DATA_STATUS) - .setAutoCancel(true) - .setTicker(title) - .setColor(mPhone.getContext().getColor( - com.android.internal.R.color.system_notification_accent_color)) - .setContentTitle(title) - .setContentText(details) - .setContentIntent(pendingIntent) - .setLocalOnly(true) - .setOnlyAlertOnce(true); - - final Notification notification = builder.build(); - try { - getNotificationManager().notify(NOTIFICATION_TAG, mPhone.getPhoneId(), notification); - } catch (final NullPointerException npe) { - Log.e(mLogTag, "showProvisioningNotification: error showing notification", npe); - } - } - - private void hideProvisioningNotification() { - try { - getNotificationManager().cancel(NOTIFICATION_TAG, mPhone.getPhoneId()); - } catch (final NullPointerException npe) { - Log.e(mLogTag, "hideProvisioningNotification: error hiding notification", npe); - } - } - - private NotificationManager getNotificationManager() { - return (NotificationManager) mPhone.getContext() - .createContextAsUser(UserHandle.ALL, 0 /* flags */) - .getSystemService(Context.NOTIFICATION_SERVICE); - } -}
diff --git a/src/java/com/android/internal/telephony/dataconnection/README.txt b/src/java/com/android/internal/telephony/dataconnection/README.txt deleted file mode 100644 index e613a00..0000000 --- a/src/java/com/android/internal/telephony/dataconnection/README.txt +++ /dev/null
@@ -1,71 +0,0 @@ -This package contains classes used to manage a DataConnection. - -A criticial aspect of this class is that most objects in this -package run on the same thread except DataConnectionTracker -This makes processing efficient as it minimizes context -switching and it eliminates issues with multi-threading. - -This can be done because all actions are either asynchronous -or are known to be non-blocking and fast. At this time only -DcTesterDeactivateAll takes specific advantage of this -single threading knowledge by using Dcc#mDcListAll so be -very careful when making changes that break this assumption. - -A related change was in DataConnectionAc I added code that -checks to see if the caller is on a different thread. If -it is then the AsyncChannel#sendMessageSynchronously is -used. If the caller is on the same thread then a getter -is used. This allows the DCAC to be used from any thread -and was required to fix a bug when Dcc called -PhoneBase#notifyDataConnection which calls DCT#getLinkProperties -and DCT#getLinkCapabilities which call Dcc all on the same -thread. Without this change there was a dead lock when -sendMessageSynchronously blocks. - - -== Testing == - -The following are Intents that can be sent for testing pruproses on -DEBUGGABLE builds (userdebug, eng) - -*) Causes bringUp and retry requests to fail for all DC's - - adb shell am broadcast -a com.android.internal.telephony.dataconnection.action_fail_bringup --ei counter 2 --ei fail_cause -3 - -*) Causes all DC's to get torn down, simulating a temporary network outage: - - adb shell am broadcast -a com.android.internal.telephony.dataconnection.action_deactivate_all - -*) To simplify testing we also have detach and attach simulations below where {x} is gsm, cdma or sip - - adb shell am broadcast -a com.android.internal.telephony.{x}.action_detached - adb shell am broadcast -a com.android.internal.telephony.{x}.action_attached - - -== System properties for Testing == - -On debuggable builds (userdebug, eng) you can change additional -settings through system properties. These properties can be set with -"setprop" for the current boot, or added to local.prop to persist -across boots. - -device# setprop key value - -device# echo "key=value" >> /data/local.prop -device# chmod 644 /data/local.prop - - --- Retry configuration -- - -You can replace the connection retry configuration. For example, you -could change it to perform 4 retries at 5 second intervals: - -device# setprop test.data_retry_config "5000,5000,5000" - - --- Roaming -- - -You can force the telephony stack to always assume that it's roaming -to verify higher-level framework functionality: - -device# setprop telephony.test.forceRoaming true
diff --git a/src/java/com/android/internal/telephony/dataconnection/TransportManager.java b/src/java/com/android/internal/telephony/dataconnection/TransportManager.java deleted file mode 100644 index 63358f4..0000000 --- a/src/java/com/android/internal/telephony/dataconnection/TransportManager.java +++ /dev/null
@@ -1,279 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.internal.telephony.dataconnection; - -import android.annotation.Nullable; -import android.os.Handler; -import android.os.Message; -import android.os.RegistrantList; -import android.telephony.AccessNetworkConstants; -import android.telephony.Annotation.ApnType; -import android.telephony.CarrierConfigManager; -import android.telephony.data.ApnSetting; -import android.util.LocalLog; -import android.util.SparseIntArray; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.telephony.Phone; -import com.android.internal.telephony.data.AccessNetworksManager; -import com.android.internal.telephony.data.TelephonyNetworkFactory; -import com.android.telephony.Rlog; - -import java.util.concurrent.TimeUnit; - -/** - * This class represents the transport manager which manages available transports (i.e. WWAN or - * WLAN) and determine the correct transport for {@link TelephonyNetworkFactory} to handle the data - * requests. - * - * The device can operate in the following modes, which is stored in the system properties - * ro.telephony.iwlan_operation_mode. If the system properties is missing, then it's tied to - * IRadio version. For 1.4 or above, it's AP-assisted mdoe. For 1.3 or below, it's legacy mode. - * - * Legacy mode: - * Frameworks send all data requests to the default data service, which is the cellular data - * service. IWLAN should be still reported as a RAT on cellular network service. - * - * AP-assisted mode: - * IWLAN is handled by IWLAN data service extending {@link android.telephony.data.DataService}, - * IWLAN network service extending {@link android.telephony.NetworkService}, and qualified - * network service extending {@link android.telephony.data.QualifiedNetworksService}. - * - * The following settings for service package name need to be configured properly for - * frameworks to bind. - * - * Package name of data service: - * The resource overlay 'config_wlan_data_service_package' or, - * the carrier config - * {@link CarrierConfigManager#KEY_CARRIER_DATA_SERVICE_WLAN_PACKAGE_OVERRIDE_STRING}. - * The carrier config takes precedence over the resource overlay if both exist. - * - * Package name of network service - * The resource overlay 'config_wlan_network_service_package' or - * the carrier config - * {@link CarrierConfigManager#KEY_CARRIER_NETWORK_SERVICE_WLAN_PACKAGE_OVERRIDE_STRING}. - * The carrier config takes precedence over the resource overlay if both exist. - * - * Package name of qualified network service - * The resource overlay 'config_qualified_networks_service_package' or - * the carrier config - * {@link CarrierConfigManager# - * KEY_CARRIER_QUALIFIED_NETWORKS_SERVICE_PACKAGE_OVERRIDE_STRING}. - * The carrier config takes precedence over the resource overlay if both exist. - */ -public class TransportManager extends Handler { - private final String mLogTag; - - private static final int EVENT_QUALIFIED_NETWORKS_CHANGED = 1; - - private static final int EVENT_EVALUATE_TRANSPORT_PREFERENCE = 2; - - // Delay the re-evaluation if transport fall back. QNS will need to quickly change the - // preference back to the original transport to avoid another handover request. - private static final long FALL_BACK_REEVALUATE_DELAY_MILLIS = TimeUnit.SECONDS.toMillis(3); - - private final Phone mPhone; - - private final LocalLog mLocalLog = new LocalLog(64); - - @Nullable - private AccessNetworksManager mAccessNetworksManager; - - /** - * The pending handover list. This is a list of APNs that are being handover to the new - * transport. The entry will be removed once handover is completed. The key - * is the APN type, and the value is the target transport that the APN is handovered to. - */ - private final SparseIntArray mPendingHandoverApns; - - /** - * The registrants for listening data handover needed events. - */ - private final RegistrantList mHandoverNeededEventRegistrants; - - /** - * Handover parameters - */ - @VisibleForTesting - public static final class HandoverParams { - /** - * The callback for handover complete. - */ - public interface HandoverCallback { - /** - * Called when handover is completed. - * - * @param success {@true} if handover succeeded, otherwise failed. - * @param fallback {@true} if handover failed, the data connection fallback to the - * original transport - */ - void onCompleted(boolean success, boolean fallback); - } - - public final @ApnType int apnType; - public final int targetTransport; - public final HandoverCallback callback; - - @VisibleForTesting - public HandoverParams(int apnType, int targetTransport, HandoverCallback callback) { - this.apnType = apnType; - this.targetTransport = targetTransport; - this.callback = callback; - } - } - - public TransportManager(Phone phone) { - mPhone = phone; - mPendingHandoverApns = new SparseIntArray(); - mHandoverNeededEventRegistrants = new RegistrantList(); - mLogTag = TransportManager.class.getSimpleName() + "-" + mPhone.getPhoneId(); - mAccessNetworksManager = mPhone.getAccessNetworksManager(); - mAccessNetworksManager.registerForQualifiedNetworksChanged(this, - EVENT_QUALIFIED_NETWORKS_CHANGED); - } - - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case EVENT_QUALIFIED_NETWORKS_CHANGED: - if (!hasMessages(EVENT_EVALUATE_TRANSPORT_PREFERENCE)) { - sendEmptyMessage(EVENT_EVALUATE_TRANSPORT_PREFERENCE); - } - break; - case EVENT_EVALUATE_TRANSPORT_PREFERENCE: - evaluateTransportPreference(); - break; - default: - loge("Unexpected event " + msg.what); - break; - } - } - - /** - * Set the current transport of apn type. - * - * @param apnType The APN type - * @param transport The transport. Must be WWAN or WLAN. - */ - private synchronized void setCurrentTransport(@ApnType int apnType, int transport) { - mAccessNetworksManager.setCurrentTransport(apnType, transport); - } - - private boolean isHandoverPending() { - return mPendingHandoverApns.size() > 0; - } - - /** - * Evaluate the preferred transport for each APN type to see if handover is needed. - */ - private void evaluateTransportPreference() { - // Simultaneously handover is not supported today. Preference will be re-evaluated after - // handover completed. - if (isHandoverPending()) return; - logl("evaluateTransportPreference"); - for (int apnType : AccessNetworksManager.SUPPORTED_APN_TYPES) { - int targetTransport = mAccessNetworksManager.getPreferredTransport(apnType); - if (targetTransport != mAccessNetworksManager.getCurrentTransport(apnType)) { - logl("Handover started for APN type: " - + ApnSetting.getApnTypeString(apnType) - + ", target transport: " - + AccessNetworkConstants.transportTypeToString(targetTransport)); - mPendingHandoverApns.put(apnType, targetTransport); - mHandoverNeededEventRegistrants.notifyResult( - new HandoverParams(apnType, targetTransport, - (success, fallback) -> { - // The callback for handover completed. - if (success) { - logl("Handover succeeded for APN type " - + ApnSetting.getApnTypeString(apnType)); - } else { - logl("APN type " - + ApnSetting.getApnTypeString(apnType) - + " handover to " - + AccessNetworkConstants.transportTypeToString( - targetTransport) + " failed" - + ", fallback=" + fallback); - } - - long delay = 0; - if (fallback) { - // No need to change the preference because we should - // fallback. Re-evaluate after few seconds to give QNS - // some time to change the preference back to the original - // transport. - delay = FALL_BACK_REEVALUATE_DELAY_MILLIS; - } else { - // If handover succeeds or failed without falling back - // to the original transport, we should move to the new - // transport (even if it is failed). - setCurrentTransport(apnType, targetTransport); - } - mPendingHandoverApns.delete(apnType); - sendEmptyMessageDelayed(EVENT_EVALUATE_TRANSPORT_PREFERENCE, - delay); - })); - - // Return here instead of processing the next APN type. The next APN type for - // handover will be evaluate again once current handover is completed. - return; - } - } - } - - /** - * Register for data handover needed event - * - * @param h The handler of the event - * @param what The id of the event - */ - public void registerForHandoverNeededEvent(Handler h, int what) { - if (h != null) { - mHandoverNeededEventRegistrants.addUnique(h, what, null); - } - } - - /** - * Unregister for data handover needed event - * - * @param h The handler - */ - public void unregisterForHandoverNeededEvent(Handler h) { - mHandoverNeededEventRegistrants.remove(h); - } - - /** - * Registers the data throttler with DcTracker. - */ - public void registerDataThrottler(DataThrottler dataThrottler) { - if (mAccessNetworksManager != null) { - mAccessNetworksManager.registerDataThrottler(dataThrottler); - } - } - - private void logl(String s) { - log(s); - mLocalLog.log(s); - } - - private void log(String s) { - Rlog.d(mLogTag, s); - } - - private void loge(String s) { - Rlog.e(mLogTag, s); - } -}
diff --git a/src/java/com/android/internal/telephony/domainselection/DomainSelectionConnection.java b/src/java/com/android/internal/telephony/domainselection/DomainSelectionConnection.java new file mode 100644 index 0000000..9a75b43 --- /dev/null +++ b/src/java/com/android/internal/telephony/domainselection/DomainSelectionConnection.java
@@ -0,0 +1,467 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.telephony.domainselection; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.AsyncResult; +import android.os.CancellationSignal; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.Looper; +import android.os.Message; +import android.telephony.AccessNetworkConstants.RadioAccessNetworkType; +import android.telephony.Annotation.DisconnectCauses; +import android.telephony.DomainSelectionService; +import android.telephony.DomainSelectionService.EmergencyScanType; +import android.telephony.DomainSelector; +import android.telephony.EmergencyRegResult; +import android.telephony.NetworkRegistrationInfo; +import android.telephony.TransportSelectorCallback; +import android.telephony.WwanSelectorCallback; +import android.util.LocalLog; +import android.util.Log; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.infra.AndroidFuture; +import com.android.internal.telephony.Phone; +import com.android.internal.telephony.util.TelephonyUtils; + +import java.io.PrintWriter; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import java.util.function.Consumer; + + +/** + * Manages the information of request and the callback binder. + */ +public class DomainSelectionConnection { + + private static final boolean DBG = TelephonyUtils.IS_DEBUGGABLE; + + protected static final int EVENT_EMERGENCY_NETWORK_SCAN_RESULT = 1; + protected static final int EVENT_QUALIFIED_NETWORKS_CHANGED = 2; + + /** Callback to receive responses from DomainSelectionConnection. */ + public interface DomainSelectionConnectionCallback { + /** + * Notifies that selection has terminated because there is no decision that can be made + * or a timeout has occurred. The call should be terminated when this method is called. + * + * @param cause Indicates the reason. + */ + void onSelectionTerminated(@DisconnectCauses int cause); + } + + /** An internal class implementing {@link TransportSelectorCallback} interface. */ + private final class TransportSelectorCallbackWrapper implements TransportSelectorCallback { + @Override + public void onCreated(@NonNull DomainSelector selector) { + mDomainSelector = selector; + DomainSelectionConnection.this.onCreated(); + } + + @Override + public void onWlanSelected(boolean useEmergencyPdn) { + DomainSelectionConnection.this.onWlanSelected(useEmergencyPdn); + } + + @Override + public @NonNull WwanSelectorCallback onWwanSelected() { + if (mWwanSelectorCallback == null) { + mWwanSelectorCallback = new WwanSelectorCallbackWrapper(); + } + DomainSelectionConnection.this.onWwanSelected(); + return mWwanSelectorCallback; + } + + @Override + public void onWwanSelected(final Consumer<WwanSelectorCallback> consumer) { + if (mWwanSelectorCallback == null) { + mWwanSelectorCallback = new WwanSelectorCallbackWrapper(); + } + if (mWwanSelectedExecutor == null) { + mWwanSelectedExecutor = Executors.newSingleThreadExecutor(); + } + mWwanSelectedExecutor.execute(() -> { + DomainSelectionConnection.this.onWwanSelected(); + consumer.accept(mWwanSelectorCallback); + }); + } + + @Override + public void onSelectionTerminated(int cause) { + DomainSelectionConnection.this.onSelectionTerminated(cause); + dispose(); + } + } + + /** An internal class implementing {@link WwanSelectorCallback} interface. */ + private final class WwanSelectorCallbackWrapper + implements WwanSelectorCallback, CancellationSignal.OnCancelListener { + @Override + public void onRequestEmergencyNetworkScan(@NonNull List<Integer> preferredNetworks, + @EmergencyScanType int scanType, @NonNull CancellationSignal signal, + @NonNull Consumer<EmergencyRegResult> consumer) { + if (signal != null) signal.setOnCancelListener(this); + mResultCallback = consumer; + initHandler(); + DomainSelectionConnection.this.onRequestEmergencyNetworkScan( + preferredNetworks.stream().mapToInt(Integer::intValue).toArray(), scanType); + } + + @Override + public void onDomainSelected(@NetworkRegistrationInfo.Domain int domain, + boolean useEmergencyPdn) { + DomainSelectionConnection.this.onDomainSelected(domain, useEmergencyPdn); + } + + @Override + public void onCancel() { + DomainSelectionConnection.this.onCancel(); + } + } + + protected final class DomainSelectionConnectionHandler extends Handler { + DomainSelectionConnectionHandler(Looper looper) { + super(looper); + } + + @Override + public void handleMessage(Message msg) { + AsyncResult ar; + switch (msg.what) { + case EVENT_EMERGENCY_NETWORK_SCAN_RESULT: + mIsWaitingForScanResult = false; + if (mResultCallback == null) break; + ar = (AsyncResult) msg.obj; + EmergencyRegResult regResult = (EmergencyRegResult) ar.result; + if (DBG) logd("EVENT_EMERGENCY_NETWORK_SCAN_RESULT result=" + regResult); + CompletableFuture.runAsync( + () -> mResultCallback.accept(regResult), + mController.getDomainSelectionServiceExecutor()).join(); + break; + case EVENT_QUALIFIED_NETWORKS_CHANGED: + onQualifiedNetworksChanged(); + break; + default: + loge("handleMessage unexpected msg=" + msg.what); + break; + } + } + } + + protected String mTag = "DomainSelectionConnection"; + + private final LocalLog mLocalLog = new LocalLog(30); + private final @NonNull TransportSelectorCallback mTransportSelectorCallback; + + /** + * Controls the communication between {@link DomainSelectionConnection} and + * {@link DomainSelectionService}. + */ + private final @NonNull DomainSelectionController mController; + /** Indicates whether the requested service is for emergency services. */ + private final boolean mIsEmergency; + + /** Interface to receive the request to trigger emergency network scan and selected domain. */ + private @Nullable WwanSelectorCallback mWwanSelectorCallback; + /** Interface to return the result of emergency network scan. */ + private @Nullable Consumer<EmergencyRegResult> mResultCallback; + /** Interface to the {@link DomainSelector} created for this service. */ + private @Nullable DomainSelector mDomainSelector; + + /** The slot requested this connection. */ + protected @NonNull Phone mPhone; + /** The requested domain selector type. */ + private @DomainSelectionService.SelectorType int mSelectorType; + + /** The attributes required to determine the domain. */ + private @Nullable DomainSelectionService.SelectionAttributes mSelectionAttributes; + + private @Nullable Looper mLooper; + protected @Nullable DomainSelectionConnectionHandler mHandler; + private boolean mRegisteredRegistrant; + private boolean mIsWaitingForScanResult; + + private @NonNull AndroidFuture<Integer> mOnComplete; + + private @Nullable Executor mWwanSelectedExecutor; + + /** + * Creates an instance. + * + * @param phone For which this service is requested. + * @param selectorType Indicates the type of the requested service. + * @param isEmergency Indicates whether this request is for emergency service. + * @param controller The controller to communicate with the domain selection service. + */ + public DomainSelectionConnection(@NonNull Phone phone, + @DomainSelectionService.SelectorType int selectorType, boolean isEmergency, + @NonNull DomainSelectionController controller) { + mController = controller; + mPhone = phone; + mSelectorType = selectorType; + mIsEmergency = isEmergency; + + mTransportSelectorCallback = new TransportSelectorCallbackWrapper(); + mOnComplete = new AndroidFuture<>(); + } + + /** + * Returns the attributes required to determine the domain for a telephony service. + * + * @return The attributes required to determine the domain. + */ + public @Nullable DomainSelectionService.SelectionAttributes getSelectionAttributes() { + return mSelectionAttributes; + } + + /** + * Returns the interface for the callbacks. + * + * @return The {@link TransportSelectorCallback} interface. + */ + @VisibleForTesting + public @NonNull TransportSelectorCallback getTransportSelectorCallback() { + return mTransportSelectorCallback; + } + + /** + * Returns the {@link CompletableFuture} to receive the selected domain. + * + * @return The callback to receive response. + */ + public @NonNull CompletableFuture<Integer> getCompletableFuture() { + return mOnComplete; + } + + /** + * Returs the {@link Phone} which requested this connection. + * + * @return The {@link Phone} instance. + */ + public @NonNull Phone getPhone() { + return mPhone; + } + + /** + * Requests the domain selection servic to select a domain. + * + * @param attr The attributes required to determine the domain. + */ + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PROTECTED) + public void selectDomain(@NonNull DomainSelectionService.SelectionAttributes attr) { + mSelectionAttributes = attr; + mController.selectDomain(attr, getTransportSelectorCallback()); + } + + /** + * Notifies that {@link DomainSelector} instance has been created for the selection request. + */ + public void onCreated() { + // Can be overridden if required + } + + /** + * Notifies that WLAN transport has been selected. + */ + public void onWlanSelected() { + // Can be overridden. + } + + /** + * Notifies that WLAN transport has been selected. + * + * @param useEmergencyPdn Indicates whether Wi-Fi emergency services use emergency PDN or not. + */ + public void onWlanSelected(boolean useEmergencyPdn) { + // Can be overridden. + onWlanSelected(); + } + + /** + * Notifies that WWAN transport has been selected. + */ + public void onWwanSelected() { + // Can be overridden. + } + + /** + * Notifies that selection has terminated because there is no decision that can be made + * or a timeout has occurred. The call should be terminated when this method is called. + * + * @param cause Indicates the reason. + */ + public void onSelectionTerminated(@DisconnectCauses int cause) { + // Can be overridden. + } + + /** + * Requests the emergency network scan. + * + * @param preferredNetworks The ordered list of preferred networks to scan. + * @param scanType Indicates the scan preference, such as full service or limited service. + */ + public void onRequestEmergencyNetworkScan( + @NonNull @RadioAccessNetworkType int[] preferredNetworks, + @EmergencyScanType int scanType) { + // Can be overridden if required + if (!mRegisteredRegistrant) { + mPhone.registerForEmergencyNetworkScan(mHandler, + EVENT_EMERGENCY_NETWORK_SCAN_RESULT, null); + mRegisteredRegistrant = true; + } + mIsWaitingForScanResult = true; + mPhone.triggerEmergencyNetworkScan(preferredNetworks, scanType, null); + } + + /** + * Notifies the domain selected. + * + * @param domain The selected domain. + */ + public void onDomainSelected(@NetworkRegistrationInfo.Domain int domain) { + // Can be overridden if required + CompletableFuture<Integer> future = getCompletableFuture(); + future.complete(domain); + } + + /** + * Notifies the domain selected. + * + * @param domain The selected domain. + * @param useEmergencyPdn Indicates whether emergency services use emergency PDN or not. + */ + public void onDomainSelected(@NetworkRegistrationInfo.Domain int domain, + boolean useEmergencyPdn) { + // Can be overridden if required + onDomainSelected(domain); + } + + /** + * Notifies that the emergency network scan is canceled. + */ + public void onCancel() { + // Can be overridden if required + onCancel(false); + } + + private void onCancel(boolean resetScan) { + if (mIsWaitingForScanResult) { + mIsWaitingForScanResult = false; + mPhone.cancelEmergencyNetworkScan(resetScan, null); + } + } + + /** + * Cancels an ongoing selection operation. It is up to the {@link DomainSelectionService} + * to clean up all ongoing operations with the framework. + */ + public void cancelSelection() { + if (mDomainSelector == null) return; + mDomainSelector.cancelSelection(); + dispose(); + } + + /** + * Requests the domain selection service to reselect a domain. + * + * @param attr The attributes required to determine the domain. + * @return The callback to receive the response. + */ + public @NonNull CompletableFuture<Integer> reselectDomain( + @NonNull DomainSelectionService.SelectionAttributes attr) { + mSelectionAttributes = attr; + if (mDomainSelector == null) return null; + mOnComplete = new AndroidFuture<>(); + mDomainSelector.reselectDomain(attr); + return mOnComplete; + } + + /** + * Finishes the selection procedure and cleans everything up. + */ + public void finishSelection() { + if (mDomainSelector == null) return; + mDomainSelector.finishSelection(); + dispose(); + } + + /** Indicates that the service connection has been removed. */ + public void onServiceDisconnected() { + // Can be overridden. + dispose(); + } + + private void dispose() { + if (mRegisteredRegistrant) { + mPhone.unregisterForEmergencyNetworkScan(mHandler); + mRegisteredRegistrant = false; + } + onCancel(true); + mController.removeConnection(this); + if (mLooper != null) mLooper.quitSafely(); + mLooper = null; + mHandler = null; + } + + protected void initHandler() { + if (mLooper == null) { + HandlerThread handlerThread = new HandlerThread(mTag); + handlerThread.start(); + mLooper = handlerThread.getLooper(); + } + if (mHandler == null) mHandler = new DomainSelectionConnectionHandler(mLooper); + } + + /** + * Notifies the change of qualified networks. + */ + protected void onQualifiedNetworksChanged() { + if (mIsEmergency + && (mSelectorType == DomainSelectionService.SELECTOR_TYPE_CALLING)) { + // DomainSelectionConnection for emergency calls shall override this. + throw new IllegalStateException("DomainSelectionConnection for emergency calls" + + " should override onQualifiedNetworksChanged()"); + } + } + + /** + * Dumps local log. + */ + public void dump(@NonNull PrintWriter printWriter) { + mLocalLog.dump(printWriter); + } + + protected void logd(String msg) { + Log.d(mTag, msg); + } + + protected void logi(String msg) { + Log.i(mTag, msg); + mLocalLog.log(msg); + } + + protected void loge(String msg) { + Log.e(mTag, msg); + mLocalLog.log(msg); + } +}
diff --git a/src/java/com/android/internal/telephony/domainselection/DomainSelectionController.java b/src/java/com/android/internal/telephony/domainselection/DomainSelectionController.java new file mode 100644 index 0000000..52c9960 --- /dev/null +++ b/src/java/com/android/internal/telephony/domainselection/DomainSelectionController.java
@@ -0,0 +1,299 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.telephony.domainselection; + +import static android.telephony.DomainSelectionService.SELECTOR_TYPE_CALLING; +import static android.telephony.DomainSelectionService.SELECTOR_TYPE_SMS; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.Context; +import android.os.AsyncResult; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.Looper; +import android.os.Message; +import android.telephony.BarringInfo; +import android.telephony.DomainSelectionService; +import android.telephony.ServiceState; +import android.telephony.TelephonyManager; +import android.telephony.TransportSelectorCallback; +import android.util.LocalLog; +import android.util.Log; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.telephony.Phone; +import com.android.internal.telephony.util.TelephonyUtils; + +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.concurrent.Executor; + +/** + * Manages the connection to {@link DomainSelectionService}. + */ +public class DomainSelectionController { + private static final String TAG = "DomainSelectionController"; + private static final boolean DBG = TelephonyUtils.IS_DEBUGGABLE; + + private static final int EVENT_SERVICE_STATE_CHANGED = 1; + private static final int EVENT_BARRING_INFO_CHANGED = 2; + + private final HandlerThread mHandlerThread = + new HandlerThread("DomainSelectionControllerHandler"); + + private final DomainSelectionService mDomainSelectionService; + private final Handler mHandler; + // Only added or removed, never accessed on purpose. + private final LocalLog mLocalLog = new LocalLog(30); + + protected final Object mLock = new Object(); + protected final Context mContext; + + protected final int[] mConnectionCounts; + private final ArrayList<DomainSelectionConnection> mConnections = new ArrayList<>(); + + private final class DomainSelectionControllerHandler extends Handler { + DomainSelectionControllerHandler(Looper looper) { + super(looper); + } + + @Override + public void handleMessage(Message msg) { + AsyncResult ar; + switch (msg.what) { + case EVENT_SERVICE_STATE_CHANGED: + ar = (AsyncResult) msg.obj; + updateServiceState((Phone) ar.userObj, (ServiceState) ar.result); + break; + case EVENT_BARRING_INFO_CHANGED: + ar = (AsyncResult) msg.obj; + updateBarringInfo((Phone) ar.userObj, (BarringInfo) ar.result); + break; + default: + loge("unexpected event=" + msg.what); + break; + } + } + } + + /** + * Creates an instance. + * + * @param context Context object from hosting application. + * @param service The {@link DomainSelectionService} instance. + */ + public DomainSelectionController(@NonNull Context context, + @NonNull DomainSelectionService service) { + this(context, service, null); + } + + /** + * Creates an instance. + * + * @param context Context object from hosting application. + * @param service The {@link DomainSelectionService} instance. + * @param looper Handles event messages. + */ + @VisibleForTesting + public DomainSelectionController(@NonNull Context context, + @NonNull DomainSelectionService service, @Nullable Looper looper) { + mContext = context; + mDomainSelectionService = service; + + if (looper == null) { + mHandlerThread.start(); + looper = mHandlerThread.getLooper(); + } + mHandler = new DomainSelectionControllerHandler(looper); + + int numPhones = TelephonyManager.getDefault().getActiveModemCount(); + mConnectionCounts = new int[numPhones]; + for (int i = 0; i < numPhones; i++) { + mConnectionCounts[i] = 0; + } + } + + /** + * Returns a {@link DomainSelectionConnection} instance. + * + * @param phone Indicates who requests the service. + * @param selectorType Indicates the selector type requested. + * @param isEmergency Indicates whether this is for emergency service. + * @return A {@link DomainSelectiionConnection} instance for the requested service. + * Returns {@code null} if the requested service is not supported. + */ + public @Nullable DomainSelectionConnection getDomainSelectionConnection( + @NonNull Phone phone, + @DomainSelectionService.SelectorType int selectorType, + boolean isEmergency) { + DomainSelectionConnection c = null; + + if (selectorType == SELECTOR_TYPE_CALLING) { + if (isEmergency) { + c = new EmergencyCallDomainSelectionConnection(phone, this); + } else { + c = new NormalCallDomainSelectionConnection(phone, this); + } + } else if (selectorType == SELECTOR_TYPE_SMS) { + if (isEmergency) { + c = new EmergencySmsDomainSelectionConnection(phone, this); + } else { + c = new SmsDomainSelectionConnection(phone, this); + } + } + + addConnection(c); + return c; + } + + private void addConnection(@Nullable DomainSelectionConnection c) { + if (c == null) return; + mConnections.add(c); + registerForStateChange(c); + } + + /** + * Releases resources for this connection. + */ + public void removeConnection(@Nullable DomainSelectionConnection c) { + if (c == null) return; + mConnections.remove(c); + unregisterForStateChange(c); + } + + /** + * Requests the domain selection. + * + * @param attr Attributetes required to determine the domain. + * @param callback A callback to receive the response. + */ + public void selectDomain(@NonNull DomainSelectionService.SelectionAttributes attr, + @NonNull TransportSelectorCallback callback) { + if (attr == null || callback == null) return; + if (DBG) logd("selectDomain"); + + Executor e = mDomainSelectionService.getCachedExecutor(); + e.execute(() -> mDomainSelectionService.onDomainSelection(attr, callback)); + } + + /** + * Notifies the change in {@link ServiceState} for a specific slot. + * + * @param phone {@link Phone} which the state changed. + * @param serviceState Updated {@link ServiceState}. + */ + private void updateServiceState(Phone phone, ServiceState serviceState) { + if (phone == null || serviceState == null) return; + if (DBG) logd("updateServiceState phoneId=" + phone.getPhoneId()); + + Executor e = mDomainSelectionService.getCachedExecutor(); + e.execute(() -> mDomainSelectionService.onServiceStateUpdated( + phone.getPhoneId(), phone.getSubId(), serviceState)); + } + + /** + * Notifies the change in {@link BarringInfo} for a specific slot. + * + * @param phone {@link Phone} which the state changed. + * @param info Updated {@link BarringInfo}. + */ + private void updateBarringInfo(Phone phone, BarringInfo info) { + if (phone == null || info == null) return; + if (DBG) logd("updateBarringInfo phoneId=" + phone.getPhoneId()); + + Executor e = mDomainSelectionService.getCachedExecutor(); + e.execute(() -> mDomainSelectionService.onBarringInfoUpdated( + phone.getPhoneId(), phone.getSubId(), info)); + } + + /** + * Registers for the notification of {@link ServiceState} and {@link BarringInfo}. + * + * @param c {@link DomainSelectionConnection} for which the registration is requested. + */ + private void registerForStateChange(DomainSelectionConnection c) { + Phone phone = c.getPhone(); + int count = mConnectionCounts[phone.getPhoneId()]; + if (count < 0) count = 0; + + mConnectionCounts[phone.getPhoneId()] = count + 1; + if (count > 0) return; + + phone.registerForServiceStateChanged(mHandler, EVENT_SERVICE_STATE_CHANGED, phone); + phone.mCi.registerForBarringInfoChanged(mHandler, EVENT_BARRING_INFO_CHANGED, phone); + + updateServiceState(phone, phone.getServiceStateTracker().getServiceState()); + updateBarringInfo(phone, phone.mCi.getLastBarringInfo()); + } + + /** + * Unregisters for the notification of {@link ServiceState} and {@link BarringInfo}. + * + * @param c {@link DomainSelectionConnection} for which the unregistration is requested. + */ + private void unregisterForStateChange(DomainSelectionConnection c) { + Phone phone = c.getPhone(); + int count = mConnectionCounts[phone.getPhoneId()]; + if (count < 1) count = 1; + + mConnectionCounts[phone.getPhoneId()] = count - 1; + if (count > 1) return; + + phone.unregisterForServiceStateChanged(mHandler); + phone.mCi.unregisterForBarringInfoChanged(mHandler); + } + + /** + * Notifies the {@link DomainSelectionConnection} instances registered + * of the service disconnection. + */ + private void notifyServiceDisconnected() { + for (DomainSelectionConnection c : mConnections) { + c.onServiceDisconnected(); + } + } + + /** + * Gets the {@link Executor} which executes methods of {@link DomainSelectionService.} + * @return {@link Executor} instance. + */ + public @NonNull Executor getDomainSelectionServiceExecutor() { + return mDomainSelectionService.getCachedExecutor(); + } + + /** + * Dumps logcal log + */ + public void dump(@NonNull PrintWriter printWriter) { + mLocalLog.dump(printWriter); + } + + private void logd(String msg) { + Log.d(TAG, msg); + } + + private void logi(String msg) { + Log.i(TAG, msg); + mLocalLog.log(msg); + } + + private void loge(String msg) { + Log.e(TAG, msg); + mLocalLog.log(msg); + } +}
diff --git a/src/java/com/android/internal/telephony/domainselection/DomainSelectionResolver.java b/src/java/com/android/internal/telephony/domainselection/DomainSelectionResolver.java new file mode 100644 index 0000000..cbb74fa --- /dev/null +++ b/src/java/com/android/internal/telephony/domainselection/DomainSelectionResolver.java
@@ -0,0 +1,207 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.telephony.domainselection; + +import static android.telephony.TelephonyManager.HAL_SERVICE_NETWORK; + +import static com.android.internal.telephony.RIL.RADIO_HAL_VERSION_2_1; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.Context; +import android.telephony.DomainSelectionService; +import android.util.IndentingPrintWriter; +import android.util.LocalLog; +import android.util.Log; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.telephony.Phone; +import com.android.internal.telephony.PhoneFactory; + +import java.io.FileDescriptor; +import java.io.PrintWriter; + +/** + * This class is an entry point to provide whether the AOSP domain selection is supported or not, + * and bind the {@link DomainSelectionController} with the given {@link DomainSelectionService} to + * provide a specific {@link DomainSelectionConnection} object for communicating with each domain + * selector. + */ +public class DomainSelectionResolver { + private static final String TAG = DomainSelectionResolver.class.getSimpleName(); + private static DomainSelectionResolver sInstance = null; + + /** + * Creates the DomainSelectionResolver singleton instance. + * + * @param context The context of the application. + * @param deviceConfigEnabled The flag to indicate whether or not the device supports + * the domain selection service or not. + */ + public static void make(Context context, boolean deviceConfigEnabled) { + if (sInstance == null) { + sInstance = new DomainSelectionResolver(context, deviceConfigEnabled); + } + } + + /** + * Returns the singleton instance of DomainSelectionResolver. + * + * @return A {@link DomainSelectionResolver} instance. + */ + public static DomainSelectionResolver getInstance() { + if (sInstance == null) { + throw new IllegalStateException("DomainSelectionResolver is not ready!"); + } + return sInstance; + } + + /** + * Sets a {@link DomainSelectionResolver} for injecting mock DomainSelectionResolver. + * + * @param resolver A {@link DomainSelectionResolver} instance to test. + */ + @VisibleForTesting + public static void setDomainSelectionResolver(DomainSelectionResolver resolver) { + sInstance = resolver; + } + + /** + * Testing interface for injecting mock DomainSelectionController. + */ + @VisibleForTesting + public interface DomainSelectionControllerFactory { + /** + * Returns a {@link DomainSelectionController} created using the specified + * context and {@link DomainSelectionService} instance. + */ + DomainSelectionController create(@NonNull Context context, + @NonNull DomainSelectionService service); + } + + private DomainSelectionControllerFactory mDomainSelectionControllerFactory = + new DomainSelectionControllerFactory() { + @Override + public DomainSelectionController create(@NonNull Context context, + @NonNull DomainSelectionService service) { + return new DomainSelectionController(context, service); + } + }; + + // Persistent Logging + private final LocalLog mEventLog = new LocalLog(10); + private final Context mContext; + // The flag to indicate whether the device supports the domain selection service or not. + private final boolean mDeviceConfigEnabled; + // DomainSelectionController, which are bound to DomainSelectionService. + private DomainSelectionController mController; + + public DomainSelectionResolver(Context context, boolean deviceConfigEnabled) { + mContext = context; + mDeviceConfigEnabled = deviceConfigEnabled; + logi("DomainSelectionResolver created: device-config=" + deviceConfigEnabled); + } + + /** + * Checks if the device supports the domain selection service to route the call / SMS / + * supplementary services to the appropriate domain. + * This checks the device-config and Radio HAL version for supporting the domain selection. + * The domain selection requires the Radio HAL version greater than or equal to 2.1. + * + * @return {@code true} if the domain selection is supported on the device, + * {@code false} otherwise. + */ + public boolean isDomainSelectionSupported() { + return mDeviceConfigEnabled && PhoneFactory.getDefaultPhone() + .getHalVersion(HAL_SERVICE_NETWORK).greaterOrEqual(RADIO_HAL_VERSION_2_1); + } + + /** + * Returns a {@link DomainSelectionConnection} instance. + * + * @param phone The Phone instance for witch this request is. + * @param selectorType Indicates the selector type requested. + * @param isEmergency Indicates whether this is for emergency service. + * @throws IllegalStateException If the {@link DomainSelectionController} is not created + * because {@link #initialize} method is not called even if the domain selection is + * supported. + * @return A {@link DomainSelectionConnection} instance if the device supports + * AOSP domain selection and IMS is available or {@code null} otherwise. + */ + public @Nullable DomainSelectionConnection getDomainSelectionConnection(Phone phone, + @DomainSelectionService.SelectorType int selectorType, boolean isEmergency) { + if (mController == null) { + // If the caller calls this method without checking whether the domain selection + // is supported or not, this exception will be thrown. + throw new IllegalStateException("DomainSelection is not supported!"); + } + + if (phone == null || !phone.isImsAvailable()) { + // If ImsPhone is null or the binder of ImsService is not available, + // CS domain is used for the telephony services. + return null; + } + + return mController.getDomainSelectionConnection(phone, selectorType, isEmergency); + } + + /** Sets a factory interface for creating {@link DomainSelectionController} instance. */ + @VisibleForTesting + public void setDomainSelectionControllerFactory(DomainSelectionControllerFactory factory) { + mDomainSelectionControllerFactory = factory; + } + + /** + * Needs to be called after the constructor to create a {@link DomainSelectionController} that + * is bound to the given {@link DomainSelectionService}. + * + * @param service A {@link DomainSelectionService} to be bound. + */ + public void initialize(@NonNull DomainSelectionService service) { + logi("Initialize."); + mController = mDomainSelectionControllerFactory.create(mContext, service); + } + + /** + * Dumps this instance into a readable format for dumpsys usage. + */ + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); + ipw.println("Resolver:"); + ipw.increaseIndent(); + ipw.println("Event Log:"); + ipw.increaseIndent(); + mEventLog.dump(ipw); + ipw.decreaseIndent(); + ipw.decreaseIndent(); + + ipw.println("Controller:"); + ipw.increaseIndent(); + DomainSelectionController controller = mController; + if (controller == null) { + ipw.println("no active controller"); + } else { + controller.dump(ipw); + } + ipw.decreaseIndent(); + } + + private void logi(String s) { + Log.i(TAG, s); + mEventLog.log(s); + } +}
diff --git a/src/java/com/android/internal/telephony/domainselection/EmergencyCallDomainSelectionConnection.java b/src/java/com/android/internal/telephony/domainselection/EmergencyCallDomainSelectionConnection.java new file mode 100644 index 0000000..5f3c3b6 --- /dev/null +++ b/src/java/com/android/internal/telephony/domainselection/EmergencyCallDomainSelectionConnection.java
@@ -0,0 +1,225 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.telephony.domainselection; + +import static android.telephony.AccessNetworkConstants.TRANSPORT_TYPE_INVALID; +import static android.telephony.AccessNetworkConstants.TRANSPORT_TYPE_WLAN; +import static android.telephony.AccessNetworkConstants.TRANSPORT_TYPE_WWAN; +import static android.telephony.DomainSelectionService.SELECTOR_TYPE_CALLING; +import static android.telephony.NetworkRegistrationInfo.DOMAIN_PS; + +import static com.android.internal.telephony.PhoneConstants.DOMAIN_NON_3GPP_PS; +import static com.android.internal.telephony.emergency.EmergencyConstants.MODE_EMERGENCY_WLAN; +import static com.android.internal.telephony.emergency.EmergencyConstants.MODE_EMERGENCY_WWAN; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.telephony.AccessNetworkConstants.TransportType; +import android.telephony.Annotation.DisconnectCauses; +import android.telephony.Annotation.NetCapability; +import android.telephony.DomainSelectionService; +import android.telephony.EmergencyRegResult; +import android.telephony.NetworkRegistrationInfo; +import android.telephony.data.ApnSetting; +import android.telephony.ims.ImsReasonInfo; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.telephony.Phone; +import com.android.internal.telephony.data.AccessNetworksManager; +import com.android.internal.telephony.emergency.EmergencyStateTracker; + +import java.util.concurrent.CompletableFuture; + +/** + * Manages the information of request and the callback binder for emergency calling. + */ +public class EmergencyCallDomainSelectionConnection extends DomainSelectionConnection { + + private static final boolean DBG = false; + + private @NonNull EmergencyStateTracker mEmergencyStateTracker = null; + private @Nullable DomainSelectionConnectionCallback mCallback; + private @TransportType int mPreferredTransportType = TRANSPORT_TYPE_INVALID; + + /** + * Create an instance. + * + * @param phone For which this service is requested. + * @param controller The controller to communicate with the domain selection service. + */ + public EmergencyCallDomainSelectionConnection(@NonNull Phone phone, + @NonNull DomainSelectionController controller) { + this(phone, controller, EmergencyStateTracker.getInstance()); + } + + /** + * Create an instance. + * + * @param phone For which this service is requested. + * @param controller The controller to communicate with the domain selection service. + * @param tracker The {@link EmergencyStateTracker} instance. + */ + @VisibleForTesting + public EmergencyCallDomainSelectionConnection(@NonNull Phone phone, + @NonNull DomainSelectionController controller, @NonNull EmergencyStateTracker tracker) { + super(phone, SELECTOR_TYPE_CALLING, true, controller); + mTag = "EmergencyCallDomainSelectionConnection"; + + mEmergencyStateTracker = tracker; + } + + /** {@inheritDoc} */ + @Override + public void onWlanSelected(boolean useEmergencyPdn) { + mEmergencyStateTracker.onEmergencyTransportChanged( + EmergencyStateTracker.EMERGENCY_TYPE_CALL, MODE_EMERGENCY_WLAN); + if (useEmergencyPdn) { + AccessNetworksManager anm = mPhone.getAccessNetworksManager(); + int transportType = anm.getPreferredTransport(ApnSetting.TYPE_EMERGENCY); + logi("onWlanSelected curTransportType=" + transportType); + if (transportType != TRANSPORT_TYPE_WLAN) { + changePreferredTransport(TRANSPORT_TYPE_WLAN); + return; + } + } + + CompletableFuture<Integer> future = getCompletableFuture(); + if (future != null) future.complete(DOMAIN_NON_3GPP_PS); + } + + /** {@inheritDoc} */ + @Override + public void onWwanSelected() { + mEmergencyStateTracker.onEmergencyTransportChanged( + EmergencyStateTracker.EMERGENCY_TYPE_CALL, MODE_EMERGENCY_WWAN); + } + + /** {@inheritDoc} */ + @Override + public void onSelectionTerminated(@DisconnectCauses int cause) { + if (mCallback != null) mCallback.onSelectionTerminated(cause); + } + + /** {@inheritDoc} */ + @Override + public void onDomainSelected(@NetworkRegistrationInfo.Domain int domain, + boolean useEmergencyPdn) { + if (domain == DOMAIN_PS && useEmergencyPdn) { + AccessNetworksManager anm = mPhone.getAccessNetworksManager(); + int transportType = anm.getPreferredTransport(ApnSetting.TYPE_EMERGENCY); + logi("onDomainSelected curTransportType=" + transportType); + if (transportType != TRANSPORT_TYPE_WWAN) { + changePreferredTransport(TRANSPORT_TYPE_WWAN); + return; + } + } + super.onDomainSelected(domain, useEmergencyPdn); + } + + /** + * Request a domain for emergency call. + * + * @param attr The attributes required to determine the domain. + * @param callback A callback to receive the response. + * @return the callback to receive the response. + */ + public @NonNull CompletableFuture<Integer> createEmergencyConnection( + @NonNull DomainSelectionService.SelectionAttributes attr, + @NonNull DomainSelectionConnectionCallback callback) { + mCallback = callback; + selectDomain(attr); + return getCompletableFuture(); + } + + private void changePreferredTransport(@TransportType int transportType) { + logi("changePreferredTransport " + transportType); + initHandler(); + mPreferredTransportType = transportType; + AccessNetworksManager anm = mPhone.getAccessNetworksManager(); + anm.registerForQualifiedNetworksChanged(mHandler, EVENT_QUALIFIED_NETWORKS_CHANGED); + mPhone.notifyEmergencyDomainSelected(transportType); + } + + private AccessNetworksManager.AccessNetworksManagerCallback mPreferredTransportCallback = + new AccessNetworksManager.AccessNetworksManagerCallback(Runnable::run) { + @Override + public void onPreferredTransportChanged(@NetCapability int capability) { + } + }; + + /** {@inheritDoc} */ + @Override + protected void onQualifiedNetworksChanged() { + AccessNetworksManager anm = mPhone.getAccessNetworksManager(); + int preferredTransport = anm.getPreferredTransport(ApnSetting.TYPE_EMERGENCY); + logi("onQualifiedNetworksChanged preferred=" + mPreferredTransportType + + ", current=" + preferredTransport); + if (preferredTransport == mPreferredTransportType) { + CompletableFuture<Integer> future = getCompletableFuture(); + if (future != null) { + if (preferredTransport == TRANSPORT_TYPE_WLAN) { + future.complete(DOMAIN_NON_3GPP_PS); + } else { + future.complete(DOMAIN_PS); + } + } + anm.unregisterForQualifiedNetworksChanged(mHandler); + } + } + + /** {@inheritDoc} */ + @Override + public void cancelSelection() { + logi("cancelSelection"); + AccessNetworksManager anm = mPhone.getAccessNetworksManager(); + anm.unregisterForQualifiedNetworksChanged(mHandler); + super.cancelSelection(); + } + + /** + * Returns the attributes required to determine the domain for a telephony service. + * + * @param slotId The slot identifier. + * @param subId The subscription identifier. + * @param exited {@code true} if the request caused the device to move out of airplane mode. + * @param callId The call identifier. + * @param number The dialed number. + * @param callFailCause The reason why the last CS attempt failed. + * @param imsReasonInfo The reason why the last PS attempt failed. + * @param emergencyRegResult The current registration result for emergency services. + * @return The attributes required to determine the domain. + */ + public static @NonNull DomainSelectionService.SelectionAttributes getSelectionAttributes( + int slotId, int subId, boolean exited, + @NonNull String callId, @NonNull String number, int callFailCause, + @Nullable ImsReasonInfo imsReasonInfo, + @Nullable EmergencyRegResult emergencyRegResult) { + DomainSelectionService.SelectionAttributes.Builder builder = + new DomainSelectionService.SelectionAttributes.Builder( + slotId, subId, SELECTOR_TYPE_CALLING) + .setEmergency(true) + .setExitedFromAirplaneMode(exited) + .setCallId(callId) + .setNumber(number) + .setCsDisconnectCause(callFailCause); + + if (imsReasonInfo != null) builder.setPsDisconnectCause(imsReasonInfo); + if (emergencyRegResult != null) builder.setEmergencyRegResult(emergencyRegResult); + + return builder.build(); + } +}
diff --git a/src/java/com/android/internal/telephony/domainselection/EmergencySmsDomainSelectionConnection.java b/src/java/com/android/internal/telephony/domainselection/EmergencySmsDomainSelectionConnection.java new file mode 100644 index 0000000..efcdf11 --- /dev/null +++ b/src/java/com/android/internal/telephony/domainselection/EmergencySmsDomainSelectionConnection.java
@@ -0,0 +1,163 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.telephony.domainselection; + +import static com.android.internal.telephony.emergency.EmergencyConstants.MODE_EMERGENCY_WLAN; +import static com.android.internal.telephony.emergency.EmergencyConstants.MODE_EMERGENCY_WWAN; + +import android.annotation.NonNull; +import android.telephony.AccessNetworkConstants; +import android.telephony.AccessNetworkConstants.TransportType; +import android.telephony.NetworkRegistrationInfo; +import android.telephony.data.ApnSetting; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.telephony.Phone; +import com.android.internal.telephony.data.AccessNetworksManager; +import com.android.internal.telephony.emergency.EmergencyStateTracker; + +/** + * Manages the information of request and the callback binder for an emergency SMS. + */ +public class EmergencySmsDomainSelectionConnection extends SmsDomainSelectionConnection { + private final Object mLock = new Object(); + private @NonNull EmergencyStateTracker mEmergencyStateTracker; + private @TransportType int mPreferredTransportType = + AccessNetworkConstants.TRANSPORT_TYPE_INVALID; + + public EmergencySmsDomainSelectionConnection( + Phone phone, DomainSelectionController controller) { + this(phone, controller, EmergencyStateTracker.getInstance()); + } + + @VisibleForTesting + public EmergencySmsDomainSelectionConnection(Phone phone, + DomainSelectionController controller, EmergencyStateTracker tracker) { + super(phone, controller, true); + mTag = "DomainSelectionConnection-EmergencySMS"; + mEmergencyStateTracker = tracker; + } + + /** + * Notifies that WLAN transport has been selected. + * + * @param useEmergencyPdn A flag specifying whether Wi-Fi emergency service uses emergency PDN + * or not. + */ + @Override + public void onWlanSelected(boolean useEmergencyPdn) { + synchronized (mLock) { + if (mPreferredTransportType != AccessNetworkConstants.TRANSPORT_TYPE_INVALID) { + logi("Domain selection completion is in progress"); + return; + } + + mEmergencyStateTracker.onEmergencyTransportChanged( + EmergencyStateTracker.EMERGENCY_TYPE_SMS, MODE_EMERGENCY_WLAN); + + if (useEmergencyPdn) { + // Change the transport type if the current preferred transport type for + // an emergency is not {@link AccessNetworkConstants#TRANSPORT_TYPE_WLAN}. + AccessNetworksManager anm = mPhone.getAccessNetworksManager(); + if (anm.getPreferredTransport(ApnSetting.TYPE_EMERGENCY) + != AccessNetworkConstants.TRANSPORT_TYPE_WLAN) { + changePreferredTransport(AccessNetworkConstants.TRANSPORT_TYPE_WLAN); + // The {@link #onDomainSlected()} will be called after the preferred transport + // is successfully changed and notified from the {@link AccessNetworksManager}. + return; + } + } + + super.onWlanSelected(useEmergencyPdn); + } + } + + @Override + public void onWwanSelected() { + mEmergencyStateTracker.onEmergencyTransportChanged( + EmergencyStateTracker.EMERGENCY_TYPE_SMS, MODE_EMERGENCY_WWAN); + } + + /** + * Notifies the domain selected. + * + * @param domain The selected domain. + * @param useEmergencyPdn A flag specifying whether emergency service uses emergency PDN or not. + */ + @Override + public void onDomainSelected(@NetworkRegistrationInfo.Domain int domain, + boolean useEmergencyPdn) { + synchronized (mLock) { + if (mPreferredTransportType != AccessNetworkConstants.TRANSPORT_TYPE_INVALID) { + logi("Domain selection completion is in progress"); + return; + } + + if (useEmergencyPdn && domain == NetworkRegistrationInfo.DOMAIN_PS) { + // Change the transport type if the current preferred transport type for + // an emergency is not {@link AccessNetworkConstants#TRANSPORT_TYPE_WWAN}. + AccessNetworksManager anm = mPhone.getAccessNetworksManager(); + if (anm.getPreferredTransport(ApnSetting.TYPE_EMERGENCY) + != AccessNetworkConstants.TRANSPORT_TYPE_WWAN) { + changePreferredTransport(AccessNetworkConstants.TRANSPORT_TYPE_WWAN); + // The {@link #onDomainSlected()} will be called after the preferred transport + // is successfully changed and notified from the {@link AccessNetworksManager}. + return; + } + } + + super.onDomainSelected(domain, useEmergencyPdn); + } + } + + @Override + public void finishSelection() { + AccessNetworksManager anm = mPhone.getAccessNetworksManager(); + + synchronized (mLock) { + if (mPreferredTransportType != AccessNetworkConstants.TRANSPORT_TYPE_INVALID) { + mPreferredTransportType = AccessNetworkConstants.TRANSPORT_TYPE_INVALID; + anm.unregisterForQualifiedNetworksChanged(mHandler); + } + } + + super.finishSelection(); + } + + @Override + protected void onQualifiedNetworksChanged() { + AccessNetworksManager anm = mPhone.getAccessNetworksManager(); + int preferredTransportType = anm.getPreferredTransport(ApnSetting.TYPE_EMERGENCY); + + synchronized (mLock) { + if (preferredTransportType == mPreferredTransportType) { + mPreferredTransportType = AccessNetworkConstants.TRANSPORT_TYPE_INVALID; + super.onDomainSelected(NetworkRegistrationInfo.DOMAIN_PS, true); + anm.unregisterForQualifiedNetworksChanged(mHandler); + } + } + } + + private void changePreferredTransport(@TransportType int transportType) { + logi("Change preferred transport: " + transportType); + initHandler(); + mPreferredTransportType = transportType; + AccessNetworksManager anm = mPhone.getAccessNetworksManager(); + anm.registerForQualifiedNetworksChanged(mHandler, EVENT_QUALIFIED_NETWORKS_CHANGED); + mPhone.notifyEmergencyDomainSelected(transportType); + } +}
diff --git a/src/java/com/android/internal/telephony/domainselection/NormalCallDomainSelectionConnection.java b/src/java/com/android/internal/telephony/domainselection/NormalCallDomainSelectionConnection.java new file mode 100644 index 0000000..e157d24 --- /dev/null +++ b/src/java/com/android/internal/telephony/domainselection/NormalCallDomainSelectionConnection.java
@@ -0,0 +1,146 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.telephony.domainselection; + +import static android.telephony.DomainSelectionService.SELECTOR_TYPE_CALLING; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.telephony.AccessNetworkConstants.RadioAccessNetworkType; +import android.telephony.Annotation.DisconnectCauses; +import android.telephony.DomainSelectionService; +import android.telephony.DomainSelectionService.EmergencyScanType; +import android.telephony.NetworkRegistrationInfo; +import android.telephony.ims.ImsReasonInfo; + +import com.android.internal.telephony.Phone; + +import java.util.concurrent.CompletableFuture; + +/** + * Manages the information of request and the callback binder for normal calling. + */ +public class NormalCallDomainSelectionConnection extends DomainSelectionConnection { + + private static final boolean DBG = false; + + private static final String PREFIX_WPS = "*272"; + + // WPS prefix when CLIR is being activated for the call. + private static final String PREFIX_WPS_CLIR_ACTIVATE = "*31#*272"; + + // WPS prefix when CLIR is being deactivated for the call. + private static final String PREFIX_WPS_CLIR_DEACTIVATE = "#31#*272"; + + + private @Nullable DomainSelectionConnectionCallback mCallback; + + /** + * Create an instance. + * + * @param phone For which this service is requested. + * @param controller The controller to communicate with the domain selection service. + */ + public NormalCallDomainSelectionConnection(@NonNull Phone phone, + @NonNull DomainSelectionController controller) { + super(phone, SELECTOR_TYPE_CALLING, false, controller); + mTag = "NormalCallDomainSelectionConnection"; + } + + /** {@inheritDoc} */ + @Override + public void onWlanSelected() { + CompletableFuture<Integer> future = getCompletableFuture(); + future.complete(NetworkRegistrationInfo.DOMAIN_PS); + } + + /** {@inheritDoc} */ + @Override + public void onWwanSelected() { + } + + /** {@inheritDoc} */ + @Override + public void onSelectionTerminated(@DisconnectCauses int cause) { + if (mCallback != null) mCallback.onSelectionTerminated(cause); + } + + /** {@inheritDoc} */ + @Override + public void onRequestEmergencyNetworkScan(@RadioAccessNetworkType int[] preferredNetworks, + @EmergencyScanType int scanType) { + // Not expected with normal calling. + // Override to prevent abnormal behavior. + } + + /** + * Request a domain for normal call. + * + * @param attr The attributes required to determine the domain. + * @param callback A callback to receive the response. + * @return A {@link CompletableFuture} callback to receive the result. + */ + public CompletableFuture<Integer> createNormalConnection( + @NonNull DomainSelectionService.SelectionAttributes attr, + @NonNull DomainSelectionConnectionCallback callback) { + mCallback = callback; + selectDomain(attr); + return getCompletableFuture(); + } + + /** + * Returns the attributes required to determine the domain for a normal call. + * + * @param slotId The slot identifier. + * @param subId The subscription identifier. + * @param callId The call identifier. + * @param number The dialed number. + * @param isVideoCall flag for video call. + * @param callFailCause The reason why the last CS attempt failed. + * @param imsReasonInfo The reason why the last PS attempt failed. + * @return The attributes required to determine the domain. + */ + public static @NonNull DomainSelectionService.SelectionAttributes getSelectionAttributes( + int slotId, int subId, @NonNull String callId, @NonNull String number, + boolean isVideoCall, int callFailCause, @Nullable ImsReasonInfo imsReasonInfo) { + + DomainSelectionService.SelectionAttributes.Builder builder = + new DomainSelectionService.SelectionAttributes.Builder( + slotId, subId, SELECTOR_TYPE_CALLING) + .setEmergency(false) + .setCallId(callId) + .setNumber(number) + .setCsDisconnectCause(callFailCause) + .setVideoCall(isVideoCall); + + if (imsReasonInfo != null) { + builder.setPsDisconnectCause(imsReasonInfo); + } + return builder.build(); + } + + /** + * Check if the call is Wireless Priority Service call + * @param dialString The number being dialed. + * @return {@code true} if dialString matches WPS pattern and {@code false} otherwise. + */ + public static boolean isWpsCall(String dialString) { + return (dialString != null) && (dialString.startsWith(PREFIX_WPS) + || dialString.startsWith(PREFIX_WPS_CLIR_ACTIVATE) + || dialString.startsWith(PREFIX_WPS_CLIR_DEACTIVATE)); + } +}
diff --git a/src/java/com/android/internal/telephony/domainselection/SmsDomainSelectionConnection.java b/src/java/com/android/internal/telephony/domainselection/SmsDomainSelectionConnection.java new file mode 100644 index 0000000..36a7b17 --- /dev/null +++ b/src/java/com/android/internal/telephony/domainselection/SmsDomainSelectionConnection.java
@@ -0,0 +1,83 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.telephony.domainselection; + +import static android.telephony.DomainSelectionService.SELECTOR_TYPE_SMS; + +import android.annotation.NonNull; +import android.telephony.Annotation.DisconnectCauses; +import android.telephony.DomainSelectionService; +import android.telephony.NetworkRegistrationInfo; + +import com.android.internal.telephony.Phone; + +import java.util.concurrent.CompletableFuture; + +/** + * Manages the information of request and the callback binder for SMS. + */ +public class SmsDomainSelectionConnection extends DomainSelectionConnection { + private DomainSelectionConnectionCallback mCallback; + + public SmsDomainSelectionConnection(Phone phone, DomainSelectionController controller) { + this(phone, controller, false); + mTag = "DomainSelectionConnection-SMS"; + } + + protected SmsDomainSelectionConnection(Phone phone, DomainSelectionController controller, + boolean isEmergency) { + super(phone, SELECTOR_TYPE_SMS, isEmergency, controller); + } + + @Override + public void onWlanSelected() { + super.onDomainSelected(NetworkRegistrationInfo.DOMAIN_PS); + } + + @Override + public void onSelectionTerminated(@DisconnectCauses int cause) { + if (mCallback != null) mCallback.onSelectionTerminated(cause); + } + + @Override + public void finishSelection() { + CompletableFuture<Integer> future = getCompletableFuture(); + + if (future != null && !future.isDone()) { + cancelSelection(); + } else { + super.finishSelection(); + } + } + + /** + * Requests a domain selection for SMS. + * + * @param attr The attributes required to determine the domain. + * @param callback A callback to notify an error of the domain selection. + * @return A {@link CompletableFuture} to get the selected domain + * {@link NetworkRegistrationInfo#DOMAIN_PS} or + * {@link NetworkRegistrationInfo#DOMAIN_CS}. + */ + public @NonNull CompletableFuture<Integer> requestDomainSelection( + @NonNull DomainSelectionService.SelectionAttributes attr, + @NonNull DomainSelectionConnectionCallback callback) { + mCallback = callback; + selectDomain(attr); + return getCompletableFuture(); + } +}
diff --git a/src/java/com/android/internal/telephony/emergency/EmergencyConstants.java b/src/java/com/android/internal/telephony/emergency/EmergencyConstants.java new file mode 100644 index 0000000..6caf5ab --- /dev/null +++ b/src/java/com/android/internal/telephony/emergency/EmergencyConstants.java
@@ -0,0 +1,69 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.telephony.emergency; + +import android.annotation.IntDef; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Define the constants for emergency call domain selection. + */ +public class EmergencyConstants { + + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"MODE_EMERGENCY_"}, + value = { + MODE_EMERGENCY_NONE, + MODE_EMERGENCY_WWAN, + MODE_EMERGENCY_WLAN, + MODE_EMERGENCY_CALLBACK, + }) + public @interface EmergencyMode {} + + /** + * Default value. + */ + public static final int MODE_EMERGENCY_NONE = 0; + /** + * Mode Type Emergency WWAN, indicates that the current domain selected for the Emergency call + * is cellular. + */ + public static final int MODE_EMERGENCY_WWAN = 1; + /** + * Mode Type Emergency WLAN, indicates that the current domain selected for the Emergency call + * is WLAN/WIFI. + */ + public static final int MODE_EMERGENCY_WLAN = 2; + /** + * Mode Type Emergency Callback, indicates that the current mode set request is for Emergency + * callback. + */ + public static final int MODE_EMERGENCY_CALLBACK = 3; + + /** Converts the {@link EmergencyMode} to String */ + public static String emergencyModeToString(int emcMode) { + switch (emcMode) { + case MODE_EMERGENCY_NONE: return "NONE"; + case MODE_EMERGENCY_WWAN: return "WWAN"; + case MODE_EMERGENCY_WLAN: return "WLAN"; + case MODE_EMERGENCY_CALLBACK: return "CALLBACK"; + default: return "UNKNOWN(" + emcMode + ")"; + } + } +}
diff --git a/src/java/com/android/internal/telephony/emergency/EmergencyNumberTracker.java b/src/java/com/android/internal/telephony/emergency/EmergencyNumberTracker.java index 276d82a..9b44001 100644 --- a/src/java/com/android/internal/telephony/emergency/EmergencyNumberTracker.java +++ b/src/java/com/android/internal/telephony/emergency/EmergencyNumberTracker.java
@@ -20,14 +20,15 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.res.Resources; import android.os.AsyncResult; import android.os.Environment; import android.os.Handler; import android.os.Message; import android.os.ParcelFileDescriptor; import android.os.PersistableBundle; -import android.os.SystemProperties; import android.telephony.CarrierConfigManager; +import android.telephony.CellIdentity; import android.telephony.PhoneNumberUtils; import android.telephony.ServiceState; import android.telephony.SubscriptionManager; @@ -36,18 +37,21 @@ import android.telephony.emergency.EmergencyNumber.EmergencyCallRouting; import android.telephony.emergency.EmergencyNumber.EmergencyServiceCategories; import android.text.TextUtils; +import android.util.ArrayMap; +import android.util.ArraySet; import android.util.LocalLog; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.telephony.CommandsInterface; -import com.android.internal.telephony.HalVersion; import com.android.internal.telephony.LocaleTracker; import com.android.internal.telephony.Phone; import com.android.internal.telephony.PhoneConstants; import com.android.internal.telephony.PhoneFactory; import com.android.internal.telephony.ServiceStateTracker; -import com.android.internal.telephony.SubscriptionController; +import com.android.internal.telephony.metrics.EmergencyNumberStats; import com.android.internal.telephony.metrics.TelephonyMetrics; +import com.android.internal.telephony.nano.PersistAtomsProto; +import com.android.internal.telephony.subscription.SubscriptionManagerService; import com.android.internal.util.IndentingPrintWriter; import com.android.phone.ecc.nano.ProtobufEccData; import com.android.phone.ecc.nano.ProtobufEccData.EccInfo; @@ -67,6 +71,9 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; import java.util.zip.GZIPInputStream; /** @@ -95,9 +102,18 @@ private final CommandsInterface mCi; private final Phone mPhone; + private int mPhoneId; private String mCountryIso; private String mLastKnownEmergencyCountryIso = ""; private int mCurrentDatabaseVersion = INVALID_DATABASE_VERSION; + private int mCurrentOtaDatabaseVersion = INVALID_DATABASE_VERSION; + private Resources mResources = null; + /** + * Used for storing all specific mnc's along with the list of emergency numbers + * for which normal routing should be supported. + */ + private Map<String, Set<String>> mNormalRoutedNumbers = new ArrayMap<>(); + /** * Indicates if the country iso is set by another subscription. * @hide @@ -141,10 +157,6 @@ @Override public void onReceive(Context context, Intent intent) { if (intent.getAction().equals( - CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)) { - onCarrierConfigChanged(); - return; - } else if (intent.getAction().equals( TelephonyManager.ACTION_NETWORK_COUNTRY_CHANGED)) { int phoneId = intent.getIntExtra(PhoneConstants.PHONE_KEY, -1); if (phoneId == mPhone.getPhoneId()) { @@ -164,27 +176,35 @@ public EmergencyNumberTracker(Phone phone, CommandsInterface ci) { mPhone = phone; mCi = ci; + mResources = mPhone.getContext().getResources(); if (mPhone != null) { + mPhoneId = phone.getPhoneId(); CarrierConfigManager configMgr = (CarrierConfigManager) mPhone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE); if (configMgr != null) { - PersistableBundle b = configMgr.getConfigForSubId(mPhone.getSubId()); - if (b != null) { + PersistableBundle b = CarrierConfigManager.getCarrierConfigSubset( + mPhone.getContext(), + mPhone.getSubId(), + CarrierConfigManager.KEY_EMERGENCY_NUMBER_PREFIX_STRING_ARRAY); + if (!b.isEmpty()) { mEmergencyNumberPrefix = b.getStringArray( CarrierConfigManager.KEY_EMERGENCY_NUMBER_PREFIX_STRING_ARRAY); } + + // Callback which directly handle config change should be executed on handler thread + configMgr.registerCarrierConfigChangeListener(this::post, + (slotIndex, subId, carrierId, specificCarrierId) -> + onCarrierConfigUpdated(slotIndex)); + + //register country change listener + IntentFilter filter = new IntentFilter( + TelephonyManager.ACTION_NETWORK_COUNTRY_CHANGED); + mPhone.getContext().registerReceiver(mIntentReceiver, filter); + } else { loge("CarrierConfigManager is null."); } - - // Receive Carrier Config Changes - IntentFilter filter = new IntentFilter( - CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED); - // Receive Telephony Network Country Changes - filter.addAction(TelephonyManager.ACTION_NETWORK_COUNTRY_CHANGED); - - mPhone.getContext().registerReceiver(mIntentReceiver, filter); } else { loge("mPhone is null."); } @@ -269,7 +289,7 @@ @VisibleForTesting public boolean isSimAbsent() { for (Phone phone: PhoneFactory.getPhones()) { - int slotId = SubscriptionController.getInstance().getSlotIndex(phone.getSubId()); + int slotId = SubscriptionManagerService.getInstance().getSlotIndex(phone.getSubId()); // If slot id is invalid, it means that there is no sim card. if (slotId != SubscriptionManager.INVALID_SIM_SLOT_INDEX) { // If there is at least one sim active, sim is not absent; it returns false @@ -284,7 +304,7 @@ // If country iso has been cached when listener is set, don't need to cache the initial // country iso and initial database. if (mCountryIso == null) { - String countryForDatabaseCache = getInitialCountryIso().toLowerCase(); + String countryForDatabaseCache = getInitialCountryIso().toLowerCase(Locale.ROOT); updateEmergencyCountryIso(countryForDatabaseCache); // Use the last known country to cache the database in APM if (TextUtils.isEmpty(countryForDatabaseCache) @@ -327,23 +347,26 @@ } } - private void onCarrierConfigChanged() { + private void onCarrierConfigUpdated(int slotIndex) { if (mPhone != null) { - CarrierConfigManager configMgr = (CarrierConfigManager) - mPhone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE); - if (configMgr != null) { - PersistableBundle b = configMgr.getConfigForSubId(mPhone.getSubId()); - if (b != null) { - String[] emergencyNumberPrefix = b.getStringArray( + if (slotIndex != mPhone.getPhoneId()) return; + + PersistableBundle b = + CarrierConfigManager.getCarrierConfigSubset( + mPhone.getContext(), + mPhone.getSubId(), CarrierConfigManager.KEY_EMERGENCY_NUMBER_PREFIX_STRING_ARRAY); - if (!Arrays.equals(mEmergencyNumberPrefix, emergencyNumberPrefix)) { - this.obtainMessage(EVENT_UPDATE_EMERGENCY_NUMBER_PREFIX, - emergencyNumberPrefix).sendToTarget(); - } + if (!b.isEmpty()) { + String[] emergencyNumberPrefix = + b.getStringArray( + CarrierConfigManager.KEY_EMERGENCY_NUMBER_PREFIX_STRING_ARRAY); + if (!Arrays.equals(mEmergencyNumberPrefix, emergencyNumberPrefix)) { + this.obtainMessage(EVENT_UPDATE_EMERGENCY_NUMBER_PREFIX, emergencyNumberPrefix) + .sendToTarget(); } } } else { - loge("onCarrierConfigChanged mPhone is null."); + loge("onCarrierConfigurationChanged mPhone is null."); } } @@ -404,7 +427,8 @@ EVENT_OVERRIDE_OTA_EMERGENCY_NUMBER_DB_FILE_PATH, null).sendToTarget(); } - private EmergencyNumber convertEmergencyNumberFromEccInfo(EccInfo eccInfo, String countryIso) { + private EmergencyNumber convertEmergencyNumberFromEccInfo(EccInfo eccInfo, String countryIso, + int emergencyCallRouting) { String phoneNumber = eccInfo.phoneNumber.trim(); if (phoneNumber.isEmpty()) { loge("EccInfo has empty phone number."); @@ -445,13 +469,65 @@ // Ignores unknown types. } } - return new EmergencyNumber(phoneNumber, countryIso, "", emergencyServiceCategoryBitmask, - new ArrayList<String>(), EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE, - EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN); + return new EmergencyNumber(phoneNumber, countryIso, "", + emergencyServiceCategoryBitmask, new ArrayList<String>(), + EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE, emergencyCallRouting); + } + + /** + * Get routing type of emergency numbers from DB. Update mnc's list with numbers that are + * to supported as normal routing type in the respective mnc's. + */ + private int getRoutingInfoFromDB(EccInfo eccInfo, + Map<String, Set<String>> normalRoutedNumbers) { + int emergencyCallRouting; + switch(eccInfo.routing) + { + case EccInfo.Routing.NORMAL : + emergencyCallRouting = EmergencyNumber.EMERGENCY_CALL_ROUTING_NORMAL; + break; + case EccInfo.Routing.EMERGENCY : + emergencyCallRouting = EmergencyNumber.EMERGENCY_CALL_ROUTING_EMERGENCY; + break; + default: + emergencyCallRouting = EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN; + } + String phoneNumber = eccInfo.phoneNumber.trim(); + if (phoneNumber.isEmpty()) { + loge("EccInfo has empty phone number."); + return emergencyCallRouting; + } + + if (eccInfo.routing == EccInfo.Routing.NORMAL) { + emergencyCallRouting = EmergencyNumber.EMERGENCY_CALL_ROUTING_NORMAL; + + if (((eccInfo.normalRoutingMncs).length != 0) + && (eccInfo.normalRoutingMncs[0].length() > 0)) { + emergencyCallRouting = EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN; + + for (String routingMnc : eccInfo.normalRoutingMncs) { + boolean mncExist = normalRoutedNumbers.containsKey(routingMnc); + Set phoneNumberList; + if (!mncExist) { + phoneNumberList = new ArraySet<String>(); + phoneNumberList.add(phoneNumber); + normalRoutedNumbers.put(routingMnc, phoneNumberList); + } else { + phoneNumberList = normalRoutedNumbers.get(routingMnc); + if (!phoneNumberList.contains(phoneNumber)) { + phoneNumberList.add(phoneNumber); + } + } + } + logd("Normal routed mncs with phoneNumbers:" + normalRoutedNumbers); + } + } + return emergencyCallRouting; } private void cacheEmergencyDatabaseByCountry(String countryIso) { int assetsDatabaseVersion; + Map<String, Set<String>> assetNormalRoutedNumbers = new ArrayMap<>(); // Read the Asset emergency number database List<EmergencyNumber> updatedAssetEmergencyNumberList = new ArrayList<>(); @@ -463,12 +539,17 @@ readInputStreamToByteArray(gzipInputStream)); assetsDatabaseVersion = allEccMessages.revision; logd(countryIso + " asset emergency database is loaded. Ver: " + assetsDatabaseVersion - + " Phone Id: " + mPhone.getPhoneId()); + + " Phone Id: " + mPhone.getPhoneId() + " countryIso: " + countryIso); for (ProtobufEccData.CountryInfo countryEccInfo : allEccMessages.countries) { - if (countryEccInfo.isoCode.equals(countryIso.toUpperCase())) { + if (countryEccInfo.isoCode.equals(countryIso.toUpperCase(Locale.ROOT))) { for (ProtobufEccData.EccInfo eccInfo : countryEccInfo.eccs) { + int emergencyCallRouting = EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN; + if (!shouldEmergencyNumberRoutingFromDbBeIgnored()) { + emergencyCallRouting = getRoutingInfoFromDB(eccInfo, + assetNormalRoutedNumbers); + } updatedAssetEmergencyNumberList.add(convertEmergencyNumberFromEccInfo( - eccInfo, countryIso)); + eccInfo, countryIso, emergencyCallRouting)); } } } @@ -479,24 +560,27 @@ } // Cache OTA emergency number database - int otaDatabaseVersion = cacheOtaEmergencyNumberDatabase(); + mCurrentOtaDatabaseVersion = cacheOtaEmergencyNumberDatabase(); // Use a valid database that has higher version. - if (otaDatabaseVersion == INVALID_DATABASE_VERSION + if (mCurrentOtaDatabaseVersion == INVALID_DATABASE_VERSION && assetsDatabaseVersion == INVALID_DATABASE_VERSION) { loge("No database available. Phone Id: " + mPhone.getPhoneId()); - } else if (assetsDatabaseVersion > otaDatabaseVersion) { + } else if (assetsDatabaseVersion > mCurrentOtaDatabaseVersion) { logd("Using Asset Emergency database. Version: " + assetsDatabaseVersion); mCurrentDatabaseVersion = assetsDatabaseVersion; mEmergencyNumberListFromDatabase = updatedAssetEmergencyNumberList; + mNormalRoutedNumbers.clear(); + mNormalRoutedNumbers = assetNormalRoutedNumbers; } else { - logd("Using Ota Emergency database. Version: " + otaDatabaseVersion); + logd("Using Ota Emergency database. Version: " + mCurrentOtaDatabaseVersion); } } private int cacheOtaEmergencyNumberDatabase() { ProtobufEccData.AllInfo allEccMessages = null; int otaDatabaseVersion = INVALID_DATABASE_VERSION; + Map<String, Set<String>> otaNormalRoutedNumbers = new ArrayMap<>(); // Read the OTA emergency number database List<EmergencyNumber> updatedOtaEmergencyNumberList = new ArrayList<>(); @@ -525,10 +609,15 @@ logd(countryIso + " ota emergency database is loaded. Ver: " + otaDatabaseVersion); otaDatabaseVersion = allEccMessages.revision; for (ProtobufEccData.CountryInfo countryEccInfo : allEccMessages.countries) { - if (countryEccInfo.isoCode.equals(countryIso.toUpperCase())) { + if (countryEccInfo.isoCode.equals(countryIso.toUpperCase(Locale.ROOT))) { for (ProtobufEccData.EccInfo eccInfo : countryEccInfo.eccs) { + int emergencyCallRouting = EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN; + if (!shouldEmergencyNumberRoutingFromDbBeIgnored()) { + emergencyCallRouting = getRoutingInfoFromDB(eccInfo, + otaNormalRoutedNumbers); + } updatedOtaEmergencyNumberList.add(convertEmergencyNumberFromEccInfo( - eccInfo, countryIso)); + eccInfo, countryIso, emergencyCallRouting)); } } } @@ -543,6 +632,8 @@ && mCurrentDatabaseVersion < otaDatabaseVersion) { mCurrentDatabaseVersion = otaDatabaseVersion; mEmergencyNumberListFromDatabase = updatedOtaEmergencyNumberList; + mNormalRoutedNumbers.clear(); + mNormalRoutedNumbers = otaNormalRoutedNumbers; } return otaDatabaseVersion; } @@ -591,9 +682,9 @@ private void updateEmergencyNumberListDatabaseAndNotify(String countryIso) { logd("updateEmergencyNumberListDatabaseAndNotify(): receiving countryIso: " + countryIso); - updateEmergencyCountryIso(countryIso.toLowerCase()); + updateEmergencyCountryIso(countryIso.toLowerCase(Locale.ROOT)); // Use cached country iso in APM to load emergency number database. - if (TextUtils.isEmpty(countryIso) && isAirplaneModeEnabled()) { + if (TextUtils.isEmpty(countryIso)) { countryIso = getCountryIsoForCachingDatabase(); logd("updateEmergencyNumberListDatabaseAndNotify(): using cached APM country " + countryIso); @@ -622,7 +713,8 @@ private void updateOtaEmergencyNumberListDatabaseAndNotify() { logd("updateOtaEmergencyNumberListDatabaseAndNotify():" + " receiving Emegency Number database OTA update"); - if (cacheOtaEmergencyNumberDatabase() != INVALID_DATABASE_VERSION) { + mCurrentOtaDatabaseVersion = cacheOtaEmergencyNumberDatabase(); + if (mCurrentOtaDatabaseVersion != INVALID_DATABASE_VERSION) { writeUpdatedEmergencyNumberListMetrics(mEmergencyNumberListFromDatabase); if (!DBG) { mEmergencyNumberListDatabaseLocalLog.log( @@ -687,7 +779,11 @@ } mergedEmergencyNumberList.addAll(mEmergencyNumberListWithPrefix); mergedEmergencyNumberList.addAll(mEmergencyNumberListFromTestMode); - EmergencyNumber.mergeSameNumbersInEmergencyNumberList(mergedEmergencyNumberList); + if (shouldDeterminingOfUrnsAndCategoriesWhileMergingIgnored()) { + EmergencyNumber.mergeSameNumbersInEmergencyNumberList(mergedEmergencyNumberList); + } else { + EmergencyNumber.mergeSameNumbersInEmergencyNumberList(mergedEmergencyNumberList, true); + } mEmergencyNumberList = mergedEmergencyNumberList; } @@ -698,11 +794,90 @@ * indication not support from the HAL. */ public List<EmergencyNumber> getEmergencyNumberList() { + List<EmergencyNumber> completeEmergencyNumberList; if (!mEmergencyNumberListFromRadio.isEmpty()) { - return Collections.unmodifiableList(mEmergencyNumberList); + completeEmergencyNumberList = Collections.unmodifiableList(mEmergencyNumberList); } else { - return getEmergencyNumberListFromEccListDatabaseAndTest(); + completeEmergencyNumberList = getEmergencyNumberListFromEccListDatabaseAndTest(); } + if (shouldAdjustForRouting()) { + return adjustRoutingForEmergencyNumbers(completeEmergencyNumberList); + } else { + return completeEmergencyNumberList; + } + } + + /** + * Util function to check whether routing type and mnc value in emergency number needs + * to be adjusted for the current network mnc. + */ + private boolean shouldAdjustForRouting() { + if (!shouldEmergencyNumberRoutingFromDbBeIgnored() && !mNormalRoutedNumbers.isEmpty()) { + return true; + } + return false; + } + + /** + * Adjust emergency numbers with mnc and routing type based on the current network mnc. + */ + private List<EmergencyNumber> adjustRoutingForEmergencyNumbers( + List<EmergencyNumber> emergencyNumbers) { + CellIdentity cellIdentity = mPhone.getCurrentCellIdentity(); + if (cellIdentity != null) { + String networkMnc = cellIdentity.getMncString(); + Set<String> normalRoutedPhoneNumbers = mNormalRoutedNumbers.get(networkMnc); + Set<String> normalRoutedPhoneNumbersWithPrefix = new ArraySet<String>(); + + if (normalRoutedPhoneNumbers != null && !normalRoutedPhoneNumbers.isEmpty()) { + for (String num : normalRoutedPhoneNumbers) { + Set<String> phoneNumbersWithPrefix = addPrefixToEmergencyNumber(num); + if (phoneNumbersWithPrefix != null && !phoneNumbersWithPrefix.isEmpty()) { + normalRoutedPhoneNumbersWithPrefix.addAll(phoneNumbersWithPrefix); + } + } + } + List<EmergencyNumber> adjustedEmergencyNumberList = new ArrayList<>(); + int routing; + String mnc; + for (EmergencyNumber num : emergencyNumbers) { + routing = num.getEmergencyCallRouting(); + mnc = num.getMnc(); + if (num.isFromSources(EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE)) { + if ((normalRoutedPhoneNumbers != null + && normalRoutedPhoneNumbers.contains(num.getNumber())) + || normalRoutedPhoneNumbersWithPrefix.contains(num.getNumber())) { + routing = EmergencyNumber.EMERGENCY_CALL_ROUTING_NORMAL; + mnc = networkMnc; + logd("adjustRoutingForEmergencyNumbers for number" + num.getNumber()); + } else if (routing == EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN) { + routing = EmergencyNumber.EMERGENCY_CALL_ROUTING_EMERGENCY; + } + } + adjustedEmergencyNumberList.add(new EmergencyNumber(num.getNumber(), + num.getCountryIso(), mnc, + num.getEmergencyServiceCategoryBitmask(), + num.getEmergencyUrns(), num.getEmergencyNumberSourceBitmask(), + routing)); + } + return adjustedEmergencyNumberList; + } else { + return emergencyNumbers; + } + } + + + /** + * Util function to add prefix to the given emergency number. + */ + private Set<String> addPrefixToEmergencyNumber(String number) { + Set<String> phoneNumbersWithPrefix = new ArraySet<String>(); + for (String prefix : mEmergencyNumberPrefix) { + if (!number.startsWith(prefix)) { + phoneNumbersWithPrefix.add(prefix + number); + } + } + return phoneNumbersWithPrefix; } /** @@ -710,7 +885,7 @@ * * @return {@code true} if it is; {@code false} otherwise. */ - public boolean isEmergencyNumber(String number, boolean exactMatch) { + public boolean isEmergencyNumber(String number) { if (number == null) { return false; } @@ -726,31 +901,14 @@ if (!mEmergencyNumberListFromRadio.isEmpty()) { for (EmergencyNumber num : mEmergencyNumberList) { - // According to com.android.i18n.phonenumbers.ShortNumberInfo, in - // these countries, if extra digits are added to an emergency number, - // it no longer connects to the emergency service. - String countryIso = getLastKnownEmergencyCountryIso(); - if (countryIso.equals("br") || countryIso.equals("cl") - || countryIso.equals("ni")) { - exactMatch = true; - } else { - exactMatch = false || exactMatch; - } - if (exactMatch) { - if (num.getNumber().equals(number)) { - logd("Found in mEmergencyNumberList [exact match] "); - return true; - } - } else { - if (number.startsWith(num.getNumber())) { - logd("Found in mEmergencyNumberList [not exact match] "); - return true; - } + if (num.getNumber().equals(number)) { + logd("Found in mEmergencyNumberList"); + return true; } } return false; } else { - boolean inEccList = isEmergencyNumberFromEccList(number, exactMatch); + boolean inEccList = isEmergencyNumberFromEccList(number); boolean inEmergencyNumberDb = isEmergencyNumberFromDatabase(number); boolean inEmergencyNumberTestList = isEmergencyNumberForTest(number); logd("Search results - inRilEccList:" + inEccList @@ -840,6 +998,10 @@ return mCurrentDatabaseVersion; } + public int getEmergencyNumberOtaDbVersion() { + return mCurrentOtaDatabaseVersion; + } + private synchronized void updateEmergencyCountryIso(String countryIso) { mCountryIso = countryIso; if (!TextUtils.isEmpty(mCountryIso)) { @@ -855,22 +1017,8 @@ */ private List<EmergencyNumber> getEmergencyNumberListFromEccList() { List<EmergencyNumber> emergencyNumberList = new ArrayList<>(); - int slotId = SubscriptionController.getInstance().getSlotIndex(mPhone.getSubId()); - String ecclist = (slotId <= 0) ? "ril.ecclist" : ("ril.ecclist" + slotId); - String emergencyNumbers = SystemProperties.get(ecclist, ""); - if (TextUtils.isEmpty(emergencyNumbers)) { - // then read-only ecclist property since old RIL only uses this - emergencyNumbers = SystemProperties.get("ro.ril.ecclist"); - } - if (!TextUtils.isEmpty(emergencyNumbers)) { - // searches through the comma-separated list for a match, - // return true if one is found. - for (String emergencyNum : emergencyNumbers.split(",")) { - emergencyNumberList.add(getLabeledEmergencyNumberForEcclist(emergencyNum)); - } - } - emergencyNumbers = ((isSimAbsent()) ? "112,911,000,08,110,118,119,999" : "112,911"); + String emergencyNumbers = ((isSimAbsent()) ? "112,911,000,08,110,118,119,999" : "112,911"); for (String emergencyNum : emergencyNumbers.split(",")) { emergencyNumberList.add(getLabeledEmergencyNumberForEcclist(emergencyNum)); } @@ -886,15 +1034,14 @@ List<EmergencyNumber> emergencyNumberListWithPrefix = new ArrayList<>(); if (emergencyNumberList != null) { for (EmergencyNumber num : emergencyNumberList) { - for (String prefix : mEmergencyNumberPrefix) { - // If an emergency number has started with the prefix, - // no need to apply the prefix. - if (!num.getNumber().startsWith(prefix)) { + Set<String> phoneNumbersWithPrefix = addPrefixToEmergencyNumber(num.getNumber()); + if (phoneNumbersWithPrefix != null && !phoneNumbersWithPrefix.isEmpty()) { + for (String numberWithPrefix : phoneNumbersWithPrefix) { emergencyNumberListWithPrefix.add(new EmergencyNumber( - prefix + num.getNumber(), num.getCountryIso(), - num.getMnc(), num.getEmergencyServiceCategoryBitmask(), - num.getEmergencyUrns(), num.getEmergencyNumberSourceBitmask(), - num.getEmergencyCallRouting())); + numberWithPrefix, num.getCountryIso(), + num.getMnc(), num.getEmergencyServiceCategoryBitmask(), + num.getEmergencyUrns(), num.getEmergencyNumberSourceBitmask(), + num.getEmergencyCallRouting())); } } } @@ -913,7 +1060,7 @@ } private boolean isEmergencyNumberFromDatabase(String number) { - if (!mPhone.getHalVersion().greaterOrEqual(new HalVersion(1, 4))) { + if (mEmergencyNumberListFromDatabase.isEmpty()) { return false; } number = PhoneNumberUtils.stripSeparators(number); @@ -936,10 +1083,10 @@ number = PhoneNumberUtils.stripSeparators(number); for (EmergencyNumber num : mEmergencyNumberListFromDatabase) { if (num.getNumber().equals(number)) { - return new EmergencyNumber(number, getLastKnownEmergencyCountryIso().toLowerCase(), - "", num.getEmergencyServiceCategoryBitmask(), + return new EmergencyNumber(number, getLastKnownEmergencyCountryIso() + .toLowerCase(Locale.ROOT), "", num.getEmergencyServiceCategoryBitmask(), new ArrayList<String>(), EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE, - EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN); + num.getEmergencyCallRouting()); } } return new EmergencyNumber(number, "", "", @@ -952,7 +1099,7 @@ * Back-up old logics for {@link PhoneNumberUtils#isEmergencyNumberInternal} for legacy * and deprecate purpose. */ - private boolean isEmergencyNumberFromEccList(String number, boolean useExactMatch) { + private boolean isEmergencyNumberFromEccList(String number) { // If the number passed in is null, just return false: if (number == null) return false; @@ -976,59 +1123,8 @@ /// @} String emergencyNumbers = ""; - int slotId = SubscriptionController.getInstance().getSlotIndex(mPhone.getSubId()); - - String ecclist = null; String countryIso = getLastKnownEmergencyCountryIso(); - - if (!mPhone.getHalVersion().greaterOrEqual(new HalVersion(1, 4))) { - //only use ril ecc list for older devices with HAL < 1.4 - // check read-write ecclist property first - ecclist = (slotId <= 0) ? "ril.ecclist" : ("ril.ecclist" + slotId); - emergencyNumbers = SystemProperties.get(ecclist, ""); - - logd("slotId:" + slotId + " country:" + countryIso + " emergencyNumbers: " - + emergencyNumbers); - - if (TextUtils.isEmpty(emergencyNumbers)) { - // then read-only ecclist property since old RIL only uses this - emergencyNumbers = SystemProperties.get("ro.ril.ecclist"); - } - - if (!TextUtils.isEmpty(emergencyNumbers)) { - // searches through the comma-separated list for a match, - // return true if one is found. - for (String emergencyNum : emergencyNumbers.split(",")) { - // According to com.android.i18n.phonenumbers.ShortNumberInfo, in - // these countries, if extra digits are added to an emergency number, - // it no longer connects to the emergency service. - if (useExactMatch || countryIso.equals("br") || countryIso.equals("cl") - || countryIso.equals("ni")) { - if (number.equals(emergencyNum)) { - return true; - } else { - for (String prefix : mEmergencyNumberPrefix) { - if (number.equals(prefix + emergencyNum)) { - return true; - } - } - } - } else { - if (number.startsWith(emergencyNum)) { - return true; - } else { - for (String prefix : mEmergencyNumberPrefix) { - if (number.startsWith(prefix + emergencyNum)) { - return true; - } - } - } - } - } - // no matches found against the list! - return false; - } - } + logd("country:" + countryIso); logd("System property doesn't provide any emergency numbers." + " Use embedded logic for determining ones."); @@ -1038,57 +1134,32 @@ emergencyNumbers = ((isSimAbsent()) ? "112,911,000,08,110,118,119,999" : "112,911"); for (String emergencyNum : emergencyNumbers.split(",")) { - if (useExactMatch) { - if (number.equals(emergencyNum)) { - return true; - } else { - for (String prefix : mEmergencyNumberPrefix) { - if (number.equals(prefix + emergencyNum)) { - return true; - } - } - } + if (number.equals(emergencyNum)) { + return true; } else { - if (number.startsWith(emergencyNum)) { - return true; - } else { - for (String prefix : mEmergencyNumberPrefix) { - if (number.equals(prefix + emergencyNum)) { - return true; - } + for (String prefix : mEmergencyNumberPrefix) { + if (number.equals(prefix + emergencyNum)) { + return true; } } } } - if(isSimAbsent()) { + if (isSimAbsent()) { // No ecclist system property, so use our own list. if (countryIso != null) { ShortNumberInfo info = ShortNumberInfo.getInstance(); - if (useExactMatch) { - if (info.isEmergencyNumber(number, countryIso.toUpperCase())) { - return true; - } else { - for (String prefix : mEmergencyNumberPrefix) { - if (info.isEmergencyNumber(prefix + number, countryIso.toUpperCase())) { - return true; - } - } - } - return false; + if (info.isEmergencyNumber(number, countryIso.toUpperCase(Locale.ROOT))) { + return true; } else { - if (info.connectsToEmergencyNumber(number, countryIso.toUpperCase())) { - return true; - } else { - for (String prefix : mEmergencyNumberPrefix) { - if (info.connectsToEmergencyNumber(prefix + number, - countryIso.toUpperCase())) { - return true; - } + for (String prefix : mEmergencyNumberPrefix) { + if (info.isEmergencyNumber(prefix + number, + countryIso.toUpperCase(Locale.ROOT))) { + return true; } } - return false; } + return false; } } @@ -1107,7 +1178,7 @@ */ private void updateEmergencyNumberListTestModeAndNotify(int action, EmergencyNumber num) { if (action == ADD_EMERGENCY_NUMBER_TEST_MODE) { - if (!isEmergencyNumber(num.getNumber(), true)) { + if (!isEmergencyNumber(num.getNumber())) { mEmergencyNumberListFromTestMode.add(num); } } else if (action == RESET_EMERGENCY_NUMBER_TEST_MODE) { @@ -1134,7 +1205,7 @@ private List<EmergencyNumber> getEmergencyNumberListFromEccListDatabaseAndTest() { List<EmergencyNumber> mergedEmergencyNumberList = getEmergencyNumberListFromEccList(); - if (mPhone.getHalVersion().greaterOrEqual(new HalVersion(1, 4))) { + if (!mEmergencyNumberListFromDatabase.isEmpty()) { loge("getEmergencyNumberListFromEccListDatabaseAndTest: radio indication is" + " unavailable in 1.4 HAL."); mergedEmergencyNumberList.addAll(mEmergencyNumberListFromDatabase); @@ -1142,7 +1213,12 @@ mEmergencyNumberListFromDatabase)); } mergedEmergencyNumberList.addAll(getEmergencyNumberListTestMode()); - EmergencyNumber.mergeSameNumbersInEmergencyNumberList(mergedEmergencyNumberList); + + if (shouldDeterminingOfUrnsAndCategoriesWhileMergingIgnored()) { + EmergencyNumber.mergeSameNumbersInEmergencyNumberList(mergedEmergencyNumberList); + } else { + EmergencyNumber.mergeSameNumbersInEmergencyNumberList(mergedEmergencyNumberList, true); + } return mergedEmergencyNumberList; } @@ -1158,16 +1234,16 @@ return new ArrayList<>(mEmergencyNumberListFromRadio); } - private static void logd(String str) { - Rlog.d(TAG, str); + private void logd(String str) { + Rlog.d(TAG, "[" + mPhoneId + "]" + str); } - private static void logw(String str) { - Rlog.w(TAG, str); + private void logw(String str) { + Rlog.w(TAG, "[" + mPhoneId + "]" + str); } - private static void loge(String str) { - Rlog.e(TAG, str); + private void loge(String str) { + Rlog.e(TAG, "[" + mPhoneId + "]" + str); } private void writeUpdatedEmergencyNumberListMetrics( @@ -1182,6 +1258,56 @@ } /** + * @return {@code true} if emergency numbers sourced from modem/config should be ignored. + * {@code false} if emergency numbers sourced from modem/config should not be ignored. + */ + @VisibleForTesting + public boolean shouldModemConfigEmergencyNumbersBeIgnored() { + return mResources.getBoolean(com.android.internal.R.bool + .ignore_modem_config_emergency_numbers); + } + + /** + * @return {@code true} if emergency number routing from the android emergency number + * database should be ignored. + * {@code false} if emergency number routing from the android emergency number database + * should not be ignored. + */ + @VisibleForTesting + public boolean shouldEmergencyNumberRoutingFromDbBeIgnored() { + return mResources.getBoolean(com.android.internal.R.bool + .ignore_emergency_number_routing_from_db); + } + + + /** + * @return {@code true} if determining of Urns & Service Categories while merging duplicate + * numbers should be ignored. + * {@code false} if determining of Urns & Service Categories while merging duplicate + * numbers should not be ignored. + */ + @VisibleForTesting + public boolean shouldDeterminingOfUrnsAndCategoriesWhileMergingIgnored() { + // TODO: Device config + return false; + } + + /** + * Captures the consolidated emergency numbers list and returns the array of + * {@link PersistAtomsProto.EmergencyNumber}. + */ + public PersistAtomsProto.EmergencyNumbersInfo[] getEmergencyNumbersProtoArray() { + int otaVersion = Math.max(0, getEmergencyNumberOtaDbVersion()); + int assetVersion = Math.max(0, getEmergencyNumberDbVersion()); + boolean isDbRoutingIgnored = shouldEmergencyNumberRoutingFromDbBeIgnored(); + List<EmergencyNumber> emergencyNumberList = getEmergencyNumberList(); + logd("log emergency number list=" + emergencyNumberList + " for otaVersion=" + otaVersion + + ", assetVersion=" + assetVersion + ", isDbRoutingIgnored=" + isDbRoutingIgnored); + return EmergencyNumberStats.getInstance().convertEmergencyNumbersListToProto( + emergencyNumberList, assetVersion, otaVersion, isDbRoutingIgnored); + } + + /** * Dump Emergency Number List info in the tracking * * @param fd FileDescriptor @@ -1190,9 +1316,6 @@ */ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); - ipw.println(" Hal Version:" + mPhone.getHalVersion()); - ipw.println(" ========================================= "); - ipw.println(" Country Iso:" + getEmergencyCountryIso()); ipw.println(" ========================================= "); @@ -1229,11 +1352,6 @@ ipw.decreaseIndent(); ipw.println(" ========================================= "); - int slotId = SubscriptionController.getInstance().getSlotIndex(mPhone.getSubId()); - String ecclist = (slotId <= 0) ? "ril.ecclist" : ("ril.ecclist" + slotId); - ipw.println(" ril.ecclist: " + SystemProperties.get(ecclist, "")); - ipw.println(" ========================================= "); - ipw.println("Emergency Number List for Phone" + "(" + mPhone.getPhoneId() + ")"); ipw.increaseIndent(); ipw.println(getEmergencyNumberList());
diff --git a/src/java/com/android/internal/telephony/emergency/EmergencyStateTracker.java b/src/java/com/android/internal/telephony/emergency/EmergencyStateTracker.java new file mode 100644 index 0000000..96cd880 --- /dev/null +++ b/src/java/com/android/internal/telephony/emergency/EmergencyStateTracker.java
@@ -0,0 +1,1208 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.telephony.emergency; + +import static com.android.internal.telephony.emergency.EmergencyConstants.MODE_EMERGENCY_CALLBACK; +import static com.android.internal.telephony.emergency.EmergencyConstants.MODE_EMERGENCY_NONE; +import static com.android.internal.telephony.emergency.EmergencyConstants.MODE_EMERGENCY_WWAN; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.AsyncResult; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.os.PersistableBundle; +import android.os.PowerManager; +import android.os.UserHandle; +import android.provider.Settings; +import android.sysprop.TelephonyProperties; +import android.telephony.Annotation.DisconnectCauses; +import android.telephony.CarrierConfigManager; +import android.telephony.DisconnectCause; +import android.telephony.EmergencyRegResult; +import android.telephony.NetworkRegistrationInfo; +import android.telephony.ServiceState; +import android.telephony.SubscriptionManager; +import android.telephony.TelephonyManager; +import android.telephony.emergency.EmergencyNumber; +import android.util.ArraySet; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.telephony.Call; +import com.android.internal.telephony.GsmCdmaPhone; +import com.android.internal.telephony.Phone; +import com.android.internal.telephony.PhoneConstants; +import com.android.internal.telephony.PhoneFactory; +import com.android.internal.telephony.TelephonyIntents; +import com.android.internal.telephony.data.PhoneSwitcher; +import com.android.telephony.Rlog; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.Arrays; +import java.util.Objects; +import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; + +/** + * Tracks the emergency call state and notifies listeners of changes to the emergency mode. + */ +public class EmergencyStateTracker { + + private static final String TAG = "EmergencyStateTracker"; + + /** + * Timeout before we continue with the emergency call without waiting for DDS switch response + * from the modem. + */ + private static final int DEFAULT_DATA_SWITCH_TIMEOUT_MS = 1000; + /** Default value for if Emergency Callback Mode is supported. */ + private static final boolean DEFAULT_EMERGENCY_CALLBACK_MODE_SUPPORTED = true; + /** Default Emergency Callback Mode exit timeout value. */ + private static final long DEFAULT_ECM_EXIT_TIMEOUT_MS = 300000; + + /** The emergency types used when setting the emergency mode on modem. */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = "EMERGENCY_TYPE_", + value = { + EMERGENCY_TYPE_CALL, + EMERGENCY_TYPE_SMS}) + public @interface EmergencyType {} + + /** Indicates the emergency type is call. */ + public static final int EMERGENCY_TYPE_CALL = 1; + /** Indicates the emergency type is SMS. */ + public static final int EMERGENCY_TYPE_SMS = 2; + + private static EmergencyStateTracker INSTANCE = null; + + private final Context mContext; + private final CarrierConfigManager mConfigManager; + private final Handler mHandler; + private final boolean mIsSuplDdsSwitchRequiredForEmergencyCall; + private final PowerManager.WakeLock mWakeLock; + private RadioOnHelper mRadioOnHelper; + @EmergencyConstants.EmergencyMode + private int mEmergencyMode = MODE_EMERGENCY_NONE; + private boolean mWasEmergencyModeSetOnModem; + private EmergencyRegResult mLastEmergencyRegResult; + private boolean mIsEmergencyModeInProgress; + private boolean mIsEmergencyCallStartedDuringEmergencySms; + + /** For emergency calls */ + private final long mEcmExitTimeoutMs; + // A runnable which is used to automatically exit from Ecm after a period of time. + private final Runnable mExitEcmRunnable = this::exitEmergencyCallbackMode; + // Tracks emergency calls by callId that have reached {@link Call.State#ACTIVE}. + private final Set<String> mActiveEmergencyCalls = new ArraySet<>(); + private Phone mPhone; + // Tracks ongoing emergency callId to handle a second emergency call + private String mOngoingCallId; + // Domain of the active emergency call. Assuming here that there will only be one domain active. + private int mEmergencyCallDomain = NetworkRegistrationInfo.DOMAIN_UNKNOWN; + private CompletableFuture<Integer> mCallEmergencyModeFuture; + private boolean mIsInEmergencyCall; + private boolean mIsInEcm; + private boolean mIsTestEmergencyNumber; + private Runnable mOnEcmExitCompleteRunnable; + + /** For emergency SMS */ + private final Set<String> mOngoingEmergencySmsIds = new ArraySet<>(); + private Phone mSmsPhone; + private CompletableFuture<Integer> mSmsEmergencyModeFuture; + private boolean mIsTestEmergencyNumberForSms; + + /** + * Listens for Emergency Callback Mode state change intents + */ + private final BroadcastReceiver mEcmExitReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (intent.getAction().equals( + TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED)) { + + boolean isInEcm = intent.getBooleanExtra( + TelephonyManager.EXTRA_PHONE_IN_ECM_STATE, false); + Rlog.d(TAG, "Received ACTION_EMERGENCY_CALLBACK_MODE_CHANGED isInEcm = " + isInEcm); + + // If we exit ECM mode, notify all connections. + if (!isInEcm) { + exitEmergencyCallbackMode(); + } + } + } + }; + + /** PhoneFactory Dependencies for testing. */ + @VisibleForTesting + public interface PhoneFactoryProxy { + Phone[] getPhones(); + } + + private PhoneFactoryProxy mPhoneFactoryProxy = PhoneFactory::getPhones; + + /** PhoneSwitcher dependencies for testing. */ + @VisibleForTesting + public interface PhoneSwitcherProxy { + + PhoneSwitcher getPhoneSwitcher(); + } + + private PhoneSwitcherProxy mPhoneSwitcherProxy = PhoneSwitcher::getInstance; + + /** + * TelephonyManager dependencies for testing. + */ + @VisibleForTesting + public interface TelephonyManagerProxy { + int getPhoneCount(); + } + + private final TelephonyManagerProxy mTelephonyManagerProxy; + + private static class TelephonyManagerProxyImpl implements TelephonyManagerProxy { + private final TelephonyManager mTelephonyManager; + + + TelephonyManagerProxyImpl(Context context) { + mTelephonyManager = new TelephonyManager(context); + } + + @Override + public int getPhoneCount() { + return mTelephonyManager.getActiveModemCount(); + } + } + + /** + * Return the handler for testing. + */ + @VisibleForTesting + public Handler getHandler() { + return mHandler; + } + + @VisibleForTesting + public static final int MSG_SET_EMERGENCY_MODE_DONE = 1; + @VisibleForTesting + public static final int MSG_EXIT_EMERGENCY_MODE_DONE = 2; + @VisibleForTesting + public static final int MSG_SET_EMERGENCY_CALLBACK_MODE_DONE = 3; + + private class MyHandler extends Handler { + + MyHandler(Looper looper) { + super(looper); + } + + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_SET_EMERGENCY_MODE_DONE: { + AsyncResult ar = (AsyncResult) msg.obj; + Integer emergencyType = (Integer) ar.userObj; + Rlog.v(TAG, "MSG_SET_EMERGENCY_MODE_DONE for " + + emergencyTypeToString(emergencyType)); + if (ar.exception == null) { + mLastEmergencyRegResult = (EmergencyRegResult) ar.result; + } else { + mLastEmergencyRegResult = null; + Rlog.w(TAG, "LastEmergencyRegResult not set. AsyncResult.exception: " + + ar.exception); + } + setEmergencyModeInProgress(false); + + if (emergencyType == EMERGENCY_TYPE_CALL) { + setIsInEmergencyCall(true); + completeEmergencyMode(emergencyType); + + // Case 1) When the emergency call is setting the emergency mode and + // the emergency SMS is being sent, completes the SMS future also. + // Case 2) When the emergency SMS is setting the emergency mode and + // the emergency call is beint started, the SMS request is cancelled and + // the call request will be handled. + if (mSmsPhone != null) { + completeEmergencyMode(EMERGENCY_TYPE_SMS); + } + } else if (emergencyType == EMERGENCY_TYPE_SMS) { + if (mPhone != null && mSmsPhone != null) { + // Clear call phone temporarily to exit the emergency mode + // if the emergency call is started. + if (mIsEmergencyCallStartedDuringEmergencySms) { + Phone phone = mPhone; + mPhone = null; + exitEmergencyMode(mSmsPhone, emergencyType); + // Restore call phone for further use. + mPhone = phone; + + if (!isSamePhone(mPhone, mSmsPhone)) { + completeEmergencyMode(emergencyType, + DisconnectCause.OUTGOING_EMERGENCY_CALL_PLACED); + } + } else { + completeEmergencyMode(emergencyType); + } + break; + } else { + completeEmergencyMode(emergencyType); + } + + if (mIsEmergencyCallStartedDuringEmergencySms) { + mIsEmergencyCallStartedDuringEmergencySms = false; + turnOnRadioAndSwitchDds(mPhone, EMERGENCY_TYPE_CALL, + mIsTestEmergencyNumber); + } + } + break; + } + case MSG_EXIT_EMERGENCY_MODE_DONE: { + AsyncResult ar = (AsyncResult) msg.obj; + Integer emergencyType = (Integer) ar.userObj; + Rlog.v(TAG, "MSG_EXIT_EMERGENCY_MODE_DONE for " + + emergencyTypeToString(emergencyType)); + setEmergencyModeInProgress(false); + + if (emergencyType == EMERGENCY_TYPE_CALL) { + setIsInEmergencyCall(false); + if (mOnEcmExitCompleteRunnable != null) { + mOnEcmExitCompleteRunnable.run(); + mOnEcmExitCompleteRunnable = null; + } + } else if (emergencyType == EMERGENCY_TYPE_SMS) { + if (mIsEmergencyCallStartedDuringEmergencySms) { + mIsEmergencyCallStartedDuringEmergencySms = false; + turnOnRadioAndSwitchDds(mPhone, EMERGENCY_TYPE_CALL, + mIsTestEmergencyNumber); + } + } + break; + } + case MSG_SET_EMERGENCY_CALLBACK_MODE_DONE: { + AsyncResult ar = (AsyncResult) msg.obj; + Integer emergencyType = (Integer) ar.userObj; + Rlog.v(TAG, "MSG_SET_EMERGENCY_CALLBACK_MODE_DONE for " + + emergencyTypeToString(emergencyType)); + setEmergencyModeInProgress(false); + // When the emergency callback mode is in progress and the emergency SMS is + // started, it needs to be completed here for the emergency SMS. + if (mSmsPhone != null) { + completeEmergencyMode(EMERGENCY_TYPE_SMS); + } + break; + } + default: + break; + } + } + } + + /** + * Creates the EmergencyStateTracker singleton instance. + * + * @param context The context of the application. + * @param isSuplDdsSwitchRequiredForEmergencyCall Whether gnss supl requires default data for + * emergency call. + */ + public static void make(Context context, boolean isSuplDdsSwitchRequiredForEmergencyCall) { + if (INSTANCE == null) { + INSTANCE = new EmergencyStateTracker(context, Looper.myLooper(), + isSuplDdsSwitchRequiredForEmergencyCall); + } + } + + /** + * Returns the singleton instance of EmergencyStateTracker. + * + * @return {@link EmergencyStateTracker} instance. + */ + public static EmergencyStateTracker getInstance() { + if (INSTANCE == null) { + throw new IllegalStateException("EmergencyStateTracker is not ready!"); + } + return INSTANCE; + } + + /** + * Initializes EmergencyStateTracker. + */ + private EmergencyStateTracker(Context context, Looper looper, + boolean isSuplDdsSwitchRequiredForEmergencyCall) { + mEcmExitTimeoutMs = DEFAULT_ECM_EXIT_TIMEOUT_MS; + mContext = context; + mHandler = new MyHandler(looper); + mIsSuplDdsSwitchRequiredForEmergencyCall = isSuplDdsSwitchRequiredForEmergencyCall; + + PowerManager pm = context.getSystemService(PowerManager.class); + mWakeLock = (pm != null) ? pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, + "telephony:" + TAG) : null; + mConfigManager = context.getSystemService(CarrierConfigManager.class); + + // Register receiver for ECM exit. + IntentFilter filter = new IntentFilter(); + filter.addAction(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED); + context.registerReceiver(mEcmExitReceiver, filter, null, mHandler); + mTelephonyManagerProxy = new TelephonyManagerProxyImpl(context); + } + + /** + * Initializes EmergencyStateTracker with injections for testing. + * + * @param context The context of the application. + * @param looper The {@link Looper} of the application. + * @param isSuplDdsSwitchRequiredForEmergencyCall Whether gnss supl requires default data for + * emergency call. + * @param phoneFactoryProxy The {@link PhoneFactoryProxy} to be injected. + * @param phoneSwitcherProxy The {@link PhoneSwitcherProxy} to be injected. + * @param telephonyManagerProxy The {@link TelephonyManagerProxy} to be + * injected. + * @param radioOnHelper The {@link RadioOnHelper} to be injected. + */ + @VisibleForTesting + public EmergencyStateTracker(Context context, Looper looper, + boolean isSuplDdsSwitchRequiredForEmergencyCall, PhoneFactoryProxy phoneFactoryProxy, + PhoneSwitcherProxy phoneSwitcherProxy, TelephonyManagerProxy telephonyManagerProxy, + RadioOnHelper radioOnHelper, long ecmExitTimeoutMs) { + mContext = context; + mHandler = new MyHandler(looper); + mIsSuplDdsSwitchRequiredForEmergencyCall = isSuplDdsSwitchRequiredForEmergencyCall; + mPhoneFactoryProxy = phoneFactoryProxy; + mPhoneSwitcherProxy = phoneSwitcherProxy; + mTelephonyManagerProxy = telephonyManagerProxy; + mRadioOnHelper = radioOnHelper; + mEcmExitTimeoutMs = ecmExitTimeoutMs; + mWakeLock = null; // Don't declare a wakelock in tests + mConfigManager = context.getSystemService(CarrierConfigManager.class); + IntentFilter filter = new IntentFilter(); + filter.addAction(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED); + context.registerReceiver(mEcmExitReceiver, filter, null, mHandler); + } + + /** + * Starts the process of an emergency call. + * + * <p> + * Handles turning on radio and switching DDS. + * + * @param phone the {@code Phone} on which to process the emergency call. + * @param callId the call id on which to process the emergency call. + * @param isTestEmergencyNumber whether this is a test emergency number. + * @return a {@code CompletableFuture} that results in {@code DisconnectCause.NOT_DISCONNECTED} + * if emergency call successfully started. + */ + public CompletableFuture<Integer> startEmergencyCall(@NonNull Phone phone, + @NonNull String callId, boolean isTestEmergencyNumber) { + Rlog.i(TAG, "startEmergencyCall: phoneId=" + phone.getPhoneId() + ", callId=" + callId); + + if (mPhone != null) { + // Create new future to return as to not interfere with any uncompleted futures. + // Case1) When 2nd emergency call is initiated during an active call on the same phone. + // Case2) While the device is in ECBM, an emergency call is initiated on the same phone. + if (isSamePhone(mPhone, phone) && (!mActiveEmergencyCalls.isEmpty() || isInEcm())) { + mOngoingCallId = callId; + mIsTestEmergencyNumber = isTestEmergencyNumber; + return CompletableFuture.completedFuture(DisconnectCause.NOT_DISCONNECTED); + } + + Rlog.e(TAG, "startEmergencyCall failed. Existing emergency call in progress."); + return CompletableFuture.completedFuture(DisconnectCause.ERROR_UNSPECIFIED); + } + + mCallEmergencyModeFuture = new CompletableFuture<>(); + + if (mSmsPhone != null) { + mIsEmergencyCallStartedDuringEmergencySms = true; + // Case1) While exiting the emergency mode on the other phone, + // the emergency mode for this call will be restarted after the exit complete. + // Case2) While entering the emergency mode on the other phone, + // exit the emergency mode when receiving the result of setting the emergency mode and + // the emergency mode for this call will be restarted after the exit complete. + if (isInEmergencyMode() && !isEmergencyModeInProgress()) { + exitEmergencyMode(mSmsPhone, EMERGENCY_TYPE_SMS); + } + + mPhone = phone; + mOngoingCallId = callId; + mIsTestEmergencyNumber = isTestEmergencyNumber; + return mCallEmergencyModeFuture; + } + + mPhone = phone; + mOngoingCallId = callId; + mIsTestEmergencyNumber = isTestEmergencyNumber; + turnOnRadioAndSwitchDds(mPhone, EMERGENCY_TYPE_CALL, mIsTestEmergencyNumber); + return mCallEmergencyModeFuture; + } + + /** + * Ends emergency call. + * + * <p> + * Enter ECM only once all active emergency calls have ended. If a call never reached + * {@link Call.State#ACTIVE}, then no need to enter ECM. + * + * @param callId the call id on which to end the emergency call. + */ + public void endCall(@NonNull String callId) { + boolean wasActive = mActiveEmergencyCalls.remove(callId); + + if (Objects.equals(mOngoingCallId, callId)) { + mOngoingCallId = null; + } + + if (wasActive && mActiveEmergencyCalls.isEmpty() + && isEmergencyCallbackModeSupported()) { + enterEmergencyCallbackMode(); + + if (mOngoingCallId == null) { + mIsEmergencyCallStartedDuringEmergencySms = false; + mCallEmergencyModeFuture = null; + } + } else if (mOngoingCallId == null) { + if (isInEcm()) { + mIsEmergencyCallStartedDuringEmergencySms = false; + mCallEmergencyModeFuture = null; + // If the emergency call was initiated during the emergency callback mode, + // the emergency callback mode should be restored when the emergency call is ended. + if (mActiveEmergencyCalls.isEmpty()) { + setEmergencyMode(mPhone, EMERGENCY_TYPE_CALL, MODE_EMERGENCY_CALLBACK, + MSG_SET_EMERGENCY_CALLBACK_MODE_DONE); + } + } else { + exitEmergencyMode(mPhone, EMERGENCY_TYPE_CALL); + clearEmergencyCallInfo(); + } + } + } + + private void clearEmergencyCallInfo() { + mEmergencyCallDomain = NetworkRegistrationInfo.DOMAIN_UNKNOWN; + mIsTestEmergencyNumber = false; + mIsEmergencyCallStartedDuringEmergencySms = false; + mCallEmergencyModeFuture = null; + mOngoingCallId = null; + mPhone = null; + } + + private void switchDdsAndSetEmergencyMode(Phone phone, @EmergencyType int emergencyType) { + switchDdsDelayed(phone, result -> { + Rlog.i(TAG, "switchDdsDelayed: result = " + result); + if (!result) { + // DDS Switch timed out/failed, but continue with call as it may still succeed. + Rlog.e(TAG, "DDS Switch failed."); + } + // Once radio is on and DDS switched, must call setEmergencyMode() before selecting + // emergency domain. EmergencyRegResult is required to determine domain and this is the + // only API that can receive it before starting domain selection. Once domain selection + // is finished, the actual emergency mode will be set when onEmergencyTransportChanged() + // is called. + setEmergencyMode(phone, emergencyType, MODE_EMERGENCY_WWAN, + MSG_SET_EMERGENCY_MODE_DONE); + }); + } + + /** + * Triggers modem to set new emergency mode. + * + * @param phone the {@code Phone} to set the emergency mode on modem. + * @param emergencyType the emergency type to identify an emergency call or SMS. + * @param mode the new emergency mode. + * @param msg the message to be sent once mode has been set. + */ + private void setEmergencyMode(Phone phone, @EmergencyType int emergencyType, + @EmergencyConstants.EmergencyMode int mode, int msg) { + Rlog.i(TAG, "setEmergencyMode from " + mEmergencyMode + " to " + mode + " for " + + emergencyTypeToString(emergencyType)); + + if (mEmergencyMode == mode) { + return; + } + mEmergencyMode = mode; + setEmergencyModeInProgress(true); + + Message m = mHandler.obtainMessage(msg, Integer.valueOf(emergencyType)); + if ((mIsTestEmergencyNumber && emergencyType == EMERGENCY_TYPE_CALL) + || (mIsTestEmergencyNumberForSms && emergencyType == EMERGENCY_TYPE_SMS)) { + Rlog.d(TAG, "TestEmergencyNumber for " + emergencyTypeToString(emergencyType) + + ": Skipping setting emergency mode on modem."); + // Send back a response for the command, but with null information + AsyncResult.forMessage(m, null, null); + // Ensure that we do not accidentally block indefinitely when trying to validate test + // emergency numbers + m.sendToTarget(); + return; + } + + mWasEmergencyModeSetOnModem = true; + phone.setEmergencyMode(mode, m); + } + + private void completeEmergencyMode(@EmergencyType int emergencyType) { + completeEmergencyMode(emergencyType, DisconnectCause.NOT_DISCONNECTED); + } + + private void completeEmergencyMode(@EmergencyType int emergencyType, + @DisconnectCauses int result) { + if (emergencyType == EMERGENCY_TYPE_CALL) { + if (mCallEmergencyModeFuture != null && !mCallEmergencyModeFuture.isDone()) { + mCallEmergencyModeFuture.complete(result); + } + + if (result != DisconnectCause.NOT_DISCONNECTED) { + clearEmergencyCallInfo(); + } + } else if (emergencyType == EMERGENCY_TYPE_SMS) { + if (mSmsEmergencyModeFuture != null && !mSmsEmergencyModeFuture.isDone()) { + mSmsEmergencyModeFuture.complete(result); + } + + if (result != DisconnectCause.NOT_DISCONNECTED) { + clearEmergencySmsInfo(); + } + } + } + + /** + * Checks if the device is currently in the emergency mode or not. + */ + @VisibleForTesting + public boolean isInEmergencyMode() { + return mEmergencyMode != MODE_EMERGENCY_NONE; + } + + /** + * Sets the flag to inidicate whether setting the emergency mode on modem is in progress or not. + */ + private void setEmergencyModeInProgress(boolean isEmergencyModeInProgress) { + mIsEmergencyModeInProgress = isEmergencyModeInProgress; + } + + /** + * Checks whether setting the emergency mode on modem is in progress or not. + */ + private boolean isEmergencyModeInProgress() { + return mIsEmergencyModeInProgress; + } + + /** + * Notifies external app listeners of emergency mode changes. + * + * @param isInEmergencyCall a flag to indicate whether there is an active emergency call. + */ + private void setIsInEmergencyCall(boolean isInEmergencyCall) { + mIsInEmergencyCall = isInEmergencyCall; + } + + /** + * Checks if there is an ongoing emergency call. + * + * @return true if in emergency call + */ + public boolean isInEmergencyCall() { + return mIsInEmergencyCall; + } + + /** + * Triggers modem to exit emergency mode. + * + * @param phone the {@code Phone} to exit the emergency mode. + * @param emergencyType the emergency type to identify an emergency call or SMS. + */ + private void exitEmergencyMode(Phone phone, @EmergencyType int emergencyType) { + Rlog.i(TAG, "exitEmergencyMode for " + emergencyTypeToString(emergencyType)); + + if (emergencyType == EMERGENCY_TYPE_CALL) { + if (mSmsPhone != null && isSamePhone(phone, mSmsPhone)) { + // Waits for exiting the emergency mode until the emergency SMS is ended. + Rlog.i(TAG, "exitEmergencyMode: waits for emergency SMS end."); + setIsInEmergencyCall(false); + return; + } + } else if (emergencyType == EMERGENCY_TYPE_SMS) { + if (mPhone != null && isSamePhone(phone, mPhone)) { + // Waits for exiting the emergency mode until the emergency call is ended. + Rlog.i(TAG, "exitEmergencyMode: waits for emergency call end."); + return; + } + } + + if (mEmergencyMode == MODE_EMERGENCY_NONE) { + return; + } + mEmergencyMode = MODE_EMERGENCY_NONE; + setEmergencyModeInProgress(true); + + Message m = mHandler.obtainMessage( + MSG_EXIT_EMERGENCY_MODE_DONE, Integer.valueOf(emergencyType)); + if (!mWasEmergencyModeSetOnModem) { + Rlog.d(TAG, "Emergency mode was not set on modem: Skipping exiting emergency mode."); + // Send back a response for the command, but with null information + AsyncResult.forMessage(m, null, null); + // Ensure that we do not accidentally block indefinitely when trying to validate + // the exit condition. + m.sendToTarget(); + return; + } + + mWasEmergencyModeSetOnModem = false; + phone.exitEmergencyMode(m); + } + + /** Returns last {@link EmergencyRegResult} as set by {@code setEmergencyMode()}. */ + public EmergencyRegResult getEmergencyRegResult() { + return mLastEmergencyRegResult; + } + + /** + * Handles emergency transport change by setting new emergency mode. + * + * @param emergencyType the emergency type to identify an emergency call or SMS + * @param mode the new emergency mode + */ + public void onEmergencyTransportChanged(@EmergencyType int emergencyType, + @EmergencyConstants.EmergencyMode int mode) { + if (mHandler.getLooper().isCurrentThread()) { + Phone phone = null; + if (emergencyType == EMERGENCY_TYPE_CALL) { + phone = mPhone; + } else if (emergencyType == EMERGENCY_TYPE_SMS) { + phone = mSmsPhone; + } + + if (phone != null) { + setEmergencyMode(phone, emergencyType, mode, MSG_SET_EMERGENCY_MODE_DONE); + } + } else { + mHandler.post(() -> { + onEmergencyTransportChanged(emergencyType, mode); + }); + } + } + + /** + * Notify the tracker that the emergency call domain has been updated. + * @param phoneType The new PHONE_TYPE_* of the call. + * @param callId The ID of the call + */ + public void onEmergencyCallDomainUpdated(int phoneType, String callId) { + Rlog.d(TAG, "domain update for callId: " + callId); + int domain = -1; + switch(phoneType) { + case (PhoneConstants.PHONE_TYPE_CDMA_LTE): + //fallthrough + case (PhoneConstants.PHONE_TYPE_GSM): + //fallthrough + case (PhoneConstants.PHONE_TYPE_CDMA): { + domain = NetworkRegistrationInfo.DOMAIN_CS; + break; + } + case (PhoneConstants.PHONE_TYPE_IMS): { + domain = NetworkRegistrationInfo.DOMAIN_PS; + break; + } + default: { + Rlog.w(TAG, "domain updated: Unexpected phoneType:" + phoneType); + } + } + if (mEmergencyCallDomain == domain) return; + Rlog.i(TAG, "domain updated: from " + mEmergencyCallDomain + " to " + domain); + mEmergencyCallDomain = domain; + } + + /** + * Handles emergency call state change. + * + * @param state the new call state + * @param callId the callId whose state has changed + */ + public void onEmergencyCallStateChanged(Call.State state, String callId) { + if (state == Call.State.ACTIVE) { + mActiveEmergencyCalls.add(callId); + } + } + + /** + * Returns {@code true} if device and carrier support emergency callback mode. + */ + private boolean isEmergencyCallbackModeSupported() { + return getConfig(mPhone.getSubId(), + CarrierConfigManager.ImsEmergency.KEY_EMERGENCY_CALLBACK_MODE_SUPPORTED_BOOL, + DEFAULT_EMERGENCY_CALLBACK_MODE_SUPPORTED); + } + + /** + * Trigger entry into emergency callback mode. + */ + private void enterEmergencyCallbackMode() { + Rlog.d(TAG, "enter ECBM"); + setIsInEmergencyCall(false); + // Check if not in ECM already. + if (!isInEcm()) { + setIsInEcm(true); + if (!mPhone.getUnitTestMode()) { + TelephonyProperties.in_ecm_mode(true); + } + + // Notify listeners of the entrance to ECM. + sendEmergencyCallbackModeChange(); + if (isInImsEcm()) { + // emergency call registrants are not notified of new emergency call until entering + // ECBM (see ImsPhone#handleEnterEmergencyCallbackMode) + ((GsmCdmaPhone) mPhone).notifyEmergencyCallRegistrants(true); + } + + // Set emergency mode on modem. + setEmergencyMode(mPhone, EMERGENCY_TYPE_CALL, MODE_EMERGENCY_CALLBACK, + MSG_SET_EMERGENCY_CALLBACK_MODE_DONE); + + // Post this runnable so we will automatically exit if no one invokes + // exitEmergencyCallbackMode() directly. + long delayInMillis = TelephonyProperties.ecm_exit_timer() + .orElse(mEcmExitTimeoutMs); + mHandler.postDelayed(mExitEcmRunnable, delayInMillis); + + // We don't want to go to sleep while in ECM. + if (mWakeLock != null) mWakeLock.acquire(delayInMillis); + } + } + + /** + * Exits emergency callback mode and notifies relevant listeners. + */ + public void exitEmergencyCallbackMode() { + Rlog.d(TAG, "exit ECBM"); + // Remove pending exit ECM runnable, if any. + mHandler.removeCallbacks(mExitEcmRunnable); + + if (isInEcm()) { + setIsInEcm(false); + if (!mPhone.getUnitTestMode()) { + TelephonyProperties.in_ecm_mode(false); + } + + // Release wakeLock. + if (mWakeLock != null && mWakeLock.isHeld()) { + try { + mWakeLock.release(); + } catch (Exception e) { + // Ignore the exception if the system has already released this WakeLock. + Rlog.d(TAG, "WakeLock already released: " + e.toString()); + } + } + + GsmCdmaPhone gsmCdmaPhone = (GsmCdmaPhone) mPhone; + // Send intents that ECM has changed. + sendEmergencyCallbackModeChange(); + gsmCdmaPhone.notifyEmergencyCallRegistrants(false); + + // Exit emergency mode on modem. + exitEmergencyMode(gsmCdmaPhone, EMERGENCY_TYPE_CALL); + } + + mEmergencyCallDomain = NetworkRegistrationInfo.DOMAIN_UNKNOWN; + mIsTestEmergencyNumber = false; + mPhone = null; + } + + /** + * Exits emergency callback mode and triggers runnable after exit response is received. + */ + public void exitEmergencyCallbackMode(Runnable onComplete) { + mOnEcmExitCompleteRunnable = onComplete; + exitEmergencyCallbackMode(); + } + + /** + * Sends intents that emergency callback mode changed. + */ + private void sendEmergencyCallbackModeChange() { + Rlog.d(TAG, "sendEmergencyCallbackModeChange: isInEcm=" + isInEcm()); + + Intent intent = new Intent(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED); + intent.putExtra(TelephonyManager.EXTRA_PHONE_IN_ECM_STATE, isInEcm()); + SubscriptionManager.putPhoneIdAndSubIdExtra(intent, mPhone.getPhoneId()); + mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); + } + + /** + * Returns {@code true} if currently in emergency callback mode. + * + * <p> + * This is a period where the phone should be using as little power as possible and be ready to + * receive an incoming call from the emergency operator. + */ + public boolean isInEcm() { + return mIsInEcm; + } + + /** + * Sets the emergency callback mode state. + * + * @param isInEcm {@code true} if currently in emergency callback mode, {@code false} otherwise. + */ + private void setIsInEcm(boolean isInEcm) { + mIsInEcm = isInEcm; + } + + /** + * Returns {@code true} if currently in emergency callback mode over PS + */ + public boolean isInImsEcm() { + return mEmergencyCallDomain == NetworkRegistrationInfo.DOMAIN_PS && isInEcm(); + } + + /** + * Returns {@code true} if currently in emergency callback mode over CS + */ + public boolean isInCdmaEcm() { + // Phone can be null in the case where we are not actively tracking an emergency call. + if (mPhone == null) return false; + // Ensure that this method doesn't return true when we are attached to GSM. + return mPhone.getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA + && mEmergencyCallDomain == NetworkRegistrationInfo.DOMAIN_CS && isInEcm(); + } + + /** + * Starts the process of an emergency SMS. + * + * @param phone the {@code Phone} on which to process the emergency SMS. + * @param smsId the SMS id on which to process the emergency SMS. + * @param isTestEmergencyNumber whether this is a test emergency number. + * @return A {@code CompletableFuture} that results in {@code DisconnectCause.NOT_DISCONNECTED} + * if the emergency SMS is successfully started. + */ + public CompletableFuture<Integer> startEmergencySms(@NonNull Phone phone, @NonNull String smsId, + boolean isTestEmergencyNumber) { + Rlog.i(TAG, "startEmergencySms: phoneId=" + phone.getPhoneId() + ", smsId=" + smsId); + + // When an emergency call is in progress, it checks whether an emergency call is already in + // progress on the different phone. + if (mPhone != null && !isSamePhone(mPhone, phone)) { + Rlog.e(TAG, "Emergency call is in progress on the other slot."); + return CompletableFuture.completedFuture(DisconnectCause.ERROR_UNSPECIFIED); + } + + // When an emergency SMS is in progress, it checks whether an emergency SMS is already in + // progress on the different phone. + if (mSmsPhone != null && !isSamePhone(mSmsPhone, phone)) { + Rlog.e(TAG, "Emergency SMS is in progress on the other slot."); + return CompletableFuture.completedFuture(DisconnectCause.ERROR_UNSPECIFIED); + } + + // When the previous emergency SMS is not completed yet, + // this new request will not be allowed. + if (mSmsPhone != null && isInEmergencyMode() && isEmergencyModeInProgress()) { + Rlog.e(TAG, "Existing emergency SMS is in progress."); + return CompletableFuture.completedFuture(DisconnectCause.ERROR_UNSPECIFIED); + } + + mSmsPhone = phone; + mIsTestEmergencyNumberForSms = isTestEmergencyNumber; + mOngoingEmergencySmsIds.add(smsId); + + // When the emergency mode is already set by the previous emergency call or SMS, + // completes the future immediately. + if (isInEmergencyMode() && !isEmergencyModeInProgress()) { + return CompletableFuture.completedFuture(DisconnectCause.NOT_DISCONNECTED); + } + + mSmsEmergencyModeFuture = new CompletableFuture<>(); + if (!isInEmergencyMode()) { + setEmergencyMode(mSmsPhone, EMERGENCY_TYPE_SMS, MODE_EMERGENCY_WWAN, + MSG_SET_EMERGENCY_MODE_DONE); + } + return mSmsEmergencyModeFuture; + } + + /** + * Ends an emergency SMS. + * This should be called once an emergency SMS is sent. + * + * @param smsId the SMS id on which to end the emergency SMS. + * @param emergencyNumber the emergency number which was used for the emergency SMS. + */ + public void endSms(@NonNull String smsId, EmergencyNumber emergencyNumber) { + mOngoingEmergencySmsIds.remove(smsId); + + // If the outgoing emergency SMSs are empty, we can try to exit the emergency mode. + if (mOngoingEmergencySmsIds.isEmpty()) { + if (isInEcm()) { + // When the emergency mode is not in MODE_EMERGENCY_CALLBACK, + // it needs to notify the emergency callback mode to modem. + if (mActiveEmergencyCalls.isEmpty() && mOngoingCallId == null) { + setEmergencyMode(mPhone, EMERGENCY_TYPE_CALL, MODE_EMERGENCY_CALLBACK, + MSG_SET_EMERGENCY_CALLBACK_MODE_DONE); + } + } else { + exitEmergencyMode(mSmsPhone, EMERGENCY_TYPE_SMS); + } + + clearEmergencySmsInfo(); + } + } + + private void clearEmergencySmsInfo() { + mOngoingEmergencySmsIds.clear(); + mIsTestEmergencyNumberForSms = false; + mSmsEmergencyModeFuture = null; + mSmsPhone = null; + } + + /** + * Returns {@code true} if any phones from PhoneFactory have radio on. + */ + private boolean isRadioOn() { + boolean result = false; + for (Phone phone : mPhoneFactoryProxy.getPhones()) { + result |= phone.isRadioOn(); + } + return result; + } + + /** + * Returns {@code true} if airplane mode is on. + */ + private boolean isAirplaneModeOn(Context context) { + return Settings.Global.getInt(context.getContentResolver(), + Settings.Global.AIRPLANE_MODE_ON, 0) > 0; + } + + /** + * Ensures that the radio is switched on and that DDS is switched for emergency call/SMS. + * + * <p> + * Once radio is on and DDS switched, must call setEmergencyMode() before completing the future + * and selecting emergency domain. EmergencyRegResult is required to determine domain and + * setEmergencyMode() is the only API that can receive it before starting domain selection. + * Once domain selection is finished, the actual emergency mode will be set when + * onEmergencyTransportChanged() is called. + * + * @param phone the {@code Phone} for the emergency call/SMS. + * @param emergencyType the emergency type to identify an emergency call or SMS. + * @param isTestEmergencyNumber a flag to inidicate whether the emergency call/SMS uses the test + * emergency number. + */ + private void turnOnRadioAndSwitchDds(Phone phone, @EmergencyType int emergencyType, + boolean isTestEmergencyNumber) { + final boolean isAirplaneModeOn = isAirplaneModeOn(mContext); + boolean needToTurnOnRadio = !isRadioOn() || isAirplaneModeOn; + + if (needToTurnOnRadio) { + Rlog.i(TAG, "turnOnRadioAndSwitchDds: phoneId=" + phone.getPhoneId() + " for " + + emergencyTypeToString(emergencyType)); + if (mRadioOnHelper == null) { + mRadioOnHelper = new RadioOnHelper(mContext); + } + + mRadioOnHelper.triggerRadioOnAndListen(new RadioOnStateListener.Callback() { + @Override + public void onComplete(RadioOnStateListener listener, boolean isRadioReady) { + if (!isRadioReady) { + // Could not turn radio on + Rlog.e(TAG, "Failed to turn on radio."); + completeEmergencyMode(emergencyType, DisconnectCause.POWER_OFF); + } else { + switchDdsAndSetEmergencyMode(phone, emergencyType); + } + } + + @Override + public boolean isOkToCall(Phone phone, int serviceState, boolean imsVoiceCapable) { + // We currently only look to make sure that the radio is on before dialing. We + // should be able to make emergency calls at any time after the radio has been + // powered on and isn't in the UNAVAILABLE state, even if it is reporting the + // OUT_OF_SERVICE state. + return phone.getServiceStateTracker().isRadioOn(); + } + + @Override + public boolean onTimeout(Phone phone, int serviceState, boolean imsVoiceCapable) { + return true; + } + }, !isTestEmergencyNumber, phone, isTestEmergencyNumber, 0); + } else { + switchDdsAndSetEmergencyMode(phone, emergencyType); + } + } + + /** + * If needed, block until the default data is switched for outgoing emergency call, or + * timeout expires. + * + * @param phone The Phone to switch the DDS on. + * @param completeConsumer The consumer to call once the default data subscription has been + * switched, provides {@code true} result if the switch happened + * successfully or {@code false} if the operation timed out/failed. + */ + @VisibleForTesting + public void switchDdsDelayed(Phone phone, Consumer<Boolean> completeConsumer) { + if (phone == null) { + // Do not block indefinitely. + completeConsumer.accept(false); + } + try { + // Waiting for PhoneSwitcher to complete the operation. + CompletableFuture<Boolean> future = possiblyOverrideDefaultDataForEmergencyCall(phone); + // In the case that there is an issue or bug in PhoneSwitcher logic, do not wait + // indefinitely for the future to complete. Instead, set a timeout that will complete + // the future as to not block the outgoing call indefinitely. + CompletableFuture<Boolean> timeout = new CompletableFuture<>(); + mHandler.postDelayed(() -> timeout.complete(false), DEFAULT_DATA_SWITCH_TIMEOUT_MS); + // Also ensure that the Consumer is completed on the main thread. + CompletableFuture<Void> unused = future.acceptEitherAsync(timeout, completeConsumer, + mHandler::post); + } catch (Exception e) { + Rlog.w(TAG, "switchDdsDelayed - exception= " + e.getMessage()); + } + } + + /** + * If needed, block until Default Data subscription is switched for outgoing emergency call. + * + * <p> + * In some cases, we need to try to switch the Default Data subscription before placing the + * emergency call on DSDS devices. This includes the following situation: - The modem does not + * support processing GNSS SUPL requests on the non-default data subscription. For some carriers + * that do not provide a control plane fallback mechanism, the SUPL request will be dropped and + * we will not be able to get the user's location for the emergency call. In this case, we need + * to swap default data temporarily. + * + * @param phone Evaluates whether or not the default data should be moved to the phone + * specified. Should not be null. + */ + private CompletableFuture<Boolean> possiblyOverrideDefaultDataForEmergencyCall( + @NonNull Phone phone) { + int phoneCount = mTelephonyManagerProxy.getPhoneCount(); + // Do not override DDS if this is a single SIM device. + if (phoneCount <= PhoneConstants.MAX_PHONE_COUNT_SINGLE_SIM) { + return CompletableFuture.completedFuture(Boolean.TRUE); + } + + // Do not switch Default data if this device supports emergency SUPL on non-DDS. + if (!mIsSuplDdsSwitchRequiredForEmergencyCall) { + Rlog.d(TAG, "possiblyOverrideDefaultDataForEmergencyCall: not switching DDS, does not " + + "require DDS switch."); + return CompletableFuture.completedFuture(Boolean.TRUE); + } + + // Only override default data if we are IN_SERVICE already. + if (!isAvailableForEmergencyCalls(phone)) { + Rlog.d(TAG, "possiblyOverrideDefaultDataForEmergencyCall: not switching DDS"); + return CompletableFuture.completedFuture(Boolean.TRUE); + } + + // Only override default data if we are not roaming, we do not want to switch onto a network + // that only supports data plane only (if we do not know). + boolean isRoaming = phone.getServiceState().getVoiceRoaming(); + // In some roaming conditions, we know the roaming network doesn't support control plane + // fallback even though the home operator does. For these operators we will need to do a DDS + // switch anyway to make sure the SUPL request doesn't fail. + boolean roamingNetworkSupportsControlPlaneFallback = true; + String[] dataPlaneRoamPlmns = getConfig(phone.getSubId(), + CarrierConfigManager.Gps.KEY_ES_SUPL_DATA_PLANE_ONLY_ROAMING_PLMN_STRING_ARRAY); + if (dataPlaneRoamPlmns != null && Arrays.asList(dataPlaneRoamPlmns) + .contains(phone.getServiceState().getOperatorNumeric())) { + roamingNetworkSupportsControlPlaneFallback = false; + } + if (isRoaming && roamingNetworkSupportsControlPlaneFallback) { + Rlog.d(TAG, "possiblyOverrideDefaultDataForEmergencyCall: roaming network is assumed " + + "to support CP fallback, not switching DDS."); + return CompletableFuture.completedFuture(Boolean.TRUE); + } + // Do not try to swap default data if we support CS fallback or it is assumed that the + // roaming network supports control plane fallback, we do not want to introduce a lag in + // emergency call setup time if possible. + final boolean supportsCpFallback = getConfig(phone.getSubId(), + CarrierConfigManager.Gps.KEY_ES_SUPL_CONTROL_PLANE_SUPPORT_INT, + CarrierConfigManager.Gps.SUPL_EMERGENCY_MODE_TYPE_CP_ONLY) + != CarrierConfigManager.Gps.SUPL_EMERGENCY_MODE_TYPE_DP_ONLY; + if (supportsCpFallback && roamingNetworkSupportsControlPlaneFallback) { + Rlog.d(TAG, "possiblyOverrideDefaultDataForEmergencyCall: not switching DDS, carrier " + + "supports CP fallback."); + return CompletableFuture.completedFuture(Boolean.TRUE); + } + + // Get extension time, may be 0 for some carriers that support ECBM as well. Use + // CarrierConfig default if format fails. + int extensionTime = 0; + try { + extensionTime = Integer.parseInt(getConfig(phone.getSubId(), + CarrierConfigManager.Gps.KEY_ES_EXTENSION_SEC_STRING, "0")); + } catch (NumberFormatException e) { + // Just use default. + } + CompletableFuture<Boolean> modemResultFuture = new CompletableFuture<>(); + try { + Rlog.d(TAG, "possiblyOverrideDefaultDataForEmergencyCall: overriding DDS for " + + extensionTime + "seconds"); + mPhoneSwitcherProxy.getPhoneSwitcher().overrideDefaultDataForEmergency( + phone.getPhoneId(), extensionTime, modemResultFuture); + // Catch all exceptions, we want to continue with emergency call if possible. + } catch (Exception e) { + Rlog.w(TAG, + "possiblyOverrideDefaultDataForEmergencyCall: exception = " + e.getMessage()); + modemResultFuture = CompletableFuture.completedFuture(Boolean.FALSE); + } + return modemResultFuture; + } + + // Helper functions for easy CarrierConfigManager access + private String getConfig(int subId, String key, String defVal) { + return getConfigBundle(subId, key).getString(key, defVal); + } + private int getConfig(int subId, String key, int defVal) { + return getConfigBundle(subId, key).getInt(key, defVal); + } + private String[] getConfig(int subId, String key) { + return getConfigBundle(subId, key).getStringArray(key); + } + private boolean getConfig(int subId, String key, boolean defVal) { + return getConfigBundle(subId, key).getBoolean(key, defVal); + } + private PersistableBundle getConfigBundle(int subId, String key) { + if (mConfigManager == null) return new PersistableBundle(); + return mConfigManager.getConfigForSubId(subId, key); + } + + /** + * Returns true if the state of the Phone is IN_SERVICE or available for emergency calling only. + */ + private boolean isAvailableForEmergencyCalls(Phone phone) { + return ServiceState.STATE_IN_SERVICE == phone.getServiceState().getState() + || phone.getServiceState().isEmergencyOnly(); + } + + /** + * Checks whether both {@code Phone}s are same or not. + */ + private static boolean isSamePhone(Phone p1, Phone p2) { + return p1 != null && p2 != null && (p1.getPhoneId() == p2.getPhoneId()); + } + + private static String emergencyTypeToString(@EmergencyType int emergencyType) { + switch (emergencyType) { + case EMERGENCY_TYPE_CALL: return "CALL"; + case EMERGENCY_TYPE_SMS: return "SMS"; + default: return "UNKNOWN"; + } + } +}
diff --git a/src/java/com/android/internal/telephony/emergency/RadioOnHelper.java b/src/java/com/android/internal/telephony/emergency/RadioOnHelper.java new file mode 100644 index 0000000..9c4ebfa --- /dev/null +++ b/src/java/com/android/internal/telephony/emergency/RadioOnHelper.java
@@ -0,0 +1,189 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.telephony.emergency; + +import android.content.Context; +import android.content.Intent; +import android.os.UserHandle; +import android.provider.Settings; +import android.telephony.TelephonyManager; + +import com.android.internal.telephony.IIntegerConsumer; +import com.android.internal.telephony.Phone; +import com.android.internal.telephony.PhoneFactory; +import com.android.internal.telephony.satellite.SatelliteController; +import com.android.telephony.Rlog; + +import java.util.ArrayList; +import java.util.List; + +/** + * Helper class that implements special behavior related to emergency calls or making phone calls + * when the radio is in the POWER_OFF STATE. Specifically, this class handles the case of the user + * trying to dial an emergency number while the radio is off (i.e. the device is in airplane mode) + * or a normal number while the radio is off (because of the device is on Bluetooth), by turning the + * radio back on, waiting for it to come up, and then retrying the call. + */ +public class RadioOnHelper implements RadioOnStateListener.Callback { + + private static final String TAG = "RadioOnStateListener"; + + private final Context mContext; + private RadioOnStateListener.Callback mCallback; + private List<RadioOnStateListener> mListeners; + private List<RadioOnStateListener> mInProgressListeners; + private boolean mIsRadioReady; + + public RadioOnHelper(Context context) { + mContext = context; + mInProgressListeners = new ArrayList<>(2); + } + + private void setupListeners() { + if (mListeners == null) { + mListeners = new ArrayList<>(2); + } + int activeModems = TelephonyManager.from(mContext).getActiveModemCount(); + // Add new listeners if active modem count increased. + while (mListeners.size() < activeModems) { + mListeners.add(new RadioOnStateListener()); + } + // Clean up listeners if active modem count decreased. + while (mListeners.size() > activeModems) { + mListeners.get(mListeners.size() - 1).cleanup(); + mListeners.remove(mListeners.size() - 1); + } + } + + /** + * Starts the "turn on radio" sequence. This is the (single) external API of the RadioOnHelper + * class. + * + * This method kicks off the following sequence: + * - Power on the radio for each Phone and disable the satellite modem + * - Listen for events telling us the radio has come up or the satellite modem is disabled. + * - Retry if we've gone a significant amount of time without any response. + * - Finally, clean up any leftover state. + * + * This method is safe to call from any thread, since it simply posts a message to the + * RadioOnHelper's handler (thus ensuring that the rest of the sequence is entirely serialized, + * and runs on the main looper.) + */ + public void triggerRadioOnAndListen(RadioOnStateListener.Callback callback, + boolean forEmergencyCall, Phone phoneForEmergencyCall, boolean isTestEmergencyNumber, + int emergencyTimeoutIntervalMillis) { + setupListeners(); + mCallback = callback; + mInProgressListeners.clear(); + mIsRadioReady = false; + for (int i = 0; i < TelephonyManager.from(mContext).getActiveModemCount(); i++) { + Phone phone = PhoneFactory.getPhone(i); + if (phone == null) { + continue; + } + + int timeoutCallbackInterval = (forEmergencyCall && phone == phoneForEmergencyCall) + ? emergencyTimeoutIntervalMillis : 0; + mInProgressListeners.add(mListeners.get(i)); + mListeners.get(i).waitForRadioOn(phone, this, forEmergencyCall, forEmergencyCall + && phone == phoneForEmergencyCall, timeoutCallbackInterval); + } + powerOnRadio(forEmergencyCall, phoneForEmergencyCall, isTestEmergencyNumber); + if (SatelliteController.getInstance().isSatelliteEnabled()) { + powerOffSatellite(phoneForEmergencyCall); + } + } + + /** + * Attempt to power on the radio (i.e. take the device out of airplane mode). We'll eventually + * get an onServiceStateChanged() callback when the radio successfully comes up. + */ + private void powerOnRadio(boolean forEmergencyCall, Phone phoneForEmergencyCall, + boolean isTestEmergencyNumber) { + + // Always try to turn on the radio here independent of APM setting - if we got here in the + // first place, the radio is off independent of APM setting. + for (Phone phone : PhoneFactory.getPhones()) { + Rlog.d(TAG, "powerOnRadio, enabling Radio"); + if (isTestEmergencyNumber) { + phone.setRadioPowerOnForTestEmergencyCall(phone == phoneForEmergencyCall); + } else { + phone.setRadioPower(true, forEmergencyCall, phone == phoneForEmergencyCall, + false); + } + } + + // If airplane mode is on, we turn it off the same way that the Settings activity turns it + // off to keep the setting in sync. + if (Settings.Global.getInt(mContext.getContentResolver(), + Settings.Global.AIRPLANE_MODE_ON, 0) > 0) { + Rlog.d(TAG, "==> Turning off airplane mode for emergency call."); + + // Change the system setting + Settings.Global.putInt(mContext.getContentResolver(), + Settings.Global.AIRPLANE_MODE_ON, 0); + + // Post the broadcast intend for change in airplane mode TODO: We really should not be + // in charge of sending this broadcast. If changing the setting is sufficient to trigger + // all of the rest of the logic, then that should also trigger the broadcast intent. + Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED); + intent.putExtra("state", false); + mContext.sendBroadcastAsUser(intent, UserHandle.ALL); + } + } + + /** + * Attempt to power off the satellite modem. We'll eventually get an + * onSatelliteModemStateChanged() callback when the satellite modem is successfully disabled. + */ + private void powerOffSatellite(Phone phoneForEmergencyCall) { + SatelliteController satelliteController = SatelliteController.getInstance(); + satelliteController.requestSatelliteEnabled(phoneForEmergencyCall.getSubId(), + false /* enableSatellite */, false /* enableDemoMode */, + new IIntegerConsumer.Stub() { + @Override + public void accept(int result) { + + } + }); + } + + /** + * This method is called from multiple Listeners on the Main Looper. Synchronization is not + * necessary. + */ + @Override + public void onComplete(RadioOnStateListener listener, boolean isRadioReady) { + mIsRadioReady |= isRadioReady; + mInProgressListeners.remove(listener); + if (mCallback != null && mInProgressListeners.isEmpty()) { + mCallback.onComplete(null, mIsRadioReady); + } + } + + @Override + public boolean isOkToCall(Phone phone, int serviceState, boolean imsVoiceCapable) { + return (mCallback == null) + ? false : mCallback.isOkToCall(phone, serviceState, imsVoiceCapable); + } + + @Override + public boolean onTimeout(Phone phone, int serviceState, boolean imsVoiceCapable) { + return (mCallback == null) + ? false : mCallback.onTimeout(phone, serviceState, imsVoiceCapable); + } +}
diff --git a/src/java/com/android/internal/telephony/emergency/RadioOnStateListener.java b/src/java/com/android/internal/telephony/emergency/RadioOnStateListener.java new file mode 100644 index 0000000..d61c146 --- /dev/null +++ b/src/java/com/android/internal/telephony/emergency/RadioOnStateListener.java
@@ -0,0 +1,584 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.telephony.emergency; + +import android.os.AsyncResult; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.telephony.ServiceState; +import android.telephony.SubscriptionManager; +import android.telephony.satellite.ISatelliteStateCallback; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.os.SomeArgs; +import com.android.internal.telephony.IIntegerConsumer; +import com.android.internal.telephony.Phone; +import com.android.internal.telephony.satellite.SatelliteController; +import com.android.telephony.Rlog; + +import java.util.Locale; + +/** + * Helper class that listens to a Phone's radio state and sends an onComplete callback when we + * return true for isOkToCall. + */ +public class RadioOnStateListener { + + public interface Callback { + /** + * Receives the result of the RadioOnStateListener's attempt to turn on the radio + * and turn off the satellite modem. + */ + void onComplete(RadioOnStateListener listener, boolean isRadioReady); + + /** + * Returns whether or not this phone is ok to call. + * If it is, onComplete will be called shortly after. + * + * @param phone The Phone associated. + * @param serviceState The service state of that phone. + * @param imsVoiceCapable The IMS voice capability of that phone. + * @return {@code true} if this phone is ok to call. Otherwise, {@code false}. + */ + boolean isOkToCall(Phone phone, int serviceState, boolean imsVoiceCapable); + + /** + * Returns whether or not this phone is ok to call. + * This callback will be called when timeout happens. + * If this returns {@code true}, onComplete will be called shortly after. + * Otherwise, a new timer will be started again to keep waiting for next timeout. + * The timeout interval will be passed to {@link #waitForRadioOn()} when registering + * this callback. + * + * @param phone The Phone associated. + * @param serviceState The service state of that phone. + * @param imsVoiceCapable The IMS voice capability of that phone. + * @return {@code true} if this phone is ok to call. Otherwise, {@code false}. + */ + boolean onTimeout(Phone phone, int serviceState, boolean imsVoiceCapable); + } + + private static final String TAG = "RadioOnStateListener"; + + // Number of times to retry the call, and time between retry attempts. + // not final for testing + private static int MAX_NUM_RETRIES = 5; + // not final for testing + private static long TIME_BETWEEN_RETRIES_MILLIS = 5000; // msec + + // Handler message codes; see handleMessage() + private static final int MSG_START_SEQUENCE = 1; + @VisibleForTesting + public static final int MSG_SERVICE_STATE_CHANGED = 2; + private static final int MSG_RETRY_TIMEOUT = 3; + @VisibleForTesting + public static final int MSG_RADIO_ON = 4; + public static final int MSG_RADIO_OFF_OR_NOT_AVAILABLE = 5; + public static final int MSG_IMS_CAPABILITY_CHANGED = 6; + public static final int MSG_TIMEOUT_ONTIMEOUT_CALLBACK = 7; + @VisibleForTesting + public static final int MSG_SATELLITE_ENABLED_CHANGED = 8; + + private final Handler mHandler = new Handler(Looper.getMainLooper()) { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_START_SEQUENCE: + SomeArgs args = (SomeArgs) msg.obj; + try { + Phone phone = (Phone) args.arg1; + RadioOnStateListener.Callback callback = + (RadioOnStateListener.Callback) args.arg2; + boolean forEmergencyCall = (boolean) args.arg3; + boolean isSelectedPhoneForEmergencyCall = (boolean) args.arg4; + int onTimeoutCallbackInterval = args.argi1; + startSequenceInternal(phone, callback, forEmergencyCall, + isSelectedPhoneForEmergencyCall, onTimeoutCallbackInterval); + } finally { + args.recycle(); + } + break; + case MSG_SERVICE_STATE_CHANGED: + onServiceStateChanged((ServiceState) ((AsyncResult) msg.obj).result); + break; + case MSG_RADIO_ON: + onRadioOn(); + break; + case MSG_RADIO_OFF_OR_NOT_AVAILABLE: + registerForRadioOn(); + break; + case MSG_RETRY_TIMEOUT: + onRetryTimeout(); + break; + case MSG_IMS_CAPABILITY_CHANGED: + onImsCapabilityChanged(); + break; + case MSG_TIMEOUT_ONTIMEOUT_CALLBACK: + onTimeoutCallbackTimeout(); + break; + case MSG_SATELLITE_ENABLED_CHANGED: + onSatelliteEnabledChanged(); + break; + default: + Rlog.w(TAG, String.format(Locale.getDefault(), + "handleMessage: unexpected message: %d.", msg.what)); + break; + } + } + }; + + private final ISatelliteStateCallback mSatelliteCallback = new ISatelliteStateCallback.Stub() { + @Override + public void onSatelliteModemStateChanged(int state) { + mHandler.obtainMessage(MSG_SATELLITE_ENABLED_CHANGED).sendToTarget(); + } + }; + + private Callback mCallback; // The callback to notify upon completion. + private Phone mPhone; // The phone that will attempt to place the call. + // SatelliteController instance to check whether satellite has been disabled. + private SatelliteController mSatelliteController; + private boolean mForEmergencyCall; // Whether radio is being turned on for emergency call. + // Whether this phone is selected to place emergency call. Can be true only if + // mForEmergencyCall is true. + private boolean mSelectedPhoneForEmergencyCall; + private int mNumRetriesSoFar; + private int mOnTimeoutCallbackInterval; // the interval between onTimeout callbacks + + /** + * Starts the "wait for radio" sequence. This is the (single) external API of the + * RadioOnStateListener class. + * + * This method kicks off the following sequence: + * - Listen for the service state change event telling us the radio has come up. + * - Listen for the satellite state changed event telling us the satellite service is disabled. + * - Retry if we've gone {@link #TIME_BETWEEN_RETRIES_MILLIS} without any response from the + * radio. + * - Finally, clean up any leftover state. + * + * This method is safe to call from any thread, since it simply posts a message to the + * RadioOnStateListener's handler (thus ensuring that the rest of the sequence is entirely + * serialized, and runs only on the handler thread.) + */ + public void waitForRadioOn(Phone phone, Callback callback, + boolean forEmergencyCall, boolean isSelectedPhoneForEmergencyCall, + int onTimeoutCallbackInterval) { + Rlog.d(TAG, "waitForRadioOn: Phone " + phone.getPhoneId()); + + if (mPhone != null) { + // If there already is an ongoing request, ignore the new one! + return; + } + + SomeArgs args = SomeArgs.obtain(); + args.arg1 = phone; + args.arg2 = callback; + args.arg3 = forEmergencyCall; + args.arg4 = isSelectedPhoneForEmergencyCall; + args.argi1 = onTimeoutCallbackInterval; + mHandler.obtainMessage(MSG_START_SEQUENCE, args).sendToTarget(); + } + + /** + * Actual implementation of waitForRadioOn(), guaranteed to run on the handler thread. + * + * @see #waitForRadioOn + */ + private void startSequenceInternal(Phone phone, Callback callback, + boolean forEmergencyCall, boolean isSelectedPhoneForEmergencyCall, + int onTimeoutCallbackInterval) { + Rlog.d(TAG, "startSequenceInternal: Phone " + phone.getPhoneId()); + mSatelliteController = SatelliteController.getInstance(); + + // First of all, clean up any state left over from a prior RadioOn call sequence. This + // ensures that we'll behave sanely if another startTurnOnRadioSequence() comes in while + // we're already in the middle of the sequence. + cleanup(); + + mPhone = phone; + mCallback = callback; + mForEmergencyCall = forEmergencyCall; + mSelectedPhoneForEmergencyCall = isSelectedPhoneForEmergencyCall; + mOnTimeoutCallbackInterval = onTimeoutCallbackInterval; + + registerForServiceStateChanged(); + // Register for RADIO_OFF to handle cases where emergency call is dialed before + // we receive UNSOL_RESPONSE_RADIO_STATE_CHANGED with RADIO_OFF. + registerForRadioOff(); + if (mSatelliteController.isSatelliteEnabled()) { + // Register for satellite modem state changed to notify when satellite is disabled. + registerForSatelliteEnabledChanged(); + } + // Next step: when the SERVICE_STATE_CHANGED or SATELLITE_ENABLED_CHANGED event comes in, + // we'll retry the call; see onServiceStateChanged() and onSatelliteEnabledChanged(). + // But also, just in case, start a timer to make sure we'll retry the call even if the + // SERVICE_STATE_CHANGED or SATELLITE_ENABLED_CHANGED events never come in for some reason. + startRetryTimer(); + registerForImsCapabilityChanged(); + startOnTimeoutCallbackTimer(); + } + + private void onImsCapabilityChanged() { + if (mPhone == null) { + return; + } + + boolean imsVoiceCapable = mPhone.isVoiceOverCellularImsEnabled(); + + Rlog.d(TAG, String.format("onImsCapabilityChanged, capable = %s, Phone = %s", + imsVoiceCapable, mPhone.getPhoneId())); + + if (isOkToCall(mPhone.getServiceState().getState(), imsVoiceCapable)) { + Rlog.d(TAG, "onImsCapabilityChanged: ok to call!"); + + onComplete(true); + cleanup(); + } else { + // The IMS capability changed, but we're still not ready to call yet. + Rlog.d(TAG, "onImsCapabilityChanged: not ready to call yet, keep waiting."); + } + } + + private void onTimeoutCallbackTimeout() { + if (mPhone == null) { + return; + } + + if (onTimeout(mPhone.getServiceState().getState(), + mPhone.isVoiceOverCellularImsEnabled())) { + Rlog.d(TAG, "onTimeout: ok to call!"); + + onComplete(true); + cleanup(); + } else if (mNumRetriesSoFar > MAX_NUM_RETRIES) { + Rlog.w(TAG, "onTimeout: Hit MAX_NUM_RETRIES; giving up."); + cleanup(); + } else { + Rlog.d(TAG, "onTimeout: not ready to call yet, keep waiting."); + startOnTimeoutCallbackTimer(); + } + } + + /** + * Handles the SERVICE_STATE_CHANGED event. This event tells us that the radio state has changed + * and is probably coming up. We can now check to see if the conditions are met to place the + * call with {@link Callback#isOkToCall} + */ + private void onServiceStateChanged(ServiceState state) { + if (mPhone == null) { + return; + } + Rlog.d(TAG, String.format("onServiceStateChanged(), new state = %s, Phone = %s", state, + mPhone.getPhoneId())); + + // Possible service states: + // - STATE_IN_SERVICE // Normal operation + // - STATE_OUT_OF_SERVICE // Still searching for an operator to register to, + // // or no radio signal + // - STATE_EMERGENCY_ONLY // Only emergency numbers are allowed; currently not used + // - STATE_POWER_OFF // Radio is explicitly powered off (airplane mode) + + if (isOkToCall(state.getState(), mPhone.isVoiceOverCellularImsEnabled())) { + // Woo hoo! It's OK to actually place the call. + Rlog.d(TAG, "onServiceStateChanged: ok to call!"); + + onComplete(true); + cleanup(); + } else { + // The service state changed, but we're still not ready to call yet. + Rlog.d(TAG, "onServiceStateChanged: not ready to call yet, keep waiting."); + } + } + + private void onRadioOn() { + if (mPhone == null) { + return; + } + ServiceState state = mPhone.getServiceState(); + Rlog.d(TAG, String.format("onRadioOn, state = %s, Phone = %s", state, mPhone.getPhoneId())); + if (isOkToCall(state.getState(), mPhone.isVoiceOverCellularImsEnabled())) { + onComplete(true); + cleanup(); + } else { + Rlog.d(TAG, "onRadioOn: not ready to call yet, keep waiting."); + } + } + + private void onSatelliteEnabledChanged() { + if (mPhone == null) { + return; + } + if (isOkToCall(mPhone.getServiceState().getState(), + mPhone.isVoiceOverCellularImsEnabled())) { + onComplete(true); + cleanup(); + } else { + Rlog.d(TAG, "onSatelliteEnabledChanged: not ready to call yet, keep waiting."); + } + } + + /** + * Callback to see if it is okay to call yet, given the current conditions. + */ + private boolean isOkToCall(int serviceState, boolean imsVoiceCapable) { + return (mCallback == null) + ? false : mCallback.isOkToCall(mPhone, serviceState, imsVoiceCapable); + } + + /** + * Callback to see if it is okay to call yet, given the current conditions. + */ + private boolean onTimeout(int serviceState, boolean imsVoiceCapable) { + return (mCallback == null) + ? false : mCallback.onTimeout(mPhone, serviceState, imsVoiceCapable); + } + + /** + * Handles the retry timer expiring. + */ + private void onRetryTimeout() { + if (mPhone == null) { + return; + } + int serviceState = mPhone.getServiceState().getState(); + Rlog.d(TAG, + String.format(Locale.getDefault(), + "onRetryTimeout(): phone state = %s, service state = %d, retries = %d.", + mPhone.getState(), serviceState, mNumRetriesSoFar)); + + // - If we're actually in a call, we've succeeded. + // - Otherwise, if the radio is now on, that means we successfully got out of airplane mode + // but somehow didn't get the service state change event. In that case, try to place the + // call. + // - If the radio is still powered off, try powering it on again. + + if (isOkToCall(serviceState, mPhone.isVoiceOverCellularImsEnabled())) { + Rlog.d(TAG, "onRetryTimeout: Radio is on. Cleaning up."); + + // Woo hoo -- we successfully got out of airplane mode. + onComplete(true); + cleanup(); + } else { + // Uh oh; we've waited the full TIME_BETWEEN_RETRIES_MILLIS and the radio is still not + // powered-on. Try again. + + mNumRetriesSoFar++; + Rlog.d(TAG, "mNumRetriesSoFar is now " + mNumRetriesSoFar); + + if (mNumRetriesSoFar > MAX_NUM_RETRIES) { + if (mHandler.hasMessages(MSG_TIMEOUT_ONTIMEOUT_CALLBACK)) { + Rlog.w(TAG, "Hit MAX_NUM_RETRIES; waiting onTimeout callback"); + return; + } + Rlog.w(TAG, "Hit MAX_NUM_RETRIES; giving up."); + cleanup(); + } else { + Rlog.d(TAG, "Trying (again) to turn the radio on and satellite modem off."); + mPhone.setRadioPower(true, mForEmergencyCall, mSelectedPhoneForEmergencyCall, + false); + if (mSatelliteController.isSatelliteEnabled()) { + mSatelliteController.requestSatelliteEnabled(mPhone.getSubId(), + false /* enableSatellite */, false /* enableDemoMode */, + new IIntegerConsumer.Stub() { + @Override + public void accept(int result) { + mHandler.obtainMessage(MSG_SATELLITE_ENABLED_CHANGED) + .sendToTarget(); + } + }); + } + startRetryTimer(); + } + } + } + + /** + * Clean up when done with the whole sequence: either after successfully turning on the radio, + * or after bailing out because of too many failures. + * + * The exact cleanup steps are: + * - Notify callback if we still hadn't sent it a response. + * - Double-check that we're not still registered for any telephony events + * - Clean up any extraneous handler messages (like retry timeouts) still in the queue + * + * Basically this method guarantees that there will be no more activity from the + * RadioOnStateListener until someone kicks off the whole sequence again with another call to + * {@link #waitForRadioOn} + * + * TODO: Do the work for the comment below: Note we don't call this method simply after a + * successful call to placeCall(), since it's still possible the call will disconnect very + * quickly with an OUT_OF_SERVICE error. + */ + public void cleanup() { + Rlog.d(TAG, "cleanup()"); + + // This will send a failure call back if callback has yet to be invoked. If the callback was + // already invoked, it's a no-op. + onComplete(false); + + unregisterForServiceStateChanged(); + unregisterForRadioOff(); + unregisterForRadioOn(); + unregisterForSatelliteEnabledChanged(); + cancelRetryTimer(); + unregisterForImsCapabilityChanged(); + + // Used for unregisterForServiceStateChanged() so we null it out here instead. + mPhone = null; + mNumRetriesSoFar = 0; + mOnTimeoutCallbackInterval = 0; + } + + private void startRetryTimer() { + cancelRetryTimer(); + mHandler.sendEmptyMessageDelayed(MSG_RETRY_TIMEOUT, TIME_BETWEEN_RETRIES_MILLIS); + } + + private void cancelRetryTimer() { + mHandler.removeMessages(MSG_RETRY_TIMEOUT); + } + + private void registerForServiceStateChanged() { + // Unregister first, just to make sure we never register ourselves twice. (We need this + // because Phone.registerForServiceStateChanged() does not prevent multiple registration of + // the same handler.) + unregisterForServiceStateChanged(); + mPhone.registerForServiceStateChanged(mHandler, MSG_SERVICE_STATE_CHANGED, null); + } + + private void unregisterForServiceStateChanged() { + // This method is safe to call even if we haven't set mPhone yet. + if (mPhone != null) { + mPhone.unregisterForServiceStateChanged(mHandler); // Safe even if unnecessary + } + mHandler.removeMessages(MSG_SERVICE_STATE_CHANGED); // Clean up any pending messages too + } + + private void registerForRadioOff() { + mPhone.mCi.registerForOffOrNotAvailable(mHandler, MSG_RADIO_OFF_OR_NOT_AVAILABLE, null); + } + + private void unregisterForRadioOff() { + // This method is safe to call even if we haven't set mPhone yet. + if (mPhone != null) { + mPhone.mCi.unregisterForOffOrNotAvailable(mHandler); // Safe even if unnecessary + } + mHandler.removeMessages(MSG_RADIO_OFF_OR_NOT_AVAILABLE); // Clean up any pending messages + } + + private void registerForRadioOn() { + unregisterForRadioOff(); + mPhone.mCi.registerForOn(mHandler, MSG_RADIO_ON, null); + } + + private void unregisterForRadioOn() { + // This method is safe to call even if we haven't set mPhone yet. + if (mPhone != null) { + mPhone.mCi.unregisterForOn(mHandler); // Safe even if unnecessary + } + mHandler.removeMessages(MSG_RADIO_ON); // Clean up any pending messages too + } + + private void registerForSatelliteEnabledChanged() { + mSatelliteController.registerForSatelliteModemStateChanged( + mPhone.getSubId(), mSatelliteCallback); + } + + private void unregisterForSatelliteEnabledChanged() { + int subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; + if (mPhone != null) { + subId = mPhone.getSubId(); + } + mSatelliteController.unregisterForSatelliteModemStateChanged(subId, mSatelliteCallback); + mHandler.removeMessages(MSG_SATELLITE_ENABLED_CHANGED); + } + + private void registerForImsCapabilityChanged() { + unregisterForImsCapabilityChanged(); + mPhone.getServiceStateTracker() + .registerForImsCapabilityChanged(mHandler, MSG_IMS_CAPABILITY_CHANGED, null); + } + + private void unregisterForImsCapabilityChanged() { + if (mPhone != null) { + mPhone.getServiceStateTracker() + .unregisterForImsCapabilityChanged(mHandler); + } + mHandler.removeMessages(MSG_IMS_CAPABILITY_CHANGED); + } + + private void startOnTimeoutCallbackTimer() { + Rlog.d(TAG, "startOnTimeoutCallbackTimer: mOnTimeoutCallbackInterval=" + + mOnTimeoutCallbackInterval); + mHandler.removeMessages(MSG_TIMEOUT_ONTIMEOUT_CALLBACK); + if (mOnTimeoutCallbackInterval > 0) { + mHandler.sendEmptyMessageDelayed(MSG_TIMEOUT_ONTIMEOUT_CALLBACK, + mOnTimeoutCallbackInterval); + } + } + + private void onComplete(boolean isRadioReady) { + if (mCallback != null) { + Callback tempCallback = mCallback; + mCallback = null; + tempCallback.onComplete(this, isRadioReady); + } + } + + @VisibleForTesting + public Handler getHandler() { + return mHandler; + } + + @VisibleForTesting + public void setMaxNumRetries(int retries) { + MAX_NUM_RETRIES = retries; + } + + @VisibleForTesting + public void setTimeBetweenRetriesMillis(long timeMs) { + TIME_BETWEEN_RETRIES_MILLIS = timeMs; + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || !getClass().equals(o.getClass())) + return false; + + RadioOnStateListener that = (RadioOnStateListener) o; + + if (mNumRetriesSoFar != that.mNumRetriesSoFar) { + return false; + } + if (mCallback != null ? !mCallback.equals(that.mCallback) : that.mCallback != null) { + return false; + } + return mPhone != null ? mPhone.equals(that.mPhone) : that.mPhone == null; + } + + @Override + public int hashCode() { + int hash = 7; + hash = 31 * hash + mNumRetriesSoFar; + hash = 31 * hash + (mCallback == null ? 0 : mCallback.hashCode()); + hash = 31 * hash + (mPhone == null ? 0 : mPhone.hashCode()); + return hash; + } +}
diff --git a/src/java/com/android/internal/telephony/euicc/EuiccCardController.java b/src/java/com/android/internal/telephony/euicc/EuiccCardController.java index f337141..2f73c91 100644 --- a/src/java/com/android/internal/telephony/euicc/EuiccCardController.java +++ b/src/java/com/android/internal/telephony/euicc/EuiccCardController.java
@@ -39,7 +39,7 @@ import android.util.Log; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.telephony.SubscriptionController; +import com.android.internal.telephony.subscription.SubscriptionManagerService; import com.android.internal.telephony.uicc.UiccCard; import com.android.internal.telephony.uicc.UiccController; import com.android.internal.telephony.uicc.UiccPort; @@ -51,6 +51,7 @@ import java.io.FileDescriptor; import java.io.PrintWriter; +import java.util.List; /** Backing implementation of {@link EuiccCardManager}. */ public class EuiccCardController extends IEuiccCardController.Stub { @@ -413,7 +414,7 @@ // if there is no iccid enabled on this port, return null. if (TextUtils.isEmpty(iccId)) { try { - callback.onComplete(EuiccCardManager.RESULT_PROFILE_NOT_FOUND, null); + callback.onComplete(EuiccCardManager.RESULT_PROFILE_DOES_NOT_EXIST, null); } catch (RemoteException exception) { loge("getEnabledProfile callback failure.", exception); } @@ -649,9 +650,8 @@ @Override public void onResult(Void result) { Log.i(TAG, "Request subscription info list refresh after delete."); - SubscriptionController.getInstance() - .requestEmbeddedSubscriptionInfoListRefresh( - mUiccController.convertToPublicCardId(cardId)); + SubscriptionManagerService.getInstance().updateEmbeddedSubscriptions( + List.of(mUiccController.convertToPublicCardId(cardId)), null); try { callback.onComplete(EuiccCardManager.RESULT_OK); } catch (RemoteException exception) { @@ -701,9 +701,8 @@ @Override public void onResult(Void result) { Log.i(TAG, "Request subscription info list refresh after reset memory."); - SubscriptionController.getInstance() - .requestEmbeddedSubscriptionInfoListRefresh( - mUiccController.convertToPublicCardId(cardId)); + SubscriptionManagerService.getInstance().updateEmbeddedSubscriptions( + List.of(mUiccController.convertToPublicCardId(cardId)), null); try { callback.onComplete(EuiccCardManager.RESULT_OK); } catch (RemoteException exception) { @@ -1190,9 +1189,8 @@ @Override public void onResult(byte[] result) { Log.i(TAG, "Request subscription info list refresh after install."); - SubscriptionController.getInstance() - .requestEmbeddedSubscriptionInfoListRefresh( - mUiccController.convertToPublicCardId(cardId)); + SubscriptionManagerService.getInstance().updateEmbeddedSubscriptions( + List.of(mUiccController.convertToPublicCardId(cardId)), null); try { callback.onComplete(EuiccCardManager.RESULT_OK, result); } catch (RemoteException exception) {
diff --git a/src/java/com/android/internal/telephony/euicc/EuiccConnector.java b/src/java/com/android/internal/telephony/euicc/EuiccConnector.java index 974acf9..c417a34 100644 --- a/src/java/com/android/internal/telephony/euicc/EuiccConnector.java +++ b/src/java/com/android/internal/telephony/euicc/EuiccConnector.java
@@ -101,7 +101,8 @@ * true or onServiceDisconnected is called (and no package change has occurred which should * force us to reestablish the binding). */ - private static final int BIND_TIMEOUT_MILLIS = 30000; + @VisibleForTesting + static final int BIND_TIMEOUT_MILLIS = 30000; /** * Maximum amount of idle time to hold the binding while in {@link ConnectedState}. After this, @@ -225,6 +226,8 @@ static class GetMetadataRequest { DownloadableSubscription mSubscription; boolean mForceDeactivateSim; + boolean mSwitchAfterDownload; + int mPortIndex; GetMetadataCommandCallback mCallback; } @@ -389,6 +392,9 @@ mSm = (SubscriptionManager) context.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE); + // TODO(b/239277548): Disable debug logging after analysing this bug. + setDbg(true); + // Unavailable/Available both monitor for package changes and update mSelectedComponent but // do not need to adjust the binding. mUnavailableState = new UnavailableState(); @@ -444,13 +450,15 @@ /** Asynchronously fetch metadata for the given downloadable subscription. */ @VisibleForTesting(visibility = PACKAGE) - public void getDownloadableSubscriptionMetadata(int cardId, - DownloadableSubscription subscription, + public void getDownloadableSubscriptionMetadata(int cardId, int portIndex, + DownloadableSubscription subscription, boolean switchAfterDownload, boolean forceDeactivateSim, GetMetadataCommandCallback callback) { GetMetadataRequest request = new GetMetadataRequest(); request.mSubscription = subscription; request.mForceDeactivateSim = forceDeactivateSim; + request.mSwitchAfterDownload = switchAfterDownload; + request.mPortIndex = portIndex; request.mCallback = callback; sendMessage(CMD_GET_DOWNLOADABLE_SUBSCRIPTION_METADATA, cardId, 0 /* arg2 */, request); } @@ -549,6 +557,11 @@ callback); } + @VisibleForTesting + public final IEuiccService getBinder() { + return mEuiccService; + } + /** * State in which no EuiccService is available. * @@ -686,6 +699,7 @@ } return HANDLED; } else if (message.what == CMD_CONNECT_TIMEOUT) { + unbind(); transitionTo(mAvailableState); return HANDLED; } else if (isEuiccCommand(message.what)) { @@ -749,7 +763,9 @@ case CMD_GET_DOWNLOADABLE_SUBSCRIPTION_METADATA: { GetMetadataRequest request = (GetMetadataRequest) message.obj; mEuiccService.getDownloadableSubscriptionMetadata(slotId, + request.mPortIndex, request.mSubscription, + request.mSwitchAfterDownload, request.mForceDeactivateSim, new IGetDownloadableSubscriptionMetadataCallback.Stub() { @Override @@ -1057,9 +1073,8 @@ for (int slotIndex = 0; slotIndex < slotInfos.length; slotIndex++) { // Report Anomaly in case UiccSlotInfo is not. if (slotInfos[slotIndex] == null) { - AnomalyReporter.reportAnomaly( - UUID.fromString("4195b83d-6cee-4999-a02f-d0b9f7079b9d"), - "EuiccConnector: Found UiccSlotInfo Null object."); + Log.i(TAG, "No UiccSlotInfo found for slotIndex: " + slotIndex); + return SubscriptionManager.INVALID_SIM_SLOT_INDEX; } String retrievedCardId = slotInfos[slotIndex] != null ? slotInfos[slotIndex].getCardId() : null;
diff --git a/src/java/com/android/internal/telephony/euicc/EuiccController.java b/src/java/com/android/internal/telephony/euicc/EuiccController.java index 965dd22..a5b95c3 100644 --- a/src/java/com/android/internal/telephony/euicc/EuiccController.java +++ b/src/java/com/android/internal/telephony/euicc/EuiccController.java
@@ -59,8 +59,8 @@ import com.android.internal.telephony.CarrierPrivilegesTracker; import com.android.internal.telephony.Phone; import com.android.internal.telephony.PhoneFactory; -import com.android.internal.telephony.SubscriptionController; import com.android.internal.telephony.euicc.EuiccConnector.OtaStatusChangedCallback; +import com.android.internal.telephony.subscription.SubscriptionManagerService; import com.android.internal.telephony.uicc.IccUtils; import com.android.internal.telephony.uicc.UiccController; import com.android.internal.telephony.uicc.UiccPort; @@ -385,6 +385,7 @@ void getDownloadableSubscriptionMetadata(int cardId, DownloadableSubscription subscription, boolean forceDeactivateSim, String callingPackage, PendingIntent callbackIntent) { + Log.d(TAG, " getDownloadableSubscriptionMetadata callingPackage: " + callingPackage); if (!callerCanWriteEmbeddedSubscriptions()) { throw new SecurityException("Must have WRITE_EMBEDDED_SUBSCRIPTIONS to get metadata"); } @@ -392,7 +393,8 @@ long token = Binder.clearCallingIdentity(); try { mConnector.getDownloadableSubscriptionMetadata(cardId, - subscription, forceDeactivateSim, + TelephonyManager.DEFAULT_PORT_INDEX, subscription, + false /* switchAfterDownload */, forceDeactivateSim, new GetMetadataCommandCallback( token, subscription, callingPackage, callbackIntent)); } finally { @@ -601,8 +603,8 @@ if (!isConsentNeededToResolvePortIndex && canManageSubscriptionOnTargetSim(cardId, callingPackage, true, portIndex)) { - mConnector.getDownloadableSubscriptionMetadata(cardId, subscription, - forceDeactivateSim, + mConnector.getDownloadableSubscriptionMetadata(cardId, portIndex, + subscription, switchAfterDownload, forceDeactivateSim, new DownloadSubscriptionGetMetadataCommandCallback(token, subscription, switchAfterDownload, callingPackage, forceDeactivateSim, callbackIntent, false /* withUserConsent */, portIndex)); @@ -713,7 +715,8 @@ Log.d(TAG, " downloadSubscriptionPrivilegedCheckMetadata cardId: " + cardId + " switchAfterDownload: " + switchAfterDownload + " portIndex: " + portIndex + " forceDeactivateSim: " + forceDeactivateSim); - mConnector.getDownloadableSubscriptionMetadata(cardId, subscription, forceDeactivateSim, + mConnector.getDownloadableSubscriptionMetadata(cardId, portIndex, + subscription, switchAfterDownload, forceDeactivateSim, new DownloadSubscriptionGetMetadataCommandCallback(callingToken, subscription, switchAfterDownload, callingPackage, forceDeactivateSim, callbackIntent, true /* withUserConsent */, portIndex)); @@ -862,6 +865,7 @@ void getDefaultDownloadableSubscriptionList(int cardId, boolean forceDeactivateSim, String callingPackage, PendingIntent callbackIntent) { + Log.d(TAG, " getDefaultDownloadableSubscriptionList callingPackage: " + callingPackage); if (!callerCanWriteEmbeddedSubscriptions()) { throw new SecurityException( "Must have WRITE_EMBEDDED_SUBSCRIPTIONS to get default list"); @@ -1147,7 +1151,8 @@ * Returns the resolved portIndex or {@link TelephonyManager#INVALID_PORT_INDEX} if calling * cannot manage any active subscription. */ - private int getResolvedPortIndexForDisableSubscription(int cardId, String callingPackage, + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) + public int getResolvedPortIndexForDisableSubscription(int cardId, String callingPackage, boolean callerCanWriteEmbeddedSubscriptions) { List<SubscriptionInfo> subInfoList = mSubscriptionManager .getActiveSubscriptionInfoList(/* userVisibleOnly */false); @@ -1175,7 +1180,8 @@ * Returns the resolved portIndex or {@link TelephonyManager#INVALID_PORT_INDEX} if no port * is available without user consent. */ - private int getResolvedPortIndexForSubscriptionSwitch(int cardId) { + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) + public int getResolvedPortIndexForSubscriptionSwitch(int cardId) { int slotIndex = getSlotIndexFromCardId(cardId); // Euicc Slot UiccSlot slot = UiccController.getInstance().getUiccSlot(slotIndex); @@ -1585,9 +1591,9 @@ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) public void refreshSubscriptionsAndSendResult( PendingIntent callbackIntent, int resultCode, Intent extrasIntent) { - SubscriptionController.getInstance() - .requestEmbeddedSubscriptionInfoListRefresh( - () -> sendResult(callbackIntent, resultCode, extrasIntent)); + SubscriptionManagerService.getInstance().updateEmbeddedSubscriptions( + List.of(mTelephonyManager.getCardIdForDefaultEuicc()), + () -> sendResult(callbackIntent, resultCode, extrasIntent)); } /** Dispatch the given callback intent with the given result code and data. */ @@ -1840,8 +1846,11 @@ // other SIM. The target SIM should be an eUICC. // For a single-active subscription phone, checks whether the caller can manage any active // embedded subscription. - // From Android T, If embedded slot supports Multiple Enabled Profiles then should check if - // the calling app has carrier privilege over the subscription on the target port index. + // From Android T, if usePortIndex is true then verify whether the calling app has carrier + // privilege over the active embedded subscription on the target port index. + // If usePortIndex is false then check whether the calling app can manage any active + // subscription on any of the active ports, if there are no active embedded subscriptions then + // verify whether the calling app can manage any active subscription on any of the other SIM. private boolean canManageSubscriptionOnTargetSim(int cardId, String callingPackage, boolean usePortIndex, int targetPortIndex) { List<SubscriptionInfo> subInfoList = mSubscriptionManager @@ -1861,11 +1870,9 @@ return false; } boolean isEuicc = false; - boolean isMultipleEnabledProfilesSupported = false; for (UiccCardInfo info : cardInfos) { if (info != null && info.getCardId() == cardId && info.isEuicc()) { isEuicc = true; - isMultipleEnabledProfilesSupported = info.isMultipleEnabledProfilesSupported(); break; } } @@ -1874,21 +1881,34 @@ return false; } - // If the caller can't manage the active embedded subscription on the target SIM, return - // false. If the caller can manage the active embedded subscription on the target SIM, - // return true directly. - for (SubscriptionInfo subInfo : subInfoList) { - // 1. subInfo.isEmbedded() can only be true for the target SIM. - // 2. Check whether the caller can manage subscription on the target portIndex - // (i.e. subInfo.getPortIndex() == targetPortIndex condition) only in case if - // isMultipleEnabledProfilesSupported and usePortIndex both are true. - if (subInfo.isEmbedded() && subInfo.getCardId() == cardId - && (!isMultipleEnabledProfilesSupported || !usePortIndex - || subInfo.getPortIndex() == targetPortIndex)) { - return mSubscriptionManager.canManageSubscription(subInfo, callingPackage); - } - } + // If the caller can't manage the active embedded subscription on the target SIM port, + // return false. If the caller can manage the active embedded subscription on the + // target SIM port, return true directly. + boolean hasActiveEmbeddedSubscription = subInfoList.stream().anyMatch( + subInfo -> subInfo.isEmbedded() && subInfo.getCardId() == cardId + && (!usePortIndex || subInfo.getPortIndex() == targetPortIndex)); + if (hasActiveEmbeddedSubscription) { + // hasActiveEmbeddedSubscription is true if there is an active embedded subscription + // on the target port(in case of usePortIndex is true) or if there is an active + // embedded subscription on any of the active ports. + // 1. If usePortIndex is true, check whether the caller can manage subscription on + // the target port. + // 2. If usePortIndex is false, check whether the caller can manage subscription on + // any of the active ports. + for (SubscriptionInfo subInfo : subInfoList) { + // subInfo.isEmbedded() can only be true for the target SIM. + if (subInfo.isEmbedded() + && subInfo.getCardId() == cardId + && (!usePortIndex || subInfo.getPortIndex() == targetPortIndex) + && mSubscriptionManager.canManageSubscription( + subInfo, callingPackage)) { + return true; + } + } + Log.i(TAG, "canManageSubscriptionOnTargetSim cannot manage embedded subscription"); + return false; + } // There is no active subscription on the target SIM, checks whether the caller can // manage any active subscription on any other SIM. final long token = Binder.clearCallingIdentity(); @@ -1923,59 +1943,84 @@ @Override public boolean isSimPortAvailable(int cardId, int portIndex, String callingPackage) { - List<UiccCardInfo> cardInfos; + mAppOpsManager.checkPackage(Binder.getCallingUid(), callingPackage); + // If calling app is targeted for Android U and beyond, check for other conditions + // to decide the port availability. + boolean shouldCheckConditionsForInactivePort = isCompatChangeEnabled(callingPackage, + EuiccManager.INACTIVE_PORT_AVAILABILITY_CHECK); + // In the event that this check is coming from ONS, WRITE_EMBEDDED_SUBSCRIPTIONS will be + // required for the case where a port is inactive but could trivially be enabled without + // requiring user consent. + boolean callerCanWriteEmbeddedSubscriptions = callerCanWriteEmbeddedSubscriptions(); final long token = Binder.clearCallingIdentity(); try { - cardInfos = mTelephonyManager.getUiccCardsInfo(); - } finally { - Binder.restoreCallingIdentity(token); - } - for (UiccCardInfo info : cardInfos) { - if (info == null || info.getCardId() != cardId) { - continue; - } - // Return false in case of non esim or passed port index is greater than - // the available ports. - if (!info.isEuicc() || (portIndex == TelephonyManager.INVALID_PORT_INDEX) - || portIndex >= info.getPorts().size()) { - return false; - } - for (UiccPortInfo portInfo : info.getPorts()) { - if (portInfo == null || portInfo.getPortIndex() != portIndex) { + List<UiccCardInfo> cardInfos = mTelephonyManager.getUiccCardsInfo(); + for (UiccCardInfo info : cardInfos) { + if (info == null || info.getCardId() != cardId) { continue; } - // Return false if port is not active. - if (!portInfo.isActive()) { + // Return false in case of non esim or passed port index is greater than + // the available ports. + if (!info.isEuicc() || (portIndex == TelephonyManager.INVALID_PORT_INDEX) + || portIndex >= info.getPorts().size()) { return false; } - // A port is available if it has no profiles enabled on it or calling app has - // Carrier privilege over the profile installed on the selected port. - if (TextUtils.isEmpty(portInfo.getIccId())) { - return true; + for (UiccPortInfo portInfo : info.getPorts()) { + if (portInfo == null || portInfo.getPortIndex() != portIndex) { + continue; + } + if (!portInfo.isActive()) { + // port is inactive, check whether the caller can activate a new profile + // seamlessly. This is possible in below condition: + // 1. Device in DSDS Mode(P+E). + // 2. pSIM slot is active but no active subscription. + // 3. Caller has carrier privileges on any phone or has + // WRITE_EMBEDDED_SUBSCRIPTIONS. The latter covers calls from ONS + // which does not have carrier privileges. + if (!shouldCheckConditionsForInactivePort) { + return false; + } + boolean hasActiveRemovableNonEuiccSlot = getRemovableNonEuiccSlot() != null + && getRemovableNonEuiccSlot().isActive(); + boolean hasCarrierPrivileges = mTelephonyManager + .checkCarrierPrivilegesForPackageAnyPhone(callingPackage) + == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS; + return mTelephonyManager.isMultiSimEnabled() + && hasActiveRemovableNonEuiccSlot + && !isRemovalNonEuiccSlotHasActiveSubscription() + && (hasCarrierPrivileges || callerCanWriteEmbeddedSubscriptions); + } + // A port is available if it has no profiles enabled on it or calling app has + // Carrier privilege over the profile installed on the selected port. + if (TextUtils.isEmpty(portInfo.getIccId())) { + return true; + } + UiccPort uiccPort = + UiccController.getInstance().getUiccPortForSlot( + info.getPhysicalSlotIndex(), portIndex); + // Some eSim Vendors return boot profile iccid if no profile is installed. + // So in this case if profile is empty, port is available. + if (uiccPort != null + && uiccPort.getUiccProfile() != null + && uiccPort.getUiccProfile().isEmptyProfile()) { + return true; + } + Phone phone = PhoneFactory.getPhone(portInfo.getLogicalSlotIndex()); + if (phone == null) { + Log.e(TAG, "Invalid logical slot: " + portInfo.getLogicalSlotIndex()); + return false; + } + CarrierPrivilegesTracker cpt = phone.getCarrierPrivilegesTracker(); + if (cpt == null) { + Log.e(TAG, "No CarrierPrivilegesTracker"); + return false; + } + return (cpt.getCarrierPrivilegeStatusForPackage(callingPackage) + == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS); } - UiccPort uiccPort = - UiccController.getInstance().getUiccPortForSlot( - info.getPhysicalSlotIndex(), portIndex); - // Some eSim Vendors return boot profile iccid if no profile is installed. - // So in this case if profile is empty, port is available. - if (uiccPort != null - && uiccPort.getUiccProfile() != null - && uiccPort.getUiccProfile().isEmptyProfile()) { - return true; - } - Phone phone = PhoneFactory.getPhone(portInfo.getLogicalSlotIndex()); - if (phone == null) { - Log.e(TAG, "Invalid logical slot: " + portInfo.getLogicalSlotIndex()); - return false; - } - CarrierPrivilegesTracker cpt = phone.getCarrierPrivilegesTracker(); - if (cpt == null) { - Log.e(TAG, "No CarrierPrivilegesTracker"); - return false; - } - return (cpt.getCarrierPrivilegeStatusForPackage(callingPackage) - == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS); } + } finally { + Binder.restoreCallingIdentity(token); } return false; }
diff --git a/src/java/com/android/internal/telephony/gsm/GsmInboundSmsHandler.java b/src/java/com/android/internal/telephony/gsm/GsmInboundSmsHandler.java index 0abd4ab..907f158 100644 --- a/src/java/com/android/internal/telephony/gsm/GsmInboundSmsHandler.java +++ b/src/java/com/android/internal/telephony/gsm/GsmInboundSmsHandler.java
@@ -24,10 +24,12 @@ import android.content.IntentFilter; import android.os.AsyncResult; import android.os.Build; +import android.os.Looper; import android.os.Message; import android.os.SystemProperties; import android.provider.Telephony.Sms.Intents; +import com.android.ims.ImsManager; import com.android.internal.telephony.CommandsInterface; import com.android.internal.telephony.InboundSmsHandler; import com.android.internal.telephony.Phone; @@ -43,7 +45,7 @@ */ public class GsmInboundSmsHandler extends InboundSmsHandler { - private static BroadcastReceiver sTestBroadcastReceiver; + private BroadcastReceiver mTestBroadcastReceiver; /** Handler for SMS-PP data download messages to UICC. */ private final UsimDataDownloadHandler mDataDownloadHandler; @@ -56,18 +58,18 @@ * Create a new GSM inbound SMS handler. */ private GsmInboundSmsHandler(Context context, SmsStorageMonitor storageMonitor, - Phone phone) { - super("GsmInboundSmsHandler", context, storageMonitor, phone); + Phone phone, Looper looper) { + super("GsmInboundSmsHandler", context, storageMonitor, phone, looper); phone.mCi.setOnNewGsmSms(getHandler(), EVENT_NEW_SMS, null); mDataDownloadHandler = new UsimDataDownloadHandler(phone.mCi, phone.getPhoneId()); mCellBroadcastServiceManager.enable(); if (TEST_MODE) { - if (sTestBroadcastReceiver == null) { - sTestBroadcastReceiver = new GsmCbTestBroadcastReceiver(); + if (mTestBroadcastReceiver == null) { + mTestBroadcastReceiver = new GsmCbTestBroadcastReceiver(); IntentFilter filter = new IntentFilter(); filter.addAction(TEST_ACTION); - context.registerReceiver(sTestBroadcastReceiver, filter, + context.registerReceiver(mTestBroadcastReceiver, filter, Context.RECEIVER_EXPORTED); } } @@ -127,8 +129,9 @@ * Wait for state machine to enter startup state. We can't send any messages until then. */ public static GsmInboundSmsHandler makeInboundSmsHandler(Context context, - SmsStorageMonitor storageMonitor, Phone phone) { - GsmInboundSmsHandler handler = new GsmInboundSmsHandler(context, storageMonitor, phone); + SmsStorageMonitor storageMonitor, Phone phone, Looper looper) { + GsmInboundSmsHandler handler = + new GsmInboundSmsHandler(context, storageMonitor, phone, looper); handler.start(); return handler; } @@ -153,7 +156,8 @@ * or {@link Activity#RESULT_OK} for delayed acknowledgment to SMSC */ @Override - protected int dispatchMessageRadioSpecific(SmsMessageBase smsb, @SmsSource int smsSource) { + protected int dispatchMessageRadioSpecific(SmsMessageBase smsb, @SmsSource int smsSource, + int token) { SmsMessage sms = (SmsMessage) smsb; if (sms.isTypeZero()) { @@ -177,7 +181,7 @@ // Send SMS-PP data download messages to UICC. See 3GPP TS 31.111 section 7.1.1. if (sms.isUsimDataDownload()) { UsimServiceTable ust = mPhone.getUsimServiceTable(); - return mDataDownloadHandler.handleUsimDataDownload(ust, sms, smsSource); + return mDataDownloadHandler.handleUsimDataDownload(ust, sms, smsSource, token); } boolean handled = false; @@ -268,4 +272,15 @@ android.telephony.SmsMessage.FORMAT_3GPP); mPhone.getSmsStats().onIncomingSmsVoicemail(false /* is3gpp2 */, smsSource); } + + /** + * sets ImsManager object. + */ + public boolean setImsManager(ImsManager imsManager) { + if (mDataDownloadHandler != null) { + mDataDownloadHandler.setImsManager(imsManager); + return true; + } + return false; + } }
diff --git a/src/java/com/android/internal/telephony/gsm/GsmMmiCode.java b/src/java/com/android/internal/telephony/gsm/GsmMmiCode.java index dec2468..9de3ee9 100644 --- a/src/java/com/android/internal/telephony/gsm/GsmMmiCode.java +++ b/src/java/com/android/internal/telephony/gsm/GsmMmiCode.java
@@ -49,6 +49,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.telephony.CallForwardInfo; import com.android.internal.telephony.CallStateException; +import com.android.internal.telephony.CallWaitingController; import com.android.internal.telephony.CommandException; import com.android.internal.telephony.CommandsInterface; import com.android.internal.telephony.GsmCdmaPhone; @@ -61,6 +62,7 @@ import com.android.internal.telephony.util.ArrayUtils; import com.android.telephony.Rlog; +import java.util.ArrayList; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -455,7 +457,7 @@ return ""; } - private String getActionStringFromReqType(SsData.RequestType rType) { + private static String getActionStringFromReqType(SsData.RequestType rType) { switch (rType) { case SS_ACTIVATION: return ACTION_ACTIVATE; @@ -520,6 +522,40 @@ } } + public static SsData.ServiceType cfReasonToServiceType(int commandInterfaceCFReason) { + switch (commandInterfaceCFReason) { + case CommandsInterface.CF_REASON_UNCONDITIONAL: + return SsData.ServiceType.SS_CFU; + case CommandsInterface.CF_REASON_BUSY: + return SsData.ServiceType.SS_CF_BUSY; + case CommandsInterface.CF_REASON_NO_REPLY: + return SsData.ServiceType.SS_CF_NO_REPLY; + case CommandsInterface.CF_REASON_NOT_REACHABLE: + return SsData.ServiceType.SS_CF_NOT_REACHABLE; + case CommandsInterface.CF_REASON_ALL: + return SsData.ServiceType.SS_CF_ALL; + case CommandsInterface.CF_REASON_ALL_CONDITIONAL: + return SsData.ServiceType.SS_CF_ALL_CONDITIONAL; + default: + return null; + } + } + + public static SsData.RequestType cfActionToRequestType(int commandInterfaceCFAction) { + switch (commandInterfaceCFAction) { + case CommandsInterface.CF_ACTION_DISABLE: + return SsData.RequestType.SS_DEACTIVATION; + case CommandsInterface.CF_ACTION_ENABLE: + return SsData.RequestType.SS_ACTIVATION; + case CommandsInterface.CF_ACTION_REGISTRATION: + return SsData.RequestType.SS_REGISTRATION; + case CommandsInterface.CF_ACTION_ERASURE: + return SsData.RequestType.SS_ERASURE; + default: + return null; + } + } + @UnsupportedAppUsage private static int siToServiceClass(String si) { @@ -623,6 +659,29 @@ } } + public static SsData.ServiceType cbFacilityToServiceType(String commandInterfaceCBFacility) { + switch(commandInterfaceCBFacility) { + case CommandsInterface.CB_FACILITY_BAOC: + return SsData.ServiceType.SS_BAOC; + case CommandsInterface.CB_FACILITY_BAOIC: + return SsData.ServiceType.SS_BAOIC; + case CommandsInterface.CB_FACILITY_BAOICxH: + return SsData.ServiceType.SS_BAOIC_EXC_HOME; + case CommandsInterface.CB_FACILITY_BAIC: + return SsData.ServiceType.SS_BAIC; + case CommandsInterface.CB_FACILITY_BAICr: + return SsData.ServiceType.SS_BAIC_ROAMING; + case CommandsInterface.CB_FACILITY_BA_ALL: + return SsData.ServiceType.SS_ALL_BARRING; + case CommandsInterface.CB_FACILITY_BA_MO: + return SsData.ServiceType.SS_OUTGOING_BARRING; + case CommandsInterface.CB_FACILITY_BA_MT: + return SsData.ServiceType.SS_INCOMING_BARRING; + default: + return null; + } + } + //***** Constructor @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @@ -892,6 +951,17 @@ return CommandsInterface.CLIR_DEFAULT; } + public static SsData.RequestType clirModeToRequestType(int commandInterfaceCLIRMode) { + switch (commandInterfaceCLIRMode) { + case CommandsInterface.CLIR_SUPPRESSION: + return SsData.RequestType.SS_ACTIVATION; + case CommandsInterface.CLIR_INVOCATION: + return SsData.RequestType.SS_DEACTIVATION; + default: + return null; + } + } + /** * Returns true if the Service Code is FAC to dial as a normal call. * @@ -1099,11 +1169,25 @@ int serviceClass = siToServiceClass(mSia); if (isActivate() || isDeactivate()) { + if (serviceClass == SERVICE_CLASS_NONE + || (serviceClass & SERVICE_CLASS_VOICE) == SERVICE_CLASS_VOICE) { + if (mPhone.getTerminalBasedCallWaitingState(true) + != CallWaitingController.TERMINAL_BASED_NOT_SUPPORTED) { + mPhone.setCallWaiting(isActivate(), serviceClass, + obtainMessage(EVENT_SET_COMPLETE, this)); + return; + } + } mPhone.mCi.setCallWaiting(isActivate(), serviceClass, obtainMessage(EVENT_SET_COMPLETE, this)); } else if (isInterrogate()) { - mPhone.mCi.queryCallWaiting(serviceClass, - obtainMessage(EVENT_QUERY_COMPLETE, this)); + if (mPhone.getTerminalBasedCallWaitingState(true) + != CallWaitingController.TERMINAL_BASED_NOT_SUPPORTED) { + mPhone.getCallWaiting(obtainMessage(EVENT_QUERY_COMPLETE, this)); + } else { + mPhone.mCi.queryCallWaiting(serviceClass, + obtainMessage(EVENT_QUERY_COMPLETE, this)); + } } else { throw new RuntimeException ("Invalid or Unsupported MMI Code"); } @@ -1333,8 +1417,8 @@ } } //***** Private instance methods - - private CharSequence getErrorMessage(AsyncResult ar) { + @VisibleForTesting + public CharSequence getErrorMessage(AsyncResult ar) { if (ar.exception instanceof CommandException) { CommandException.Error err = ((CommandException)(ar.exception)).getCommandError(); @@ -1362,6 +1446,13 @@ } else if (err == CommandException.Error.OEM_ERROR_1) { Rlog.i(LOG_TAG, "OEM_ERROR_1 USSD_MODIFIED_TO_DIAL_VIDEO"); return mContext.getText(com.android.internal.R.string.stk_cc_ussd_to_dial_video); + } else if (err == CommandException.Error.REQUEST_NOT_SUPPORTED + || err == CommandException.Error.OPERATION_NOT_ALLOWED) { + Rlog.i(LOG_TAG, "REQUEST_NOT_SUPPORTED/OPERATION_NOT_ALLOWED"); + // getResources().getText() is the same as getText(), however getText() is final and + // cannot be mocked in tests. + return mContext.getResources().getText( + com.android.internal.R.string.mmiErrorNotSupported); } } @@ -1827,6 +1918,120 @@ return this.mCallbackReceiver; } + /** + * Returns list of control strings for a supplementary service request + * as defined in TS 22.030 6.5 + * @param requestType request type associated with the supplementary service + * @param serviceType supplementary service type + * @return list of control strings associated with the supplementary service. + */ + public static ArrayList<String> getControlStrings(SsData.RequestType requestType, + SsData.ServiceType serviceType) { + ArrayList<String> controlStrings = new ArrayList<>(); + if (requestType == null || serviceType == null) { + return controlStrings; + } + + String actionStr = getActionStringFromReqType(requestType); + switch (serviceType) { + case SS_CFU: + controlStrings.add(actionStr + SC_CFU); + controlStrings.add(actionStr + SC_CF_All); + break; + case SS_CF_BUSY: + controlStrings.add(actionStr + SC_CFB); + controlStrings.add(actionStr + SC_CF_All_Conditional); + controlStrings.add(actionStr + SC_CF_All); + break; + case SS_CF_NO_REPLY: + controlStrings.add(actionStr + SC_CFNRy); + controlStrings.add(actionStr + SC_CF_All_Conditional); + controlStrings.add(actionStr + SC_CF_All); + break; + case SS_CF_NOT_REACHABLE: + controlStrings.add(actionStr + SC_CFNR); + controlStrings.add(actionStr + SC_CF_All_Conditional); + controlStrings.add(actionStr + SC_CF_All); + break; + case SS_CF_ALL: + controlStrings.add(actionStr + SC_CF_All); + break; + case SS_CF_ALL_CONDITIONAL: + controlStrings.add(actionStr + SC_CF_All_Conditional); + controlStrings.add(actionStr + SC_CF_All); + break; + case SS_CLIP: + controlStrings.add(actionStr + SC_CLIP); + break; + case SS_CLIR: + controlStrings.add(actionStr + SC_CLIR); + break; + case SS_WAIT: + controlStrings.add(actionStr + SC_WAIT); + break; + case SS_BAOC: + controlStrings.add(actionStr + SC_BAOC); + controlStrings.add(actionStr + SC_BA_MO); + controlStrings.add(actionStr + SC_BA_ALL); + break; + case SS_BAOIC: + controlStrings.add(actionStr + SC_BAOIC); + controlStrings.add(actionStr + SC_BA_MO); + controlStrings.add(actionStr + SC_BA_ALL); + break; + case SS_BAOIC_EXC_HOME: + controlStrings.add(actionStr + SC_BAOICxH); + controlStrings.add(actionStr + SC_BA_MO); + controlStrings.add(actionStr + SC_BA_ALL); + break; + case SS_BAIC: + controlStrings.add(actionStr + SC_BAIC); + controlStrings.add(actionStr + SC_BA_MT); + controlStrings.add(actionStr + SC_BA_ALL); + break; + case SS_BAIC_ROAMING: + controlStrings.add(actionStr + SC_BAICr); + controlStrings.add(actionStr + SC_BA_MT); + controlStrings.add(actionStr + SC_BA_ALL); + break; + case SS_ALL_BARRING: + controlStrings.add(actionStr + SC_BA_ALL); + break; + case SS_OUTGOING_BARRING: + controlStrings.add(actionStr + SC_BA_MO); + controlStrings.add(actionStr + SC_BA_ALL); + break; + case SS_INCOMING_BARRING: + controlStrings.add(actionStr + SC_BA_MT); + controlStrings.add(actionStr + SC_BA_ALL); + break; + } + return controlStrings; + } + + /** + * Returns control strings for registration of new password as per TS 22.030 6.5.4 + * @param requestType request type associated with the supplementary service + * @param serviceType supplementary service type + * @return list of control strings for new password registration. + */ + public static ArrayList<String> getControlStringsForPwd(SsData.RequestType requestType, + SsData.ServiceType serviceType) { + ArrayList<String> controlStrings = new ArrayList<>(); + if (requestType == null || serviceType == null) { + return controlStrings; + } + + controlStrings = getControlStrings(SsData.RequestType.SS_ACTIVATION, serviceType); + String actionStr = getActionStringFromReqType(requestType); + ArrayList<String> controlStringsPwd = new ArrayList<>(); + for(String controlString : controlStrings) { + // Prepend each control string with **SC_PWD + controlStringsPwd.add(actionStr + SC_PWD + controlString); + } + return controlStringsPwd; + } + /*** * TODO: It would be nice to have a method here that can take in a dialstring and * figure out if there is an MMI code embedded within it. This code would replace
diff --git a/src/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java b/src/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java index 1f237b2..5b1f36d 100644 --- a/src/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java +++ b/src/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
@@ -120,6 +120,21 @@ } @Override + protected SmsMessageBase.SubmitPduBase getSubmitPdu(String scAddr, String destAddr, + String message, boolean statusReportRequested, SmsHeader smsHeader, int priority, + int validityPeriod, int messageRef) { + return SMSDispatcherUtil.getSubmitPduGsm(scAddr, destAddr, message, statusReportRequested, + validityPeriod, messageRef); + } + + @Override + protected SmsMessageBase.SubmitPduBase getSubmitPdu(String scAddr, String destAddr, + int destPort, byte[] message, boolean statusReportRequested, int messageRef) { + return SMSDispatcherUtil.getSubmitPduGsm(scAddr, destAddr, destPort, message, + statusReportRequested, messageRef); + } + + @Override protected TextEncodingDetails calculateLength(CharSequence messageBody, boolean use7bitOnly) { return SMSDispatcherUtil.calculateLengthGsm(messageBody, use7bitOnly); }
diff --git a/src/java/com/android/internal/telephony/gsm/SuppServiceNotification.java b/src/java/com/android/internal/telephony/gsm/SuppServiceNotification.java index 41e76ae..396e94c 100644 --- a/src/java/com/android/internal/telephony/gsm/SuppServiceNotification.java +++ b/src/java/com/android/internal/telephony/gsm/SuppServiceNotification.java
@@ -18,6 +18,8 @@ import android.telephony.PhoneNumberUtils; +import java.util.Arrays; + /** * Represents a Supplementary Service Notification received from the network. * @@ -200,7 +202,7 @@ + (notificationType == 0 ? " originated " : " terminated ") + " code: " + code + " index: " + index - + " history: " + history + + " history: " + Arrays.toString(history) + " \"" + PhoneNumberUtils.stringFromStringAndTOA(number, type) + "\" "; }
diff --git a/src/java/com/android/internal/telephony/gsm/UsimDataDownloadHandler.java b/src/java/com/android/internal/telephony/gsm/UsimDataDownloadHandler.java index ed819c1..bae56d1 100644 --- a/src/java/com/android/internal/telephony/gsm/UsimDataDownloadHandler.java +++ b/src/java/com/android/internal/telephony/gsm/UsimDataDownloadHandler.java
@@ -17,13 +17,19 @@ package com.android.internal.telephony.gsm; import android.app.Activity; +import android.content.res.Resources; +import android.content.res.Resources.NotFoundException; import android.os.AsyncResult; import android.os.Handler; import android.os.Message; import android.provider.Telephony.Sms.Intents; import android.telephony.PhoneNumberUtils; import android.telephony.SmsManager; +import android.telephony.ims.stub.ImsSmsImplBase; +import com.android.ims.ImsException; +import com.android.ims.ImsManager; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.telephony.CommandsInterface; import com.android.internal.telephony.InboundSmsHandler; import com.android.internal.telephony.PhoneFactory; @@ -61,10 +67,14 @@ private final CommandsInterface mCi; private final int mPhoneId; + private ImsManager mImsManager; + Resources mResource; public UsimDataDownloadHandler(CommandsInterface commandsInterface, int phoneId) { mCi = commandsInterface; mPhoneId = phoneId; + mImsManager = null; // will get initialized when ImsManager connection is ready + mResource = Resources.getSystem(); } /** @@ -79,7 +89,7 @@ * @return {@code Activity.RESULT_OK} on success; {@code RESULT_SMS_GENERIC_ERROR} on failure */ int handleUsimDataDownload(UsimServiceTable ust, SmsMessage smsMessage, - @InboundSmsHandler.SmsSource int smsSource) { + @InboundSmsHandler.SmsSource int smsSource, int token) { // If we receive an SMS-PP message before the UsimServiceTable has been loaded, // assume that the data download service is not present. This is very unlikely to // happen because the IMS connection will not be established until after the ISIM @@ -87,7 +97,7 @@ if (ust != null && ust.isAvailable( UsimServiceTable.UsimService.DATA_DL_VIA_SMS_PP)) { Rlog.d(TAG, "Received SMS-PP data download, sending to UICC."); - return startDataDownload(smsMessage, smsSource); + return startDataDownload(smsMessage, smsSource, token); } else { Rlog.d(TAG, "DATA_DL_VIA_SMS_PP service not available, storing message to UICC."); String smsc = IccUtils.bytesToHexString( @@ -95,7 +105,8 @@ smsMessage.getServiceCenterAddress())); mCi.writeSmsToSim(SmsManager.STATUS_ON_ICC_UNREAD, smsc, IccUtils.bytesToHexString(smsMessage.getPdu()), - obtainMessage(EVENT_WRITE_SMS_COMPLETE)); + obtainMessage(EVENT_WRITE_SMS_COMPLETE, + new int[]{ smsSource, smsMessage.mMessageRef, token })); addUsimDataDownloadToMetrics(false, smsSource); return Activity.RESULT_OK; // acknowledge after response from write to USIM } @@ -111,9 +122,9 @@ * @return {@code Activity.RESULT_OK} on success; {@code RESULT_SMS_GENERIC_ERROR} on failure */ public int startDataDownload(SmsMessage smsMessage, - @InboundSmsHandler.SmsSource int smsSource) { + @InboundSmsHandler.SmsSource int smsSource, int token) { if (sendMessage(obtainMessage(EVENT_START_DATA_DOWNLOAD, - smsSource, 0 /* unused */, smsMessage))) { + smsSource, token, smsMessage))) { return Activity.RESULT_OK; // we will send SMS ACK/ERROR based on UICC response } else { Rlog.e(TAG, "startDataDownload failed to send message to start data download."); @@ -122,7 +133,7 @@ } private void handleDataDownload(SmsMessage smsMessage, - @InboundSmsHandler.SmsSource int smsSource) { + @InboundSmsHandler.SmsSource int smsSource, int token) { int dcs = smsMessage.getDataCodingScheme(); int pid = smsMessage.getProtocolIdentifier(); byte[] pdu = smsMessage.getPdu(); // includes SC address @@ -139,6 +150,7 @@ byte[] envelope = new byte[totalLength]; int index = 0; + Rlog.d(TAG, "smsSource: " + smsSource + "Token: " + token); // SMS-PP download tag and length (assumed to be < 256 bytes). envelope[index++] = (byte) BER_SMS_PP_DOWNLOAD_TAG; @@ -173,14 +185,16 @@ // Verify that we calculated the payload size correctly. if (index != envelope.length) { Rlog.e(TAG, "startDataDownload() calculated incorrect envelope length, aborting."); - acknowledgeSmsWithError(CommandsInterface.GSM_SMS_FAIL_CAUSE_UNSPECIFIED_ERROR); + acknowledgeSmsWithError(CommandsInterface.GSM_SMS_FAIL_CAUSE_UNSPECIFIED_ERROR, + smsSource, token, smsMessage.mMessageRef); addUsimDataDownloadToMetrics(false, smsSource); return; } String encodedEnvelope = IccUtils.bytesToHexString(envelope); mCi.sendEnvelopeWithStatus(encodedEnvelope, obtainMessage( - EVENT_SEND_ENVELOPE_RESPONSE, new int[]{ dcs, pid })); + EVENT_SEND_ENVELOPE_RESPONSE, new int[]{ dcs, pid, smsSource, + smsMessage.mMessageRef, token })); addUsimDataDownloadToMetrics(true, smsSource); } @@ -211,7 +225,8 @@ * @param response UICC response encoded as hexadecimal digits. First two bytes are the * UICC SW1 and SW2 status bytes. */ - private void sendSmsAckForEnvelopeResponse(IccIoResult response, int dcs, int pid) { + private void sendSmsAckForEnvelopeResponse(IccIoResult response, int dcs, int pid, + int smsSource, int token, int messageRef) { int sw1 = response.sw1; int sw2 = response.sw2; @@ -221,7 +236,8 @@ success = true; } else if (sw1 == 0x93 && sw2 == 0x00) { Rlog.e(TAG, "USIM data download failed: Toolkit busy"); - acknowledgeSmsWithError(CommandsInterface.GSM_SMS_FAIL_CAUSE_USIM_APP_TOOLKIT_BUSY); + acknowledgeSmsWithError(CommandsInterface.GSM_SMS_FAIL_CAUSE_USIM_APP_TOOLKIT_BUSY, + smsSource, token, messageRef); return; } else if (sw1 == 0x62 || sw1 == 0x63) { Rlog.e(TAG, "USIM data download failed: " + response.toString()); @@ -234,10 +250,11 @@ byte[] responseBytes = response.payload; if (responseBytes == null || responseBytes.length == 0) { if (success) { - mCi.acknowledgeLastIncomingGsmSms(true, 0, null); + acknowledgeSmsWithSuccess(0, smsSource, token, messageRef); } else { acknowledgeSmsWithError( - CommandsInterface.GSM_SMS_FAIL_CAUSE_USIM_DATA_DOWNLOAD_ERROR); + CommandsInterface.GSM_SMS_FAIL_CAUSE_USIM_DATA_DOWNLOAD_ERROR, smsSource, + token, messageRef); } return; } @@ -268,12 +285,32 @@ System.arraycopy(responseBytes, 0, smsAckPdu, index, responseBytes.length); - mCi.acknowledgeIncomingGsmSmsWithPdu(success, - IccUtils.bytesToHexString(smsAckPdu), null); + if (smsSource == InboundSmsHandler.SOURCE_INJECTED_FROM_IMS && ackViaIms()) { + acknowledgeImsSms(token, messageRef, true, smsAckPdu); + } else { + mCi.acknowledgeIncomingGsmSmsWithPdu(success, + IccUtils.bytesToHexString(smsAckPdu), null); + } } - private void acknowledgeSmsWithError(int cause) { - mCi.acknowledgeLastIncomingGsmSms(false, cause, null); + private void acknowledgeSmsWithSuccess(int cause, int smsSource, int token, int messageRef) { + Rlog.d(TAG, "acknowledgeSmsWithSuccess- cause: " + cause + " smsSource: " + smsSource + + " token: " + token + " messageRef: " + messageRef); + if (smsSource == InboundSmsHandler.SOURCE_INJECTED_FROM_IMS && ackViaIms()) { + acknowledgeImsSms(token, messageRef, true, null); + } else { + mCi.acknowledgeLastIncomingGsmSms(true, cause, null); + } + } + + private void acknowledgeSmsWithError(int cause, int smsSource, int token, int messageRef) { + Rlog.d(TAG, "acknowledgeSmsWithError- cause: " + cause + " smsSource: " + smsSource + + " token: " + token + " messageRef: " + messageRef); + if (smsSource == InboundSmsHandler.SOURCE_INJECTED_FROM_IMS && ackViaIms()) { + acknowledgeImsSms(token, messageRef, false, null); + } else { + mCi.acknowledgeLastIncomingGsmSms(false, cause, null); + } } /** @@ -300,6 +337,45 @@ } /** + * Route resposes via ImsManager based on config + */ + private boolean ackViaIms() { + boolean isViaIms; + + try { + isViaIms = mResource.getBoolean( + com.android.internal.R.bool.config_smppsim_response_via_ims); + } catch (NotFoundException e) { + isViaIms = false; + } + + Rlog.d(TAG, "ackViaIms : " + isViaIms); + return isViaIms; + } + + /** + * Acknowledges IMS SMS and delivers the result based on the envelope or SIM saving respose + * received from SIM for SMS-PP Data. + */ + private void acknowledgeImsSms(int token, int messageRef, boolean success, byte[] pdu) { + int result = success ? ImsSmsImplBase.DELIVER_STATUS_OK : + ImsSmsImplBase.DELIVER_STATUS_ERROR_GENERIC; + Rlog.d(TAG, "sending result via acknowledgeImsSms: " + result + " token: " + token); + + try { + if (mImsManager != null) { + if (pdu != null && pdu.length > 0) { + mImsManager.acknowledgeSms(token, messageRef, result, pdu); + } else { + mImsManager.acknowledgeSms(token, messageRef, result); + } + } + } catch (ImsException e) { + Rlog.e(TAG, "Failed to acknowledgeSms(). Error: " + e.getMessage()); + } + } + + /** * Handle UICC envelope response and send SMS acknowledgement. * * @param msg the message to handle @@ -307,35 +383,60 @@ @Override public void handleMessage(Message msg) { AsyncResult ar; + int smsSource = InboundSmsHandler.SOURCE_INJECTED_FROM_UNKNOWN; + int token = 0; + int messageRef = 0; + int[] responseInfo; switch (msg.what) { case EVENT_START_DATA_DOWNLOAD: - handleDataDownload((SmsMessage) msg.obj, msg.arg1 /* smsSource */); + Rlog.d(TAG, "EVENT_START_DATA_DOWNLOAD"); + handleDataDownload((SmsMessage) msg.obj, msg.arg1 /* smsSource */, + msg.arg2 /* token */); break; case EVENT_SEND_ENVELOPE_RESPONSE: ar = (AsyncResult) msg.obj; + responseInfo = (int[]) ar.userObj; + smsSource = responseInfo[2]; + messageRef = responseInfo[3]; + token = responseInfo[4]; + + Rlog.d(TAG, "Received EVENT_SEND_ENVELOPE_RESPONSE from source : " + smsSource); + if (ar.exception != null) { Rlog.e(TAG, "UICC Send Envelope failure, exception: " + ar.exception); + acknowledgeSmsWithError( - CommandsInterface.GSM_SMS_FAIL_CAUSE_USIM_DATA_DOWNLOAD_ERROR); + CommandsInterface.GSM_SMS_FAIL_CAUSE_USIM_DATA_DOWNLOAD_ERROR, + smsSource, token, messageRef); return; } - int[] dcsPid = (int[]) ar.userObj; - sendSmsAckForEnvelopeResponse((IccIoResult) ar.result, dcsPid[0], dcsPid[1]); + Rlog.d(TAG, "Successful in sending envelope response"); + sendSmsAckForEnvelopeResponse((IccIoResult) ar.result, responseInfo[0], + responseInfo[1], smsSource, token, messageRef); break; case EVENT_WRITE_SMS_COMPLETE: ar = (AsyncResult) msg.obj; + + responseInfo = (int[]) ar.userObj; + smsSource = responseInfo[0]; + messageRef = responseInfo[1]; + token = responseInfo[2]; + + Rlog.d(TAG, "Received EVENT_WRITE_SMS_COMPLETE from source : " + smsSource); + if (ar.exception == null) { Rlog.d(TAG, "Successfully wrote SMS-PP message to UICC"); - mCi.acknowledgeLastIncomingGsmSms(true, 0, null); + acknowledgeSmsWithSuccess(0, smsSource, token, messageRef); } else { Rlog.d(TAG, "Failed to write SMS-PP message to UICC", ar.exception); - mCi.acknowledgeLastIncomingGsmSms(false, - CommandsInterface.GSM_SMS_FAIL_CAUSE_UNSPECIFIED_ERROR, null); + acknowledgeSmsWithError( + CommandsInterface.GSM_SMS_FAIL_CAUSE_UNSPECIFIED_ERROR, + smsSource, token, messageRef); } break; @@ -343,4 +444,23 @@ Rlog.e(TAG, "Ignoring unexpected message, what=" + msg.what); } } + + /** + * Called when ImsManager connection is ready. ImsManager object will be used to send ACK to IMS + * which doesn't use RIL interface. + * @param imsManager object + */ + public void setImsManager(ImsManager imsManager) { + mImsManager = imsManager; + } + + /** + * Called to set mocked object of type Resources during unit testing of this file. + * @param resource object + */ + @VisibleForTesting + public void setResourcesForTest(Resources resource) { + mResource = resource; + Rlog.d(TAG, "setResourcesForTest"); + } }
diff --git a/src/java/com/android/internal/telephony/gsm/UsimPhoneBookManager.java b/src/java/com/android/internal/telephony/gsm/UsimPhoneBookManager.java old mode 100755 new mode 100644 index e594ab6..48be16c --- a/src/java/com/android/internal/telephony/gsm/UsimPhoneBookManager.java +++ b/src/java/com/android/internal/telephony/gsm/UsimPhoneBookManager.java
@@ -32,6 +32,7 @@ import com.android.telephony.Rlog; import java.util.ArrayList; +import java.util.Locale; /** * This class implements reading and parsing USIM records. @@ -233,7 +234,7 @@ int emailEfid = email.getEfid(); log("EF_EMAIL exists in PBR. efid = 0x" + - Integer.toHexString(emailEfid).toUpperCase()); + Integer.toHexString(emailEfid).toUpperCase(Locale.ROOT)); /** * Make sure this EF_EMAIL was never read earlier. Sometimes two PBR record points @@ -348,7 +349,7 @@ emailList = new ArrayList<String>(); } log("Adding email #" + i + " list to index 0x" + - Integer.toHexString(index).toUpperCase()); + Integer.toHexString(index).toUpperCase(Locale.ROOT)); emailList.add(email); mEmailsForAdnRec.put(index, emailList); } @@ -402,7 +403,7 @@ } emailList.add(email); log("Adding email list to index 0x" + - Integer.toHexString(index).toUpperCase()); + Integer.toHexString(index).toUpperCase(Locale.ROOT)); mEmailsForAdnRec.put(index, emailList); } } @@ -446,8 +447,9 @@ System.arraycopy(emailList.toArray(), 0, emails, 0, emailList.size()); rec.setEmails(emails); log("Adding email list to ADN (0x" + - Integer.toHexString(mPhoneBookRecords.get(i).getEfid()).toUpperCase() + - ") record #" + mPhoneBookRecords.get(i).getRecId()); + Integer.toHexString(mPhoneBookRecords.get(i).getEfid()) + .toUpperCase(Locale.ROOT) + ") record #" + + mPhoneBookRecords.get(i).getRecId()); mPhoneBookRecords.set(i, rec); } }
diff --git a/src/java/com/android/internal/telephony/ims/ImsEnablementTracker.java b/src/java/com/android/internal/telephony/ims/ImsEnablementTracker.java new file mode 100644 index 0000000..e54561f --- /dev/null +++ b/src/java/com/android/internal/telephony/ims/ImsEnablementTracker.java
@@ -0,0 +1,939 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.telephony.ims; + +import android.content.ComponentName; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.RemoteException; +import android.telephony.SubscriptionManager; +import android.telephony.ims.aidl.IImsServiceController; +import android.util.Log; +import android.util.SparseArray; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.IState; +import com.android.internal.util.State; +import com.android.internal.util.StateMachine; + +import java.util.HashMap; +import java.util.Map; + +/** + * This class will abstract away all the new enablement logic and take the reset/enable/disable + * IMS commands as inputs. + * The IMS commands will call enableIms, disableIms or resetIms to match the enablement state only + * when it changes. + */ +public class ImsEnablementTracker { + private static final String LOG_TAG = "ImsEnablementTracker"; + private static final long REQUEST_THROTTLE_TIME_MS = 3 * 1000L; // 3 seconds + + private static final int COMMAND_NONE_MSG = 0; + // Indicate that the enableIms command has been received. + @VisibleForTesting + public static final int COMMAND_ENABLE_MSG = 1; + // Indicate that the disableIms command has been received. + @VisibleForTesting + public static final int COMMAND_DISABLE_MSG = 2; + // Indicate that the resetIms command has been received. + private static final int COMMAND_RESET_MSG = 3; + // Indicate that the internal enable message with delay has been received. + private static final int COMMAND_ENABLING_DONE = 4; + // Indicate that the internal disable message with delay has been received. + private static final int COMMAND_DISABLING_DONE = 5; + // Indicate that the internal reset message with delay has been received. + @VisibleForTesting + public static final int COMMAND_RESETTING_DONE = 6; + // The ImsServiceController binder is connected. + private static final int COMMAND_CONNECTED_MSG = 7; + // The ImsServiceController binder is disconnected. + private static final int COMMAND_DISCONNECTED_MSG = 8; + // The subId is changed to INVALID_SUBSCRIPTION_ID. + private static final int COMMAND_INVALID_SUBID_MSG = 9; + // Indicate that the internal post reset message with delay has been received. + @VisibleForTesting + public static final int COMMAND_POST_RESETTING_DONE = 10; + + private static final Map<Integer, String> EVENT_DESCRIPTION = new HashMap<>(); + static { + EVENT_DESCRIPTION.put(COMMAND_NONE_MSG, "COMMAND_NONE_MSG"); + EVENT_DESCRIPTION.put(COMMAND_ENABLE_MSG, "COMMAND_ENABLE_MSG"); + EVENT_DESCRIPTION.put(COMMAND_DISABLE_MSG, "COMMAND_DISABLE_MSG"); + EVENT_DESCRIPTION.put(COMMAND_RESET_MSG, "COMMAND_RESET_MSG"); + EVENT_DESCRIPTION.put(COMMAND_ENABLING_DONE, "COMMAND_ENABLING_DONE"); + EVENT_DESCRIPTION.put(COMMAND_DISABLING_DONE, "COMMAND_DISABLING_DONE"); + EVENT_DESCRIPTION.put(COMMAND_RESETTING_DONE, "COMMAND_RESETTING_DONE"); + EVENT_DESCRIPTION.put(COMMAND_CONNECTED_MSG, "COMMAND_CONNECTED_MSG"); + EVENT_DESCRIPTION.put(COMMAND_DISCONNECTED_MSG, "COMMAND_DISCONNECTED_MSG"); + EVENT_DESCRIPTION.put(COMMAND_INVALID_SUBID_MSG, "COMMAND_INVALID_SUBID_MSG"); + } + + @VisibleForTesting + protected static final int STATE_IMS_DISCONNECTED = 0; + @VisibleForTesting + protected static final int STATE_IMS_DEFAULT = 1; + @VisibleForTesting + protected static final int STATE_IMS_ENABLED = 2; + @VisibleForTesting + protected static final int STATE_IMS_DISABLING = 3; + @VisibleForTesting + protected static final int STATE_IMS_DISABLED = 4; + @VisibleForTesting + protected static final int STATE_IMS_ENABLING = 5; + @VisibleForTesting + protected static final int STATE_IMS_RESETTING = 6; + + @VisibleForTesting + protected static final int STATE_IMS_POSTRESETTING = 7; + + protected final Object mLock = new Object(); + private IImsServiceController mIImsServiceController; + private long mLastImsOperationTimeMs = 0L; + private final ComponentName mComponentName; + private final SparseArray<ImsEnablementTrackerStateMachine> mStateMachines; + + private final Looper mLooper; + private final int mState; + + /** + * Provides Ims Enablement Tracker State Machine responsible for ims enable/disable/reset + * command interactions with Ims service controller binder. + * The enable/disable/reset ims commands have a time interval of at least + * {@link ImsEnablementTracker#REQUEST_THROTTLE_TIME_MS} second between + * processing each command. + * For example, the enableIms command is received and the binder's enableIms is called. + * After that, if the disableIms command is received, the binder's disableIms will be + * called after {@link ImsEnablementTracker#REQUEST_THROTTLE_TIME_MS} second. + * A time of {@link ImsEnablementTracker#REQUEST_THROTTLE_TIME_MS} will be used + * {@link Handler#sendMessageDelayed(Message, long)}, + * and the enabled, disabled and reset states are responsible for waiting for + * that delay message. + */ + class ImsEnablementTrackerStateMachine extends StateMachine { + /** + * The initial state of this class and waiting for an ims commands. + */ + @VisibleForTesting + public final Default mDefault; + + /** + * Indicates that {@link IImsServiceController#enableIms(int, int)} has been called and + * waiting for an ims commands. + * Common transitions are to + * {@link #mDisabling} state when the disable command is received + * or {@link #mResetting} state when the reset command is received + * or {@link #mDisconnected} if the binder is disconnected. + */ + @VisibleForTesting + public final Enabled mEnabled; + + /** + * Indicates that the state waiting for the throttle time to elapse before calling + * {@link IImsServiceController#disableIms(int, int)}. + * Common transitions are to + * {@link #mEnabled} when the enable command is received. + * or {@link #mResetting} when the reset command is received. + * or {@link #mDisabled} the previous binder API call has passed + * {@link ImsEnablementTracker#REQUEST_THROTTLE_TIME_MS} second, and if + * {@link IImsServiceController#disableIms(int, int)} called. + * or {@link #mDisabling} received a disableIms message and the previous binder API call + * has not passed {@link ImsEnablementTracker#REQUEST_THROTTLE_TIME_MS} second. + * Then send a disableIms message with delay. + * or {@link #mDisconnected} if the binder is disconnected. + */ + @VisibleForTesting + public final Disabling mDisabling; + + /** + * Indicates that {@link IImsServiceController#disableIms(int, int)} has been called and + * waiting for an ims commands. + * Common transitions are to + * {@link #mEnabling} state when the enable command is received. + * or {@link #mDisconnected} if the binder is disconnected. + */ + @VisibleForTesting + public final Disabled mDisabled; + + /** + * Indicates that the state waiting for the throttle time to elapse before calling + * {@link IImsServiceController#enableIms(int, int)}. + * Common transitions are to + * {@link #mEnabled} the previous binder API call has passed + * {@link ImsEnablementTracker#REQUEST_THROTTLE_TIME_MS} second, and + * {@link IImsServiceController#enableIms(int, int)} called. + * or {@link #mDisabled} when the disable command is received. + * or {@link #mEnabling} received an enableIms message and the previous binder API call + * has not passed {@link ImsEnablementTracker#REQUEST_THROTTLE_TIME_MS} second. + * Then send an enableIms message with delay. + * or {@link #mDisconnected} if the binder is disconnected. + */ + @VisibleForTesting + public final Enabling mEnabling; + + /** + * Indicates that the state waiting for the throttle time to elapse before calling + * {@link IImsServiceController#resetIms(int, int)}. + * Common transitions are to + * {@link #mPostResetting} state to call either enableIms or disableIms after calling + * {@link IImsServiceController#resetIms(int, int)} + * or {@link #mDisconnected} if the binder is disconnected. + */ + @VisibleForTesting + public final Resetting mResetting; + + /** + * Indicates that the state waiting after resetIms for the throttle time to elapse before + * calling {@link IImsServiceController#enableIms(int, int)} or + * {@link IImsServiceController#disableIms(int, int)}. + * Common transitions are to + * {@link #mEnabled} state when the disable command is received, + * {@link #mDisabled} state when the enable command is received after calling + * {@link IImsServiceController#enableIms(int, int)}, + * {@link IImsServiceController#disableIms(int, int)} + * or {@link #mDisconnected} if the binder is disconnected. + */ + public final PostResetting mPostResetting; + + /** + * Indicates that {@link IImsServiceController} has not been set. + * Common transition is to + * {@link #mDefault} state when the binder is set. + * or {@link #mDisabling} If the disable command is received while the binder is + * disconnected + * or {@link #mEnabling} If the enable command is received while the binder is + * disconnected + */ + + private final Disconnected mDisconnected; + private int mSlotId; + private int mSubId; + + private final int mPhoneId; + + private IState mPreviousState; + + private int mLastMsg = COMMAND_NONE_MSG; + + ImsEnablementTrackerStateMachine(String name, Looper looper, int state, int slotId) { + super(name, looper); + mPhoneId = slotId; + mDefault = new Default(); + mEnabled = new Enabled(); + mDisabling = new Disabling(); + mDisabled = new Disabled(); + mEnabling = new Enabling(); + mResetting = new Resetting(); + mDisconnected = new Disconnected(); + mPostResetting = new PostResetting(); + + addState(mDefault); + addState(mEnabled); + addState(mDisabling); + addState(mDisabled); + addState(mEnabling); + addState(mResetting); + addState(mDisconnected); + addState(mPostResetting); + + setInitialState(getState(state)); + mPreviousState = getState(state); + } + + public void clearAllMessage() { + Log.d(LOG_TAG, "clearAllMessage"); + removeMessages(COMMAND_ENABLE_MSG); + removeMessages(COMMAND_DISABLE_MSG); + removeMessages(COMMAND_RESET_MSG); + removeMessages(COMMAND_ENABLING_DONE); + removeMessages(COMMAND_DISABLING_DONE); + removeMessages(COMMAND_RESETTING_DONE); + removeMessages(COMMAND_POST_RESETTING_DONE); + } + + public void serviceBinderConnected() { + clearAllMessage(); + sendMessage(COMMAND_CONNECTED_MSG); + } + + public void serviceBinderDisconnected() { + clearAllMessage(); + sendMessage(COMMAND_DISCONNECTED_MSG); + } + + @VisibleForTesting + public boolean isState(int state) { + State expect = null; + switch (state) { + case Default.STATE_NO: + expect = mDefault; + break; + case Enabled.STATE_NO: + expect = mEnabled; + break; + case Disabling.STATE_NO: + expect = mDisabling; + break; + case Disabled.STATE_NO: + expect = mDisabled; + break; + case Enabling.STATE_NO: + expect = mEnabling; + break; + case Resetting.STATE_NO: + expect = mResetting; + break; + case Disconnected.STATE_NO: + expect = mDisconnected; + break; + case PostResetting.STATE_NO: + expect = mPostResetting; + break; + default: + break; + } + return (getCurrentState() == expect) ? true : false; + } + + private State getState(int state) { + switch (state) { + case ImsEnablementTracker.STATE_IMS_ENABLED: + return mEnabled; + case ImsEnablementTracker.STATE_IMS_DISABLING: + return mDisabling; + case ImsEnablementTracker.STATE_IMS_DISABLED: + return mDisabled; + case ImsEnablementTracker.STATE_IMS_ENABLING: + return mEnabling; + case ImsEnablementTracker.STATE_IMS_RESETTING: + return mResetting; + case ImsEnablementTracker.STATE_IMS_DISCONNECTED: + return mDisconnected; + case ImsEnablementTracker.STATE_IMS_POSTRESETTING: + return mPostResetting; + default: + return mDefault; + } + } + + private void handleInvalidSubIdMessage() { + clearAllMessage(); + transitionState(mDefault); + } + + private void transitionState(State state) { + mPreviousState = getCurrentState(); + transitionTo(state); + } + + class Default extends State { + private static final int STATE_NO = STATE_IMS_DEFAULT; + + @Override + public void enter() { + Log.d(LOG_TAG, "[" + mPhoneId + "]Default state:enter"); + mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; + } + + @Override + public boolean processMessage(Message message) { + Log.d(LOG_TAG, "[" + mPhoneId + "]Default state:processMessage. msg.what=" + + EVENT_DESCRIPTION.get(message.what) + ",component:" + mComponentName); + + switch (message.what) { + // When enableIms() is called, enableIms of binder is call and the state + // change to the enabled state. + case COMMAND_ENABLE_MSG: + sendEnableIms(message.arg1, message.arg2); + transitionState(mEnabled); + return HANDLED; + // When disableIms() is called, disableIms of binder is call and the state + // change to the disabled state. + case COMMAND_DISABLE_MSG: + sendDisableIms(message.arg1, message.arg2); + transitionState(mDisabled); + return HANDLED; + // When resetIms() is called, change to the resetting state to call enableIms + // after calling resetIms of binder. + case COMMAND_RESET_MSG: + mSlotId = message.arg1; + mSubId = message.arg2; + transitionState(mResetting); + return HANDLED; + case COMMAND_DISCONNECTED_MSG: + transitionState(mDisconnected); + return HANDLED; + default: + return NOT_HANDLED; + } + } + } + + class Enabled extends State { + private static final int STATE_NO = STATE_IMS_ENABLED; + +