Merge "Request again when the latest signal strength report request and report state of modem are diferent" into 24D1-dev am: 3316b43f04

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/opt/telephony/+/27514096

Change-Id: Ib44a05a846fe710c25a2824e1c9ac70e7f55ce61
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/flags/Android.bp b/flags/Android.bp
index 8f363b6..1885032 100644
--- a/flags/Android.bp
+++ b/flags/Android.bp
@@ -21,6 +21,7 @@
 aconfig_declarations {
     name: "telephony_flags",
     package: "com.android.internal.telephony.flags",
+    container: "system",
     srcs: [
         "calling.aconfig",
         "data.aconfig",
@@ -32,7 +33,6 @@
         "subscription.aconfig",
         "uicc.aconfig",
         "satellite.aconfig",
-        "iwlan.aconfig",
-        "telephony.aconfig",
+        "iwlan.aconfig"
     ],
 }
diff --git a/flags/calling.aconfig b/flags/calling.aconfig
index e67ebc6..c1dc7e7 100644
--- a/flags/calling.aconfig
+++ b/flags/calling.aconfig
@@ -1,15 +1,31 @@
 package: "com.android.internal.telephony.flags"
+container: "system"
 
+# OWNER=breadley TARGET=24Q3
 flag {
   name: "simultaneous_calling_indications"
+  is_exported: true
   namespace: "telephony"
   description: "APIs that are used to notify simultaneous calling changes to other applications."
   bug: "297446980"
+  is_exported: true
 }
 
+# OWNER=yomna TARGET=24Q3
 flag {
   name: "show_call_fail_notification_for_2g_toggle"
   namespace: "telephony"
   description: "Used in DisconnectCause and TelephonyConnection if a non-emergency call fails on a device with no 2G, to guard whether a user can see an updated error message reminding the 2G is disabled and potentially disrupting their call connectivity"
   bug: "300142897"
-}
\ No newline at end of file
+}
+
+# OWNER=stevestatia TARGET=24Q4
+flag {
+    name: "remove_country_code_from_local_singapore_calls"
+    namespace: "telephony"
+    description: "Fix bug where the country code is being shown when merging in local Singapore numbers to conference calls."
+    bug:"284416645"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
diff --git a/flags/data.aconfig b/flags/data.aconfig
index 6334803..f93999b 100644
--- a/flags/data.aconfig
+++ b/flags/data.aconfig
@@ -1,5 +1,18 @@
 package: "com.android.internal.telephony.flags"
+container: "system"
 
+# OWNER=linggm TARGET=24Q4
+flag {
+  name: "keep_empty_requests_network"
+  namespace: "telephony"
+  description: "Don't tear down network even if no requests attached to it."
+  bug: "331301784"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
+
+# OWNER=linggm TARGET=24Q3
 flag {
   name: "auto_data_switch_allow_roaming"
   namespace: "telephony"
@@ -10,6 +23,7 @@
   }
 }
 
+# OWNER=linggm TARGET=24Q3
 flag {
   name: "auto_data_switch_rat_ss"
   namespace: "telephony"
@@ -17,6 +31,7 @@
   bug:"260928808"
 }
 
+# OWNER=linggm TARGET=24Q2
 flag {
   name: "use_alarm_callback"
   namespace: "telephony"
@@ -24,6 +39,7 @@
   bug: "311476875"
 }
 
+# OWNER=linggm TARGET=24Q2
 flag {
   name: "refine_preferred_data_profile_selection"
   namespace: "telephony"
@@ -31,6 +47,7 @@
   bug: "311476883"
 }
 
+# OWNER=linggm TARGET=24Q2
 flag {
   name: "unthrottle_check_transport"
   namespace: "telephony"
@@ -38,6 +55,7 @@
   bug: "303922311"
 }
 
+# OWNER=linggm TARGET=24Q1
 flag {
   name: "relax_ho_teardown"
   namespace: "telephony"
@@ -45,6 +63,7 @@
   bug: "270895912"
 }
 
+# OWNER=linggm TARGET=24Q2
 flag {
   name: "allow_mmtel_in_non_vops"
   namespace: "telephony"
@@ -52,6 +71,7 @@
   bug: "241198464"
 }
 
+# OWNER=jackyu TARGET=24Q2
 flag {
   name: "metered_embb_urlcc"
   namespace: "telephony"
@@ -59,27 +79,34 @@
   bug: "301310451"
   }
 
+# OWNER=sarahchin TARGET=24Q3
 flag {
   name: "slicing_additional_error_codes"
+  is_exported: true
   namespace: "telephony"
   description: "Support additional slicing error codes and functionality."
   bug: "307378699"
 }
 
+# OWNER=nagendranb TARGET=24Q3
 flag {
   name: "apn_setting_field_support_flag"
+  is_exported: true
   namespace: "telephony"
   description: "Expose apn setting supporting field"
   bug: "307038091"
 }
 
+# OWNER=sangyun TARGET=24Q3
 flag {
   name: "network_validation"
+  is_exported: true
   namespace: "telephony"
   description: "Request network validation for data networks and response status."
   bug:"286171724"
 }
 
+# OWNER=nagendranb TARGET=24Q2
 flag {
  name: "notify_data_activity_changed_with_slot"
   namespace: "telephony"
@@ -87,6 +114,7 @@
   bug: "309896936"
 }
 
+# OWNER=qingqi TARGET=24Q3
 flag {
   name: "vonr_enabled_metric"
   namespace: "telephony"
@@ -94,6 +122,7 @@
   bug:"288449751"
 }
 
+# OWNER=willycwhu TARGET=24Q2
 flag {
   name: "ignore_existing_networks_for_internet_allowed_checking"
   namespace: "telephony"
@@ -101,6 +130,7 @@
   bug: "284420611"
 }
 
+# OWNER=apsankar TARGET=24Q3
 flag {
   name: "data_call_session_stats_captures_cross_sim_calling"
   namespace: "telephony"
@@ -108,6 +138,7 @@
   bug: "313956117"
 }
 
+# OWNER=jackyu TARGET=24Q2
 flag {
   name: "force_iwlan_mms"
   namespace: "telephony"
@@ -115,6 +146,7 @@
   bug: "316211526"
 }
 
+# OWNER=sewook TARGET=24Q3
 flag {
   name: "reconnect_qualified_network"
   namespace: "telephony"
@@ -122,9 +154,18 @@
   bug: "319520561"
 }
 
+# OWNER=jackyu TARGET=24Q3
 flag {
   name: "dsrs_diagnostics_enabled"
   namespace: "telephony"
   description: "Enable DSRS diagnostics."
   bug: "319601607"
-}
\ No newline at end of file
+}
+
+# OWNER=jackyu TARGET=24Q3
+flag {
+  name: "data_rat_metric_enabled"
+  namespace: "telephony"
+  description: "Write DataRatStateChanged atom"
+  bug:"318519337"
+}
diff --git a/flags/domainselection.aconfig b/flags/domainselection.aconfig
index 2e1dfc8..623c3b6 100644
--- a/flags/domainselection.aconfig
+++ b/flags/domainselection.aconfig
@@ -1,5 +1,7 @@
 package: "com.android.internal.telephony.flags"
+container: "system"
 
+# OWNER=forestchoi TARGET=24Q3
 flag {
     name: "ap_domain_selection_enabled"
     namespace: "telephony"
@@ -7,6 +9,7 @@
     bug:"258112541"
 }
 
+# OWNER=forestchoi TARGET=24Q3
 flag {
     name: "use_aosp_domain_selection_service"
     namespace: "telephony"
@@ -14,13 +17,16 @@
     bug:"258112541"
 }
 
+# OWNER=forestchoi TARGET=24Q3
 flag {
     name: "use_oem_domain_selection_service"
+    is_exported: true
     namespace: "telephony"
     description: "This flag controls OEMs' domain selection service supported."
     bug:"258112541"
 }
 
+# OWNER=forestchoi TARGET=24Q3
 flag {
     name: "domain_selection_metrics_enabled"
     namespace: "telephony"
diff --git a/flags/ims.aconfig b/flags/ims.aconfig
index d09259e..4eff505 100644
--- a/flags/ims.aconfig
+++ b/flags/ims.aconfig
@@ -1,5 +1,7 @@
 package: "com.android.internal.telephony.flags"
+container: "system"
 
+# OWNER=hyosunkim TARGET=24Q2
 flag {
     name: "conference_hold_unhold_changed_to_send_message"
     namespace: "telephony"
@@ -7,6 +9,7 @@
     bug:"288002989"
 }
 
+# OWNER=joonhunshin TARGET=24Q2
 flag {
     name: "ignore_already_terminated_incoming_call_before_registering_listener"
     namespace: "telephony"
@@ -14,6 +17,7 @@
     bug:"289461637"
 }
 
+# OWNER=joonhunshin TARGET=24Q2
 flag {
     name: "clear_cached_ims_phone_number_when_device_lost_ims_registration"
     namespace: "telephony"
@@ -21,6 +25,7 @@
     bug:"288002989"
 }
 
+# OWNER=sangyun TARGET=24Q2
 flag {
     name: "update_ims_service_by_gathering_provisioning_changes"
     namespace: "telephony"
@@ -28,13 +33,16 @@
     bug:"302281114"
 }
 
+# OWNER=shmun TARGET=24Q3
 flag {
     name: "add_rat_related_suggested_action_to_ims_registration"
+    is_exported: true
     namespace: "telephony"
     description: "This flag is for adding suggested actions related to RAT to ims registration"
     bug:"290573256"
 }
 
+# OWNER=joonhunshin TARGET=24Q3
 flag {
     name: "terminate_active_video_call_when_accepting_second_video_call_as_audio_only"
     namespace: "telephony"
@@ -42,13 +50,16 @@
     bug:"309548300"
 }
 
+# OWNER=sewookseo TARGET=24Q3
 flag {
     name: "emergency_registration_state"
+    is_exported: true
     namespace: "telephony"
     description: "This flag is created to notify emergency registration state changed."
     bug:"312101946"
 }
 
+# OWNER=apsankar TARGET=24Q3
 flag {
     name: "call_extra_for_non_hold_supported_carriers"
     namespace: "telephony"
@@ -56,9 +67,51 @@
     bug:"315993953"
 }
 
+# OWNER=joonhunshin TARGET=24Q3
 flag {
     name: "update_roaming_state_to_set_wfc_mode"
     namespace: "telephony"
     description: "This flag updates roaming state to set wfc mode"
     bug:"317298331"
 }
+
+# OWNER=joonhunshin TARGET=24Q3
+flag {
+    name: "enable_sip_subscribe_retry"
+    namespace: "telephony"
+    description: "This flag controls whether framework supports SIP subscribe retry or not"
+    bug:"297023230"
+}
+
+# OWNER=joonhunshin TARGET=24Q3
+flag {
+    name: "answer_audio_only_when_answering_via_mmi_code"
+    namespace: "telephony"
+    description: "This flag changes the media type when answering incoming call via MMI code"
+    bug:"286499659"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
+
+# OWNER=joonhunshin TARGET=24Q3
+flag {
+    name: "notify_initial_ims_provisioning_status"
+    namespace: "telephony"
+    description: "This flag allows to notify initial IMS provisioning status when IFeatureProvisioningCallback registered or ImsService connected"
+    bug:"330082572"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
+
+# OWNER=joonhunshin TARGET=24Q3
+flag {
+    name: "set_number_of_sim_for_ims_enable"
+    namespace: "telephony"
+    description: "This flag allows to set number of SIM for IMS enable/disable for each slot when the eSIM is added while the binding with ImsService exists"
+    bug:"331971397"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
diff --git a/flags/iwlan.aconfig b/flags/iwlan.aconfig
index 0dc9f8d..f16ee38 100644
--- a/flags/iwlan.aconfig
+++ b/flags/iwlan.aconfig
@@ -1,13 +1,19 @@
 package: "com.android.internal.telephony.flags"
+container: "system"
 
+# OWNER=jmunikrishna TARGET=24Q3
 flag {
     name: "enable_aead_algorithms"
+    is_exported: true
     namespace: "telephony"
     description: "Add AEAD algorithms AES-GCM-8, AES-GCM-12 and AES-GCM-16 to IWLAN"
     bug:"306119890"
 }
+
+# OWNER=jmunikrishna TARGET=24Q3
 flag {
     name: "enable_multiple_sa_proposals"
+    is_exported: true
     namespace: "telephony"
     description: "Add multiple proposals of cipher suites in IKE SA and Child SA"
     bug:"287296642"
diff --git a/flags/messaging.aconfig b/flags/messaging.aconfig
index 1ba89ba..1030ba7 100644
--- a/flags/messaging.aconfig
+++ b/flags/messaging.aconfig
@@ -1,12 +1,7 @@
 package: "com.android.internal.telephony.flags"
+container: "system"
 
-flag {
-  name: "reject_bad_sub_id_interaction"
-  namespace: "telephony"
-  description: "Previously, the DB allows insertion of a random sub Id, but doesn't allow query it. This change rejects such interaction."
-  bug: "294125411"
-}
-
+# OWNER=hwangoo TARGET=24Q2
 flag {
   name: "sms_domain_selection_enabled"
   namespace: "telephony"
@@ -14,9 +9,33 @@
   bug: "262804071"
 }
 
+# OWNER=tnd TARGET=24Q3
 flag {
   name: "mms_disabled_error"
+  is_exported: true
   namespace: "telephony"
   description: "This flag controls the support of the new MMS error code MMS_ERROR_MMS_DISABLED."
   bug: "305062594"
+}
+
+# OWNER=linggm TARGET=24Q4
+flag {
+  name: "mms_get_apn_from_pdsc"
+  namespace: "telephony"
+  description: "This flag controls get APN details from PDSC instead of telephony provider."
+  bug: "324280016"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
+
+# OWNER=stevestatia TARGET=24Q3
+flag {
+    name: "unregister_sms_broadcast_receiver_from_cat_service"
+    namespace: "telephony"
+    description: "This flag will unregister the sms broadcast receiver in the CatService when the process is disposed."
+    bug: "338936403"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
 }
\ No newline at end of file
diff --git a/flags/misc.aconfig b/flags/misc.aconfig
index 1e714c5..9d5cbd6 100644
--- a/flags/misc.aconfig
+++ b/flags/misc.aconfig
@@ -1,5 +1,18 @@
 package: "com.android.internal.telephony.flags"
+container: "system"
 
+# OWNER=linggm TARGET=24Q3
+flag {
+    name: "combine_ril_death_handle"
+    namespace: "telephony"
+    description: "Upon radio service death, combine its handling to prevent race condition"
+    bug:"319612362"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
+
+# OWNER=tjstuart TARGET=24Q3
 flag {
   name: "do_not_override_precise_label"
   namespace: "telephony"
@@ -8,6 +21,7 @@
   is_fixed_read_only: true
 }
 
+# OWNER=tnd TARGET=24Q1
 flag {
   name: "log_mms_sms_database_access_info"
   namespace: "telephony"
@@ -15,6 +29,7 @@
   bug: "275225402"
 }
 
+# OWNER=tjstuart TARGET=24Q3
 flag {
   name: "stop_spamming_emergency_notification"
   namespace: "telephony"
@@ -22,13 +37,16 @@
   bug: "275225402"
 }
 
+# OWNER=avinashmp TARGET=24Q3
 flag {
   name: "enable_wps_check_api_flag"
+  is_exported: true
   namespace: "telephony"
   description: "Enable system api isWpsCallNumber. Its an utility api to check if the dialed number is for Wireless Priority Service call."
   bug: "304272356"
 }
 
+# OWNER=grantmenke TARGET=24Q3
 flag {
   name: "ensure_access_to_call_settings_is_restricted"
   namespace: "telephony"
@@ -36,6 +54,7 @@
   bug: "309655251"
 }
 
+# OWNER=sangyun TARGET=24Q2
 flag {
   name: "reorganize_roaming_notification"
   namespace: "telephony"
@@ -43,6 +62,7 @@
   bug: "310594087"
 }
 
+# OWNER=sangyun TARGET=24Q2
 flag {
   name: "dismiss_network_selection_notification_on_sim_disable"
   namespace: "telephony"
@@ -50,6 +70,7 @@
   bug: "310594186"
 }
 
+# OWNER=nagendranb TARGET=24Q3
 flag {
   name: "enable_telephony_analytics"
   namespace: "telephony"
@@ -57,20 +78,25 @@
   bug: "309896524"
 }
 
+# OWNER=rambowang TARGET=24Q3
 flag {
   name: "show_call_id_and_call_waiting_in_additional_settings_menu"
+  is_exported: true
   namespace: "telephony"
   description: "Expose carrier config KEY_ADDITIONAL_SETTINGS_CALLER_ID_VISIBILITY_BOOL and KEY_ADDITIONAL_SETTINGS_CALL_WAITING_VISIBILITY_BOOL."
   bug: "310264981"
 }
 
+# OWNER=rambowang TARGET=24Q3
 flag {
     name: "reset_mobile_network_settings"
+    is_exported: true
     namespace: "telephony"
     description: "Allows applications to launch Reset Mobile Network Settings page in Settings app."
     bug:"271921464"
 }
 
+# OWNER=rambowang TARGET=24Q3
 flag {
     name: "fix_crash_on_getting_config_when_phone_is_gone"
     namespace: "telephony"
@@ -81,6 +107,7 @@
     }
 }
 
+# OWNER=rambowang TARGET=24Q3
 flag {
     name: "add_anomaly_when_notify_config_changed_with_invalid_phone"
     namespace: "telephony"
@@ -91,6 +118,7 @@
     }
 }
 
+# OWNER=rambowang TARGET=24Q3
 flag {
     name: "hide_preinstalled_carrier_app_at_most_once"
     namespace: "telephony"
@@ -100,3 +128,92 @@
         purpose: PURPOSE_BUGFIX
     }
 }
+
+# OWNER=sangyun TARGET=24Q3
+flag {
+    name: "roaming_notification_for_single_data_network"
+    namespace: "telephony"
+    description: "Fix bug where roaming notification was not shown on a single data network."
+    bug:"249908996"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
+
+# OWNER=joonhunshin TARGET=24Q3
+flag {
+    name: "enforce_telephony_feature_mapping"
+    namespace: "telephony"
+    description: "This flag controls telephony feature flags mapping."
+    bug:"297989574"
+}
+
+# OWNER=joonhunshin TARGET=24Q3
+flag {
+    name: "enforce_telephony_feature_mapping_for_public_apis"
+    namespace: "telephony"
+    description: "This flag controls telephony feature flags mapping for public APIs and CTS."
+    bug:"297989574"
+}
+
+# OWNER=stevestatia TARGET=24Q3
+flag {
+    name: "prevent_system_server_and_phone_deadlock"
+    namespace: "telephony"
+    description: "This flag controls the order of the binder to prevent deadlock in system_server"
+    bug: "315973270"
+}
+
+# OWNER=joonhunshin TARGET=24Q3
+flag {
+    name: "prevent_invocation_repeat_of_ril_call_when_device_does_not_support_voice"
+    namespace: "telephony"
+    description: "This flag prevents repeat invocation of call related APIs in RIL when the device is not voice capable"
+    bug: "290833783"
+}
+
+# OWNER=jackyu TARGET=24Q3
+flag {
+    name: "minimal_telephony_cdm_check"
+    namespace: "telephony"
+    description: "This flag disables Calling/Data/Messaging features if their respective feature is missing"
+    bug: "310710841"
+}
+
+# OWNER=jackyu TARGET=24Q3
+flag {
+    name: "minimal_telephony_managers_conditional_on_features"
+    namespace: "telephony"
+    description: "This flag enables checking for telephony features before initializing corresponding managers"
+    bug: "310710841"
+}
+
+# OWNER=joonhunshin TARGET=24Q3
+flag {
+    name: "set_no_reply_timer_for_cfnry"
+    namespace: "telephony"
+    description: "This flag supports setting the no reply timer for CFNRy based on carrier config"
+    bug:"314732435"
+}
+
+# OWNER=joonhunshin TARGET=24Q3
+flag {
+    name: "change_method_of_obtaining_ims_registration_radio_tech"
+    namespace: "telephony"
+    description: "This flag changes the method of obtaining IMS registration radio technology"
+    bug:"330120237"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
+
+# OWNER=sasindran TARGET=24Q3
+flag {
+    name: "use_relaxed_id_match"
+    namespace: "telephony"
+    description: "This flag supports relaxed id match for radio state changes"
+    bug:"336916327"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
diff --git a/flags/network.aconfig b/flags/network.aconfig
index ab917f0..7c09ba3 100644
--- a/flags/network.aconfig
+++ b/flags/network.aconfig
@@ -1,5 +1,7 @@
 package: "com.android.internal.telephony.flags"
+container: "system"
 
+# OWNER=nharold TARGET=24Q1
 flag {
     name: "enable_carrier_config_n1_control_attempt2"
     namespace: "telephony"
@@ -11,20 +13,25 @@
     }
 }
 
+# OWNER=sarahchin TARGET=24Q1
 flag {
   name: "hide_roaming_icon"
+  is_exported: true
   namespace: "telephony"
   description: "Allow carriers to hide the roaming (R) icon when roaming."
   bug: "301467052"
 }
 
+# OWNER=cukie TARGET=24Q3
 flag {
   name: "enable_identifier_disclosure_transparency"
+  is_exported: true
   namespace: "telephony"
   description: "Guards APIs for enabling and disabling identifier disclosure transparency"
   bug: "276752426"
 }
 
+# OWNER=cukie TARGET=24Q3
 flag {
   name: "enable_identifier_disclosure_transparency_unsol_events"
   namespace: "telephony"
@@ -32,13 +39,16 @@
   bug: "276752426"
 }
 
+# OWNER=cukie TARGET=24Q3
 flag {
   name: "enable_modem_cipher_transparency"
+  is_exported: true
   namespace: "telephony"
   description: "Guards APIs for enabling and disabling modem cipher transparency."
   bug: "283336425"
 }
 
+# OWNER=cukie TARGET=24Q3
 flag {
   name: "enable_modem_cipher_transparency_unsol_events"
   namespace: "telephony"
@@ -46,13 +56,16 @@
   bug: "283336425"
 }
 
+# OWNER=songferngwang TARGET=24Q3
 flag {
   name: "hide_prefer_3g_item"
+  is_exported: true
   namespace: "telephony"
   description: "Used in the Preferred Network Types menu to determine if the 3G option is displayed."
   bug: "310639009"
 }
 
+# OWNER=sarahchin TARGET=24Q2
 flag {
   name: "support_nr_sa_rrc_idle"
   namespace: "telephony"
@@ -60,9 +73,23 @@
   bug: "298233308"
 }
 
+# OWNER=nharold TARGET=24Q3
 flag {
   name: "network_registration_info_reject_cause"
+  is_exported: true
   namespace: "telephony"
   description: "Elevate NRI#getRejectCause from System to Public"
   bug: "239730435"
 }
+
+# OWNER=sangyun TARGET=24Q3
+flag {
+    name: "backup_and_restore_for_enable_2g"
+    namespace: "telephony"
+    description: "Support backup & restore for allow 2g (setting) option."
+    bug:"314734614"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
+
diff --git a/flags/satellite.aconfig b/flags/satellite.aconfig
index 798ce40..27ab0b1 100644
--- a/flags/satellite.aconfig
+++ b/flags/satellite.aconfig
@@ -1,19 +1,25 @@
 package: "com.android.internal.telephony.flags"
+container: "system"
 
+# OWNER=amallampati TARGET=24Q3
 flag {
     name: "oem_enabled_satellite_flag"
+    is_exported: true
     namespace: "telephony"
     description: "This flag controls satellite communication supported by OEMs."
     bug:"291811962"
 }
 
+# OWNER=amallampati TARGET=24Q3
 flag {
     name: "carrier_enabled_satellite_flag"
+    is_exported: true
     namespace: "telephony"
     description: "This flag controls satellite communication supported by carriers."
     bug:"296437388"
 }
 
+# OWNER=nagendranb TARGET=24Q3
 flag {
     name: "satellite_internet"
     namespace: "telephony"
diff --git a/flags/subscription.aconfig b/flags/subscription.aconfig
index cebedd5..32e8f2d 100644
--- a/flags/subscription.aconfig
+++ b/flags/subscription.aconfig
@@ -1,26 +1,34 @@
 package: "com.android.internal.telephony.flags"
+container: "system"
 
+# OWNER=linggm TARGET=24Q3
 flag {
   name: "work_profile_api_split"
+  is_exported: true
   namespace: "telephony"
   description: "To support separation between personal and work from TelephonyManager and SubscriptionManager API perspective."
   bug: "296076674"
 }
 
+# OWNER=linggm TARGET=24Q3
 flag {
   name: "enforce_subscription_user_filter"
+  is_exported: true
   namespace: "telephony"
   description: "Enabled flag means subscriptions enforce filtering result base on calling user handle. It marks the telephony completion of user filtering."
   bug: "296076674"
 }
 
+# OWNER=rambowang TARGET=24Q3
 flag {
   name: "data_only_cellular_service"
+  is_exported: true
   namespace: "telephony"
   description: "Supports customized cellular service capabilities per subscription."
   bug: "296097429"
 }
 
+# OWNER=rambowang TARGET=24Q3
 flag {
   name: "data_only_service_allow_emergency_call_only"
   namespace: "telephony"
@@ -28,16 +36,32 @@
   bug: "296097429"
 }
 
+# OWNER=hhshin TARGET=24Q3
 flag {
   name: "support_psim_to_esim_conversion"
+  is_exported: true
   namespace: "telephony"
   description: "Support the psim to esim conversion."
   bug: "315073761"
 }
 
+# OWNER=bookatz TARGET=24Q3
 flag {
   name: "subscription_user_association_query"
+  is_exported: true
   namespace: "telephony"
   description: "Supports querying if a subscription is associated with the caller"
   bug: "325045841"
-}
\ No newline at end of file
+}
+
+# OWNER=nharold TARGET=24Q3
+flag {
+  name: "safer_get_phone_number"
+  namespace: "telephony"
+  description: "Safety and performance improvements for getPhoneNumber()"
+  bug: "317673478"
+
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
diff --git a/flags/telephony.aconfig b/flags/telephony.aconfig
deleted file mode 100644
index 9ef70b1..0000000
--- a/flags/telephony.aconfig
+++ /dev/null
@@ -1,43 +0,0 @@
-package: "com.android.internal.telephony.flags"
-
-flag {
-    name: "enforce_telephony_feature_mapping"
-    namespace: "telephony"
-    description: "This flag controls telephony feature flags mapping."
-    bug:"297989574"
-}
-
-flag {
-    name: "enforce_telephony_feature_mapping_for_public_apis"
-    namespace: "telephony"
-    description: "This flag controls telephony feature flags mapping for public APIs and CTS."
-    bug:"297989574"
-}
-
-flag {
-    name: "prevent_system_server_and_phone_deadlock"
-    namespace: "telephony"
-    description: "This flag controls the order of the binder to prevent deadlock in system_server"
-    bug: "315973270"
-}
-
-flag {
-    name: "prevent_invocation_repeat_of_ril_call_when_device_does_not_support_voice"
-    namespace: "telephony"
-    description: "This flag prevents repeat invocation of call related APIs in RIL when the device is not voice capable"
-    bug: "290833783"
-}
-
-flag {
-    name: "minimal_telephony_cdm_check"
-    namespace: "telephony"
-    description: "This flag disables Calling/Data/Messaging features if their respective feature is missing"
-    bug: "310710841"
-}
-
-flag {
-    name: "minimal_telephony_managers_conditional_on_features"
-    namespace: "telephony"
-    description: "This flag enables checking for telephony features before initializing corresponding managers"
-    bug: "310710841"
-}
diff --git a/flags/uicc.aconfig b/flags/uicc.aconfig
index c1b860f..2d7b643 100644
--- a/flags/uicc.aconfig
+++ b/flags/uicc.aconfig
@@ -1,32 +1,52 @@
 package: "com.android.internal.telephony.flags"
+container: "system"
 
+# OWNER=jayachandranc TARGET=24Q3
 flag {
     name: "esim_bootstrap_provisioning_flag"
     namespace: "telephony"
     description: "This flag controls eSIM Bootstrap provisioning feature support."
     bug:"298567545"
 }
+
+# OWNER=arunvoddu TARGET=24Q3
 flag {
     name: "imsi_key_retry_download_on_phone_unlock"
     namespace: "telephony"
     description: "This flag controls to download the IMSI encryption keys after user unlocks the phone."
     bug:"303780982"
 }
+
+# OWNER=arunvoddu TARGET=24Q3
 flag {
     name: "carrier_restriction_status"
+    is_exported: true
     namespace: "telephony"
     description: "This flag controls the visibility of the getCarrierRestrictionStatus in carrierRestrictionRules class."
     bug:"313553044"
 }
+
+# OWNER=arunvoddu TARGET=24Q3
 flag {
     name: "carrier_restriction_rules_enhancement"
     namespace: "telephony"
     description: "This flag controls the new enhancements to the existing carrier restrictions rules"
     bug:"317226653"
 }
+
+# OWNER=rafahs TARGET=24Q3
 flag {
     name: "esim_available_memory"
+    is_exported: true
     namespace: "telephony"
     description: "This flag controls eSIM available memory feature."
     bug:"318348580"
 }
+
+# OWNER=rambowang TARGET=24Q3
+flag {
+    name: "cleanup_open_logical_channel_record_on_dispose"
+    namespace: "telephony"
+    description: "This flag cleans up the OpenLogicalChannelRecord once SIM is removed"
+    bug:"335046531"
+}
diff --git a/proto/src/persist_atoms.proto b/proto/src/persist_atoms.proto
index d116159..c5be118 100644
--- a/proto/src/persist_atoms.proto
+++ b/proto/src/persist_atoms.proto
@@ -232,6 +232,12 @@
     /* Timestamp of last satellite_sos_message_recommender pull. */
     optional int64 satellite_sos_message_recommender_pull_timestamp_millis = 69;
 
+    /* Data Network Validation statistics and information. */
+    repeated DataNetworkValidation data_network_validation = 70;
+
+    /* Timestamp of last data_network_validation pull. */
+    optional int64 data_network_validation_pull_timestamp_millis = 71;
+
     /* Snapshot of carrier roaming satellite session. */
     repeated CarrierRoamingSatelliteSession carrier_roaming_satellite_session = 72;
 
@@ -399,6 +405,7 @@
     optional bool is_non_dds = 22;
     optional bool is_iwlan_cross_sim = 23;
     optional bool is_ntn = 24;
+    optional bool is_satellite_transport = 25;
 }
 
 message CellularServiceState {
@@ -488,6 +495,12 @@
         PRIORITIZE_BANDWIDTH = 2;
         CBS = 3;
         ENTERPRISE = 4;
+        SATELLITE_INTERNET_RESTRICTED = 5;
+        SATELLITE_MMS_RESTRICTED = 6;
+        SATELLITE_IMS_RESTRICTED = 7;
+        SATELLITE_XCAP_RESTRICTED = 8;
+        SATELLITE_EIMS_RESTRICTED = 9;
+        SATELLITE_SUPL_RESTRICTED =10;
     }
     optional int32 carrier_id = 1;
     optional NetworkCapability capability = 2;
@@ -742,6 +755,16 @@
     optional bool is_satellite_allowed_in_current_location = 8;
 }
 
+message DataNetworkValidation {
+    optional int32 network_type = 1;
+    optional int32 apn_type_bitmask = 2;
+    optional int32 signal_strength = 3;
+    optional int32 validation_result = 4;
+    optional int64 elapsed_time_in_millis = 5;
+    optional bool handover_attempted = 6;
+    optional int32 network_validation_count = 7;
+}
+
 message CarrierRoamingSatelliteSession {
     optional int32 carrier_id = 1;
     optional bool is_ntn_roaming_in_home_country = 2;
diff --git a/proto/src/telephony.proto b/proto/src/telephony.proto
index b87728b..92f62cc 100644
--- a/proto/src/telephony.proto
+++ b/proto/src/telephony.proto
@@ -259,8 +259,8 @@
   // Roaming type
   enum RoamingType {
 
-    // Unknown. The default value. Different from ROAMING_TYPE_UNKNOWN.
-    UNKNOWN = -1;
+    // Undefined. The default value. Different from ROAMING_TYPE_UNKNOWN.
+    ROAMING_TYPE_UNDEFINED = -1;
 
     // In home network
     ROAMING_TYPE_NOT_ROAMING = 0;
@@ -346,10 +346,10 @@
   optional TelephonyOperator data_operator = 2;
 
   // Current voice network roaming type
-  optional RoamingType voice_roaming_type = 3 [default = UNKNOWN];
+  optional RoamingType voice_roaming_type = 3 [default = ROAMING_TYPE_UNDEFINED];
 
   // Current data network roaming type
-  optional RoamingType data_roaming_type = 4 [default = UNKNOWN];
+  optional RoamingType data_roaming_type = 4 [default = ROAMING_TYPE_UNDEFINED];
 
   // Current voice radio technology
   optional RadioAccessTechnology voice_rat = 5 [default = UNKNOWN];
diff --git a/src/java/com/android/internal/telephony/CarrierInfoManager.java b/src/java/com/android/internal/telephony/CarrierInfoManager.java
index 863db93..8364c0a 100644
--- a/src/java/com/android/internal/telephony/CarrierInfoManager.java
+++ b/src/java/com/android/internal/telephony/CarrierInfoManager.java
@@ -33,6 +33,7 @@
 import android.util.Log;
 import android.util.Pair;
 
+import com.android.internal.telephony.flags.Flags;
 import com.android.internal.telephony.metrics.TelephonyMetrics;
 
 import java.security.PublicKey;
@@ -297,7 +298,12 @@
         final TelephonyManager telephonyManager = context.getSystemService(TelephonyManager.class)
                 .createForSubscriptionId(subId);
         int carrierId = telephonyManager.getSimCarrierId();
-        deleteCarrierInfoForImsiEncryption(context, subId, carrierId);
+        if (Flags.imsiKeyRetryDownloadOnPhoneUnlock()) {
+            String simOperator = telephonyManager.getSimOperator();
+            deleteCarrierInfoForImsiEncryption(context, subId, carrierId, simOperator);
+        } else {
+            deleteCarrierInfoForImsiEncryption(context, subId, carrierId);
+        }
         Intent resetIntent = new Intent(TelephonyIntents.ACTION_CARRIER_CERTIFICATE_DOWNLOAD);
         SubscriptionManager.putPhoneIdAndSubIdExtra(resetIntent, mPhoneId);
         context.sendBroadcastAsUser(resetIntent, UserHandle.ALL);
@@ -312,13 +318,29 @@
      */
     public static void deleteCarrierInfoForImsiEncryption(Context context, int subId,
             int carrierId) {
+        deleteCarrierInfoForImsiEncryption(context, subId, carrierId, null);
+    }
+
+    /**
+     * Deletes all the keys for a given Carrier from the device keystore.
+     * @param context Context
+     * @param subId SubscriptionId
+     * @param carrierId delete the key which matches the carrierId
+     * @param simOperator delete the key which matches the MCCMNC
+     *
+     */
+    public static void deleteCarrierInfoForImsiEncryption(Context context, int subId,
+            int carrierId, String simOperator) {
         Log.i(LOG_TAG, "deleting carrier key from db for subId=" + subId);
         String mcc = "";
         String mnc = "";
 
-        final TelephonyManager telephonyManager = context.getSystemService(TelephonyManager.class)
-                .createForSubscriptionId(subId);
-        String simOperator = telephonyManager.getSimOperator();
+        if (TextUtils.isEmpty(simOperator)) {
+            final TelephonyManager telephonyManager = context.getSystemService(
+                            TelephonyManager.class)
+                    .createForSubscriptionId(subId);
+            simOperator = telephonyManager.getSimOperator();
+        }
         if (!TextUtils.isEmpty(simOperator)) {
             mcc = simOperator.substring(0, 3);
             mnc = simOperator.substring(3);
diff --git a/src/java/com/android/internal/telephony/CarrierKeyDownloadManager.java b/src/java/com/android/internal/telephony/CarrierKeyDownloadManager.java
index 9143f21..10a3a32 100644
--- a/src/java/com/android/internal/telephony/CarrierKeyDownloadManager.java
+++ b/src/java/com/android/internal/telephony/CarrierKeyDownloadManager.java
@@ -18,6 +18,7 @@
 
 import static java.nio.charset.StandardCharsets.UTF_8;
 
+import android.annotation.NonNull;
 import android.app.AlarmManager;
 import android.app.DownloadManager;
 import android.app.KeyguardManager;
@@ -27,6 +28,8 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.database.Cursor;
+import android.net.ConnectivityManager;
+import android.net.Network;
 import android.net.Uri;
 import android.os.Handler;
 import android.os.Message;
@@ -34,19 +37,22 @@
 import android.os.UserManager;
 import android.telephony.CarrierConfigManager;
 import android.telephony.ImsiEncryptionInfo;
+import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 import android.util.Log;
 import android.util.Pair;
 
+
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.telephony.flags.FeatureFlags;
+import com.android.internal.telephony.flags.Flags;
 
 import org.json.JSONArray;
 import org.json.JSONException;
 import org.json.JSONObject;
 
+
 import java.io.BufferedReader;
 import java.io.ByteArrayInputStream;
 import java.io.FileInputStream;
@@ -57,6 +63,7 @@
 import java.security.cert.CertificateFactory;
 import java.security.cert.X509Certificate;
 import java.util.Date;
+import java.util.List;
 import java.util.Random;
 import java.util.zip.GZIPInputStream;
 import java.util.zip.ZipException;
@@ -99,6 +106,8 @@
 
     private static final int EVENT_ALARM_OR_CONFIG_CHANGE = 0;
     private static final int EVENT_DOWNLOAD_COMPLETE = 1;
+    private static final int EVENT_NETWORK_AVAILABLE = 2;
+    private static final int EVENT_SCREEN_UNLOCKED = 3;
 
 
     private static final int[] CARRIER_KEY_TYPES = {TelephonyManager.KEY_TYPE_EPDG,
@@ -111,47 +120,77 @@
     private boolean mAllowedOverMeteredNetwork = false;
     private boolean mDeleteOldKeyAfterDownload = false;
     private boolean mIsRequiredToHandleUnlock;
-    private TelephonyManager mTelephonyManager;
+    private final TelephonyManager mTelephonyManager;
     private UserManager mUserManager;
-
     @VisibleForTesting
-    public String mMccMncForDownload;
-    public int mCarrierId;
+    public String mMccMncForDownload = "";
+    public int mCarrierId = TelephonyManager.UNKNOWN_CARRIER_ID;
     @VisibleForTesting
     public long mDownloadId;
-    private final FeatureFlags mFeatureFlags;
+    private DefaultNetworkCallback mDefaultNetworkCallback;
+    private ConnectivityManager mConnectivityManager;
+    private KeyguardManager mKeyguardManager;
 
-    public CarrierKeyDownloadManager(Phone phone, FeatureFlags featureFlags) {
+    public CarrierKeyDownloadManager(Phone phone) {
         mPhone = phone;
-        mFeatureFlags = featureFlags;
         mContext = phone.getContext();
         IntentFilter filter = new IntentFilter();
         filter.addAction(INTENT_KEY_RENEWAL_ALARM_PREFIX);
         filter.addAction(TelephonyIntents.ACTION_CARRIER_CERTIFICATE_DOWNLOAD);
-        filter.addAction(Intent.ACTION_USER_PRESENT);
+        if (Flags.imsiKeyRetryDownloadOnPhoneUnlock()) {
+            filter.addAction(Intent.ACTION_USER_UNLOCKED);
+        }
         mContext.registerReceiver(mBroadcastReceiver, filter, null, phone);
         mDownloadManager = (DownloadManager) mContext.getSystemService(Context.DOWNLOAD_SERVICE);
         mTelephonyManager = mContext.getSystemService(TelephonyManager.class)
                 .createForSubscriptionId(mPhone.getSubId());
-        mUserManager = mContext.getSystemService(UserManager.class);
+        if (Flags.imsiKeyRetryDownloadOnPhoneUnlock()) {
+            mKeyguardManager = mContext.getSystemService(KeyguardManager.class);
+        } else {
+            mUserManager = mContext.getSystemService(UserManager.class);
+        }
         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) -> {
-                    boolean isUserUnlocked = mUserManager.isUserUnlocked();
-
-                    if (isUserUnlocked && slotIndex == mPhone.getPhoneId()) {
-                        Log.d(LOG_TAG, "Carrier Config changed: slotIndex=" + slotIndex);
-                        handleAlarmOrConfigChange();
+                    if (Flags.imsiKeyRetryDownloadOnPhoneUnlock()) {
+                        logd("CarrierConfig changed slotIndex = " + slotIndex + " subId = " + subId
+                                + " CarrierId = " + carrierId + " phoneId = "
+                                + mPhone.getPhoneId());
+                        // Below checks are necessary to optimise the logic.
+                        if ((slotIndex == mPhone.getPhoneId()) && (carrierId > 0
+                                || !TextUtils.isEmpty(
+                                mMccMncForDownload))) {
+                            mCarrierId = carrierId;
+                            updateSimOperator();
+                            // If device is screen locked do not proceed to handle
+                            // EVENT_ALARM_OR_CONFIG_CHANGE
+                            if (mKeyguardManager.isDeviceLocked()) {
+                                logd("Device is Locked");
+                                mIsRequiredToHandleUnlock = true;
+                            } else {
+                                logd("Carrier Config changed: slotIndex=" + slotIndex);
+                                sendEmptyMessage(EVENT_ALARM_OR_CONFIG_CHANGE);
+                            }
+                        }
                     } else {
-                        Log.d(LOG_TAG, "User is locked");
-                        mContext.registerReceiver(mUserUnlockedReceiver, new IntentFilter(
-                                Intent.ACTION_USER_UNLOCKED));
+                        boolean isUserUnlocked = mUserManager.isUserUnlocked();
+
+                        if (isUserUnlocked && slotIndex == mPhone.getPhoneId()) {
+                            Log.d(LOG_TAG, "Carrier Config changed: slotIndex=" + slotIndex);
+                            handleAlarmOrConfigChange();
+                        } else {
+                            Log.d(LOG_TAG, "User is locked");
+                            mContext.registerReceiver(mUserUnlockedReceiver, new IntentFilter(
+                                    Intent.ACTION_USER_UNLOCKED));
+                        }
                     }
                 });
+        mConnectivityManager = mContext.getSystemService(ConnectivityManager.class);
     }
 
+    // TODO remove this method upon imsiKeyRetryDownloadOnPhoneUnlock enabled.
     private final BroadcastReceiver mUserUnlockedReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
@@ -167,7 +206,7 @@
         public void onReceive(Context context, Intent intent) {
             String action = intent.getAction();
             if (action.equals(DownloadManager.ACTION_DOWNLOAD_COMPLETE)) {
-                Log.d(LOG_TAG, "Download Complete");
+                logd("Download Complete");
                 sendMessage(obtainMessage(EVENT_DOWNLOAD_COMPLETE,
                         intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, 0)));
             }
@@ -180,27 +219,36 @@
             String action = intent.getAction();
             int slotIndex = SubscriptionManager.getSlotIndex(mPhone.getSubId());
             int phoneId = mPhone.getPhoneId();
-            if (action.equals(INTENT_KEY_RENEWAL_ALARM_PREFIX)) {
-                int slotIndexExtra = intent.getIntExtra(SubscriptionManager.EXTRA_SLOT_INDEX, -1);
-                if (slotIndexExtra == slotIndex) {
-                    Log.d(LOG_TAG, "Handling key renewal alarm: " + action);
-                    sendEmptyMessage(EVENT_ALARM_OR_CONFIG_CHANGE);
+            switch (action) {
+                case INTENT_KEY_RENEWAL_ALARM_PREFIX -> {
+                    int slotIndexExtra = intent.getIntExtra(SubscriptionManager.EXTRA_SLOT_INDEX,
+                            -1);
+                    if (slotIndexExtra == slotIndex) {
+                        logd("Handling key renewal alarm: " + action);
+                        if (Flags.imsiKeyRetryDownloadOnPhoneUnlock()) {
+                            updateSimOperator();
+                        }
+                        sendEmptyMessage(EVENT_ALARM_OR_CONFIG_CHANGE);
+                    }
                 }
-            } else if (action.equals(TelephonyIntents.ACTION_CARRIER_CERTIFICATE_DOWNLOAD)) {
-                if (phoneId == intent.getIntExtra(PhoneConstants.PHONE_KEY,
-                        SubscriptionManager.INVALID_SIM_SLOT_INDEX)) {
-                    Log.d(LOG_TAG, "Handling reset intent: " + action);
-                    sendEmptyMessage(EVENT_ALARM_OR_CONFIG_CHANGE);
+                case TelephonyIntents.ACTION_CARRIER_CERTIFICATE_DOWNLOAD -> {
+                    if (phoneId == intent.getIntExtra(PhoneConstants.PHONE_KEY,
+                            SubscriptionManager.INVALID_SIM_SLOT_INDEX)) {
+                        logd("Handling reset intent: " + action);
+                        sendEmptyMessage(EVENT_ALARM_OR_CONFIG_CHANGE);
+                    }
                 }
-            }  else if (action.equals(Intent.ACTION_USER_PRESENT)) {
-                // The Carrier key download fails when SIM is inserted while device is locked
-                // hence adding a retry logic when device is unlocked.
-                Log.d(LOG_TAG,
-                        "device unlocked, isRequiredToHandleUnlock = " + mIsRequiredToHandleUnlock
-                                + ", slotIndex = " + slotIndex);
-                if (mIsRequiredToHandleUnlock) {
-                    mIsRequiredToHandleUnlock = false;
-                    sendEmptyMessage(EVENT_ALARM_OR_CONFIG_CHANGE);
+                case Intent.ACTION_USER_UNLOCKED -> {
+                    // The Carrier key download fails when SIM is inserted while device is locked
+                    // hence adding a retry logic when device is unlocked.
+                    logd("device fully unlocked, isRequiredToHandleUnlock = "
+                            + mIsRequiredToHandleUnlock
+                            + ", slotIndex = " + slotIndex + "  hasActiveDataNetwork = " + (
+                            mConnectivityManager.getActiveNetwork() != null));
+                    if (mIsRequiredToHandleUnlock) {
+                        mIsRequiredToHandleUnlock = false;
+                        sendEmptyMessage(EVENT_SCREEN_UNLOCKED);
+                    }
                 }
             }
         }
@@ -209,76 +257,124 @@
     @Override
     public void handleMessage (Message msg) {
         switch (msg.what) {
-            case EVENT_ALARM_OR_CONFIG_CHANGE:
-                handleAlarmOrConfigChange();
-                break;
-            case EVENT_DOWNLOAD_COMPLETE:
+            case EVENT_ALARM_OR_CONFIG_CHANGE, EVENT_NETWORK_AVAILABLE, EVENT_SCREEN_UNLOCKED ->
+                    handleAlarmOrConfigChange();
+            case EVENT_DOWNLOAD_COMPLETE -> {
                 long carrierKeyDownloadIdentifier = (long) msg.obj;
-                String currentMccMnc = getSimOperator();
-                int carrierId = getSimCarrierId();
+                String currentMccMnc = Flags.imsiKeyRetryDownloadOnPhoneUnlock()
+                        ? mTelephonyManager.getSimOperator(mPhone.getSubId()) : getSimOperator();
+                int carrierId = Flags.imsiKeyRetryDownloadOnPhoneUnlock()
+                        ? mTelephonyManager.getSimCarrierId() : getSimCarrierId();
                 if (isValidDownload(currentMccMnc, carrierKeyDownloadIdentifier, carrierId)) {
                     onDownloadComplete(carrierKeyDownloadIdentifier, currentMccMnc, carrierId);
-                    onPostDownloadProcessing(carrierKeyDownloadIdentifier);
+                    onPostDownloadProcessing();
                 }
-                break;
+            }
         }
     }
 
-    private void onPostDownloadProcessing(long carrierKeyDownloadIdentifier) {
+    private void onPostDownloadProcessing() {
         resetRenewalAlarm();
-        cleanupDownloadInfo();
-
+        if(Flags.imsiKeyRetryDownloadOnPhoneUnlock()) {
+            mDownloadId = -1;
+        } else {
+            cleanupDownloadInfo();
+        }
         // unregister from DOWNLOAD_COMPLETE
         mContext.unregisterReceiver(mDownloadReceiver);
     }
 
     private void handleAlarmOrConfigChange() {
-        if (carrierUsesKeys()) {
-            if (areCarrierKeysAbsentOrExpiring()) {
-                boolean downloadStartedSuccessfully = downloadKey();
-                // if the download was attempted, but not started successfully, and if carriers uses
-                // keys, we'll still want to renew the alarms, and try downloading the key a day
-                // later.
-                if (!downloadStartedSuccessfully) {
-                    // If download fails due to the device lock, we will reattempt once the device
-                    // is unlocked.
-                    if (mFeatureFlags.imsiKeyRetryDownloadOnPhoneUnlock()) {
-                        KeyguardManager keyguardManager = mContext.getSystemService(
-                                KeyguardManager.class);
-                        if (keyguardManager.isKeyguardSecure()) {
-                            Log.e(LOG_TAG, "Key download failed in device lock state");
-                            mIsRequiredToHandleUnlock = true;
+        if (Flags.imsiKeyRetryDownloadOnPhoneUnlock()) {
+            if (carrierUsesKeys()) {
+                if (areCarrierKeysAbsentOrExpiring()) {
+                    boolean hasActiveDataNetwork =
+                            (mConnectivityManager.getActiveNetwork() != null);
+                    boolean downloadStartedSuccessfully = hasActiveDataNetwork && downloadKey();
+                    // if the download was attempted, but not started successfully, and if
+                    // carriers uses keys, we'll still want to renew the alarms, and try
+                    // downloading the key a day later.
+                    int slotIndex = SubscriptionManager.getSlotIndex(mPhone.getSubId());
+                    if (downloadStartedSuccessfully) {
+                        unregisterDefaultNetworkCb(slotIndex);
+                    } else {
+                        // If download fails due to the device lock, we will reattempt once the
+                        // device is unlocked.
+                        mIsRequiredToHandleUnlock = mKeyguardManager.isDeviceLocked();
+                        loge("hasActiveDataConnection = " + hasActiveDataNetwork
+                                + "    isDeviceLocked = " + mIsRequiredToHandleUnlock);
+                        if (!hasActiveDataNetwork) {
+                            registerDefaultNetworkCb(slotIndex);
                         }
+                        resetRenewalAlarm();
                     }
-                    resetRenewalAlarm();
                 }
+                logd("handleAlarmOrConfigChange :: areCarrierKeysAbsentOrExpiring returned false");
             } else {
-                return;
+                cleanupRenewalAlarms();
+                if (!isOtherSlotHasCarrier()) {
+                    // delete any existing alarms.
+                    mPhone.deleteCarrierInfoForImsiEncryption(getSimCarrierId(), getSimOperator());
+                }
+                cleanupDownloadInfo();
             }
         } else {
-            // delete any existing alarms.
-            cleanupRenewalAlarms();
-            mPhone.deleteCarrierInfoForImsiEncryption(getSimCarrierId());
-
+            if (carrierUsesKeys()) {
+                if (areCarrierKeysAbsentOrExpiring()) {
+                    boolean downloadStartedSuccessfully = downloadKey();
+                    // if the download was attempted, but not started successfully, and if
+                    // carriers uses keys, we'll still want to renew the alarms, and try
+                    // downloading the key a day later.
+                    if (!downloadStartedSuccessfully) {
+                        resetRenewalAlarm();
+                    }
+                }
+            } else {
+                // delete any existing alarms.
+                cleanupRenewalAlarms();
+                mPhone.deleteCarrierInfoForImsiEncryption(getSimCarrierId());
+            }
         }
     }
 
+    private boolean isOtherSlotHasCarrier() {
+        SubscriptionManager subscriptionManager = mPhone.getContext().getSystemService(
+                SubscriptionManager.class);
+        List<SubscriptionInfo> subscriptionInfoList =
+                subscriptionManager.getActiveSubscriptionInfoList();
+        loge("handleAlarmOrConfigChange ActiveSubscriptionInfoList = " + (
+                (subscriptionInfoList != null) ? subscriptionInfoList.size() : null));
+        for (SubscriptionInfo subInfo : subscriptionInfoList) {
+            if (mPhone.getSubId() != subInfo.getSubscriptionId()
+                    && subInfo.getCarrierId() == mPhone.getCarrierId()) {
+                // We do not proceed to remove the Key from the DB as another slot contains
+                // same operator sim which is in active state.
+                loge("handleAlarmOrConfigChange same operator sim in another slot");
+                return true;
+            }
+        }
+        return false;
+    }
+
     private void cleanupDownloadInfo() {
-        Log.d(LOG_TAG, "Cleaning up download info");
+        logd("Cleaning up download info");
         mDownloadId = -1;
-        mMccMncForDownload = null;
+        if (Flags.imsiKeyRetryDownloadOnPhoneUnlock()) {
+            mMccMncForDownload = "";
+        } else {
+            mMccMncForDownload = null;
+        }
         mCarrierId = TelephonyManager.UNKNOWN_CARRIER_ID;
     }
 
     private void cleanupRenewalAlarms() {
-        Log.d(LOG_TAG, "Cleaning up existing renewal alarms");
+        logd("Cleaning up existing renewal alarms");
         int slotIndex = SubscriptionManager.getSlotIndex(mPhone.getSubId());
         Intent intent = new Intent(INTENT_KEY_RENEWAL_ALARM_PREFIX);
         intent.putExtra(SubscriptionManager.EXTRA_SLOT_INDEX, slotIndex);
         PendingIntent carrierKeyDownloadIntent = PendingIntent.getBroadcast(mContext, 0, intent,
                 PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
-        AlarmManager alarmManager =
-                (AlarmManager) mContext.getSystemService(mContext.ALARM_SERVICE);
+        AlarmManager alarmManager =mContext.getSystemService(AlarmManager.class);
         alarmManager.cancel(carrierKeyDownloadIntent);
     }
 
@@ -331,7 +427,7 @@
         cleanupRenewalAlarms();
         int slotIndex = SubscriptionManager.getSlotIndex(mPhone.getSubId());
         long minExpirationDate = getExpirationDate();
-        Log.d(LOG_TAG, "minExpirationDate: " + new Date(minExpirationDate));
+        logd("minExpirationDate: " + new Date(minExpirationDate));
         final AlarmManager alarmManager = (AlarmManager) mContext.getSystemService(
                 Context.ALARM_SERVICE);
         Intent intent = new Intent(INTENT_KEY_RENEWAL_ALARM_PREFIX);
@@ -339,16 +435,35 @@
         PendingIntent carrierKeyDownloadIntent = PendingIntent.getBroadcast(mContext, 0, intent,
                 PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
         alarmManager.set(AlarmManager.RTC_WAKEUP, minExpirationDate, carrierKeyDownloadIntent);
-        Log.d(LOG_TAG, "setRenewalAlarm: action=" + intent.getAction() + " time="
+        logd("setRenewalAlarm: action=" + intent.getAction() + " time="
                 + new Date(minExpirationDate));
     }
 
     /**
+     * Read the store the sim operetor value and update the value in case of change in the sim
+     * operetor.
+     */
+    public void updateSimOperator() {
+        if (Flags.imsiKeyRetryDownloadOnPhoneUnlock()) {
+            String simOperator = mPhone.getOperatorNumeric();
+            if (!TextUtils.isEmpty(simOperator) && !simOperator.equals(mMccMncForDownload)) {
+                mMccMncForDownload = simOperator;
+                logd("updateSimOperator, Initialized mMccMncForDownload = " + mMccMncForDownload);
+            }
+        }
+    }
+
+    /**
      * Returns the sim operator.
      **/
     @VisibleForTesting
     public String getSimOperator() {
-        return mTelephonyManager.getSimOperator(mPhone.getSubId());
+        if (Flags.imsiKeyRetryDownloadOnPhoneUnlock()) {
+            updateSimOperator();
+            return mMccMncForDownload;
+        } else {
+            return mTelephonyManager.getSimOperator(mPhone.getSubId());
+        }
     }
 
     /**
@@ -356,7 +471,11 @@
      **/
     @VisibleForTesting
     public int getSimCarrierId() {
-        return mTelephonyManager.getSimCarrierId();
+        if (Flags.imsiKeyRetryDownloadOnPhoneUnlock()) {
+            return (mCarrierId > 0) ? mCarrierId : mPhone.getCarrierId();
+        } else {
+            return mTelephonyManager.getSimCarrierId();
+        }
     }
 
     /**
@@ -367,7 +486,7 @@
     @VisibleForTesting
     public boolean isValidDownload(String currentMccMnc, long currentDownloadId, int carrierId) {
         if (currentDownloadId != mDownloadId) {
-            Log.e(LOG_TAG, "download ID=" + currentDownloadId
+            loge( "download ID=" + currentDownloadId
                     + " for completed download does not match stored id=" + mDownloadId);
             return false;
         }
@@ -375,12 +494,12 @@
         if (TextUtils.isEmpty(currentMccMnc) || TextUtils.isEmpty(mMccMncForDownload)
                 || !TextUtils.equals(currentMccMnc, mMccMncForDownload)
                 || mCarrierId != carrierId) {
-            Log.e(LOG_TAG, "currentMccMnc=" + currentMccMnc + " storedMccMnc =" + mMccMncForDownload
+            loge( "currentMccMnc=" + currentMccMnc + " storedMccMnc =" + mMccMncForDownload
                     + "currentCarrierId = " + carrierId + "  storedCarrierId = " + mCarrierId);
             return false;
         }
 
-        Log.d(LOG_TAG, "Matched MccMnc =  " + currentMccMnc + ", carrierId = " + carrierId
+        logd("Matched MccMnc =  " + currentMccMnc + ", carrierId = " + carrierId
                 + ", downloadId: " + currentDownloadId);
         return true;
     }
@@ -390,7 +509,7 @@
      **/
     private void onDownloadComplete(long carrierKeyDownloadIdentifier, String mccMnc,
             int carrierId) {
-        Log.d(LOG_TAG, "onDownloadComplete: " + carrierKeyDownloadIdentifier);
+        logd("onDownloadComplete: " + carrierKeyDownloadIdentifier);
         String jsonStr;
         DownloadManager.Query query = new DownloadManager.Query();
         query.setFilterById(carrierKeyDownloadIdentifier);
@@ -405,22 +524,21 @@
                 try {
                     jsonStr = convertToString(mDownloadManager, carrierKeyDownloadIdentifier);
                     if (TextUtils.isEmpty(jsonStr)) {
-                        Log.d(LOG_TAG, "fallback to no gzip");
+                        logd("fallback to no gzip");
                         jsonStr = convertToStringNoGZip(mDownloadManager,
                                 carrierKeyDownloadIdentifier);
                     }
                     parseJsonAndPersistKey(jsonStr, mccMnc, carrierId);
                 } catch (Exception e) {
-                    Log.e(LOG_TAG, "Error in download:" + carrierKeyDownloadIdentifier
+                    loge( "Error in download:" + carrierKeyDownloadIdentifier
                             + ". " + e);
                 } finally {
                     mDownloadManager.remove(carrierKeyDownloadIdentifier);
                 }
             }
-            Log.d(LOG_TAG, "Completed downloading keys");
+            logd("Completed downloading keys");
         }
         cursor.close();
-        return;
     }
 
     /**
@@ -442,7 +560,7 @@
                     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.");
+            loge( "CarrierConfigLoader is not available.");
         }
         if (b == null || b.isEmpty()) {
             return false;
@@ -452,10 +570,10 @@
         mURL = b.getString(CarrierConfigManager.IMSI_KEY_DOWNLOAD_URL_STRING);
         mAllowedOverMeteredNetwork = b.getBoolean(
                 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
-                            + " mURL=" + mURL);
+            logd("Carrier not enabled or invalid values. mKeyAvailability=" + mKeyAvailability
+                    + " mURL=" + mURL);
             return false;
         }
         for (int key_type : CARRIER_KEY_TYPES) {
@@ -469,7 +587,7 @@
     private static String convertToStringNoGZip(DownloadManager downloadManager, long downloadId) {
         StringBuilder sb = new StringBuilder();
         try (InputStream source = new FileInputStream(
-                    downloadManager.openDownloadedFile(downloadId).getFileDescriptor())) {
+                downloadManager.openDownloadedFile(downloadId).getFileDescriptor())) {
             // If the carrier does not have the data gzipped, fallback to assuming it is not zipped.
             // parseJsonAndPersistKey may still fail if the data is malformed, so we won't be
             // persisting random bogus strings thinking it's the cert
@@ -488,8 +606,8 @@
 
     private static String convertToString(DownloadManager downloadManager, long downloadId) {
         try (InputStream source = new FileInputStream(
-                    downloadManager.openDownloadedFile(downloadId).getFileDescriptor());
-                    InputStream gzipIs = new GZIPInputStream(source)) {
+                downloadManager.openDownloadedFile(downloadId).getFileDescriptor());
+             InputStream gzipIs = new GZIPInputStream(source)) {
             BufferedReader reader = new BufferedReader(new InputStreamReader(gzipIs, UTF_8));
             StringBuilder sb = new StringBuilder();
 
@@ -523,7 +641,7 @@
     public void parseJsonAndPersistKey(String jsonStr, String mccMnc, int carrierId) {
         if (TextUtils.isEmpty(jsonStr) || TextUtils.isEmpty(mccMnc)
                 || carrierId == TelephonyManager.UNKNOWN_CARRIER_ID) {
-            Log.e(LOG_TAG, "jsonStr or mcc, mnc: is empty or carrierId is UNKNOWN_CARRIER_ID");
+            loge( "jsonStr or mcc, mnc: is empty or carrierId is UNKNOWN_CARRIER_ID");
             return;
         }
         try {
@@ -548,22 +666,28 @@
                     if (typeString.equals(JSON_TYPE_VALUE_EPDG)) {
                         type = TelephonyManager.KEY_TYPE_EPDG;
                     } else if (!typeString.equals(JSON_TYPE_VALUE_WLAN)) {
-                        Log.e(LOG_TAG, "Invalid key-type specified: " + typeString);
+                        loge( "Invalid key-type specified: " + typeString);
                     }
                 }
                 String identifier = key.getString(JSON_IDENTIFIER);
                 Pair<PublicKey, Long> keyInfo =
                         getKeyInformation(cleanCertString(cert).getBytes());
                 if (mDeleteOldKeyAfterDownload) {
-                    mPhone.deleteCarrierInfoForImsiEncryption(TelephonyManager.UNKNOWN_CARRIER_ID);
+                    if (Flags.imsiKeyRetryDownloadOnPhoneUnlock()) {
+                        mPhone.deleteCarrierInfoForImsiEncryption(
+                                TelephonyManager.UNKNOWN_CARRIER_ID, null);
+                    } else {
+                        mPhone.deleteCarrierInfoForImsiEncryption(
+                                TelephonyManager.UNKNOWN_CARRIER_ID);
+                    }
                     mDeleteOldKeyAfterDownload = false;
                 }
                 savePublicKey(keyInfo.first, type, identifier, keyInfo.second, mcc, mnc, carrierId);
             }
         } catch (final JSONException e) {
-            Log.e(LOG_TAG, "Json parsing error: " + e.getMessage());
+            loge( "Json parsing error: " + e.getMessage());
         } catch (final Exception e) {
-            Log.e(LOG_TAG, "Exception getting certificate: " + e);
+            loge( "Exception getting certificate: " + e);
         }
     }
 
@@ -584,7 +708,7 @@
     public static boolean isKeyEnabled(int keyType, int keyAvailability) {
         // since keytype has values of 1, 2.... we need to subtract 1 from the keytype.
         int returnValue = (keyAvailability >> (keyType - 1)) & 1;
-        return (returnValue == 1) ? true : false;
+        return returnValue == 1;
     }
 
     /**
@@ -603,31 +727,42 @@
             ImsiEncryptionInfo imsiEncryptionInfo =
                     mPhone.getCarrierInfoForImsiEncryption(key_type, false);
             if (imsiEncryptionInfo == null) {
-                Log.d(LOG_TAG, "Key not found for: " + key_type);
+                logd("Key not found for: " + key_type);
                 return true;
             } else if (imsiEncryptionInfo.getCarrierId() == TelephonyManager.UNKNOWN_CARRIER_ID) {
-                Log.d(LOG_TAG, "carrier key is unknown carrier, so prefer to reDownload");
+                logd("carrier key is unknown carrier, so prefer to reDownload");
                 mDeleteOldKeyAfterDownload = true;
                 return true;
             }
             Date imsiDate = imsiEncryptionInfo.getExpirationTime();
             long timeToExpire = imsiDate.getTime() - System.currentTimeMillis();
-            return (timeToExpire < START_RENEWAL_WINDOW_DAYS * DAY_IN_MILLIS) ? true : false;
+            return timeToExpire < START_RENEWAL_WINDOW_DAYS * DAY_IN_MILLIS;
         }
         return false;
     }
 
     private boolean downloadKey() {
-        Log.d(LOG_TAG, "starting download from: " + mURL);
-        String mccMnc = getSimOperator();
-        int carrierId = getSimCarrierId();
-        if (!TextUtils.isEmpty(mccMnc) || carrierId != TelephonyManager.UNKNOWN_CARRIER_ID) {
-            Log.d(LOG_TAG, "downloading key for mccmnc : " + mccMnc + ", carrierId : "
-                    + carrierId);
+        logd("starting download from: " + mURL);
+        String mccMnc = null;
+        int carrierId = -1;
+        if (Flags.imsiKeyRetryDownloadOnPhoneUnlock()) {
+            if (TextUtils.isEmpty(mMccMncForDownload)
+                    || mCarrierId == TelephonyManager.UNKNOWN_CARRIER_ID) {
+                loge("mccmnc or carrierId is UnKnown");
+                return false;
+            }
         } else {
-            Log.e(LOG_TAG, "mccmnc or carrierId is UnKnown");
-            return false;
+            mccMnc = getSimOperator();
+            carrierId = getSimCarrierId();
+            if (!TextUtils.isEmpty(mccMnc) || carrierId != TelephonyManager.UNKNOWN_CARRIER_ID) {
+                Log.d(LOG_TAG, "downloading key for mccmnc : " + mccMnc + ", carrierId : "
+                        + carrierId);
+            } else {
+                Log.e(LOG_TAG, "mccmnc or carrierId is UnKnown");
+                return false;
+            }
         }
+
         try {
             // register the broadcast receiver to listen for download complete
             IntentFilter filter = new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE);
@@ -641,15 +776,16 @@
             request.setAllowedOverMetered(mAllowedOverMeteredNetwork);
             request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_HIDDEN);
             request.addRequestHeader("Accept-Encoding", "gzip");
-            Long carrierKeyDownloadRequestId = mDownloadManager.enqueue(request);
-
-            Log.d(LOG_TAG, "saving values mccmnc: " + mccMnc + ", downloadId: "
-                    + carrierKeyDownloadRequestId + ", carrierId: " + carrierId);
-            mMccMncForDownload = mccMnc;
-            mCarrierId = carrierId;
+            long carrierKeyDownloadRequestId = mDownloadManager.enqueue(request);
+            if (!Flags.imsiKeyRetryDownloadOnPhoneUnlock()) {
+                mMccMncForDownload = mccMnc;
+                mCarrierId = carrierId;
+            }
             mDownloadId = carrierKeyDownloadRequestId;
+            logd("saving values mccmnc: " + mMccMncForDownload + ", downloadId: "
+                    + carrierKeyDownloadRequestId + ", carrierId: " + mCarrierId);
         } catch (Exception e) {
-            Log.e(LOG_TAG, "exception trying to download key from url: " + mURL + ", Exception = "
+            loge( "exception trying to download key from url: " + mURL + ",  Exception = "
                     + e.getMessage());
             return false;
         }
@@ -667,7 +803,7 @@
         CertificateFactory cf = CertificateFactory.getInstance("X.509");
         X509Certificate cert = (X509Certificate) cf.generateCertificate(inStream);
         Pair<PublicKey, Long> keyInformation =
-                new Pair(cert.getPublicKey(), cert.getNotAfter().getTime());
+                new Pair<>(cert.getPublicKey(), cert.getNotAfter().getTime());
         return keyInformation;
     }
 
@@ -699,4 +835,59 @@
                 cert.indexOf(CERT_BEGIN_STRING),
                 cert.indexOf(CERT_END_STRING) + CERT_END_STRING.length());
     }
+
+    /**
+     * Registering the callback to listen on data connection availability.
+     *
+     * @param slotId Sim slotIndex that tries to download the key.
+     */
+    private void registerDefaultNetworkCb(int slotId) {
+        logd("RegisterDefaultNetworkCb for slotId = " + slotId);
+        if (mDefaultNetworkCallback == null
+                && slotId != SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
+            mDefaultNetworkCallback = new DefaultNetworkCallback(slotId);
+            mConnectivityManager.registerDefaultNetworkCallback(mDefaultNetworkCallback, this);
+        }
+    }
+
+    /**
+     * Unregister the data connection monitor listener.
+     */
+    private void unregisterDefaultNetworkCb(int slotId) {
+        logd("unregisterDefaultNetworkCb for slotId = " + slotId);
+        if (mDefaultNetworkCallback != null) {
+            mConnectivityManager.unregisterNetworkCallback(mDefaultNetworkCallback);
+            mDefaultNetworkCallback = null;
+        }
+    }
+
+    final class DefaultNetworkCallback extends ConnectivityManager.NetworkCallback {
+        final int mSlotIndex;
+
+        public DefaultNetworkCallback(int slotId) {
+            mSlotIndex = slotId;
+        }
+
+        /** Called when the framework connects and has declared a new network ready for use. */
+        @Override
+        public void onAvailable(@NonNull Network network) {
+            logd("Data network connected, slotId = " + mSlotIndex);
+            if (mConnectivityManager.getActiveNetwork() != null && SubscriptionManager.getSlotIndex(
+                    mPhone.getSubId()) == mSlotIndex) {
+                mIsRequiredToHandleUnlock = false;
+                unregisterDefaultNetworkCb(mSlotIndex);
+                sendEmptyMessage(EVENT_NETWORK_AVAILABLE);
+            }
+        }
+    }
+
+    private void loge(String logStr) {
+        String TAG = LOG_TAG + " [" + mPhone.getPhoneId() + "]";
+        Log.e(TAG, logStr);
+    }
+
+    private void logd(String logStr) {
+        String TAG = LOG_TAG + " [" + mPhone.getPhoneId() + "]";
+        Log.d(TAG, logStr);
+    }
 }
diff --git a/src/java/com/android/internal/telephony/GsmCdmaPhone.java b/src/java/com/android/internal/telephony/GsmCdmaPhone.java
index db0fb2d..c4d9a17 100644
--- a/src/java/com/android/internal/telephony/GsmCdmaPhone.java
+++ b/src/java/com/android/internal/telephony/GsmCdmaPhone.java
@@ -411,7 +411,7 @@
 
         mLinkBandwidthEstimator = mTelephonyComponentFactory
                 .inject(LinkBandwidthEstimator.class.getName())
-                .makeLinkBandwidthEstimator(this);
+                .makeLinkBandwidthEstimator(this, getLooper());
 
         mCallWaitingController = new CallWaitingController(this);
 
@@ -539,7 +539,7 @@
         mContext.registerReceiver(mBroadcastReceiver, filter,
                 android.Manifest.permission.MODIFY_PHONE_STATE, null, Context.RECEIVER_EXPORTED);
 
-        mCDM = new CarrierKeyDownloadManager(this, mFeatureFlags);
+        mCDM = new CarrierKeyDownloadManager(this);
         mCIM = new CarrierInfoManager();
 
         mCi.registerForImeiMappingChanged(this, EVENT_IMEI_MAPPING_CHANGED, null);
@@ -2124,8 +2124,13 @@
 
     @Override
     public void deleteCarrierInfoForImsiEncryption(int carrierId) {
+        CarrierInfoManager.deleteCarrierInfoForImsiEncryption(mContext, getSubId(), carrierId);
+    }
+
+    @Override
+    public void deleteCarrierInfoForImsiEncryption(int carrierId, String simOperator) {
         CarrierInfoManager.deleteCarrierInfoForImsiEncryption(mContext, getSubId(),
-                carrierId);
+                carrierId, simOperator);
     }
 
     @Override
@@ -2298,6 +2303,11 @@
     }
 
     @Override
+    protected void onSetNetworkSelectionModeCompleted() {
+        mSST.pollState();
+    }
+
+    @Override
     public String getCdmaPrlVersion() {
         return mSST.getPrlVersion();
     }
@@ -3669,6 +3679,7 @@
             case EVENT_SUBSCRIPTIONS_CHANGED:
                 logd("EVENT_SUBSCRIPTIONS_CHANGED");
                 updateUsageSetting();
+                updateNullCipherNotifier();
                 break;
             case EVENT_SET_NULL_CIPHER_AND_INTEGRITY_DONE:
                 logd("EVENT_SET_NULL_CIPHER_AND_INTEGRITY_DONE");
@@ -3769,7 +3780,8 @@
                         && mNullCipherNotifier != null) {
                     ar = (AsyncResult) msg.obj;
                     SecurityAlgorithmUpdate update = (SecurityAlgorithmUpdate) ar.result;
-                    mNullCipherNotifier.onSecurityAlgorithmUpdate(mContext, getSubId(), update);
+                    mNullCipherNotifier.onSecurityAlgorithmUpdate(mContext, getPhoneId(),
+                            getSubId(), update);
                 }
                 break;
 
@@ -5444,6 +5456,25 @@
                 obtainMessage(EVENT_SET_SECURITY_ALGORITHMS_UPDATED_ENABLED_DONE));
     }
 
+    /**
+     * Update the phoneId -> subId mapping of the null cipher notifier.
+     */
+    @VisibleForTesting
+    public void updateNullCipherNotifier() {
+        if (!mFeatureFlags.enableModemCipherTransparencyUnsolEvents()) {
+            return;
+        }
+
+        SubscriptionInfoInternal subInfo = mSubscriptionManagerService
+                .getSubscriptionInfoInternal(getSubId());
+        boolean active = false;
+        if (subInfo != null) {
+            active = subInfo.isActive();
+        }
+        mNullCipherNotifier.setSubscriptionMapping(mContext, getPhoneId(),
+                active ? subInfo.getSubscriptionId() : -1);
+    }
+
     @Override
     public boolean isNullCipherAndIntegritySupported() {
         return mIsNullCipherAndIntegritySupported;
diff --git a/src/java/com/android/internal/telephony/MultiSimSettingController.java b/src/java/com/android/internal/telephony/MultiSimSettingController.java
index 61cebbf..83d58af 100644
--- a/src/java/com/android/internal/telephony/MultiSimSettingController.java
+++ b/src/java/com/android/internal/telephony/MultiSimSettingController.java
@@ -18,6 +18,7 @@
 
 import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
 import static android.telephony.SubscriptionManager.PROFILE_CLASS_PROVISIONING;
+import static android.telephony.SubscriptionManager.TRANSFER_STATUS_CONVERTED;
 import static android.telephony.TelephonyManager.ACTION_PRIMARY_SUBSCRIPTION_LIST_CHANGED;
 import static android.telephony.TelephonyManager.EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE;
 import static android.telephony.TelephonyManager.EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_ALL;
@@ -159,6 +160,11 @@
     /** The number of active modem count. */
     private int mActiveModemCount;
 
+    private boolean mNeedSetDefaultVoice;
+    private boolean mNeedSetDefaultSms;
+    private boolean mNeedSetDefaultData;
+    private int mConvertedPsimSubId;
+
     private static final String SETTING_USER_PREF_DATA_SUB = "user_preferred_data_sub";
 
     private static class DataSettingsControllerCallback extends DataSettingsManagerCallback {
@@ -244,6 +250,8 @@
         ccm.registerCarrierConfigChangeListener(this::post,
                 (slotIndex, subId, carrierId, specificCarrierId) ->
                         onCarrierConfigChanged(slotIndex, subId));
+
+        mConvertedPsimSubId = getConvertedPsimSubscriptionId();
     }
 
     private boolean hasCalling() {
@@ -404,6 +412,7 @@
         if (DBG) log("onAllSubscriptionsLoaded: mSubInfoInitialized=" + mSubInfoInitialized);
         if (!mSubInfoInitialized) {
             mSubInfoInitialized = true;
+            mConvertedPsimSubId = getConvertedPsimSubscriptionId();
             for (Phone phone : PhoneFactory.getPhones()) {
                 phone.mCi.registerForRadioStateChanged(this, EVENT_RADIO_STATE_CHANGED, null);
             }
@@ -445,12 +454,13 @@
             return;
         }
 
-        CarrierConfigManager cm = mContext.getSystemService(CarrierConfigManager.class);
-        if (cm != null) {
-            if (CarrierConfigManager.isConfigForIdentifiedCarrier(cm.getConfigForSubId(subId))) {
-                mCarrierConfigLoadedSubIds[phoneId] = subId;
-                reEvaluateAll();
-            }
+        CarrierConfigManager cm;
+        if (!SubscriptionManager.isValidSubscriptionId(subId) // record SIM absent.
+                || ((cm = mContext.getSystemService(CarrierConfigManager.class)) != null
+                && CarrierConfigManager.isConfigForIdentifiedCarrier(
+                        cm.getConfigForSubId(subId)))) {
+            mCarrierConfigLoadedSubIds[phoneId] = subId;
+            reEvaluateAll();
         }
     }
 
@@ -608,7 +618,6 @@
      */
     protected void updateDefaults() {
         if (DBG) log("updateDefaults");
-
         if (!isReadyToReevaluate()) return;
 
         List<SubscriptionInfo> activeSubInfos = mSubscriptionManagerService
@@ -687,7 +696,12 @@
         // preference auto selection logic or display notification for end used to
         // select voice/data/SMS preferences.
         if (!autoFallbackEnabled) {
-            sendSubChangeNotificationIfNeeded(change, dataSelected, voiceSelected, smsSelected);
+            // Hide the dialog for preferred SIM/data pick if the primary subscription change is
+            // due to the pSIM conversion.
+            if (!setDefaultForPsimConversionChanged(change, dataSelected, voiceSelected,
+                    smsSelected)) {
+                sendSubChangeNotificationIfNeeded(change, dataSelected, voiceSelected, smsSelected);
+            }
         } else {
             updateUserPreferences(mPrimarySubList, dataSelected, voiceSelected, smsSelected);
         }
@@ -803,6 +817,119 @@
         }
     }
 
+    /**
+     * Check that the primary subscription has changed due to the pSIM conversion.
+     * @param change Whether to update the mPrimarySubList.
+     * @param dataSelected Whether the default data subscription is updated
+     * @param voiceSelected Whether the default voice subscription is updated
+     * @param smsSelected Whether the default sms subscription is updated
+     * @return {@code true} if the primary subscription has changed due to the pSIM conversion,
+     * {@code false} otherwise.
+     */
+    private boolean setDefaultForPsimConversionChanged(int change, boolean dataSelected,
+            boolean voiceSelected, boolean smsSelected) {
+        if (!mFeatureFlags.supportPsimToEsimConversion()) {
+            log("pSIM to eSIM conversion is not supported");
+            return false;
+        }
+        if (mSubscriptionManagerService.isEsimBootStrapProvisioningActivated()) {
+            log("esim bootstrap activation in progress, skip notification");
+            return false;
+        }
+
+        @TelephonyManager.DefaultSubscriptionSelectType
+        int simSelectDialogType = getSimSelectDialogType(
+                change, dataSelected, voiceSelected, smsSelected);
+        SimCombinationWarningParams simCombinationParams = getSimCombinationWarningParams(change);
+        log("[setDefaultForPsimConversionChanged]showing dialog type:" + simSelectDialogType);
+        if (simSelectDialogType != EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_NONE
+                || simCombinationParams.mWarningType != EXTRA_SIM_COMBINATION_WARNING_TYPE_NONE) {
+            log("[setDefaultForPsimConversionChanged]Converted pSIM:" + mConvertedPsimSubId);
+            int subId = getConvertedPsimSubscriptionId();
+            if (subId != INVALID_SUBSCRIPTION_ID && subId != mConvertedPsimSubId) {
+                // 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 (simSelectDialogType == EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_ALL) {
+                    // check if pSIM's preference is voice.
+                    if (mSubscriptionManagerService.getDefaultVoiceSubId()
+                            == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+                        mNeedSetDefaultVoice = true;
+                    }
+                    // check if pSIM's preference is sms.
+                    if (mSubscriptionManagerService.getDefaultSmsSubId()
+                            == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+                        mNeedSetDefaultSms = true;
+                    }
+                    // check if pSIM's preference is data.
+                    if (mSubscriptionManagerService.getDefaultDataSubId()
+                            == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+                        mNeedSetDefaultData = true;
+                    }
+                    log("select type all, set preferred SIM :" + mPrimarySubList.get(0));
+                    mSubscriptionManagerService.setDefaultVoiceSubId(mPrimarySubList.get(0));
+                    mSubscriptionManagerService.setDefaultSmsSubId(mPrimarySubList.get(0));
+                    mSubscriptionManagerService.setDefaultDataSubId(mPrimarySubList.get(0));
+                    return true;
+                } else if (simSelectDialogType == EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_DATA) {
+                    // If another primary subscription is added or default data is not selected, ask
+                    // user to select default for data as it's most important.
+                    int newSubId = mPrimarySubList.get(0);
+                    log("need to set voice:" + mNeedSetDefaultVoice
+                            + ", sms:" + mNeedSetDefaultSms
+                            + ", data:" + mNeedSetDefaultData);
+                    // if the converted pSIM's preference is voice, set the default
+                    // setting for the changed primary subscription to voice.
+                    if (mNeedSetDefaultVoice) {
+                        log("set preferred call, subId:" + newSubId);
+                        mSubscriptionManagerService.setDefaultVoiceSubId(newSubId);
+                        mNeedSetDefaultVoice = false;
+                    }
+                    // if the converted pSIM's preference is sms, set the default
+                    // setting for the changed primary subscription to sms.
+                    if (mNeedSetDefaultSms) {
+                        log("set preferred sms, subId:" + newSubId);
+                        mSubscriptionManagerService.setDefaultSmsSubId(newSubId);
+                        mNeedSetDefaultSms = false;
+                    }
+                    // if the converted pSIM's preference is data, set the default
+                    // setting for the changed primary subscription to data.
+                    if (mNeedSetDefaultData) {
+                        log("set preferred data, subId:" + newSubId);
+                        mSubscriptionManagerService.setDefaultDataSubId(newSubId);
+                        mNeedSetDefaultData = false;
+                    }
+                    mConvertedPsimSubId = subId;
+                    log("set converted pSIM subId:" + mConvertedPsimSubId);
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    private int getConvertedPsimSubscriptionId() {
+        // Check to see if any subscription has been converted due to the pSIM conversion.
+        // When the primary subscription is changed, if it is the same subscription as
+        // the previously converted subscription, it is not due to the pSIM conversion.
+        // So the dialog for preferred SIM/data pick should show.
+        // TODO(b/332261793): On Android W, we need to add CONVERTING status.
+        //  The CONVERTING status allows us to determine if pSIM is in the process of converting,
+        //  so we don't need to check for information about previously converted subscriptions.
+        int convertedSubId = INVALID_SUBSCRIPTION_ID;
+        if (mFeatureFlags.supportPsimToEsimConversion()) {
+            List<SubscriptionInfo> infos =
+                    mSubscriptionManagerService.getAvailableSubscriptionInfoList(
+                            mContext.getOpPackageName(), mContext.getAttributionTag());
+            for (SubscriptionInfo info : infos) {
+                if (!info.isEmbedded() && info.getTransferStatus() == TRANSFER_STATUS_CONVERTED) {
+                    convertedSubId = info.getSubscriptionId();
+                }
+            }
+        }
+        log("getConvertedPsimSubscriptionId: convertedSubId=" + convertedSubId);
+        return convertedSubId;
+    }
+
     private int getSimSelectDialogType(int change, boolean dataSelected,
             boolean voiceSelected, boolean smsSelected) {
         int dialogType = EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_NONE;
diff --git a/src/java/com/android/internal/telephony/Phone.java b/src/java/com/android/internal/telephony/Phone.java
index bf973e1..d327a45 100644
--- a/src/java/com/android/internal/telephony/Phone.java
+++ b/src/java/com/android/internal/telephony/Phone.java
@@ -779,6 +779,7 @@
             case EVENT_SET_NETWORK_MANUAL_COMPLETE:
             case EVENT_SET_NETWORK_AUTOMATIC_COMPLETE:
                 handleSetSelectNetwork((AsyncResult) msg.obj);
+                onSetNetworkSelectionModeCompleted();
                 return;
         }
 
@@ -1536,6 +1537,12 @@
     }
 
     /**
+     * Called when setting network selection mode is complete.
+     */
+    protected void onSetNetworkSelectionModeCompleted() {
+    }
+
+    /**
      * Query the radio for the current network selection mode.
      *
      * Return values:
@@ -4018,6 +4025,15 @@
         return;
     }
 
+    /**
+     * Deletes all the keys for a given Carrier from the device keystore.
+     * @param carrierId : the carrier ID which needs to be matched in the delete query
+     * @param simOperator : MccMnc which needs to be matched in the delete query.
+     */
+    public void deleteCarrierInfoForImsiEncryption(int carrierId, String simOperator) {
+
+    }
+
     public int getCarrierId() {
         return TelephonyManager.UNKNOWN_CARRIER_ID;
     }
@@ -4899,7 +4915,7 @@
     /**
      * @return The data network controller
      */
-    public @Nullable DataNetworkController getDataNetworkController() {
+    public @NonNull DataNetworkController getDataNetworkController() {
         return mDataNetworkController;
     }
 
diff --git a/src/java/com/android/internal/telephony/PhoneConfigurationManager.java b/src/java/com/android/internal/telephony/PhoneConfigurationManager.java
index ef96d89..ffa5b69 100644
--- a/src/java/com/android/internal/telephony/PhoneConfigurationManager.java
+++ b/src/java/com/android/internal/telephony/PhoneConfigurationManager.java
@@ -45,6 +45,7 @@
 
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Map;
 import java.util.NoSuchElementException;
 import java.util.Optional;
@@ -97,7 +98,9 @@
 
     private static PhoneConfigurationManager sInstance = null;
     private final Context mContext;
-    private PhoneCapability mStaticCapability;
+    // Static capability retrieved from the modem - may be null in the case where no info has been
+    // retrieved yet.
+    private PhoneCapability mStaticCapability = null;
     private final Set<Integer> mSlotsSupportingSimultaneousCellularCalls = new HashSet<>(3);
     private final Set<Integer> mSubIdsSupportingSimultaneousCellularCalls = new HashSet<>(3);
     private final HashSet<Consumer<Set<Integer>>> mSimultaneousCellularCallingListeners =
@@ -151,8 +154,6 @@
         mFeatureFlags = featureFlags;
         // TODO: send commands to modem once interface is ready.
         mTelephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
-        //initialize with default, it'll get updated when RADIO is ON/AVAILABLE
-        mStaticCapability = getDefaultCapability();
         mRadioConfig = RadioConfig.getInstance();
         mHandler = new ConfigManagerHandler();
         mPhoneStatusMap = new HashMap<>();
@@ -256,7 +257,8 @@
         boolean halSupportSimulCalling = mRadioConfig != null
                 && mRadioConfig.getRadioConfigProxy(null).getVersion().greaterOrEqual(
                         RIL.RADIO_HAL_VERSION_2_2)
-                && getPhoneCount() > 1 && mStaticCapability.getMaxActiveVoiceSubscriptions() > 1;
+                && getPhoneCount() > 1
+                && getCellularStaticPhoneCapability().getMaxActiveVoiceSubscriptions() > 1;
         // Register for simultaneous calling support changes in the modem if the HAL supports it
         if (halSupportSimulCalling) {
             updateSimultaneousCallingSupport();
@@ -318,7 +320,7 @@
                         log("Unable to add phoneStatus to cache. "
                                 + "No phone object provided for event " + msg.what);
                     }
-                    getStaticPhoneCapability();
+                    updateRadioCapability();
                     break;
                 case EVENT_SWITCH_DSDS_CONFIG_DONE:
                     ar = (AsyncResult) msg.obj;
@@ -343,7 +345,7 @@
                 case EVENT_GET_PHONE_CAPABILITY_DONE:
                     ar = (AsyncResult) msg.obj;
                     if (ar != null && ar.exception == null) {
-                        mStaticCapability = (PhoneCapability) ar.result;
+                        setStaticPhoneCapability((PhoneCapability) ar.result);
                         notifyCapabilityChanged();
                         for (Listener l : mListeners) {
                             l.onPhoneCapabilityChanged();
@@ -376,12 +378,12 @@
                     }
                     ar = (AsyncResult) msg.obj;
                     if (ar != null && ar.exception == null) {
-                        int[] returnedIntArray = (int[]) ar.result;
+                        List<Integer> returnedArrayList = (List<Integer>) ar.result;
                         if (!mSlotsSupportingSimultaneousCellularCalls.isEmpty()) {
                             mSlotsSupportingSimultaneousCellularCalls.clear();
                         }
                         int maxValidPhoneSlot = getPhoneCount() - 1;
-                        for (int i : returnedIntArray) {
+                        for (int i : returnedArrayList) {
                             if (i < 0 || i > maxValidPhoneSlot) {
                                 loge("Invalid slot supporting DSDA =" + i + ". Disabling DSDA.");
                                 mSlotsSupportingSimultaneousCellularCalls.clear();
@@ -534,21 +536,43 @@
     }
 
     /**
-     * get static overall phone capabilities for all phones.
+     * @return static overall phone capabilities for all phones, including voice overrides.
      */
     public synchronized PhoneCapability getStaticPhoneCapability() {
-        if (getDefaultCapability().equals(mStaticCapability)) {
-            log("getStaticPhoneCapability: sending the request for getting PhoneCapability");
-            Message callback = Message.obtain(
-                    mHandler, EVENT_GET_PHONE_CAPABILITY_DONE);
-            mRadioConfig.getPhoneCapability(callback);
-        }
-        mStaticCapability = maybeOverrideMaxActiveVoiceSubscriptions(mStaticCapability);
-        log("getStaticPhoneCapability: mStaticCapability " + mStaticCapability);
+        boolean isDefault = mStaticCapability == null;
+        PhoneCapability caps = isDefault ? getDefaultCapability() : mStaticCapability;
+        caps = maybeOverrideMaxActiveVoiceSubscriptions(caps);
+        log("getStaticPhoneCapability: isDefault=" + isDefault + ", caps=" + caps);
+        return caps;
+    }
+
+    /**
+     * @return untouched capabilities returned from the modem
+     */
+    private synchronized PhoneCapability getCellularStaticPhoneCapability() {
+        log("getCellularStaticPhoneCapability: mStaticCapability " + mStaticCapability);
         return mStaticCapability;
     }
 
     /**
+     * Caches the static PhoneCapability returned by the modem
+     */
+    public synchronized void setStaticPhoneCapability(PhoneCapability capability) {
+        log("setStaticPhoneCapability: mStaticCapability " + capability);
+        mStaticCapability = capability;
+    }
+
+    /**
+     * Query the modem to return its static PhoneCapability and cache it
+     */
+    @VisibleForTesting
+    public void updateRadioCapability() {
+        log("updateRadioCapability: sending the request for getting PhoneCapability");
+        Message callback = Message.obtain(mHandler, EVENT_GET_PHONE_CAPABILITY_DONE);
+        mRadioConfig.getPhoneCapability(callback);
+    }
+
+    /**
      * get configuration related status of each phone.
      */
     public PhoneCapability getCurrentPhoneCapability() {
@@ -556,12 +580,11 @@
     }
 
     public int getNumberOfModemsWithSimultaneousDataConnections() {
-        return mStaticCapability.getMaxActiveDataSubscriptions();
+        return getStaticPhoneCapability().getMaxActiveDataSubscriptions();
     }
 
     public int getNumberOfModemsWithSimultaneousVoiceConnections() {
-        return maybeOverrideMaxActiveVoiceSubscriptions(mStaticCapability)
-                .getMaxActiveVoiceSubscriptions();
+        return getStaticPhoneCapability().getMaxActiveVoiceSubscriptions();
     }
 
     public boolean isVirtualDsdaEnabled() {
@@ -591,8 +614,7 @@
     }
 
     private void notifyCapabilityChanged() {
-        mNotifier.notifyPhoneCapabilityChanged(maybeOverrideMaxActiveVoiceSubscriptions(
-                mStaticCapability));
+        mNotifier.notifyPhoneCapabilityChanged(getStaticPhoneCapability());
     }
 
     /**
diff --git a/src/java/com/android/internal/telephony/PhoneFactory.java b/src/java/com/android/internal/telephony/PhoneFactory.java
index 803fb19..d9c5c9c 100644
--- a/src/java/com/android/internal/telephony/PhoneFactory.java
+++ b/src/java/com/android/internal/telephony/PhoneFactory.java
@@ -187,7 +187,7 @@
                     Rlog.i(LOG_TAG, "Network Mode set to " + Integer.toString(networkModes[i]));
                     sCommandsInterfaces[i] = new RIL(context,
                             RadioAccessFamily.getRafFromNetworkType(networkModes[i]),
-                            cdmaSubscription, i);
+                            cdmaSubscription, i, featureFlags);
                 }
 
                 if (numPhones > 0) {
@@ -312,7 +312,7 @@
             for (int i = prevActiveModemCount; i < activeModemCount; i++) {
                 sCommandsInterfaces[i] = new RIL(context, RadioAccessFamily.getRafFromNetworkType(
                         RILConstants.PREFERRED_NETWORK_MODE),
-                        cdmaSubscription, i);
+                        cdmaSubscription, i, sFeatureFlags);
                 sPhones[i] = createPhone(context, i);
                 if (context.getPackageManager().hasSystemFeature(
                         PackageManager.FEATURE_TELEPHONY_IMS)) {
diff --git a/src/java/com/android/internal/telephony/PhoneSubInfoController.java b/src/java/com/android/internal/telephony/PhoneSubInfoController.java
index a65bfeb..073e242 100644
--- a/src/java/com/android/internal/telephony/PhoneSubInfoController.java
+++ b/src/java/com/android/internal/telephony/PhoneSubInfoController.java
@@ -33,6 +33,7 @@
 import android.os.Binder;
 import android.os.Build;
 import android.os.RemoteException;
+import android.os.SystemProperties;
 import android.os.TelephonyServiceManager.ServiceRegisterer;
 import android.telephony.ImsiEncryptionInfo;
 import android.telephony.PhoneNumberUtils;
@@ -64,6 +65,7 @@
     private AppOpsManager mAppOps;
     private FeatureFlags mFeatureFlags;
     private PackageManager mPackageManager;
+    private final int mVendorApiLevel;
 
     public PhoneSubInfoController(Context context) {
         this(context, new FeatureFlagsImpl());
@@ -80,6 +82,8 @@
         mContext = context;
         mPackageManager = context.getPackageManager();
         mFeatureFlags = featureFlags;
+        mVendorApiLevel = SystemProperties.getInt(
+                "ro.vendor.api_level", Build.VERSION.DEVICE_INITIAL_SDK_INT);
     }
 
     @Deprecated
@@ -799,7 +803,11 @@
 
         if (!mFeatureFlags.enforceTelephonyFeatureMappingForPublicApis()
                 || !CompatChanges.isChangeEnabled(ENABLE_FEATURE_MAPPING, callingPackage,
-                Binder.getCallingUserHandle())) {
+                Binder.getCallingUserHandle())
+                || mVendorApiLevel < Build.VERSION_CODES.VANILLA_ICE_CREAM) {
+            // Skip to check associated telephony feature,
+            // if compatibility change is not enabled for the current process or
+            // the SDK version of vendor partition is less than Android V.
             return;
         }
 
diff --git a/src/java/com/android/internal/telephony/RIL.java b/src/java/com/android/internal/telephony/RIL.java
index 86b4a60..8abebe2 100644
--- a/src/java/com/android/internal/telephony/RIL.java
+++ b/src/java/com/android/internal/telephony/RIL.java
@@ -88,6 +88,7 @@
 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.flags.FeatureFlags;
 import com.android.internal.telephony.gsm.SmsBroadcastConfigInfo;
 import com.android.internal.telephony.imsphone.ImsCallInfo;
 import com.android.internal.telephony.metrics.ModemRestartStats;
@@ -151,6 +152,15 @@
     public static final HalVersion RADIO_HAL_VERSION_UNKNOWN = HalVersion.UNKNOWN;
 
     /** @hide */
+    public static final HalVersion RADIO_HAL_VERSION_1_1 = new HalVersion(1, 1);
+
+    /** @hide */
+    public static final HalVersion RADIO_HAL_VERSION_1_2 = new HalVersion(1, 2);
+
+    /** @hide */
+    public static final HalVersion RADIO_HAL_VERSION_1_3 = new HalVersion(1, 3);
+
+    /** @hide */
     public static final HalVersion RADIO_HAL_VERSION_1_4 = new HalVersion(1, 4);
 
     /** @hide */
@@ -197,6 +207,8 @@
 
     boolean mIsRadioProxyInitialized = false;
 
+    Boolean mIsRadioVersion20Cached = null;
+
     // When we are testing emergency calls using ril.test.emergencynumber, this will trigger test
     // ECbM when the call is ended.
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@@ -210,6 +222,8 @@
 
     public static final int MAX_SERVICE_IDX = HAL_SERVICE_IMS;
 
+    @NonNull private final FeatureFlags mFeatureFlags;
+
     /**
      * An array of sets that records if services are disabled in the HAL for a specific phone ID
      * slot to avoid further getService requests for that service. See XXX_SERVICE for the indices.
@@ -373,12 +387,27 @@
                 case EVENT_AIDL_PROXY_DEAD:
                     int aidlService = msg.arg1;
                     long msgCookie = (long) msg.obj;
-                    riljLog("handleMessage: EVENT_AIDL_PROXY_DEAD cookie = " + msgCookie
-                            + ", service = " + serviceToString(aidlService) + ", cookie = "
-                            + mServiceCookies.get(aidlService));
-                    if (msgCookie == mServiceCookies.get(aidlService).get()) {
-                        mIsRadioProxyInitialized = false;
-                        resetProxyAndRequestList(aidlService);
+                    if (mFeatureFlags.combineRilDeathHandle()) {
+                        if (msgCookie == mServiceCookies.get(aidlService).get()) {
+                            riljLog("handleMessage: EVENT_AIDL_PROXY_DEAD cookie = " + msgCookie
+                                    + ", service = " + serviceToString(aidlService) + ", cookie = "
+                                    + mServiceCookies.get(aidlService));
+                            mIsRadioProxyInitialized = false;
+                            resetProxyAndRequestList(aidlService);
+                            // Remove duplicate death message to avoid duplicate reset.
+                            mRilHandler.removeMessages(EVENT_AIDL_PROXY_DEAD);
+                        } else {
+                            riljLog("Ignore stale EVENT_AIDL_PROXY_DEAD for service "
+                                    + serviceToString(aidlService));
+                        }
+                    } else {
+                        riljLog("handleMessage: EVENT_AIDL_PROXY_DEAD cookie = " + msgCookie
+                                + ", service = " + serviceToString(aidlService) + ", cookie = "
+                                + mServiceCookies.get(aidlService));
+                        if (msgCookie == mServiceCookies.get(aidlService).get()) {
+                            mIsRadioProxyInitialized = false;
+                            resetProxyAndRequestList(aidlService);
+                        }
                     }
                     break;
             }
@@ -423,24 +452,33 @@
         public void serviceDied(long cookie) {
             // Deal with service going away
             riljLog("serviceDied");
-            mRilHandler.sendMessage(mRilHandler.obtainMessage(EVENT_RADIO_PROXY_DEAD,
-                    HAL_SERVICE_RADIO, 0 /* ignored arg2 */, cookie));
+            if (mFeatureFlags.combineRilDeathHandle()) {
+                mRilHandler.sendMessageAtFrontOfQueue(mRilHandler.obtainMessage(
+                        EVENT_RADIO_PROXY_DEAD,
+                        HAL_SERVICE_RADIO, 0 /* ignored arg2 */, cookie));
+            } else {
+                mRilHandler.sendMessage(mRilHandler.obtainMessage(EVENT_RADIO_PROXY_DEAD,
+                        HAL_SERVICE_RADIO, 0 /* ignored arg2 */, cookie));
+            }
         }
     }
 
     private final class BinderServiceDeathRecipient implements IBinder.DeathRecipient {
         private IBinder mBinder;
         private final int mService;
+        private long mLinkedFlags;
 
         BinderServiceDeathRecipient(int service) {
             mService = service;
+            mLinkedFlags = 0;
         }
 
         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());
+                mLinkedFlags = mServiceCookies.get(mService).incrementAndGet();
+                mBinder.linkToDeath(this, (int) mLinkedFlags);
             } else {
                 riljLoge("Unable to link to death for service " + serviceToString(mService));
             }
@@ -448,32 +486,58 @@
 
         public synchronized void unlinkToDeath() {
             if (mBinder != null) {
-                mBinder.unlinkToDeath(this, 0);
+                mBinder.unlinkToDeath(this, (int) mLinkedFlags);
                 mBinder = null;
+                mLinkedFlags = 0;
             }
         }
 
         @Override
         public void binderDied() {
             riljLog("Service " + serviceToString(mService) + " has died.");
-            mRilHandler.sendMessage(mRilHandler.obtainMessage(EVENT_AIDL_PROXY_DEAD, mService,
-                    0 /* ignored arg2 */, mServiceCookies.get(mService).get()));
+            if (mFeatureFlags.combineRilDeathHandle()) {
+                mRilHandler.sendMessageAtFrontOfQueue(mRilHandler.obtainMessage(
+                        EVENT_AIDL_PROXY_DEAD, mService, 0 /* ignored arg2 */,
+                        mLinkedFlags));
+            } else {
+                mRilHandler.sendMessage(mRilHandler.obtainMessage(EVENT_AIDL_PROXY_DEAD, mService,
+                        0 /* ignored arg2 */, mLinkedFlags));
+            }
             unlinkToDeath();
         }
     }
 
-    private synchronized void resetProxyAndRequestList(int service) {
+    /**
+     * Reset services. If one of the AIDL service is reset, all the other AIDL services will be
+     * reset as well.
+     * @param service The service to reset.
+     */
+    private synchronized void resetProxyAndRequestList(@HalService int service) {
         if (service == HAL_SERVICE_RADIO) {
             mRadioProxy = null;
+            // Increment the cookie so that death notification can be ignored
+            mServiceCookies.get(service).incrementAndGet();
         } else {
-            mServiceProxies.get(service).clear();
+            if (mFeatureFlags.combineRilDeathHandle()) {
+                // Reset all aidl services.
+                for (int i = MIN_SERVICE_IDX; i <= MAX_SERVICE_IDX; i++) {
+                    if (i == HAL_SERVICE_RADIO) continue;
+                    if (mServiceProxies.get(i) == null) {
+                        // This should only happen in tests
+                        riljLoge("Null service proxy for service " + serviceToString(i));
+                        continue;
+                    }
+                    mServiceProxies.get(i).clear();
+                    // Increment the cookie so that death notification can be ignored
+                    mServiceCookies.get(i).incrementAndGet();
+                }
+            } else {
+                mServiceProxies.get(service).clear();
+                // Increment the cookie so that death notification can be ignored
+                mServiceCookies.get(service).incrementAndGet();
+            }
         }
 
-        // Increment the cookie so that death notification can be ignored
-        mServiceCookies.get(service).incrementAndGet();
-
-        // TODO: If a service doesn't exist or is unimplemented, it shouldn't cause the radio to
-        //  become unavailable for all other services
         setRadioState(TelephonyManager.RADIO_POWER_UNAVAILABLE, true /* forceNotifyRegistrants */);
 
         RILRequest.resetSerial();
@@ -483,7 +547,20 @@
         if (service == HAL_SERVICE_RADIO) {
             getRadioProxy();
         } else {
-            getRadioServiceProxy(service);
+            if (mFeatureFlags.combineRilDeathHandle()) {
+                // Reset all aidl services.
+                for (int i = MIN_SERVICE_IDX; i <= MAX_SERVICE_IDX; i++) {
+                    if (i == HAL_SERVICE_RADIO) continue;
+                    if (mServiceProxies.get(i) == null) {
+                        // This should only happen in tests
+                        riljLoge("Null service proxy for service " + serviceToString(i));
+                        continue;
+                    }
+                    getRadioServiceProxy(i);
+                }
+            } else {
+                getRadioServiceProxy(service);
+            }
         }
     }
 
@@ -501,10 +578,6 @@
             mMockModem = null;
 
             mMockModem = new MockModem(mContext, serviceName, mPhoneId);
-            if (mMockModem == null) {
-                riljLoge("MockModem create fail.");
-                return false;
-            }
 
             // Disable HIDL service
             if (mRadioProxy != null) {
@@ -541,8 +614,14 @@
 
             if (serviceBound) {
                 mIsRadioProxyInitialized = false;
-                for (int service = MIN_SERVICE_IDX; service <= MAX_SERVICE_IDX; service++) {
-                    resetProxyAndRequestList(service);
+                if (mFeatureFlags.combineRilDeathHandle()) {
+                    // Reset both hidl and aidl proxies.
+                    resetProxyAndRequestList(HAL_SERVICE_RADIO);
+                    resetProxyAndRequestList(HAL_SERVICE_DATA);
+                } else {
+                    for (int service = MIN_SERVICE_IDX; service <= MAX_SERVICE_IDX; service++) {
+                        resetProxyAndRequestList(service);
+                    }
                 }
             }
         }
@@ -570,7 +649,15 @@
                             mHalVersion.put(service, RADIO_HAL_VERSION_UNSUPPORTED);
                         }
                     }
-                    resetProxyAndRequestList(service);
+                    if (!mFeatureFlags.combineRilDeathHandle()) {
+                        resetProxyAndRequestList(service);
+                    }
+                }
+                if (mFeatureFlags.combineRilDeathHandle()) {
+                    // Reset both hidl and aidl proxies. Must be after cleaning mocked halVersion,
+                    // otherwise an aidl service will be incorrectly considered as disabled.
+                    resetProxyAndRequestList(HAL_SERVICE_RADIO);
+                    resetProxyAndRequestList(HAL_SERVICE_DATA);
                 }
             }
         }
@@ -982,16 +1069,33 @@
     @Override
     public synchronized void onSlotActiveStatusChange(boolean active) {
         mIsRadioProxyInitialized = false;
-        for (int service = MIN_SERVICE_IDX; service <= MAX_SERVICE_IDX; service++) {
+        if (mFeatureFlags.combineRilDeathHandle()) {
             if (active) {
-                // Try to connect to RIL services and set response functions.
-                if (service == HAL_SERVICE_RADIO) {
-                    getRadioProxy();
-                } else {
-                    getRadioServiceProxy(service);
+                for (int service = MIN_SERVICE_IDX; service <= MAX_SERVICE_IDX; service++) {
+                    // Try to connect to RIL services and set response functions.
+                    if (service == HAL_SERVICE_RADIO) {
+                        getRadioProxy();
+                    } else {
+                        getRadioServiceProxy(service);
+                    }
                 }
             } else {
-                resetProxyAndRequestList(service);
+                // Reset both hidl and aidl proxies
+                resetProxyAndRequestList(HAL_SERVICE_RADIO);
+                resetProxyAndRequestList(HAL_SERVICE_DATA);
+            }
+        } else {
+            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 == HAL_SERVICE_RADIO) {
+                        getRadioProxy();
+                    } else {
+                        getRadioServiceProxy(service);
+                    }
+                } else {
+                    resetProxyAndRequestList(service);
+                }
             }
         }
     }
@@ -999,19 +1103,16 @@
     //***** Constructors
 
     @UnsupportedAppUsage
-    public RIL(Context context, int allowedNetworkTypes, int cdmaSubscription) {
-        this(context, allowedNetworkTypes, cdmaSubscription, null);
-    }
-
-    @UnsupportedAppUsage
-    public RIL(Context context, int allowedNetworkTypes, int cdmaSubscription, Integer instanceId) {
-        this(context, allowedNetworkTypes, cdmaSubscription, instanceId, null);
+    public RIL(Context context, int allowedNetworkTypes, int cdmaSubscription, Integer instanceId,
+            @NonNull FeatureFlags flags) {
+        this(context, allowedNetworkTypes, cdmaSubscription, instanceId, null, flags);
     }
 
     @VisibleForTesting
     public RIL(Context context, int allowedNetworkTypes, int cdmaSubscription, Integer instanceId,
-            SparseArray<RadioServiceProxy> proxies) {
+            SparseArray<RadioServiceProxy> proxies, @NonNull FeatureFlags flags) {
         super(context);
+        mFeatureFlags = flags;
         if (RILJ_LOGD) {
             riljLog("RIL: init allowedNetworkTypes=" + allowedNetworkTypes
                     + " cdmaSubscription=" + cdmaSubscription + ")");
@@ -1118,7 +1219,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 (!isRadioServiceSupported(service)) {
+            if (isRadioVersion2_0() && !isRadioServiceSupported(service)) {
                 riljLog("Not initializing " + serviceToString(service) + " (not supported)");
                 continue;
             }
@@ -1140,12 +1241,13 @@
     }
 
     private boolean isRadioVersion2_0() {
+        if (mIsRadioVersion20Cached != null) return mIsRadioVersion20Cached;
         for (int service = HAL_SERVICE_DATA; service <= MAX_SERVICE_IDX; service++) {
             if (isRadioServiceSupported(service)) {
-                return true;
+                return mIsRadioVersion20Cached = true;
             }
         }
-        return false;
+        return mIsRadioVersion20Cached = false;
     }
 
     private boolean isRadioServiceSupported(int service) {
@@ -1286,17 +1388,6 @@
         } else if (proxy instanceof RadioImsProxy) {
             service = HAL_SERVICE_IMS;
         }
-
-        if (mHalVersion.get(service).less(version)) {
-            riljLoge(String.format("%s not supported on service %s < %s.",
-                    request, serviceToString(service), version));
-            if (result != null) {
-                AsyncResult.forMessage(result, null,
-                        CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
-                result.sendToTarget();
-            }
-            return false;
-        }
         if (proxy == null || proxy.isEmpty()) {
             riljLoge(String.format("Unable to complete %s because service %s is not available.",
                     request, serviceToString(service)));
@@ -1307,6 +1398,16 @@
             }
             return false;
         }
+        if (mHalVersion.get(service).less(version)) {
+            riljLoge(String.format("%s not supported on service %s < %s.",
+                    request, serviceToString(service), version));
+            if (result != null) {
+                AsyncResult.forMessage(result, null,
+                        CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
+                result.sendToTarget();
+            }
+            return false;
+        }
         return true;
     }
 
diff --git a/src/java/com/android/internal/telephony/RadioConfig.java b/src/java/com/android/internal/telephony/RadioConfig.java
index 13f6502..da20639 100644
--- a/src/java/com/android/internal/telephony/RadioConfig.java
+++ b/src/java/com/android/internal/telephony/RadioConfig.java
@@ -61,10 +61,6 @@
 
     static final int EVENT_HIDL_SERVICE_DEAD = 1;
     static final int EVENT_AIDL_SERVICE_DEAD = 2;
-    static final HalVersion RADIO_CONFIG_HAL_VERSION_UNKNOWN = new HalVersion(-1, -1);
-    static final HalVersion RADIO_CONFIG_HAL_VERSION_1_1 = new HalVersion(1, 1);
-    static final HalVersion RADIO_CONFIG_HAL_VERSION_1_3 = new HalVersion(1, 3);
-    static final HalVersion RADIO_CONFIG_HAL_VERSION_2_0 = new HalVersion(2, 0);
 
     private final boolean mIsMobileNetworkSupported;
     private final SparseArray<RILRequest> mRequestList = new SparseArray<>();
@@ -294,13 +290,12 @@
 
         if (service != null) {
             mRadioConfigProxy.setAidl(
-                    RADIO_CONFIG_HAL_VERSION_2_0,
                     android.hardware.radio.config.IRadioConfig.Stub.asInterface(service));
         }
 
         if (mRadioConfigProxy.isEmpty()) {
             try {
-                mRadioConfigProxy.setHidl(RADIO_CONFIG_HAL_VERSION_1_3,
+                mRadioConfigProxy.setHidl(RIL.RADIO_HAL_VERSION_1_3,
                         android.hardware.radio.config.V1_3.IRadioConfig.getService(true));
             } catch (RemoteException | NoSuchElementException e) {
                 mRadioConfigProxy.clear();
@@ -310,7 +305,7 @@
 
         if (mRadioConfigProxy.isEmpty()) {
             try {
-                mRadioConfigProxy.setHidl(RADIO_CONFIG_HAL_VERSION_1_1,
+                mRadioConfigProxy.setHidl(RIL.RADIO_HAL_VERSION_1_1,
                         android.hardware.radio.config.V1_1.IRadioConfig.getService(true));
             } catch (RemoteException | NoSuchElementException e) {
                 mRadioConfigProxy.clear();
@@ -515,7 +510,7 @@
         RadioConfigProxy proxy = getRadioConfigProxy(null);
         if (proxy.isEmpty()) return;
 
-        if (proxy.getVersion().less(RADIO_CONFIG_HAL_VERSION_1_1)) {
+        if (proxy.getVersion().less(RIL.RADIO_HAL_VERSION_1_1)) {
             if (result != null) {
                 AsyncResult.forMessage(result, null,
                         CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
@@ -543,7 +538,7 @@
      */
     public boolean isSetPreferredDataCommandSupported() {
         RadioConfigProxy proxy = getRadioConfigProxy(null);
-        return !proxy.isEmpty() && proxy.getVersion().greaterOrEqual(RADIO_CONFIG_HAL_VERSION_1_1);
+        return !proxy.isEmpty() && proxy.getVersion().greaterOrEqual(RIL.RADIO_HAL_VERSION_1_1);
     }
 
     /**
@@ -574,7 +569,7 @@
         RadioConfigProxy proxy = getRadioConfigProxy(result);
         if (proxy.isEmpty()) return;
 
-        if (proxy.getVersion().less(RADIO_CONFIG_HAL_VERSION_1_1)) {
+        if (proxy.getVersion().less(RIL.RADIO_HAL_VERSION_1_1)) {
             if (result != null) {
                 AsyncResult.forMessage(
                         result, null, CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
@@ -628,7 +623,7 @@
         RadioConfigProxy proxy = getRadioConfigProxy(Message.obtain(result));
         if (proxy.isEmpty()) return;
 
-        if (proxy.getVersion().less(RADIO_CONFIG_HAL_VERSION_1_3)) {
+        if (proxy.getVersion().less(RIL.RADIO_HAL_VERSION_1_3)) {
             if (result != null) {
                 if (DBG) {
                     logd("RIL_REQUEST_GET_HAL_DEVICE_CAPABILITIES > REQUEST_NOT_SUPPORTED");
diff --git a/src/java/com/android/internal/telephony/RadioConfigIndicationAidl.java b/src/java/com/android/internal/telephony/RadioConfigIndicationAidl.java
index 9aa1aaa..127631d 100644
--- a/src/java/com/android/internal/telephony/RadioConfigIndicationAidl.java
+++ b/src/java/com/android/internal/telephony/RadioConfigIndicationAidl.java
@@ -17,14 +17,14 @@
 package com.android.internal.telephony;
 
 import android.os.AsyncResult;
-import android.os.RemoteException;
 import android.os.Trace;
 
 import com.android.internal.telephony.uicc.IccSlotStatus;
 import com.android.telephony.Rlog;
 
 import java.util.ArrayList;
-import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
 
 /**
  * This class is the AIDL implementation of IRadioConfigIndication interface.
@@ -58,9 +58,11 @@
      */
     @Override
     public void onSimultaneousCallingSupportChanged(int[] enabledLogicalSlots) {
-        ArrayList<Integer> ret = RILUtils.primitiveArrayToArrayList(enabledLogicalSlots);
+        List<Integer> ret = (enabledLogicalSlots == null) ? Collections.emptyList() :
+                RILUtils.primitiveArrayToArrayList(enabledLogicalSlots);
         logd("onSimultaneousCallingSupportChanged: enabledLogicalSlots = " + ret);
         if (mRadioConfig.mSimultaneousCallingSupportStatusRegistrant != null) {
+            logd("onSimultaneousCallingSupportChanged: notifying registrant");
             mRadioConfig.mSimultaneousCallingSupportStatusRegistrant.notifyRegistrant(
                     new AsyncResult(null, ret, null));
         }
diff --git a/src/java/com/android/internal/telephony/RadioConfigProxy.java b/src/java/com/android/internal/telephony/RadioConfigProxy.java
index b6c6d68..9f34e29 100644
--- a/src/java/com/android/internal/telephony/RadioConfigProxy.java
+++ b/src/java/com/android/internal/telephony/RadioConfigProxy.java
@@ -31,14 +31,15 @@
  * downstream users.
  */
 public class RadioConfigProxy {
-    private final HalVersion mRadioHalVersion;
+    private static final String TAG = "RadioConfigProxy";
+    private HalVersion mRadioHalVersion;
     private final RadioConfigHidlServiceDeathRecipient mRadioConfigHidlServiceDeathRecipient;
     private final RadioConfigAidlServiceDeathRecipient mRadioConfigAidlServiceDeathRecipient;
 
     private volatile android.hardware.radio.config.V1_1.IRadioConfig mHidlRadioConfigProxy = null;
     private volatile android.hardware.radio.config.IRadioConfig mAidlRadioConfigProxy = null;
 
-    private HalVersion mRadioConfigHalVersion = RadioConfig.RADIO_CONFIG_HAL_VERSION_UNKNOWN;
+    private HalVersion mRadioConfigHalVersion = RIL.RADIO_HAL_VERSION_UNKNOWN;
     private boolean mIsAidl;
 
     public RadioConfigProxy(RadioConfig radioConfig, HalVersion radioHalVersion) {
@@ -83,13 +84,15 @@
     /**
      * Set IRadioConfig as the AIDL implementation for RadioConfigProxy
      *
-     * @param radioConfigHalVersion RadioConfig HAL version
      * @param radioConfig IRadioConfig implementation
      */
-    public void setAidl(
-            HalVersion radioConfigHalVersion,
-            android.hardware.radio.config.IRadioConfig radioConfig) {
-        mRadioConfigHalVersion = radioConfigHalVersion;
+    public void setAidl(android.hardware.radio.config.IRadioConfig radioConfig) {
+        try {
+            mRadioConfigHalVersion = RIL.getServiceHalVersion(radioConfig.getInterfaceVersion());
+            Rlog.d(TAG, "setAidl: setting HAL version to version = " + mRadioConfigHalVersion);
+        } catch (RemoteException e) {
+            Rlog.e(TAG, "setAidl: " + e);
+        }
         mAidlRadioConfigProxy = radioConfig;
         mIsAidl = true;
         mRadioConfigAidlServiceDeathRecipient.setService(radioConfig.asBinder());
@@ -106,7 +109,7 @@
 
     /** Reset RadioConfigProxy */
     public void clear() {
-        mRadioConfigHalVersion = RadioConfig.RADIO_CONFIG_HAL_VERSION_UNKNOWN;
+        mRadioConfigHalVersion = RIL.RADIO_HAL_VERSION_UNKNOWN;
         mHidlRadioConfigProxy = null;
         mAidlRadioConfigProxy = null;
         mRadioConfigHidlServiceDeathRecipient.clear();
diff --git a/src/java/com/android/internal/telephony/ServiceStateTracker.java b/src/java/com/android/internal/telephony/ServiceStateTracker.java
index ba6479e..2a9bf7f 100644
--- a/src/java/com/android/internal/telephony/ServiceStateTracker.java
+++ b/src/java/com/android/internal/telephony/ServiceStateTracker.java
@@ -227,7 +227,6 @@
     private final RegistrantList mAreaCodeChangedRegistrants = new RegistrantList();
 
     /* Radio power off pending flag */
-    // @GuardedBy("this")
     private volatile boolean mPendingRadioPowerOffAfterDataOff = false;
 
     /** Waiting period before recheck gprs and voice registration. */
@@ -3728,10 +3727,18 @@
             // because operatorNumeric would be SIM's mcc/mnc when device is on IWLAN), but if the
             // device has camped on a cell either to attempt registration or for emergency services,
             // then for purposes of setting the locale, we don't care if registration fails or is
-            // incomplete.
+            // incomplete. Additionally, if there is no cellular service and ims is registered over
+            // the IWLAN, the locale will not be updated.
             // CellIdentity can return a null MCC and MNC in CDMA
             String localeOperator = operatorNumeric;
-            if (mSS.getDataNetworkType() == TelephonyManager.NETWORK_TYPE_IWLAN) {
+            int dataNetworkType = mSS.getDataNetworkType();
+            if (dataNetworkType == TelephonyManager.NETWORK_TYPE_IWLAN
+                    || (dataNetworkType == TelephonyManager.NETWORK_TYPE_UNKNOWN
+                            && getImsRegistrationTech()
+                                    == ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN)) {
+                // TODO(b/333346537#comment10): Complete solution would be ignore mcc/mnc reported
+                //  by the unsolicited indication OPERATOR from RIL, but only relies on MCC/MNC from
+                //  data registration or voice registration.
                 localeOperator = null;
             }
             if (isInvalidOperatorNumeric(localeOperator)) {
@@ -5008,20 +5015,10 @@
     }
 
     /**
-     * process the pending request to turn radio off after data is disconnected
-     *
-     * return true if there is pending request to process; false otherwise.
+     * return true if there is pending disconnect data request to process; false otherwise.
      */
-    public boolean processPendingRadioPowerOffAfterDataOff() {
-        synchronized(this) {
-            if (mPendingRadioPowerOffAfterDataOff) {
-                if (DBG) log("Process pending request to turn radio off.");
-                hangupAndPowerOff();
-                mPendingRadioPowerOffAfterDataOff = false;
-                return true;
-            }
-            return false;
-        }
+    public boolean isPendingRadioPowerOffAfterDataOff() {
+        return mPendingRadioPowerOffAfterDataOff;
     }
 
     private void onCarrierConfigurationChanged(int slotIndex) {
@@ -5911,4 +5908,17 @@
     public @Nullable CellIdentity getLastKnownCellIdentity() {
         return mLastKnownCellIdentity;
     }
+
+    /**
+     * Get the tech where ims is currently registered.
+     * @return Returns the tech of ims registered. if not registered or no phome for ims, returns
+     *   {@link ImsRegistrationImplBase#REGISTRATION_TECH_NONE}.
+     */
+    private @ImsRegistrationImplBase.ImsRegistrationTech int getImsRegistrationTech() {
+        ImsPhone imsPhone = (ImsPhone) mPhone.getImsPhone();
+        if (imsPhone != null) {
+            return imsPhone.getImsRegistrationTech();
+        }
+        return ImsRegistrationImplBase.REGISTRATION_TECH_NONE;
+    }
 }
diff --git a/src/java/com/android/internal/telephony/SignalStrengthController.java b/src/java/com/android/internal/telephony/SignalStrengthController.java
index b11d7e5..98f84b2 100644
--- a/src/java/com/android/internal/telephony/SignalStrengthController.java
+++ b/src/java/com/android/internal/telephony/SignalStrengthController.java
@@ -50,6 +50,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.subscription.SubscriptionManagerService;
 import com.android.internal.telephony.util.ArrayUtils;
+import com.android.internal.telephony.util.TelephonyUtils;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.telephony.Rlog;
 
@@ -60,6 +61,7 @@
 import java.util.Iterator;
 import java.util.List;
 import java.util.NoSuchElementException;
+import java.util.Objects;
 import java.util.Set;
 import java.util.TreeSet;
 import java.util.UUID;
@@ -101,7 +103,7 @@
     private static final int EVENT_GET_SIGNAL_STRENGTH                      = 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;
+    public static final int EVENT_POLL_SIGNAL_STRENGTH_DONE                = 9;
     private static final int EVENT_SERVICE_STATE_CHANGED                    = 10;
 
     @NonNull
@@ -330,7 +332,7 @@
      * @param signalStrength The new SignalStrength used for updating {@code mSignalStrength}.
      */
     private void updateSignalStrength(@NonNull SignalStrength signalStrength) {
-        mSignalStrength = signalStrength;
+        mSignalStrength = maybeOverrideSignalStrengthForTest(signalStrength);
         ServiceStateTracker serviceStateTracker = mPhone.getServiceStateTracker();
         if (serviceStateTracker != null) {
             mSignalStrength.updateLevel(mCarrierConfig, serviceStateTracker.mSS);
@@ -342,6 +344,18 @@
     }
 
     /**
+     * For debug test build, override signal strength for testing.
+     * @param original The real signal strength to use if not in testing mode.
+     * @return The signal strength to broadcast to the external.
+     */
+    @NonNull private SignalStrength maybeOverrideSignalStrengthForTest(
+            @NonNull SignalStrength original) {
+        return TelephonyUtils.IS_DEBUGGABLE && mPhone.getTelephonyTester() != null
+                ? Objects.requireNonNullElse(mPhone.getTelephonyTester()
+                .getOverriddenSignalStrength(), original) : original;
+    }
+
+    /**
      * @return signal strength
      */
     @NonNull
@@ -750,7 +764,7 @@
     }
 
     void setSignalStrengthDefaultValues() {
-        mSignalStrength = new SignalStrength();
+        mSignalStrength = maybeOverrideSignalStrengthForTest(new SignalStrength());
         mSignalStrengthUpdatedTime = System.currentTimeMillis();
     }
 
diff --git a/src/java/com/android/internal/telephony/SimultaneousCallingTracker.java b/src/java/com/android/internal/telephony/SimultaneousCallingTracker.java
index 0a14ccd..0b427f8 100644
--- a/src/java/com/android/internal/telephony/SimultaneousCallingTracker.java
+++ b/src/java/com/android/internal/telephony/SimultaneousCallingTracker.java
@@ -494,7 +494,7 @@
                 l.onSimultaneousCallingSupportChanged(simultaneousCallSubscriptionIdMap);
             }
         } catch (Exception e) {
-            Log.w(LOG_TAG, "handleVideoCapabilitiesChanged: Exception = " + e);
+            Log.w(LOG_TAG, "handleSimultaneousCallingSupportChanged: Exception = " + e);
         }
     }
 
diff --git a/src/java/com/android/internal/telephony/SmsController.java b/src/java/com/android/internal/telephony/SmsController.java
index da1b07a..59184d8 100644
--- a/src/java/com/android/internal/telephony/SmsController.java
+++ b/src/java/com/android/internal/telephony/SmsController.java
@@ -38,6 +38,7 @@
 import android.os.Binder;
 import android.os.Build;
 import android.os.Bundle;
+import android.os.SystemProperties;
 import android.os.TelephonyServiceManager.ServiceRegisterer;
 import android.os.UserHandle;
 import android.provider.Telephony.Sms.Intents;
@@ -70,6 +71,8 @@
 
     private final Context mContext;
     private final PackageManager mPackageManager;
+    private final int mVendorApiLevel;
+
     @NonNull private final FeatureFlags mFlags;
 
     @VisibleForTesting
@@ -83,6 +86,9 @@
         if (smsServiceRegisterer.get() == null) {
             smsServiceRegisterer.register(this);
         }
+
+        mVendorApiLevel = SystemProperties.getInt(
+                "ro.vendor.api_level", Build.VERSION.DEVICE_INITIAL_SDK_INT);
     }
 
     private Phone getPhone(int subId) {
@@ -296,16 +302,17 @@
         SubscriptionInfo info;
         try {
             info = getSubscriptionInfo(subId);
+
+            if (isBluetoothSubscription(info)) {
+                sendBluetoothText(info, destAddr, text, sentIntent, deliveryIntent);
+            } else {
+                sendIccText(subId, callingPackage, destAddr, scAddr, text, sentIntent,
+                        deliveryIntent, persistMessageForNonDefaultSmsApp, messageId,
+                        skipShortCodeCheck);
+            }
         } finally {
             Binder.restoreCallingIdentity(token);
         }
-
-        if (isBluetoothSubscription(info)) {
-            sendBluetoothText(info, destAddr, text, sentIntent, deliveryIntent);
-        } else {
-            sendIccText(subId, callingPackage, destAddr, scAddr, text, sentIntent, deliveryIntent,
-                    persistMessageForNonDefaultSmsApp, messageId, skipShortCodeCheck);
-        }
     }
 
     private boolean isBluetoothSubscription(SubscriptionInfo info) {
@@ -1224,7 +1231,11 @@
 
         if (!mFlags.enforceTelephonyFeatureMappingForPublicApis()
                 || !CompatChanges.isChangeEnabled(ENABLE_FEATURE_MAPPING, callingPackage,
-                Binder.getCallingUserHandle())) {
+                Binder.getCallingUserHandle())
+                || mVendorApiLevel < Build.VERSION_CODES.VANILLA_ICE_CREAM) {
+            // Skip to check associated telephony feature,
+            // if compatibility change is not enabled for the current process or
+            // the SDK version of vendor partition is less than Android V.
             return;
         }
 
diff --git a/src/java/com/android/internal/telephony/TelephonyComponentFactory.java b/src/java/com/android/internal/telephony/TelephonyComponentFactory.java
index 5da4b12..0b0f9d3 100644
--- a/src/java/com/android/internal/telephony/TelephonyComponentFactory.java
+++ b/src/java/com/android/internal/telephony/TelephonyComponentFactory.java
@@ -523,8 +523,8 @@
     /**
      * Create a new LinkBandwidthEstimator.
      */
-    public LinkBandwidthEstimator makeLinkBandwidthEstimator(Phone phone) {
-        return new LinkBandwidthEstimator(phone, mTelephonyFacade);
+    public LinkBandwidthEstimator makeLinkBandwidthEstimator(Phone phone, Looper looper) {
+        return new LinkBandwidthEstimator(phone, looper, mTelephonyFacade);
     }
 
     /**
diff --git a/src/java/com/android/internal/telephony/TelephonyTester.java b/src/java/com/android/internal/telephony/TelephonyTester.java
index b9e04c8..7d3d75d 100644
--- a/src/java/com/android/internal/telephony/TelephonyTester.java
+++ b/src/java/com/android/internal/telephony/TelephonyTester.java
@@ -17,16 +17,21 @@
 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;
 import android.content.IntentFilter;
 import android.net.Uri;
+import android.os.AsyncResult;
 import android.os.BadParcelableException;
 import android.os.Bundle;
+import android.os.PersistableBundle;
 import android.telephony.AccessNetworkConstants;
+import android.telephony.CellSignalStrengthLte;
 import android.telephony.NetworkRegistrationInfo;
 import android.telephony.ServiceState;
+import android.telephony.SignalStrength;
 import android.telephony.TelephonyManager;
 import android.telephony.ims.ImsCallProfile;
 import android.telephony.ims.ImsConferenceState;
@@ -46,6 +51,7 @@
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
+import java.lang.reflect.Field;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
@@ -163,6 +169,7 @@
     private static List<ImsExternalCallState> mImsExternalCallStates = null;
 
     private Intent mServiceStateTestIntent;
+    private SignalStrengthTestable mSignalStrengthTest;
 
     private Phone mPhone;
 
@@ -386,11 +393,68 @@
     }
 
     /**
+     * Testable signal strength that mocks its fields.
+     */
+    private class SignalStrengthTestable extends SignalStrength {
+        private SignalStrengthTestable() {
+            super();
+        }
+
+        public void mockLevel(int level) {
+            try {
+                Field lteField = SignalStrength.class.getDeclaredField("mLte");
+                lteField.setAccessible(true);
+                CellSignalStrengthLte lte = (CellSignalStrengthLte) lteField.get(this);
+
+                Field lvlField = CellSignalStrengthLte.class.getDeclaredField("mLevel");
+                lvlField.setAccessible(true);
+                lvlField.set(lte, level);
+            } catch (Exception e) {
+                log("SignalStrengthTestable: mockLevel " + e);
+            }
+        }
+
+        @Override
+        public void updateLevel(PersistableBundle cc, ServiceState ss) {
+            log("SignalStrengthTestable: updateLevel: do nothing ");
+        }
+
+        @Override
+        public String toString() {
+            return "SignalStrengthTestable-" + getLevel();
+        }
+    }
+
+    /** {@link android.telephony.SignalStrength} */
+    public void setSignalStrength(int level) {
+        if (level > -1) {
+            log("setSignalStrength: level " + level);
+            mSignalStrengthTest = new SignalStrengthTestable();
+            mSignalStrengthTest.mockLevel(level);
+            AsyncResult ar = new AsyncResult(null, mSignalStrengthTest, null);
+            mPhone.getSignalStrengthController().sendMessage(mPhone.getSignalStrengthController()
+                    .obtainMessage(SignalStrengthController.EVENT_POLL_SIGNAL_STRENGTH_DONE, ar));
+        } else {
+            log("setSignalStrength: clear mock");
+            mSignalStrengthTest = null;
+            mPhone.getSignalStrengthController().getSignalStrengthFromCi();
+        }
+    }
+
+    /** {@link android.telephony.SignalStrength} */
+    @Nullable
+    public SignalStrength getOverriddenSignalStrength() {
+        return mSignalStrengthTest;
+    }
+
+    /**
      * Set the service state test intent.
      *
      * @param intent The service state test intent.
      */
     public void setServiceStateTestIntent(@NonNull Intent intent) {
+        // Don't process if the intent is not prepared for this phone slot.
+        if (mPhone.getPhoneId() != intent.getIntExtra(EXTRA_PHONE_ID, mPhone.getPhoneId())) return;
         mServiceStateTestIntent = intent;
         // Trigger the service state update. The replacement will be done in
         // overrideServiceState().
@@ -400,10 +464,6 @@
 
     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");
diff --git a/src/java/com/android/internal/telephony/cat/CatService.java b/src/java/com/android/internal/telephony/cat/CatService.java
index 7742ca1..4da1622 100644
--- a/src/java/com/android/internal/telephony/cat/CatService.java
+++ b/src/java/com/android/internal/telephony/cat/CatService.java
@@ -284,10 +284,12 @@
             CatLog.d(this, "Disposing CatService object");
             mIccRecords.unregisterForRecordsLoaded(this);
 
-            try {
-                mContext.unregisterReceiver(mSmsBroadcastReceiver);
-            } catch (IllegalArgumentException e) {
-                CatLog.e(this, "mSmsBroadcastReceiver: was not registered" + e);
+            if (sFlags.unregisterSmsBroadcastReceiverFromCatService()) {
+                try {
+                    mContext.unregisterReceiver(mSmsBroadcastReceiver);
+                } catch (IllegalArgumentException e) {
+                    CatLog.e(this, "mSmsBroadcastReceiver: was not registered" + e);
+                }
             }
 
             // Clean up stk icon if dispose is called
diff --git a/src/java/com/android/internal/telephony/data/AccessNetworksManager.java b/src/java/com/android/internal/telephony/data/AccessNetworksManager.java
index 3d3fbe9..bc684af 100644
--- a/src/java/com/android/internal/telephony/data/AccessNetworksManager.java
+++ b/src/java/com/android/internal/telephony/data/AccessNetworksManager.java
@@ -69,7 +69,6 @@
 import java.util.UUID;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.Executor;
-import java.util.function.Consumer;
 import java.util.stream.Collectors;
 
 /**
@@ -86,7 +85,8 @@
     /**
      * The counters to detect frequent QNS attempt to change preferred network transport by ApnType.
      */
-    private final @NonNull SparseArray<SlidingWindowEventCounter> mApnTypeToQnsChangeNetworkCounter;
+    @NonNull
+    private final SparseArray<SlidingWindowEventCounter> mApnTypeToQnsChangeNetworkCounter;
 
     private final String mLogTag;
     private final LocalLog mLocalLog = new LocalLog(64);
@@ -109,12 +109,11 @@
 
     private final CarrierConfigManager mCarrierConfigManager;
 
-    private @Nullable DataConfigManager mDataConfigManager;
+    @Nullable
+    private DataConfigManager mDataConfigManager;
 
     private IQualifiedNetworksService mIQualifiedNetworksService;
 
-    private AccessNetworksManagerDeathRecipient mDeathRecipient;
-
     private String mTargetBindingPackageName;
 
     private QualifiedNetworksServiceConnection mServiceConnection;
@@ -122,7 +121,8 @@
     // Available networks. Key is the APN type.
     private final SparseArray<int[]> mAvailableNetworks = new SparseArray<>();
 
-    private final @TransportType int[] mAvailableTransports;
+    @TransportType
+    private final int[] mAvailableTransports;
 
     private final RegistrantList mQualifiedNetworksChangedRegistrants = new RegistrantList();
 
@@ -135,7 +135,8 @@
     /**
      * Callbacks for passing information to interested clients.
      */
-    private final @NonNull Set<AccessNetworksManagerCallback> mAccessNetworksManagerCallbacks =
+    @NonNull
+    private final Set<AccessNetworksManagerCallback> mAccessNetworksManagerCallbacks =
             new ArraySet<>();
 
     private final FeatureFlags mFeatureFlags;
@@ -144,9 +145,12 @@
      * Represents qualified network types list on a specific APN type.
      */
     public static class QualifiedNetworks {
-        public final @ApnType int apnType;
+        @ApnType
+        public final int apnType;
         // The qualified networks in preferred order. Each network is a AccessNetworkType.
-        public final @NonNull @RadioAccessNetworkType int[] qualifiedNetworks;
+        @NonNull
+        @RadioAccessNetworkType
+        public final int[] qualifiedNetworks;
         public QualifiedNetworks(@ApnType int apnType, @NonNull int[] qualifiedNetworks) {
             this.apnType = apnType;
             this.qualifiedNetworks = Arrays.stream(qualifiedNetworks)
@@ -198,11 +202,12 @@
         public void onServiceConnected(ComponentName name, IBinder service) {
             if (DBG) log("onServiceConnected " + name);
             mIQualifiedNetworksService = IQualifiedNetworksService.Stub.asInterface(service);
-            mDeathRecipient = new AccessNetworksManagerDeathRecipient();
+            AccessNetworksManagerDeathRecipient deathRecipient =
+                    new AccessNetworksManagerDeathRecipient();
             mLastBoundPackageName = getQualifiedNetworksServicePackageName();
 
             try {
-                service.linkToDeath(mDeathRecipient, 0 /* flags */);
+                service.linkToDeath(deathRecipient, 0 /* flags */);
                 mIQualifiedNetworksService.createNetworkAvailabilityProvider(mPhone.getPhoneId(),
                         new QualifiedNetworksServiceCallback());
             } catch (RemoteException e) {
@@ -310,7 +315,7 @@
 
         /**
          * Called when QualifiedNetworksService requests network validation.
-         *
+         * <p>
          * Since the data network in the connected state corresponding to the given network
          * capability must be validated, a request is tossed to the data network controller.
          * @param networkCapability network capability
@@ -328,27 +333,22 @@
             log("onNetworkValidationRequested: networkCapability = ["
                     + DataUtils.networkCapabilityToString(networkCapability) + "]");
 
-            dnc.requestNetworkValidation(networkCapability, new Consumer<Integer>() {
-                @Override
-                public void accept(Integer result) {
-                    post(() -> {
-                        try {
-                            log("onNetworkValidationRequestDone:"
-                                    + DataServiceCallback.resultCodeToString(result));
-                            resultCodeCallback.accept(result.intValue());
-                        } catch (RemoteException e) {
-                            // Ignore if the remote process is no longer available to call back.
-                            loge("onNetworkValidationRequestDone RemoteException" + e);
-                        }
-                    });
+            dnc.requestNetworkValidation(networkCapability, result -> post(() -> {
+                try {
+                    log("onNetworkValidationRequestDone:"
+                            + DataServiceCallback.resultCodeToString(result));
+                    resultCodeCallback.accept(result);
+                } catch (RemoteException e) {
+                    // Ignore if the remote process is no longer available to call back.
+                    loge("onNetworkValidationRequestDone RemoteException" + e);
                 }
-            });
+            }));
         }
 
         @Override
-        public void onReconnectQualifedNetworkType(int apnTypes, int qualifiedNetworkType) {
+        public void onReconnectQualifiedNetworkType(int apnTypes, int qualifiedNetworkType) {
             if (mFeatureFlags.reconnectQualifiedNetwork()) {
-                log("onReconnectQualifedNetworkType: apnTypes = ["
+                log("onReconnectQualifiedNetworkType: apnTypes = ["
                         + ApnSetting.getApnTypesStringFromBitmask(apnTypes)
                         + "], networks = [" + AccessNetworkType.toString(qualifiedNetworkType)
                         + "]");
@@ -431,7 +431,8 @@
             mPhone.getDataNetworkController().getDataRetryManager().registerCallback(
                     new DataRetryManager.DataRetryManagerCallback(this::post) {
                         @Override
-                        public void onThrottleStatusChanged(List<ThrottleStatus> throttleStatuses) {
+                        public void onThrottleStatusChanged(
+                                @NonNull List<ThrottleStatus> throttleStatuses) {
                             try {
                                 logl("onThrottleStatusChanged: " + throttleStatuses);
                                 if (mIQualifiedNetworksService != null) {
@@ -473,7 +474,7 @@
      */
     private void bindQualifiedNetworksService() {
         post(() -> {
-            Intent intent = null;
+            Intent intent;
             String packageName = getQualifiedNetworksServicePackageName();
             String className = getQualifiedNetworksServiceClassName();
 
@@ -540,7 +541,7 @@
             b = mCarrierConfigManager.getConfigForSubId(mPhone.getSubId(),
                     CarrierConfigManager
                             .KEY_CARRIER_QUALIFIED_NETWORKS_SERVICE_PACKAGE_OVERRIDE_STRING);
-            if (b != null && !b.isEmpty()) {
+            if (!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);
@@ -571,7 +572,7 @@
             b = mCarrierConfigManager.getConfigForSubId(mPhone.getSubId(),
                     CarrierConfigManager
                             .KEY_CARRIER_QUALIFIED_NETWORKS_SERVICE_CLASS_OVERRIDE_STRING);
-            if (b != null && !b.isEmpty()) {
+            if (!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);
@@ -587,7 +588,8 @@
         return className;
     }
 
-    private @NonNull List<QualifiedNetworks> getQualifiedNetworksList() {
+    @NonNull
+    private List<QualifiedNetworks> getQualifiedNetworksList() {
         List<QualifiedNetworks> qualifiedNetworksList = new ArrayList<>();
         for (int i = 0; i < mAvailableNetworks.size(); i++) {
             qualifiedNetworksList.add(new QualifiedNetworks(mAvailableNetworks.keyAt(i),
@@ -619,11 +621,13 @@
     /**
      * @return The available transports.
      */
-    public @NonNull int[] getAvailableTransports() {
+    @NonNull
+    public int[] getAvailableTransports() {
         return mAvailableTransports;
     }
 
-    private static @TransportType int getTransportFromAccessNetwork(int accessNetwork) {
+    @TransportType
+    private static int getTransportFromAccessNetwork(int accessNetwork) {
         return accessNetwork == AccessNetworkType.IWLAN
                 ? AccessNetworkConstants.TRANSPORT_TYPE_WLAN
                 : AccessNetworkConstants.TRANSPORT_TYPE_WWAN;
@@ -656,7 +660,8 @@
      * @param apnType APN type
      * @return The preferred transport.
      */
-    public @TransportType int getPreferredTransport(@ApnType int apnType) {
+    @TransportType
+    public int getPreferredTransport(@ApnType int apnType) {
         return mPreferredTransports.get(apnType) == null
                 ? AccessNetworkConstants.TRANSPORT_TYPE_WWAN : mPreferredTransports.get(apnType);
     }
@@ -668,8 +673,8 @@
      * supported.)
      * @return The preferred transport.
      */
-    public @TransportType int getPreferredTransportByNetworkCapability(
-            @NetCapability int networkCapability) {
+    @TransportType
+    public int getPreferredTransportByNetworkCapability(@NetCapability int networkCapability) {
         int apnType = DataUtils.networkCapabilityToApnType(networkCapability);
         // For non-APN type capabilities, always route to WWAN.
         if (apnType == ApnSetting.TYPE_NONE) {
diff --git a/src/java/com/android/internal/telephony/data/AutoDataSwitchController.java b/src/java/com/android/internal/telephony/data/AutoDataSwitchController.java
index 343bb0b..10371ab 100644
--- a/src/java/com/android/internal/telephony/data/AutoDataSwitchController.java
+++ b/src/java/com/android/internal/telephony/data/AutoDataSwitchController.java
@@ -137,17 +137,26 @@
     private static final long RETRY_LONG_DELAY_TIMER_THRESHOLD_MILLIS = TimeUnit
             .MINUTES.toMillis(1);
 
-    private final @NonNull LocalLog mLocalLog = new LocalLog(128);
-    private final @NonNull Context mContext;
-    private static @NonNull FeatureFlags sFeatureFlags = new FeatureFlagsImpl();
-    private final @NonNull SubscriptionManagerService mSubscriptionManagerService;
-    private final @NonNull PhoneSwitcher mPhoneSwitcher;
-    private final @NonNull AutoDataSwitchControllerCallback mPhoneSwitcherCallback;
-    private final @NonNull AlarmManager mAlarmManager;
+    @NonNull
+    private final LocalLog mLocalLog = new LocalLog(128);
+    @NonNull
+    private final Context mContext;
+    @NonNull
+    private static FeatureFlags sFeatureFlags = new FeatureFlagsImpl();
+    @NonNull
+    private final SubscriptionManagerService mSubscriptionManagerService;
+    @NonNull
+    private final PhoneSwitcher mPhoneSwitcher;
+    @NonNull
+    private final AutoDataSwitchControllerCallback mPhoneSwitcherCallback;
+    @NonNull
+    private final AlarmManager mAlarmManager;
     /** A map of a scheduled event to its associated extra for action when the event fires off. */
-    private final @NonNull Map<Integer, Object> mScheduledEventsToExtras;
+    @NonNull
+    private final Map<Integer, Object> mScheduledEventsToExtras;
     /** A map of an event to its associated alarm listener callback for when the event fires off. */
-    private final @NonNull Map<Integer, AlarmManager.OnAlarmListener> mEventsToAlarmListener;
+    @NonNull
+    private final Map<Integer, AlarmManager.OnAlarmListener> mEventsToAlarmListener;
     /**
      * Event extras for checking environment stability.
      * @param targetPhoneId The target phone Id to switch to when the stability check pass.
@@ -193,7 +202,7 @@
      * To indicate whether allow using roaming nDDS if user enabled its roaming when the DDS is not
      * usable(OOS or disabled roaming)
      */
-    private boolean mAllowNddsRoamning = true;
+    private boolean mAllowNddsRoaming = true;
     /** The count of consecutive auto switch validation failure **/
     private int mAutoSwitchValidationFailedCount = 0;
     /**
@@ -202,7 +211,8 @@
     private int mAutoDataSwitchValidationMaxRetry;
 
     /** The signal status of phones, where index corresponds to phone Id. */
-    private @NonNull PhoneSignalStatus[] mPhonesSignalStatus;
+    @NonNull
+    private PhoneSignalStatus[] mPhonesSignalStatus;
     /**
      * The phone Id of the pending switching phone. Used for pruning frequent switch evaluation.
      */
@@ -268,23 +278,24 @@
             boolean isUsingNonTerrestrialNetwork = sFeatureFlags.carrierEnabledSatelliteFlag()
                     && (serviceState != null) && serviceState.isUsingNonTerrestrialNetwork();
 
-            switch (mDataRegState) {
-                case NetworkRegistrationInfo.REGISTRATION_STATE_HOME:
+            return switch (mDataRegState) {
+                case NetworkRegistrationInfo.REGISTRATION_STATE_HOME -> {
                     if (isUsingNonTerrestrialNetwork) {
-                        return UsableState.NON_TERRESTRIAL;
+                        yield UsableState.NON_TERRESTRIAL;
                     }
-                    return UsableState.HOME;
-                case NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING:
+                    yield UsableState.HOME;
+                }
+                case NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING -> {
                     if (mPhone.getDataRoamingEnabled()) {
                         if (isUsingNonTerrestrialNetwork) {
-                            return UsableState.NON_TERRESTRIAL;
+                            yield UsableState.NON_TERRESTRIAL;
                         }
-                        return UsableState.ROAMING_ENABLED;
+                        yield UsableState.ROAMING_ENABLED;
                     }
-                    return UsableState.NOT_USABLE;
-                default:
-                    return UsableState.NOT_USABLE;
-            }
+                    yield UsableState.NOT_USABLE;
+                }
+                default -> UsableState.NOT_USABLE;
+            };
         }
 
         @Override
@@ -450,7 +461,7 @@
         DataConfigManager dataConfig = phone.getDataNetworkController().getDataConfigManager();
         mScoreTolerance =  dataConfig.getAutoDataSwitchScoreTolerance();
         mRequirePingTestBeforeSwitch = dataConfig.isPingTestBeforeAutoDataSwitchRequired();
-        mAllowNddsRoamning = dataConfig.doesAutoDataSwitchAllowRoaming();
+        mAllowNddsRoaming = dataConfig.doesAutoDataSwitchAllowRoaming();
         mAutoDataSwitchAvailabilityStabilityTimeThreshold =
                 dataConfig.getAutoDataSwitchAvailabilityStabilityTimeThreshold();
         mAutoDataSwitchPerformanceStabilityTimeThreshold =
@@ -929,7 +940,7 @@
      * @return {@code true} If the feature of switching to roaming non DDS is enabled.
      */
     private boolean isNddsRoamingEnabled() {
-        return sFeatureFlags.autoDataSwitchAllowRoaming() && mAllowNddsRoamning;
+        return sFeatureFlags.autoDataSwitchAllowRoaming() && mAllowNddsRoaming;
     }
 
     /**
@@ -1012,19 +1023,20 @@
     }
 
     /** Auto data switch evaluation reason to string. */
-    public static @NonNull String evaluationReasonToString(
+    @NonNull
+    public static String evaluationReasonToString(
             @AutoDataSwitchEvaluationReason int reason) {
-        switch (reason) {
-            case EVALUATION_REASON_REGISTRATION_STATE_CHANGED: return "REGISTRATION_STATE_CHANGED";
-            case EVALUATION_REASON_DISPLAY_INFO_CHANGED: return "DISPLAY_INFO_CHANGED";
-            case EVALUATION_REASON_SIGNAL_STRENGTH_CHANGED: return "SIGNAL_STRENGTH_CHANGED";
-            case EVALUATION_REASON_DEFAULT_NETWORK_CHANGED: return "DEFAULT_NETWORK_CHANGED";
-            case EVALUATION_REASON_DATA_SETTINGS_CHANGED: return "DATA_SETTINGS_CHANGED";
-            case EVALUATION_REASON_RETRY_VALIDATION: return "RETRY_VALIDATION";
-            case EVALUATION_REASON_SIM_LOADED: return "SIM_LOADED";
-            case EVALUATION_REASON_VOICE_CALL_END: return "VOICE_CALL_END";
-        }
-        return "Unknown(" + reason + ")";
+        return switch (reason) {
+            case EVALUATION_REASON_REGISTRATION_STATE_CHANGED -> "REGISTRATION_STATE_CHANGED";
+            case EVALUATION_REASON_DISPLAY_INFO_CHANGED -> "DISPLAY_INFO_CHANGED";
+            case EVALUATION_REASON_SIGNAL_STRENGTH_CHANGED -> "SIGNAL_STRENGTH_CHANGED";
+            case EVALUATION_REASON_DEFAULT_NETWORK_CHANGED -> "DEFAULT_NETWORK_CHANGED";
+            case EVALUATION_REASON_DATA_SETTINGS_CHANGED -> "DATA_SETTINGS_CHANGED";
+            case EVALUATION_REASON_RETRY_VALIDATION -> "RETRY_VALIDATION";
+            case EVALUATION_REASON_SIM_LOADED -> "SIM_LOADED";
+            case EVALUATION_REASON_VOICE_CALL_END -> "VOICE_CALL_END";
+            default -> "Unknown(" + reason + ")";
+        };
     }
 
     /** @return {@code true} if the sub is active. */
@@ -1085,8 +1097,9 @@
      * @param isDueToAutoSwitch {@code true} if the switch was due to auto data switch feature.
      */
     public void displayAutoDataSwitchNotification(int phoneId, boolean isDueToAutoSwitch) {
-        NotificationManager notificationManager = (NotificationManager)
-                mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+        NotificationManager notificationManager = mContext.getSystemService(
+                NotificationManager.class);
+        if (notificationManager == null) return;
         if (mDisplayedNotification) {
             // cancel posted notification if any exist
             notificationManager.cancel(AUTO_DATA_SWITCH_NOTIFICATION_TAG,
diff --git a/src/java/com/android/internal/telephony/data/CellularDataService.java b/src/java/com/android/internal/telephony/data/CellularDataService.java
index 80d6b53..a75d4df 100644
--- a/src/java/com/android/internal/telephony/data/CellularDataService.java
+++ b/src/java/com/android/internal/telephony/data/CellularDataService.java
@@ -18,6 +18,7 @@
 
 import static android.telephony.data.DataServiceCallback.RESULT_SUCCESS;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.net.LinkProperties;
 import android.os.AsyncResult;
@@ -41,6 +42,7 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 
 /**
  * This class represents cellular data service which handles telephony data requests and response
@@ -61,6 +63,7 @@
     private static final int CANCEL_HANDOVER                        = 8;
     private static final int APN_UNTHROTTLED                        = 9;
 
+    @SuppressWarnings("unchecked")
     private class CellularDataServiceProvider extends DataService.DataServiceProvider {
 
         private final Map<Message, DataServiceCallback> mCallbackMap = new HashMap<>();
@@ -69,14 +72,15 @@
 
         private final Phone mPhone;
 
+        @SuppressWarnings("unchecked")
         private CellularDataServiceProvider(int slotId) {
             super(slotId);
 
             mPhone = PhoneFactory.getPhone(getSlotIndex());
 
-            mHandler = new Handler(Looper.myLooper()) {
+            mHandler = new Handler(Objects.requireNonNull(Looper.myLooper())) {
                 @Override
-                public void handleMessage(Message message) {
+                public void handleMessage(@NonNull Message message) {
                     DataServiceCallback callback = mCallbackMap.remove(message);
 
                     AsyncResult ar = (AsyncResult) message.obj;
@@ -147,8 +151,7 @@
             if (t == null) {
                 return RESULT_SUCCESS;
             } else {
-                if (t instanceof CommandException) {
-                    CommandException ce = (CommandException) t;
+                if (t instanceof CommandException ce) {
                     if (ce.getCommandError() == CommandException.Error.REQUEST_NOT_SUPPORTED) {
                         return DataServiceCallback.RESULT_ERROR_UNSUPPORTED;
                     } else {
@@ -163,10 +166,10 @@
         }
 
         @Override
-        public void setupDataCall(int accessNetworkType, DataProfile dataProfile,
+        public void setupDataCall(int accessNetworkType, @NonNull DataProfile dataProfile,
                 boolean isRoaming, boolean allowRoaming, int reason, LinkProperties linkProperties,
                 int pduSessionId, NetworkSliceInfo sliceInfo, TrafficDescriptor trafficDescriptor,
-                boolean matchAllRuleAllowed, DataServiceCallback callback) {
+                boolean matchAllRuleAllowed, @Nullable DataServiceCallback callback) {
             // TODO: remove isRoaming parameter
             if (DBG) log("setupDataCall " + getSlotIndex());
 
@@ -199,8 +202,8 @@
         }
 
         @Override
-        public void setInitialAttachApn(DataProfile dataProfile, boolean isRoaming,
-                DataServiceCallback callback) {
+        public void setInitialAttachApn(@NonNull DataProfile dataProfile, boolean isRoaming,
+                @Nullable DataServiceCallback callback) {
             // TODO: remove isRoaming parameter
             if (DBG) log("setInitialAttachApn " + getSlotIndex());
 
@@ -216,8 +219,8 @@
         }
 
         @Override
-        public void setDataProfile(List<DataProfile> dps, boolean isRoaming,
-                DataServiceCallback callback) {
+        public void setDataProfile(@NonNull List<DataProfile> dps, boolean isRoaming,
+                @Nullable DataServiceCallback callback) {
             // TODO: remove isRoaming parameter
             if (DBG) log("setDataProfile " + getSlotIndex());
 
@@ -229,11 +232,11 @@
                 mCallbackMap.put(message, callback);
             }
 
-            mPhone.mCi.setDataProfile(dps.toArray(new DataProfile[dps.size()]), message);
+            mPhone.mCi.setDataProfile(dps.toArray(new DataProfile[0]), message);
         }
 
         @Override
-        public void requestDataCallList(DataServiceCallback callback) {
+        public void requestDataCallList(@Nullable DataServiceCallback callback) {
             if (DBG) log("requestDataCallList " + getSlotIndex());
 
             Message message = null;
@@ -247,7 +250,7 @@
         }
 
         @Override
-        public void startHandover(int cid, DataServiceCallback callback) {
+        public void startHandover(int cid, @Nullable DataServiceCallback callback) {
             if (DBG) log("startHandover " + getSlotIndex());
             Message message = null;
             // Only obtain the message when the caller wants a callback. If the caller doesn't care
@@ -260,7 +263,7 @@
         }
 
         @Override
-        public void cancelHandover(int cid, DataServiceCallback callback) {
+        public void cancelHandover(int cid, @Nullable DataServiceCallback callback) {
             Message message = null;
             // Only obtain the message when the caller wants a callback. If the caller doesn't care
             // the request completed or results, then no need to pass the message down.
diff --git a/src/java/com/android/internal/telephony/data/CellularNetworkValidator.java b/src/java/com/android/internal/telephony/data/CellularNetworkValidator.java
index c1d1203..ad1a8aa 100644
--- a/src/java/com/android/internal/telephony/data/CellularNetworkValidator.java
+++ b/src/java/com/android/internal/telephony/data/CellularNetworkValidator.java
@@ -20,6 +20,7 @@
 import static android.telephony.CarrierConfigManager.KEY_DATA_SWITCH_VALIDATION_MIN_INTERVAL_MILLIS_LONG;
 import static android.telephony.NetworkRegistrationInfo.DOMAIN_PS;
 
+import android.annotation.NonNull;
 import android.content.Context;
 import android.net.ConnectivityManager;
 import android.net.Network;
@@ -58,9 +59,6 @@
  */
 public class CellularNetworkValidator {
     private static final String LOG_TAG = "NetworkValidator";
-    // If true, upon validated network cache hit, we report validationDone only when
-    // network becomes available. Otherwise, we report validationDone immediately.
-    private static boolean sWaitForNetworkAvailableWhenCacheHit = true;
 
     // States of validator. Only one validation can happen at once.
     // IDLE: no validation going on.
@@ -69,7 +67,7 @@
     private static final int STATE_VALIDATING          = 1;
     // VALIDATED: validation is done and successful.
     // Waiting for stopValidation() to release
-    // validationg NetworkRequest.
+    // validation NetworkRequest.
     private static final int STATE_VALIDATED           = 2;
 
     // Singleton instance.
@@ -79,13 +77,11 @@
 
     private int mState = STATE_IDLE;
     private int mSubId;
-    private long mTimeoutInMs;
     private boolean mReleaseAfterValidation;
 
-    private NetworkRequest mNetworkRequest;
     private ValidationCallback mValidationCallback;
-    private Context mContext;
-    private ConnectivityManager mConnectivityManager;
+    private final Context mContext;
+    private final ConnectivityManager mConnectivityManager;
     @VisibleForTesting
     public Handler mHandler = new Handler();
     @VisibleForTesting
@@ -96,18 +92,11 @@
         // A cache with fixed size. It remembers 10 most recently successfully validated networks.
         private static final int VALIDATED_NETWORK_CACHE_SIZE = 10;
         private final PriorityQueue<ValidatedNetwork> mValidatedNetworkPQ =
-                new PriorityQueue((Comparator<ValidatedNetwork>) (n1, n2) -> {
-                    if (n1.mValidationTimeStamp < n2.mValidationTimeStamp) {
-                        return -1;
-                    } else if (n1.mValidationTimeStamp > n2.mValidationTimeStamp) {
-                        return 1;
-                    } else {
-                        return 0;
-                    }
-                });
-        private final Map<String, ValidatedNetwork> mValidatedNetworkMap = new HashMap();
+                new PriorityQueue<>((Comparator<ValidatedNetwork>) Comparator.comparingLong(
+                        (ValidatedNetwork n) -> n.mValidationTimeStamp));
+        private final Map<String, ValidatedNetwork> mValidatedNetworkMap = new HashMap<>();
 
-        private final class ValidatedNetwork {
+        private static final class ValidatedNetwork {
             ValidatedNetwork(String identity, long timeStamp) {
                 mValidationIdentity = identity;
                 mValidationTimeStamp = timeStamp;
@@ -165,7 +154,6 @@
 
         private String getValidationNetworkIdentity(int subId) {
             if (!SubscriptionManager.isUsableSubscriptionId(subId)) return null;
-            if (SubscriptionManagerService.getInstance() == null) return null;
             Phone phone = PhoneFactory.getPhone(SubscriptionManagerService.getInstance()
                     .getPhoneId(subId));
             if (phone == null || phone.getServiceState() == null) return null;
@@ -270,26 +258,18 @@
             stopValidation();
         }
 
-        if (!sWaitForNetworkAvailableWhenCacheHit && mValidatedNetworkCache
-                .isRecentlyValidated(subId)) {
-            callback.onValidationDone(true, subId);
-            return;
-        }
-
         mState = STATE_VALIDATING;
         mSubId = subId;
-        mTimeoutInMs = timeoutInMs;
         mValidationCallback = callback;
         mReleaseAfterValidation = releaseAfterValidation;
-        mNetworkRequest = createNetworkRequest();
 
-        logd("Start validating subId " + mSubId + " mTimeoutInMs " + mTimeoutInMs
+        logd("Start validating subId " + mSubId + " timeoutInMs " + timeoutInMs
                 + " mReleaseAfterValidation " + mReleaseAfterValidation);
 
         mNetworkCallback = new ConnectivityNetworkCallback(subId);
 
-        mConnectivityManager.requestNetwork(mNetworkRequest, mNetworkCallback, mHandler);
-        mHandler.postDelayed(() -> onValidationTimeout(subId), mTimeoutInMs);
+        mConnectivityManager.requestNetwork(createNetworkRequest(), mNetworkCallback, mHandler);
+        mHandler.postDelayed(() -> onValidationTimeout(subId), timeoutInMs);
     }
 
     private synchronized void onValidationTimeout(int subId) {
@@ -351,7 +331,7 @@
             mState = STATE_VALIDATED;
             // If validation passed and per request to NOT release after validation, delay cleanup.
             if (!mReleaseAfterValidation && passed) {
-                mHandler.postDelayed(()-> stopValidation(), 500);
+                mHandler.postDelayed(this::stopValidation, 500);
             } else {
                 stopValidation();
             }
@@ -379,7 +359,7 @@
          * ConnectivityManager.NetworkCallback implementation
          */
         @Override
-        public void onAvailable(Network network) {
+        public void onAvailable(@NonNull Network network) {
             logd("network onAvailable " + network);
             TelephonyMetrics.getInstance().writeNetworkValidate(
                     TelephonyEvent.NetworkValidationState.NETWORK_VALIDATION_STATE_AVAILABLE);
@@ -393,7 +373,7 @@
         }
 
         @Override
-        public void onLosing(Network network, int maxMsToLive) {
+        public void onLosing(@NonNull Network network, int maxMsToLive) {
             logd("network onLosing " + network + " maxMsToLive " + maxMsToLive);
             mValidatedNetworkCache.storeLastValidationResult(
                     ConnectivityNetworkCallback.this.mSubId, false);
@@ -401,7 +381,7 @@
         }
 
         @Override
-        public void onLost(Network network) {
+        public void onLost(@NonNull Network network) {
             logd("network onLost " + network);
             mValidatedNetworkCache.storeLastValidationResult(
                     ConnectivityNetworkCallback.this.mSubId, false);
@@ -417,7 +397,7 @@
         }
 
         @Override
-        public void onCapabilitiesChanged(Network network,
+        public void onCapabilitiesChanged(@NonNull Network network,
                 NetworkCapabilities networkCapabilities) {
             if (networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)) {
                 logd("onValidated");
diff --git a/src/java/com/android/internal/telephony/data/DataCallback.java b/src/java/com/android/internal/telephony/data/DataCallback.java
index 1fafe8c..05d1851 100644
--- a/src/java/com/android/internal/telephony/data/DataCallback.java
+++ b/src/java/com/android/internal/telephony/data/DataCallback.java
@@ -28,7 +28,8 @@
  */
 public class DataCallback {
     /** The executor of the callback. */
-    private final @NonNull Executor mExecutor;
+    @NonNull
+    private final Executor mExecutor;
 
     /**
      * Constructor
@@ -43,7 +44,8 @@
      * @return The executor of the callback.
      */
     @VisibleForTesting
-    public @NonNull Executor getExecutor() {
+    @NonNull
+    public Executor getExecutor() {
         return mExecutor;
     }
 
diff --git a/src/java/com/android/internal/telephony/data/DataConfigManager.java b/src/java/com/android/internal/telephony/data/DataConfigManager.java
index 6ad89ce..4177cc4 100644
--- a/src/java/com/android/internal/telephony/data/DataConfigManager.java
+++ b/src/java/com/android/internal/telephony/data/DataConfigManager.java
@@ -64,6 +64,7 @@
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.Executor;
+import java.util.concurrent.TimeUnit;
 import java.util.stream.Collectors;
 
 /**
@@ -163,6 +164,13 @@
     /** Network type NR_SA_MMWAVE. Should not be used outside of DataConfigManager. */
     private static final String DATA_CONFIG_NETWORK_TYPE_NR_SA_MMWAVE = "NR_SA_MMWAVE";
 
+    /**
+     * The delay in milliseconds to re-evaluate existing data networks for bootstrap sim data usage
+     * limit.
+     */
+    private static final long REEVALUATE_BOOTSTRAP_SIM_DATA_USAGE_MILLIS =
+            TimeUnit.SECONDS.toMillis(60);
+
     @StringDef(prefix = {"DATA_CONFIG_NETWORK_TYPE_"}, value = {
             DATA_CONFIG_NETWORK_TYPE_GPRS,
             DATA_CONFIG_NETWORK_TYPE_EDGE,
@@ -192,8 +200,8 @@
     private @interface DataConfigNetworkType {}
 
     /** Data config update callbacks. */
-    private final @NonNull Set<DataConfigManagerCallback> mDataConfigManagerCallbacks =
-            new ArraySet<>();
+    @NonNull
+    private final 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";
@@ -258,55 +266,77 @@
      */
     private boolean mIsApnConfigAnomalyReportEnabled;
 
-    private @NonNull final Phone mPhone;
-    private @NonNull final String mLogTag;
-
-    @NonNull private final FeatureFlags mFeatureFlags;
-    private @NonNull final CarrierConfigManager mCarrierConfigManager;
-    private @NonNull PersistableBundle mCarrierConfig = null;
-    private @NonNull Resources mResources = null;
+    @NonNull
+    private final Phone mPhone;
+    @NonNull
+    private final String mLogTag;
+    @NonNull
+    private final FeatureFlags mFeatureFlags;
+    @NonNull
+    private final CarrierConfigManager mCarrierConfigManager;
+    @NonNull
+    private PersistableBundle mCarrierConfig = null;
+    @NonNull
+    private Resources mResources = null;
 
     /** The network capability priority map */
-    private @NonNull final Map<Integer, Integer> mNetworkCapabilityPriorityMap =
-            new ConcurrentHashMap<>();
+    @NonNull
+    private final Map<Integer, Integer> mNetworkCapabilityPriorityMap = new ConcurrentHashMap<>();
     /** The data setup retry rules */
-    private @NonNull final List<DataSetupRetryRule> mDataSetupRetryRules = new ArrayList<>();
+    @NonNull
+    private final List<DataSetupRetryRule> mDataSetupRetryRules = new ArrayList<>();
     /** The data handover retry rules */
-    private @NonNull final List<DataHandoverRetryRule> mDataHandoverRetryRules = new ArrayList<>();
+    @NonNull
+    private final List<DataHandoverRetryRule> mDataHandoverRetryRules = new ArrayList<>();
     /** The metered APN types for home network */
-    private @NonNull final @ApnType Set<Integer> mMeteredApnTypes = new HashSet<>();
+    @NonNull
+    @ApnType
+    private final Set<Integer> mMeteredApnTypes = new HashSet<>();
     /** The metered APN types for roaming network */
-    private @NonNull final @ApnType Set<Integer> mRoamingMeteredApnTypes = new HashSet<>();
+    @NonNull
+    @ApnType
+    private final Set<Integer> mRoamingMeteredApnTypes = new HashSet<>();
     /** 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<>();
+    @NonNull
+    @NetworkType
+    private final List<Integer> mSingleDataNetworkTypeList = new ArrayList<>();
+    @NonNull
+    @NetCapability
+    private final Set<Integer> mCapabilitiesExemptFromSingleDataList = new HashSet<>();
     /** The network types that support temporarily not metered */
-    private @NonNull final @DataConfigNetworkType Set<String> mUnmeteredNetworkTypes =
-            new HashSet<>();
+    @NonNull
+    @DataConfigNetworkType
+    private final Set<String> mUnmeteredNetworkTypes = new HashSet<>();
     /** The network types that support temporarily not metered when roaming */
-    private @NonNull final @DataConfigNetworkType Set<String> mRoamingUnmeteredNetworkTypes =
-            new HashSet<>();
+    @NonNull
+    @DataConfigNetworkType
+    private final Set<String> mRoamingUnmeteredNetworkTypes = new HashSet<>();
     /** A map of network types to the downlink and uplink bandwidth values for that network type */
-    private @NonNull final @DataConfigNetworkType Map<String, DataNetwork.NetworkBandwidth>
-            mBandwidthMap = new ConcurrentHashMap<>();
-    /** A map of network types to the TCP buffer sizes for that network type */
-    private @NonNull final @DataConfigNetworkType Map<String, String> mTcpBufferSizeMap =
+    @NonNull
+    @DataConfigNetworkType
+    private final Map<String, DataNetwork.NetworkBandwidth> mBandwidthMap =
             new ConcurrentHashMap<>();
+    /** A map of network types to the TCP buffer sizes for that network type */
+    @NonNull
+    @DataConfigNetworkType
+    private final Map<String, String> mTcpBufferSizeMap = new ConcurrentHashMap<>();
     /** Rules for handover between IWLAN and cellular network. */
-    private @NonNull final List<HandoverRule> mHandoverRuleList = new ArrayList<>();
+    @NonNull
+    private 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;
     /** The set of network types that enable VOPS even in non VOPS area. */
-    @NonNull private final @CarrierConfigManager.Ims.NetworkType List<Integer>
-            mEnabledVopsNetworkTypesInNonVops = new ArrayList<>();
+    @NonNull
+    @CarrierConfigManager.Ims.NetworkType
+    private final List<Integer> mEnabledVopsNetworkTypesInNonVops = new ArrayList<>();
     /**
      * A map of network types to the estimated downlink values by signal strength 0 - 4 for that
      * network type
      */
-    private @NonNull final @DataConfigNetworkType Map<String, int[]>
-            mAutoDataSwitchNetworkTypeSignalMap = new ConcurrentHashMap<>();
+    @NonNull
+    @DataConfigNetworkType
+    private final Map<String, int[]> mAutoDataSwitchNetworkTypeSignalMap =
+            new ConcurrentHashMap<>();
 
     /**
      * Constructor
@@ -546,14 +576,16 @@
     /**
      * @return The data setup retry rules from carrier config.
      */
-    public @NonNull List<DataSetupRetryRule> getDataSetupRetryRules() {
+    @NonNull
+    public List<DataSetupRetryRule> getDataSetupRetryRules() {
         return Collections.unmodifiableList(mDataSetupRetryRules);
     }
 
     /**
      * @return The data handover retry rules from carrier config.
      */
-    public @NonNull List<DataHandoverRetryRule> getDataHandoverRetryRules() {
+    @NonNull
+    public List<DataHandoverRetryRule> getDataHandoverRetryRules() {
         return Collections.unmodifiableList(mDataHandoverRetryRules);
     }
 
@@ -596,7 +628,9 @@
      *
      * @return The metered network capabilities when connected to a home network.
      */
-    public @NonNull @NetCapability Set<Integer> getMeteredNetworkCapabilities(boolean isRoaming) {
+    @NonNull
+    @NetCapability
+    public Set<Integer> getMeteredNetworkCapabilities(boolean isRoaming) {
         Set<Integer> meteredApnTypes = isRoaming ? mRoamingMeteredApnTypes : mMeteredApnTypes;
         Set<Integer> meteredCapabilities = meteredApnTypes.stream()
                 .map(DataUtils::apnTypeToNetworkCapability)
@@ -695,7 +729,9 @@
     /**
      * @return The list of {@link NetworkType} that only supports single data networks
      */
-    public @NonNull @NetworkType List<Integer> getNetworkTypesOnlySupportSingleDataNetwork() {
+    @NonNull
+    @NetworkType
+    public List<Integer> getNetworkTypesOnlySupportSingleDataNetwork() {
         return Collections.unmodifiableList(mSingleDataNetworkTypeList);
     }
 
@@ -703,7 +739,9 @@
      * @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() {
+    @NonNull
+    @NetCapability
+    public Set<Integer> getCapabilitiesExemptFromSingleDataNetwork() {
         return Collections.unmodifiableSet(mCapabilitiesExemptFromSingleDataList);
     }
 
@@ -836,7 +874,8 @@
      * @param displayInfo The {@link TelephonyDisplayInfo} to get the bandwidth for.
      * @return The pre-configured bandwidth estimate from carrier config.
      */
-    public @NonNull DataNetwork.NetworkBandwidth getBandwidthForNetworkType(
+    @NonNull
+    public DataNetwork.NetworkBandwidth getBandwidthForNetworkType(
             @NonNull TelephonyDisplayInfo displayInfo) {
         DataNetwork.NetworkBandwidth bandwidth = mBandwidthMap.get(
                 getDataConfigNetworkType(displayInfo));
@@ -878,6 +917,21 @@
     }
 
     /**
+     * @return the interval in millisecond used to re-evaluate bootstrap sim data usage during esim
+     * bootstrap activation
+     */
+    public long getReevaluateBootstrapSimDataUsageMillis() {
+        long bootStrapSimDataUsageReevaluateInterval = mResources.getInteger(
+                com.android.internal.R.integer.config_reevaluate_bootstrap_sim_data_usage_millis);
+
+        if (bootStrapSimDataUsageReevaluateInterval <= 0) {
+            bootStrapSimDataUsageReevaluateInterval = REEVALUATE_BOOTSTRAP_SIM_DATA_USAGE_MILLIS;
+        }
+
+        return bootStrapSimDataUsageReevaluateInterval;
+    }
+
+    /**
      * Update the TCP buffer sizes from the resource overlays.
      */
     private void updateTcpBuffers() {
@@ -908,7 +962,8 @@
      * Anomaly report thresholds for frequent setup data call failure.
      * @return EventFrequency to trigger the anomaly report
      */
-    public @NonNull EventFrequency getAnomalySetupDataCallThreshold() {
+    @NonNull
+    public EventFrequency getAnomalySetupDataCallThreshold() {
         return mSetupDataCallAnomalyReportThreshold;
     }
 
@@ -917,7 +972,8 @@
      * at {@link TelephonyNetworkAgent#onNetworkUnwanted}
      * @return EventFrequency to trigger the anomaly report
      */
-    public @NonNull EventFrequency getAnomalyNetworkUnwantedThreshold() {
+    @NonNull
+    public EventFrequency getAnomalyNetworkUnwantedThreshold() {
         return mNetworkUnwantedAnomalyReportThreshold;
     }
 
@@ -925,7 +981,8 @@
      * Anomaly report thresholds for back to back release-request of IMS.
      * @return EventFrequency to trigger the anomaly report
      */
-    public @NonNull EventFrequency getAnomalyImsReleaseRequestThreshold() {
+    @NonNull
+    public EventFrequency getAnomalyImsReleaseRequestThreshold() {
         return mImsReleaseRequestAnomalyReportThreshold;
     }
 
@@ -1096,7 +1153,8 @@
      * @return The TCP configuration string for the given display info or the default value from
      *         {@code config_tcp_buffers} if unavailable.
      */
-    public @NonNull String getTcpConfigString(@NonNull TelephonyDisplayInfo displayInfo) {
+    @NonNull
+    public String getTcpConfigString(@NonNull TelephonyDisplayInfo displayInfo) {
         String config = mTcpBufferSizeMap.get(getDataConfigNetworkType(displayInfo));
         if (TextUtils.isEmpty(config)) {
             config = getDefaultTcpConfigString();
@@ -1107,7 +1165,8 @@
     /**
      * @return The fixed TCP buffer size configured based on the device's memory and performance.
      */
-    public @NonNull String getDefaultTcpConfigString() {
+    @NonNull
+    public String getDefaultTcpConfigString() {
         return mResources.getString(com.android.internal.R.string.config_tcp_buffers);
     }
 
@@ -1150,20 +1209,21 @@
     /**
      * @return The bandwidth estimation source.
      */
-    public @DataNetwork.BandwidthEstimationSource int getBandwidthEstimateSource() {
+    @DataNetwork.BandwidthEstimationSource
+    public int getBandwidthEstimateSource() {
         String source = mResources.getString(
                 com.android.internal.R.string.config_bandwidthEstimateSource);
-        switch (source) {
-            case BANDWIDTH_SOURCE_MODEM_STRING_VALUE:
-                return DataNetwork.BANDWIDTH_SOURCE_MODEM;
-            case BANDWIDTH_SOURCE_CARRIER_CONFIG_STRING_VALUE:
-                return DataNetwork.BANDWIDTH_SOURCE_CARRIER_CONFIG;
-            case BANDWIDTH_SOURCE_BANDWIDTH_ESTIMATOR_STRING_VALUE:
-                return DataNetwork.BANDWIDTH_SOURCE_BANDWIDTH_ESTIMATOR;
-            default:
+        return switch (source) {
+            case BANDWIDTH_SOURCE_MODEM_STRING_VALUE -> DataNetwork.BANDWIDTH_SOURCE_MODEM;
+            case BANDWIDTH_SOURCE_CARRIER_CONFIG_STRING_VALUE ->
+                    DataNetwork.BANDWIDTH_SOURCE_CARRIER_CONFIG;
+            case BANDWIDTH_SOURCE_BANDWIDTH_ESTIMATOR_STRING_VALUE ->
+                    DataNetwork.BANDWIDTH_SOURCE_BANDWIDTH_ESTIMATOR;
+            default -> {
                 loge("Invalid bandwidth estimation source config: " + source);
-                return DataNetwork.BANDWIDTH_SOURCE_UNKNOWN;
-        }
+                yield DataNetwork.BANDWIDTH_SOURCE_UNKNOWN;
+            }
+        };
     }
 
     /**
@@ -1172,8 +1232,9 @@
      * @param displayInfo The {@link TelephonyDisplayInfo} used to determine the type.
      * @return The equivalent {@link DataConfigNetworkType}.
      */
-    private static @NonNull @DataConfigNetworkType String getDataConfigNetworkType(
-            @NonNull TelephonyDisplayInfo displayInfo) {
+    @NonNull
+    @DataConfigNetworkType
+    private static String getDataConfigNetworkType(@NonNull TelephonyDisplayInfo displayInfo) {
         int networkType = displayInfo.getNetworkType();
         switch (displayInfo.getOverrideNetworkType()) {
             case TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED:
@@ -1282,7 +1343,8 @@
      *
      * @see CarrierConfigManager#KEY_IWLAN_HANDOVER_POLICY_STRING_ARRAY
      */
-    public @NonNull List<HandoverRule> getHandoverRules() {
+    @NonNull
+    public List<HandoverRule> getHandoverRules() {
         return Collections.unmodifiableList(mHandoverRuleList);
     }
 
@@ -1300,52 +1362,33 @@
      * @param networkType The network type
      * @return The equivalent data config network type
      */
-    private static @NonNull @DataConfigNetworkType String networkTypeToDataConfigNetworkType(
+    @NonNull
+    @DataConfigNetworkType
+    private static String networkTypeToDataConfigNetworkType(
             @NetworkType int networkType) {
-        switch (networkType) {
-            case TelephonyManager.NETWORK_TYPE_GPRS:
-                return DATA_CONFIG_NETWORK_TYPE_GPRS;
-            case TelephonyManager.NETWORK_TYPE_EDGE:
-                return DATA_CONFIG_NETWORK_TYPE_EDGE;
-            case TelephonyManager.NETWORK_TYPE_UMTS:
-                return DATA_CONFIG_NETWORK_TYPE_UMTS;
-            case TelephonyManager.NETWORK_TYPE_HSDPA:
-                return DATA_CONFIG_NETWORK_TYPE_HSDPA;
-            case TelephonyManager.NETWORK_TYPE_HSUPA:
-                return DATA_CONFIG_NETWORK_TYPE_HSUPA;
-            case TelephonyManager.NETWORK_TYPE_HSPA:
-                return DATA_CONFIG_NETWORK_TYPE_HSPA;
-            case TelephonyManager.NETWORK_TYPE_CDMA:
-                return DATA_CONFIG_NETWORK_TYPE_CDMA;
-            case TelephonyManager.NETWORK_TYPE_EVDO_0:
-                return DATA_CONFIG_NETWORK_TYPE_EVDO_0;
-            case TelephonyManager.NETWORK_TYPE_EVDO_A:
-                return DATA_CONFIG_NETWORK_TYPE_EVDO_A;
-            case TelephonyManager.NETWORK_TYPE_EVDO_B:
-                return DATA_CONFIG_NETWORK_TYPE_EVDO_B;
-            case TelephonyManager.NETWORK_TYPE_1xRTT:
-                return DATA_CONFIG_NETWORK_TYPE_1xRTT;
-            case TelephonyManager.NETWORK_TYPE_LTE:
-                return DATA_CONFIG_NETWORK_TYPE_LTE;
-            case TelephonyManager.NETWORK_TYPE_EHRPD:
-                return DATA_CONFIG_NETWORK_TYPE_EHRPD;
-            case TelephonyManager.NETWORK_TYPE_IDEN:
-                return DATA_CONFIG_NETWORK_TYPE_IDEN;
-            case TelephonyManager.NETWORK_TYPE_HSPAP:
-                return DATA_CONFIG_NETWORK_TYPE_HSPAP;
-            case TelephonyManager.NETWORK_TYPE_GSM:
-                return DATA_CONFIG_NETWORK_TYPE_GSM;
-            case TelephonyManager.NETWORK_TYPE_TD_SCDMA:
-                return DATA_CONFIG_NETWORK_TYPE_TD_SCDMA;
-            case TelephonyManager.NETWORK_TYPE_IWLAN:
-                return DATA_CONFIG_NETWORK_TYPE_IWLAN;
-            case TelephonyManager.NETWORK_TYPE_LTE_CA:
-                return DATA_CONFIG_NETWORK_TYPE_LTE_CA;
-            case TelephonyManager.NETWORK_TYPE_NR:
-                return DATA_CONFIG_NETWORK_TYPE_NR_SA;
-            default:
-                return "";
-        }
+        return switch (networkType) {
+            case TelephonyManager.NETWORK_TYPE_GPRS -> DATA_CONFIG_NETWORK_TYPE_GPRS;
+            case TelephonyManager.NETWORK_TYPE_EDGE -> DATA_CONFIG_NETWORK_TYPE_EDGE;
+            case TelephonyManager.NETWORK_TYPE_UMTS -> DATA_CONFIG_NETWORK_TYPE_UMTS;
+            case TelephonyManager.NETWORK_TYPE_HSDPA -> DATA_CONFIG_NETWORK_TYPE_HSDPA;
+            case TelephonyManager.NETWORK_TYPE_HSUPA -> DATA_CONFIG_NETWORK_TYPE_HSUPA;
+            case TelephonyManager.NETWORK_TYPE_HSPA -> DATA_CONFIG_NETWORK_TYPE_HSPA;
+            case TelephonyManager.NETWORK_TYPE_CDMA -> DATA_CONFIG_NETWORK_TYPE_CDMA;
+            case TelephonyManager.NETWORK_TYPE_EVDO_0 -> DATA_CONFIG_NETWORK_TYPE_EVDO_0;
+            case TelephonyManager.NETWORK_TYPE_EVDO_A -> DATA_CONFIG_NETWORK_TYPE_EVDO_A;
+            case TelephonyManager.NETWORK_TYPE_EVDO_B -> DATA_CONFIG_NETWORK_TYPE_EVDO_B;
+            case TelephonyManager.NETWORK_TYPE_1xRTT -> DATA_CONFIG_NETWORK_TYPE_1xRTT;
+            case TelephonyManager.NETWORK_TYPE_LTE -> DATA_CONFIG_NETWORK_TYPE_LTE;
+            case TelephonyManager.NETWORK_TYPE_EHRPD -> DATA_CONFIG_NETWORK_TYPE_EHRPD;
+            case TelephonyManager.NETWORK_TYPE_IDEN -> DATA_CONFIG_NETWORK_TYPE_IDEN;
+            case TelephonyManager.NETWORK_TYPE_HSPAP -> DATA_CONFIG_NETWORK_TYPE_HSPAP;
+            case TelephonyManager.NETWORK_TYPE_GSM -> DATA_CONFIG_NETWORK_TYPE_GSM;
+            case TelephonyManager.NETWORK_TYPE_TD_SCDMA -> DATA_CONFIG_NETWORK_TYPE_TD_SCDMA;
+            case TelephonyManager.NETWORK_TYPE_IWLAN -> DATA_CONFIG_NETWORK_TYPE_IWLAN;
+            case TelephonyManager.NETWORK_TYPE_LTE_CA -> DATA_CONFIG_NETWORK_TYPE_LTE_CA;
+            case TelephonyManager.NETWORK_TYPE_NR -> DATA_CONFIG_NETWORK_TYPE_NR_SA;
+            default -> "";
+        };
     }
 
     /**
@@ -1353,7 +1396,8 @@
      *
      * @see CarrierConfigManager#KEY_DATA_STALL_RECOVERY_TIMERS_LONG_ARRAY
      */
-    public @NonNull long[] getDataStallRecoveryDelayMillis() {
+    @NonNull
+    public long[] getDataStallRecoveryDelayMillis() {
         return mCarrierConfig.getLongArray(
             CarrierConfigManager.KEY_DATA_STALL_RECOVERY_TIMERS_LONG_ARRAY);
     }
@@ -1363,7 +1407,8 @@
      *
      * @see CarrierConfigManager#KEY_DATA_STALL_RECOVERY_SHOULD_SKIP_BOOL_ARRAY
      */
-    public @NonNull boolean[] getDataStallRecoveryShouldSkipArray() {
+    @NonNull
+    public boolean[] getDataStallRecoveryShouldSkipArray() {
         return mCarrierConfig.getBooleanArray(
             CarrierConfigManager.KEY_DATA_STALL_RECOVERY_SHOULD_SKIP_BOOL_ARRAY);
     }
@@ -1372,7 +1417,8 @@
      * @return The default preferred APN. An empty string if not configured. This is used for the
      * first time boot up where preferred APN is not set.
      */
-    public @NonNull String getDefaultPreferredApn() {
+    @NonNull
+    public String getDefaultPreferredApn() {
         return TextUtils.emptyIfNull(mCarrierConfig.getString(
                 CarrierConfigManager.KEY_DEFAULT_PREFERRED_APN_NAME_STRING));
     }
@@ -1391,7 +1437,9 @@
      * cases that we need to use "user-added" APN for initial attach. The regular way to configure
      * IA APN is by adding "IA" type to the APN in APN config.
      */
-    public @NonNull @ApnType List<Integer> getAllowedInitialAttachApnTypes() {
+    @NonNull
+    @ApnType
+    public List<Integer> getAllowedInitialAttachApnTypes() {
         String[] apnTypesArray = mCarrierConfig.getStringArray(
                 CarrierConfigManager.KEY_ALLOWED_INITIAL_ATTACH_APN_TYPES_STRING_ARRAY);
         if (apnTypesArray != null) {
@@ -1438,7 +1486,8 @@
     /**
      * @return The capabilities that network will be forced to mark as cellular transport.
      */
-    public @NetCapability Set<Integer> getForcedCellularTransportCapabilities() {
+    @NetCapability
+    public Set<Integer> getForcedCellularTransportCapabilities() {
         String[] forcedCellularTransportCapabilities = mResources.getStringArray(
                 com.android.internal.R.array.config_force_cellular_transport_capabilities);
 
diff --git a/src/java/com/android/internal/telephony/data/DataEvaluation.java b/src/java/com/android/internal/telephony/data/DataEvaluation.java
index a54884f..f5eae91 100644
--- a/src/java/com/android/internal/telephony/data/DataEvaluation.java
+++ b/src/java/com/android/internal/telephony/data/DataEvaluation.java
@@ -36,25 +36,30 @@
  */
 public class DataEvaluation {
     /** The reason for this evaluation */
-    private final @NonNull DataEvaluationReason mDataEvaluationReason;
+    @NonNull
+    private final DataEvaluationReason mDataEvaluationReason;
 
     /** Data disallowed reasons. There could be multiple reasons for not allowing data. */
-    private final @NonNull Set<DataDisallowedReason> mDataDisallowedReasons = new HashSet<>();
+    @NonNull
+    private final Set<DataDisallowedReason> mDataDisallowedReasons = new HashSet<>();
 
     /** Data allowed reason. It is intended to only have one allowed reason. */
-    private @NonNull DataAllowedReason mDataAllowedReason = DataAllowedReason.NONE;
+    @NonNull
+    private DataAllowedReason mDataAllowedReason = DataAllowedReason.NONE;
 
-    private @Nullable DataProfile mCandidateDataProfile = null;
+    @Nullable
+    private DataProfile mCandidateDataProfile = null;
 
     /** The timestamp of evaluation time */
-    private @CurrentTimeMillisLong long mEvaluatedTime = 0;
+    @CurrentTimeMillisLong
+    private long mEvaluatedTime = 0;
 
     /**
      * Constructor
      *
      * @param reason The reason for this evaluation.
      */
-    public DataEvaluation(DataEvaluationReason reason) {
+    public DataEvaluation(@NonNull DataEvaluationReason reason) {
         mDataEvaluationReason = reason;
     }
 
@@ -100,14 +105,16 @@
     /**
      * @return List of data disallowed reasons.
      */
-    public @NonNull List<DataDisallowedReason> getDataDisallowedReasons() {
+    @NonNull
+    public List<DataDisallowedReason> getDataDisallowedReasons() {
         return new ArrayList<>(mDataDisallowedReasons);
     }
 
     /**
      * @return The data allowed reason.
      */
-    public @NonNull DataAllowedReason getDataAllowedReason() {
+    @NonNull
+    public DataAllowedReason getDataAllowedReason() {
         return mDataAllowedReason;
     }
 
@@ -123,7 +130,8 @@
     /**
      * @return The candidate data profile for setup data network.
      */
-    public @Nullable DataProfile getCandidateDataProfile() {
+    @Nullable
+    public DataProfile getCandidateDataProfile() {
         return mCandidateDataProfile;
     }
 
@@ -131,7 +139,7 @@
      * @return {@code true} if the evaluation contains disallowed reasons.
      */
     public boolean containsDisallowedReasons() {
-        return mDataDisallowedReasons.size() != 0;
+        return !mDataDisallowedReasons.isEmpty();
     }
 
     /**
@@ -417,8 +425,9 @@
     @Override
     public String toString() {
         StringBuilder evaluationStr = new StringBuilder();
-        evaluationStr.append("Data evaluation: evaluation reason:" + mDataEvaluationReason + ", ");
-        if (mDataDisallowedReasons.size() > 0) {
+        evaluationStr.append("Data evaluation: evaluation reason:")
+                .append(mDataEvaluationReason).append(", ");
+        if (!mDataDisallowedReasons.isEmpty()) {
             evaluationStr.append("Data disallowed reasons:");
             for (DataDisallowedReason reason : mDataDisallowedReasons) {
                 evaluationStr.append(" ").append(reason);
@@ -427,8 +436,8 @@
             evaluationStr.append("Data allowed reason:");
             evaluationStr.append(" ").append(mDataAllowedReason);
         }
-        evaluationStr.append(", candidate profile=" + mCandidateDataProfile);
-        evaluationStr.append(", time=" + DataUtils.systemTimeToString(mEvaluatedTime));
+        evaluationStr.append(", candidate profile=").append(mCandidateDataProfile);
+        evaluationStr.append(", time=").append(DataUtils.systemTimeToString(mEvaluatedTime));
         return evaluationStr.toString();
     }
 
diff --git a/src/java/com/android/internal/telephony/data/DataNetwork.java b/src/java/com/android/internal/telephony/data/DataNetwork.java
index 7ca504b..57d242b 100644
--- a/src/java/com/android/internal/telephony/data/DataNetwork.java
+++ b/src/java/com/android/internal/telephony/data/DataNetwork.java
@@ -103,6 +103,7 @@
 import com.android.internal.telephony.data.TelephonyNetworkAgent.TelephonyNetworkAgentCallback;
 import com.android.internal.telephony.flags.FeatureFlags;
 import com.android.internal.telephony.metrics.DataCallSessionStats;
+import com.android.internal.telephony.metrics.DataNetworkValidationStats;
 import com.android.internal.telephony.metrics.TelephonyMetrics;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.FunctionalUtils;
@@ -136,11 +137,11 @@
 
 /**
  * DataNetwork class represents a single PDN (Packet Data Network).
- *
+ * <p>
  * The life cycle of a data network starts from {@link ConnectingState}. If setup data request
  * succeeds, then it enters {@link ConnectedState}, otherwise it enters
  * {@link DisconnectedState}.
- *
+ * <p>
  * When data network is in {@link ConnectingState}, it can enter {@link HandoverState} if handover
  * between IWLAN and cellular occurs. After handover completes or fails, it return back to
  * {@link ConnectedState}. When the data network is about to be disconnected, it first enters
@@ -149,9 +150,9 @@
  * notifies data disconnected. Note that an unsolicited disconnected event from {@link DataService}
  * or any vendor HAL failure response can immediately move data network from {@link ConnectedState}
  * to {@link DisconnectedState}. {@link DisconnectedState} is the final state of a data network.
- *
+ * <p>
  * State machine diagram:
- *
+ * <p>
  *
  *                                  ┌─────────┐
  *                                  │Handover │
@@ -201,6 +202,9 @@
     /** Event for bandwidth estimation from the modem changed. */
     private static final int EVENT_BANDWIDTH_ESTIMATE_FROM_MODEM_CHANGED = 11;
 
+    /** Event to report anomaly {@link #EVENT_NOTIFY_HANDOVER_CANCELLED_RESPONSE} not received. */
+    private static final int EVENT_CANCEL_HANDOVER_NO_RESPONSE = 12;
+
     /** Event for display info changed. This is for getting 5G NSA or mmwave information. */
     private static final int EVENT_DISPLAY_INFO_CHANGED = 13;
 
@@ -270,17 +274,6 @@
     /** Invalid context id. */
     private static final int INVALID_CID = -1;
 
-    /**
-     * The data network providing default internet will have a higher score of 50. Other network
-     * will have a slightly lower score of 45. The intention is other connections will not cause
-     * connectivity service to tear down default internet connection. For example, to validate
-     * internet connection on non-default data SIM, we'll set up a temporary internet network on
-     * that data SIM. In this case, score of 45 is assigned so connectivity service will not replace
-     * the default internet network with it.
-     */
-    private static final int DEFAULT_INTERNET_NETWORK_SCORE = 50;
-    private static final int OTHER_NETWORK_SCORE = 45;
-
     @IntDef(prefix = {"TEAR_DOWN_REASON_"},
             value = {
                     TEAR_DOWN_REASON_NONE,
@@ -512,10 +505,12 @@
     private final DisconnectedState mDisconnectedState = new DisconnectedState();
 
     /** The phone instance. */
-    private final @NonNull Phone mPhone;
+    @NonNull
+    private final Phone mPhone;
 
     /** Feature flags */
-    private final @NonNull FeatureFlags mFlags;
+    @NonNull
+    private final FeatureFlags mFlags;
 
     /**
      * The subscription id. This is assigned when the network is created, and not supposed to
@@ -524,7 +519,8 @@
     private final int mSubId;
 
     /** The network score of this network. */
-    private @NonNull NetworkScore mNetworkScore;
+    @NonNull
+    private NetworkScore mNetworkScore;
 
     /**
      * Indicates that
@@ -539,13 +535,15 @@
     private boolean mEverConnected = false;
 
     /** RIL interface. */
-    private final @NonNull CommandsInterface mRil;
+    @NonNull
+    private final CommandsInterface mRil;
 
     /** Local log. */
     private final LocalLog mLocalLog = new LocalLog(128);
 
     /** The callback to receives data network state update. */
-    private final @NonNull DataNetworkCallback mDataNetworkCallback;
+    @NonNull
+    private final DataNetworkCallback mDataNetworkCallback;
 
     /** The log tag. */
     private String mLogTag;
@@ -553,6 +551,10 @@
     /** Metrics of per data network connection. */
     private final DataCallSessionStats mDataCallSessionStats;
 
+    /** Metrics of per data network validation. */
+    @NonNull
+    private final DataNetworkValidationStats mDataNetworkValidationStats;
+
     /**
      * The unique context id assigned by the data service in {@link DataCallResponse#getId()}. One
      * for {@link AccessNetworkConstants#TRANSPORT_TYPE_WWAN} and one for
@@ -575,71 +577,92 @@
      * Data service managers for accessing {@link AccessNetworkConstants#TRANSPORT_TYPE_WWAN} and
      * {@link AccessNetworkConstants#TRANSPORT_TYPE_WLAN} data services.
      */
-    private final @NonNull SparseArray<DataServiceManager> mDataServiceManagers;
+    @NonNull
+    private final SparseArray<DataServiceManager> mDataServiceManagers;
 
     /** Access networks manager. */
-    private final @NonNull AccessNetworksManager mAccessNetworksManager;
+    @NonNull
+    private final AccessNetworksManager mAccessNetworksManager;
 
     /** Data network controller. */
-    private final @NonNull DataNetworkController mDataNetworkController;
+    @NonNull
+    private final DataNetworkController mDataNetworkController;
 
     /** Data network controller callback. */
-    private final @NonNull DataNetworkController.DataNetworkControllerCallback
+    @NonNull
+    private final DataNetworkController.DataNetworkControllerCallback
             mDataNetworkControllerCallback;
 
     /** Data settings manager callback. */
-    private @NonNull DataSettingsManagerCallback mDataSettingsManagerCallback;
+    @NonNull
+    private DataSettingsManagerCallback mDataSettingsManagerCallback;
 
     /** Data config manager. */
-    private final @NonNull DataConfigManager mDataConfigManager;
+    @NonNull
+    private final DataConfigManager mDataConfigManager;
 
     /** VCN manager. */
-    private final @Nullable VcnManager mVcnManager;
+    @Nullable
+    private final VcnManager mVcnManager;
 
     /** VCN policy changed listener. */
-    private @Nullable VcnNetworkPolicyChangeListener mVcnPolicyChangeListener;
+    @Nullable
+    private VcnNetworkPolicyChangeListener mVcnPolicyChangeListener;
 
     /** The network agent associated with this data network. */
-    private @NonNull TelephonyNetworkAgent mNetworkAgent;
+    @NonNull
+    private TelephonyNetworkAgent mNetworkAgent;
 
     /** QOS callback tracker. This is only created after network connected on WWAN. */
-    private @Nullable QosCallbackTracker mQosCallbackTracker;
+    @Nullable
+    private QosCallbackTracker mQosCallbackTracker;
 
     /** NAT keepalive tracker. */
-    private @Nullable KeepaliveTracker mKeepaliveTracker;
+    @Nullable
+    private KeepaliveTracker mKeepaliveTracker;
 
     /** The data profile used to establish this data network. */
-    private @NonNull DataProfile mDataProfile;
+    @NonNull
+    private DataProfile mDataProfile;
 
     /**
      * The data profile used for data handover. Some carriers might use different data profile
      * between IWLAN and cellular. Only set before handover started.
      */
-    private @Nullable DataProfile mHandoverDataProfile;
+    @Nullable
+    private DataProfile mHandoverDataProfile;
 
     /** The network capabilities of this data network. */
-    private @NonNull NetworkCapabilities mNetworkCapabilities;
+    @NonNull
+    private NetworkCapabilities mNetworkCapabilities;
 
     /** The matched traffic descriptor returned from setup data call request. */
-    private final @NonNull List<TrafficDescriptor> mTrafficDescriptors = new ArrayList<>();
+    @NonNull
+    private final List<TrafficDescriptor> mTrafficDescriptors = new ArrayList<>();
 
     /** The link properties of this data network. */
-    private @NonNull LinkProperties mLinkProperties;
+    @NonNull
+    private LinkProperties mLinkProperties;
 
     /** The network slice info. */
-    private @Nullable NetworkSliceInfo mNetworkSliceInfo;
+    @Nullable
+    private NetworkSliceInfo mNetworkSliceInfo;
 
     /** The link status (i.e. RRC state). */
-    private @LinkStatus int mLinkStatus = DataCallResponse.LINK_STATUS_UNKNOWN;
+    @LinkStatus
+    private int mLinkStatus = DataCallResponse.LINK_STATUS_UNKNOWN;
 
     /** The network bandwidth. */
-    private @NonNull NetworkBandwidth mNetworkBandwidth = new NetworkBandwidth(14, 14);
+    @NonNull
+    private NetworkBandwidth mNetworkBandwidth = new NetworkBandwidth(14, 14);
 
     /** The TCP buffer sizes config. */
-    private @NonNull String mTcpBufferSizes;
+    @NonNull
+    private String mTcpBufferSizes;
 
     /** The telephony display info. */
-    private @NonNull TelephonyDisplayInfo mTelephonyDisplayInfo;
+    @NonNull
+    private TelephonyDisplayInfo mTelephonyDisplayInfo;
 
     /** Whether {@link NetworkCapabilities#NET_CAPABILITY_TEMPORARILY_NOT_METERED} is supported. */
     private boolean mTempNotMeteredSupported = false;
@@ -651,7 +674,8 @@
     private boolean mCongested = false;
 
     /** The network requests associated with this data network */
-    private final @NonNull NetworkRequestList mAttachedNetworkRequestList =
+    @NonNull
+    private final NetworkRequestList mAttachedNetworkRequestList =
             new NetworkRequestList();
 
     /**
@@ -660,18 +684,21 @@
      * {@link DataServiceCallback#onDataCallListChanged(List)}. The very first update must be
      * from {@link DataServiceCallback#onSetupDataCallComplete(int, DataCallResponse)}.
      */
-    private @Nullable DataCallResponse mDataCallResponse = null;
+    @Nullable
+    private DataCallResponse mDataCallResponse = null;
 
     /**
      * The fail cause from either setup data failure or unsolicited disconnect reported by data
      * service.
      */
-    private @DataFailureCause int mFailCause = DataFailCause.NONE;
+    @DataFailureCause
+    private 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;
+    @TearDownReason
+    private int mTearDownReason = TEAR_DOWN_REASON_NONE;
 
     /**
      * The retry delay in milliseconds from setup data failure.
@@ -691,12 +718,14 @@
      * The current transport of the data network. For handover, the current transport will be set
      * after handover completes.
      */
-    private @TransportType int mTransport;
+    @TransportType
+    private int mTransport;
 
     /**
      * The last known data network type.
      */
-    private @NetworkType int mLastKnownDataNetworkType;
+    @NetworkType
+    private int mLastKnownDataNetworkType;
 
     /**
      * The last known roaming state of this data network.
@@ -709,27 +738,33 @@
     private final boolean mIsSatellite;
 
     /** The reason that why setting up this data network is allowed. */
-    private @NonNull DataAllowedReason mDataAllowedReason;
+    @NonNull
+    private final DataAllowedReason mDataAllowedReason;
 
     /**
      * PCO (Protocol Configuration Options) data received from the network. The first key is the
      * cid of the PCO data, the second key is the PCO id, the value is the PCO data.
      */
-    private final @NonNull Map<Integer, Map<Integer, PcoData>> mPcoData = new ArrayMap<>();
+    @NonNull
+    private final Map<Integer, Map<Integer, PcoData>> mPcoData = new ArrayMap<>();
 
     /** The QOS bearer sessions. */
-    private final @NonNull List<QosBearerSession> mQosBearerSessions = new ArrayList<>();
+    @NonNull
+    private final List<QosBearerSession> mQosBearerSessions = new ArrayList<>();
 
     /** The QOS for the Default Bearer, should be non-null on LTE and NR */
-    private @Nullable Qos mDefaultQos;
+    @Nullable
+    private Qos mDefaultQos;
 
     /**
      * The UIDs of packages that have carrier privilege.
      */
-    private @NonNull int[] mAdministratorUids = new int[0];
+    @NonNull
+    private int[] mAdministratorUids = new int[0];
 
     /** Carrier privileges callback to monitor administrator UID change. */
-    private @Nullable TelephonyManager.CarrierPrivilegesCallback mCarrierPrivilegesCallback;
+    @Nullable
+    private TelephonyManager.CarrierPrivilegesCallback mCarrierPrivilegesCallback;
 
     /**
      * Carrier service package uid. This UID will not change through the life cycle of data network.
@@ -739,30 +774,42 @@
     /**
      * Link bandwidth estimator callback for receiving latest link bandwidth information.
      */
-    private @Nullable LinkBandwidthEstimatorCallback mLinkBandwidthEstimatorCallback;
+    @Nullable
+    private LinkBandwidthEstimatorCallback mLinkBandwidthEstimatorCallback;
 
     /**
      * Data config callback for carrier config update.
      */
-    private @Nullable DataConfigManagerCallback mDataConfigManagerCallback;
+    @Nullable
+    private DataConfigManagerCallback mDataConfigManagerCallback;
 
     /**
      * Network validation status for this data network. If the data service provider does not
      * support the network validation feature, should be UNSUPPORTED.
      */
-    private @PreciseDataConnectionState.NetworkValidationStatus int mNetworkValidationStatus =
+    @PreciseDataConnectionState.NetworkValidationStatus
+    private int mNetworkValidationStatus =
             PreciseDataConnectionState.NETWORK_VALIDATION_UNSUPPORTED;
 
     /**
      * Callback used to respond to a network validation request to determine whether the request is
      * successfully submitted. If the request has been submitted, change it to null.
      */
-    private @Nullable Consumer<Integer> mNetworkValidationResultCodeCallback;
+    @Nullable
+    private Consumer<Integer> mNetworkValidationResultCodeCallback;
 
     /**
      * Callback used to listen QNS preference changes.
      */
-    private @Nullable AccessNetworksManagerCallback mAccessNetworksManagerCallback;
+    @Nullable
+    private AccessNetworksManagerCallback mAccessNetworksManagerCallback;
+
+    /**
+     * PreciseDataConnectionState, the most recently notified. If it has never been notified, it is
+     * null.
+     */
+    @Nullable
+    private PreciseDataConnectionState mPreciseDataConnectionState;
 
     /**
      * The network bandwidth.
@@ -987,6 +1034,7 @@
                 mDataNetworkControllerCallback);
         mDataConfigManager = mDataNetworkController.getDataConfigManager();
         mDataCallSessionStats = new DataCallSessionStats(mPhone);
+        mDataNetworkValidationStats = new DataNetworkValidationStats(mPhone);
         mDataNetworkCallback = callback;
         mDataProfile = dataProfile;
         if (dataProfile.getTrafficDescriptor() != null) {
@@ -1075,7 +1123,8 @@
      *
      * @return The telephony network agent.
      */
-    private @NonNull TelephonyNetworkAgent createNetworkAgent() {
+    @NonNull
+    private TelephonyNetworkAgent createNetworkAgent() {
         final NetworkAgentConfig.Builder configBuilder = new NetworkAgentConfig.Builder();
         configBuilder.setLegacyType(ConnectivityManager.TYPE_MOBILE);
         configBuilder.setLegacyTypeName("MOBILE");
@@ -1108,13 +1157,8 @@
                 mPhone.getPhoneId());
         final NetworkProvider provider = (null == factory) ? null : factory.getProvider();
 
-        // Always prefer IWLAN network for MMS designated network.
-        // TODO(b/293656884) Proper use of primary transport to avoid conflicting with DSDA.
-        boolean isPreferred = mTransport == AccessNetworkConstants.TRANSPORT_TYPE_WLAN
-                && getApnTypeNetworkCapability() == NetworkCapabilities.NET_CAPABILITY_MMS;
-
-        mNetworkScore = new NetworkScore.Builder().setTransportPrimary(isPreferred)
-                .setKeepConnectedReason(isHandoverInProgress()
+        mNetworkScore = new NetworkScore.Builder()
+               .setKeepConnectedReason(isHandoverInProgress()
                         ? NetworkScore.KEEP_CONNECTED_FOR_HANDOVER
                         : NetworkScore.KEEP_CONNECTED_NONE).build();
 
@@ -1189,7 +1233,7 @@
 
             mCarrierPrivilegesCallback =
                     (Set<String> privilegedPackageNames, Set<Integer> privilegedUids) -> {
-                        log("onCarrierPrivilegesChanged, Uids=" + privilegedUids.toString());
+                        log("onCarrierPrivilegesChanged, Uids=" + privilegedUids);
                         Message message = obtainMessage(EVENT_CARRIER_PRIVILEGED_UIDS_CHANGED);
                         AsyncResult.forMessage(
                                 message,
@@ -1341,11 +1385,13 @@
                     break;
                 }
                 case EVENT_NOTIFY_HANDOVER_CANCELLED_RESPONSE:
+                    removeMessages(EVENT_CANCEL_HANDOVER_NO_RESPONSE);
                     log("Notified handover cancelled.");
                     break;
                 case EVENT_BANDWIDTH_ESTIMATE_FROM_MODEM_CHANGED:
                 case EVENT_TEAR_DOWN_NETWORK:
                 case EVENT_STUCK_IN_TRANSIENT_STATE:
+                case EVENT_CANCEL_HANDOVER_NO_RESPONSE:
                 case EVENT_DISPLAY_INFO_CHANGED:
                 case EVENT_WAITING_FOR_TEARING_DOWN_CONDITION_MET:
                 case EVENT_CSS_INDICATOR_CHANGED:
@@ -1441,7 +1487,10 @@
                         setupData();
                     } else {
                         mRetryDelayMillis = DataCallResponse.RETRY_DURATION_UNDEFINED;
-                        mFailCause = DataFailCause.NO_RETRY_FAILURE;
+                        if (!mFlags.keepEmptyRequestsNetwork()) {
+                            // This will mark the data profile as no retry perm failure.
+                            mFailCause = DataFailCause.NO_RETRY_FAILURE;
+                        }
                         transitionTo(mDisconnectedState);
                     }
                     break;
@@ -1519,7 +1568,7 @@
 
             int apnTypeBitmask = mDataProfile.getApnSetting() != null
                     ? mDataProfile.getApnSetting().getApnTypeBitmask() : ApnSetting.TYPE_NONE;
-            mDataCallSessionStats.onSetupDataCall(apnTypeBitmask);
+            mDataCallSessionStats.onSetupDataCall(apnTypeBitmask, isSatellite());
 
             logl("setupData: accessNetwork="
                     + AccessNetworkType.toString(accessNetwork) + ", " + mDataProfile
@@ -1567,12 +1616,7 @@
 
                 updateDataNetwork(response);
 
-                // TODO: Evaluate all network requests and see if each request still can be
-                //  satisfied.
-                //  For requests that can't be satisfied anymore, we need to put them back to the
-                //  unsatisfied pool. If none of network requests can be satisfied, then there is no
-                //  need to mark network agent connected. Just silently deactivate the data network.
-                if (mAttachedNetworkRequestList.isEmpty()) {
+                if (!mFlags.keepEmptyRequestsNetwork() && mAttachedNetworkRequestList.isEmpty()) {
                     log("Tear down the network since there is no live network request.");
                     // Directly call onTearDown here. Calling tearDown will cause deadlock because
                     // EVENT_TEAR_DOWN_NETWORK is deferred until state machine enters connected
@@ -1659,9 +1703,7 @@
 
             // 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);
-                    });
+                    .forEach((pcoId, pcoData) -> onPcoDataChanged(pcoData));
 
             mDataNetworkCallback.invokeFromExecutor(
                     () -> mDataNetworkCallback.onLinkStatusChanged(DataNetwork.this, mLinkStatus));
@@ -1735,6 +1777,12 @@
                     // Network validation request can be accepted if the data is in connected state
                     handleDataNetworkValidationRequest((Consumer<Integer>) msg.obj);
                     break;
+                case EVENT_CANCEL_HANDOVER_NO_RESPONSE:
+                    reportAnomaly("Cancel handover no response within "
+                            + TimeUnit.MILLISECONDS.toSeconds(
+                            mDataConfigManager.getNetworkHandoverTimeoutMs())
+                            + " seconds.", "ad320988-0601-4955-836a-e6b67289c294");
+                    break;
                 default:
                     return NOT_HANDLED;
             }
@@ -1750,6 +1798,7 @@
     private final class HandoverState extends State {
         @Override
         public void enter() {
+            removeMessages(EVENT_CANCEL_HANDOVER_NO_RESPONSE);
             sendMessageDelayed(EVENT_STUCK_IN_TRANSIENT_STATE,
                     mDataConfigManager.getNetworkHandoverTimeoutMs());
             notifyPreciseDataConnectionState();
@@ -1933,6 +1982,9 @@
                 if (mTransport == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) {
                     unregisterForWwanEvents();
                 }
+                // Since NetworkValidation is able to request only in the Connected state,
+                // if ever connected, log for onDataNetworkDisconnected.
+                mDataNetworkValidationStats.onDataNetworkDisconnected(getDataNetworkType());
             } else {
                 mDataNetworkCallback.invokeFromExecutor(() -> mDataNetworkCallback
                         .onSetupDataFailed(DataNetwork.this,
@@ -2033,7 +2085,7 @@
                 log("Successfully attached network request " + networkRequest);
             }
         }
-        if (failedList.size() > 0) {
+        if (!failedList.isEmpty()) {
             mDataNetworkCallback.invokeFromExecutor(() -> mDataNetworkCallback
                     .onAttachFailed(DataNetwork.this, failedList));
         }
@@ -2198,8 +2250,7 @@
     private static boolean areImmutableCapabilitiesChanged(
             @NonNull NetworkCapabilities oldCapabilities,
             @NonNull NetworkCapabilities newCapabilities) {
-        if (oldCapabilities == null
-                || ArrayUtils.isEmpty(oldCapabilities.getCapabilities())) return false;
+        if (ArrayUtils.isEmpty(oldCapabilities.getCapabilities())) return false;
 
         // Remove mutable capabilities from both old and new capabilities, the remaining
         // capabilities would be immutable capabilities.
@@ -2229,6 +2280,7 @@
         // will always be registered with NOT_SUSPENDED capability.
         mNetworkAgent = createNetworkAgent();
         mNetworkAgent.markConnected();
+        notifyPreciseDataConnectionState();
         // Because network agent is always created with NOT_SUSPENDED, we need to update
         // the suspended if it's was in suspended state.
         if (mSuspended) {
@@ -2457,8 +2509,8 @@
                 // If we find another data data profile that can support MMS on IWLAN, then remove
                 // the MMS capability from this cellular network. This will allow IWLAN to be
                 // brought up for MMS later.
-                if (dataProfile != null && !dataProfile.equals(mDataProfile)) {
-                    log("Found a different data profile " + mDataProfile.getApn()
+                if (dataProfile != null && !dataProfile.getApn().equals(mDataProfile.getApn())) {
+                    log("Found a different apn name " + mDataProfile.getApn()
                             + " that can serve MMS on IWLAN.");
                     builder.removeCapability(NetworkCapabilities.NET_CAPABILITY_MMS);
                 }
@@ -2525,21 +2577,24 @@
     /**
      * @return The network capabilities of this data network.
      */
-    public @NonNull NetworkCapabilities getNetworkCapabilities() {
+    @NonNull
+    public NetworkCapabilities getNetworkCapabilities() {
         return mNetworkCapabilities;
     }
 
     /**
      * @return The link properties of this data network.
      */
-    public @NonNull LinkProperties getLinkProperties() {
+    @NonNull
+    public LinkProperties getLinkProperties() {
         return mLinkProperties;
     }
 
     /**
      * @return The data profile of this data network.
      */
-    public @NonNull DataProfile getDataProfile() {
+    @NonNull
+    public DataProfile getDataProfile() {
         return mDataProfile;
     }
 
@@ -2560,7 +2615,6 @@
         // Never set suspended for emergency apn. Emergency data connection
         // can work while device is not in service.
         if (mNetworkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_EIMS)) {
-            newSuspendedState = false;
             // If we are not in service, change to suspended.
         } else if (nri.getRegistrationState()
                 != NetworkRegistrationInfo.REGISTRATION_STATE_HOME
@@ -2604,7 +2658,8 @@
      *
      * @return The fail cause. {@link DataFailCause#NONE} if succeeds.
      */
-    private @DataFailureCause int getFailCauseFromDataCallResponse(
+    @DataFailureCause
+    private int getFailCauseFromDataCallResponse(
             @DataServiceCallback.ResultCode int resultCode, @Nullable DataCallResponse response) {
         int failCause = DataFailCause.NONE;
         switch (resultCode) {
@@ -2635,7 +2690,8 @@
      *
      * @param response The data call response from data service.
      */
-    private void updateDataNetwork(@NonNull DataCallResponse response) {
+    private void updateDataNetwork(@Nullable DataCallResponse response) {
+        if (response == null) return;
         mCid.put(mTransport, response.getId());
         LinkProperties linkProperties = new LinkProperties();
 
@@ -2662,7 +2718,7 @@
         }
 
         // Set link addresses
-        if (response.getAddresses().size() > 0) {
+        if (!response.getAddresses().isEmpty()) {
             for (LinkAddress la : response.getAddresses()) {
                 if (!la.getAddress().isAnyLocalAddress()) {
                     logv("addr/pl=" + la.getAddress() + "/" + la.getPrefixLength());
@@ -2674,7 +2730,7 @@
         }
 
         // Set DNS servers
-        if (response.getDnsAddresses().size() > 0) {
+        if (!response.getDnsAddresses().isEmpty()) {
             for (InetAddress dns : response.getDnsAddresses()) {
                 if (!dns.isAnyLocalAddress()) {
                     linkProperties.addDnsServer(dns);
@@ -2685,7 +2741,7 @@
         }
 
         // Set PCSCF
-        if (response.getPcscfAddresses().size() > 0) {
+        if (!response.getPcscfAddresses().isEmpty()) {
             for (InetAddress pcscf : response.getPcscfAddresses()) {
                 linkProperties.addPcscfServer(pcscf);
             }
@@ -2824,22 +2880,22 @@
                             == 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);
+                        if (response.getAddresses().stream().noneMatch(
+                                la -> la.getAddress() instanceof java.net.Inet4Address)) {
+                            loge("Invalid DataCallResponse. Requested IPv4 but didn't get an "
+                                    + "IPv4 address." + response);
                             reportAnomaly(underlyingDataService + " reported mismatched IP "
-                                            + "type. Requested IPv4 but got IPv6 address.",
-                                    "7744f920-fb64-4db0-ba47-de0eae485a81");
+                                    + "type. Requested IPv4 but didn't get an IPv4 "
+                                    + "address.", "7744f920-fb64-4db0-ba47-de0eae485a82");
                         }
                     } 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);
+                        if (response.getAddresses().stream().noneMatch(
+                                la -> la.getAddress() instanceof java.net.Inet6Address)) {
+                            loge("Invalid DataCallResponse. Requested IPv6 but didn't get an "
+                                    + "IPv6 address." + response);
                             reportAnomaly(underlyingDataService + " reported mismatched IP "
-                                            + "type. Requested IPv6 but got IPv4 address.",
-                                    "7744f920-fb64-4db0-ba47-de0eae485a81");
+                                    + "type. Requested IPv6 but didn't get an IPv6 "
+                                    + "address.", "7744f920-fb64-4db0-ba47-de0eae485a82");
                         }
                     }
                 }
@@ -2941,8 +2997,8 @@
      * will be performed. {@code null} if the data network is already disconnected or being
      * disconnected.
      */
-    public @Nullable Runnable tearDownWhenConditionMet(@TearDownReason int reason,
-            long timeoutMillis) {
+    @Nullable
+    public Runnable tearDownWhenConditionMet(@TearDownReason int reason, long timeoutMillis) {
         if (getCurrentState() == null || isDisconnected() || isDisconnecting()) {
             loge("tearDownWhenConditionMet: Not in the right state. State=" + getCurrentState());
             return null;
@@ -2981,6 +3037,7 @@
                 mDataCallResponse = response;
                 if (response.getLinkStatus() != DataCallResponse.LINK_STATUS_INACTIVE) {
                     updateDataNetwork(response);
+                    notifyPreciseDataConnectionState();
                 } else {
                     log("onDataStateChanged: PDN inactive reported by "
                             + AccessNetworkConstants.transportTypeToString(mTransport)
@@ -3054,12 +3111,12 @@
         NetworkBandwidth bandwidthFromConfig = mDataConfigManager.getBandwidthForNetworkType(
                 mTelephonyDisplayInfo);
 
-        if (downlinkBandwidthKbps == LinkCapacityEstimate.INVALID && bandwidthFromConfig != null) {
+        if (downlinkBandwidthKbps == LinkCapacityEstimate.INVALID) {
             // Fallback to carrier config.
             downlinkBandwidthKbps = bandwidthFromConfig.downlinkBandwidthKbps;
         }
 
-        if (uplinkBandwidthKbps == LinkCapacityEstimate.INVALID && bandwidthFromConfig != null) {
+        if (uplinkBandwidthKbps == LinkCapacityEstimate.INVALID) {
             // Fallback to carrier config.
             uplinkBandwidthKbps = bandwidthFromConfig.uplinkBandwidthKbps;
         }
@@ -3204,7 +3261,8 @@
     /**
      * @return The current network type reported by the network service.
      */
-    private @NetworkType int getDataNetworkType() {
+    @NetworkType
+    private int getDataNetworkType() {
         return getDataNetworkType(mTransport);
     }
 
@@ -3214,7 +3272,8 @@
      * @param transport The transport.
      * @return The data network type.
      */
-    private @NetworkType int getDataNetworkType(@TransportType int transport) {
+    @NetworkType
+    private int getDataNetworkType(@TransportType int transport) {
         // WLAN transport can't have network type other than IWLAN. Ideally service state tracker
         // should report the correct RAT, but sometimes race condition could happen that service
         // state is reset to out of service and RAT not updated to IWLAN yet.
@@ -3234,7 +3293,8 @@
     /**
      * @return The physical link status (i.e. RRC state).
      */
-    public @LinkStatus int getLinkStatus() {
+    @LinkStatus
+    public int getLinkStatus() {
         return mLinkStatus;
     }
 
@@ -3257,7 +3317,8 @@
     /**
      * @return Network registration info on the current transport.
      */
-    private @Nullable NetworkRegistrationInfo getNetworkRegistrationInfo() {
+    @Nullable
+    private NetworkRegistrationInfo getNetworkRegistrationInfo() {
         NetworkRegistrationInfo nri = mPhone.getServiceStateTracker().getServiceState()
                 .getNetworkRegistrationInfo(NetworkRegistrationInfo.DOMAIN_PS, mTransport);
         if (nri == null) {
@@ -3279,7 +3340,8 @@
      *
      * @see #getPriority()
      */
-    public @NetCapability int getApnTypeNetworkCapability() {
+    @NetCapability
+    public int getApnTypeNetworkCapability() {
         if (!mAttachedNetworkRequestList.isEmpty()) {
             // The highest priority network request is always at the top of list.
             return mAttachedNetworkRequestList.get(0).getApnTypeNetworkCapability();
@@ -3320,7 +3382,8 @@
     /**
      * @return The attached network request list.
      */
-    public @NonNull NetworkRequestList getAttachedNetworkRequestList() {
+    @NonNull
+    public NetworkRequestList getAttachedNetworkRequestList() {
         return mAttachedNetworkRequestList;
     }
 
@@ -3369,11 +3432,13 @@
     /**
      * @return The current transport of the data network.
      */
-    public @TransportType int getTransport() {
+    @TransportType
+    public int getTransport() {
         return mTransport;
     }
 
-    private @DataState int getState() {
+    @DataState
+    private int getState() {
         IState state = getCurrentState();
         if (state == null || isDisconnected()) {
             return TelephonyManager.DATA_DISCONNECTED;
@@ -3418,6 +3483,7 @@
         return new PreciseDataConnectionState.Builder()
                 .setTransportType(mTransport)
                 .setId(mCid.get(mTransport))
+                .setNetworkAgentId(mNetworkAgent.getId())
                 .setState(getState())
                 .setApnSetting(mDataProfile.getApnSetting())
                 .setLinkProperties(mLinkProperties)
@@ -3431,16 +3497,27 @@
     /**
      * Send the precise data connection state to the listener of
      * {@link android.telephony.TelephonyCallback.PreciseDataConnectionStateListener}.
+     * <p>
+     * Note that notify only when {@link DataState} or {@link
+     * PreciseDataConnectionState.NetworkValidationStatus} or {@link TelephonyNetworkAgent#getId}
+     * changes.
      */
     private void notifyPreciseDataConnectionState() {
         PreciseDataConnectionState pdcs = getPreciseDataConnectionState();
-        logv("notifyPreciseDataConnectionState=" + pdcs);
-        mPhone.notifyDataConnection(pdcs);
+        if (mPreciseDataConnectionState == null
+                || mPreciseDataConnectionState.getState() != pdcs.getState()
+                || mPreciseDataConnectionState.getNetworkValidationStatus()
+                        != pdcs.getNetworkValidationStatus()
+                || mPreciseDataConnectionState.getNetId() != pdcs.getNetId()) {
+            mPreciseDataConnectionState = pdcs;
+            logv("notifyPreciseDataConnectionState=" + pdcs);
+            mPhone.notifyDataConnection(pdcs);
+        }
     }
 
     /**
      * Request the data network to handover to the target transport.
-     *
+     * <p>
      * 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,
@@ -3525,6 +3602,8 @@
                 DataService.REQUEST_REASON_HANDOVER, mLinkProperties, mPduSessionId,
                 mNetworkSliceInfo, mHandoverDataProfile.getTrafficDescriptor(), true,
                 obtainMessage(EVENT_HANDOVER_RESPONSE, retryEntry));
+
+        mDataNetworkValidationStats.onHandoverAttempted();
     }
 
     /**
@@ -3572,6 +3651,8 @@
             // id can be released if it is preserved for handover.
             mDataServiceManagers.get(mTransport).cancelHandover(mCid.get(mTransport),
                     obtainMessage(EVENT_NOTIFY_HANDOVER_CANCELLED_RESPONSE));
+            sendMessageDelayed(EVENT_CANCEL_HANDOVER_NO_RESPONSE,
+                    mDataConfigManager.getNetworkHandoverTimeoutMs());
 
             long retry = response != null ? response.getRetryDurationMillis()
                     : DataCallResponse.RETRY_DURATION_UNDEFINED;
@@ -3647,7 +3728,8 @@
     /**
      * @return The last known data network type of the data network.
      */
-    public @NetworkType int getLastKnownDataNetworkType() {
+    @NetworkType
+    public int getLastKnownDataNetworkType() {
         return mLastKnownDataNetworkType;
     }
 
@@ -3661,7 +3743,8 @@
     /**
      * @return The PCO data received from the network.
      */
-    public @NonNull Map<Integer, PcoData> getPcoData() {
+    @NonNull
+    public Map<Integer, PcoData> getPcoData() {
         if (mTransport == AccessNetworkConstants.TRANSPORT_TYPE_WLAN
                 || mCid.get(mTransport) == INVALID_CID) {
             return Collections.emptyMap();
@@ -3695,7 +3778,7 @@
     }
 
     /**
-     * The network validation requests moves to process on the statemachich handler. A request is
+     * The network validation requests moves to process on the state machine handler. A request is
      * processed according to state of the data network.
      */
     public void requestNetworkValidation(@NonNull Consumer<Integer> resultCodeCallback) {
@@ -3719,6 +3802,11 @@
         // Request validation directly from the data service.
         mDataServiceManagers.get(mTransport).requestNetworkValidation(
                 mCid.get(mTransport), obtainMessage(EVENT_DATA_NETWORK_VALIDATION_RESPONSE));
+
+        int apnTypeBitmask = mDataProfile.getApnSetting() != null
+                ? mDataProfile.getApnSetting().getApnTypeBitmask() : ApnSetting.TYPE_NONE;
+        mDataNetworkValidationStats.onRequestNetworkValidation(apnTypeBitmask);
+
         log("handleDataNetworkValidationRequest, network validation requested");
     }
 
@@ -3747,8 +3835,7 @@
 
     /**
      * Update the validation status from {@link DataCallResponse}, convert to network validation
-     * status {@link PreciseDataConnectionState.NetworkValidationStatus} and notify to
-     * {@link PreciseDataConnectionState} if status was changed.
+     * status {@link PreciseDataConnectionState.NetworkValidationStatus}.
      *
      * @param networkValidationStatus {@link PreciseDataConnectionState.NetworkValidationStatus}
      */
@@ -3765,8 +3852,10 @@
                     + PreciseDataConnectionState.networkValidationStatusToString(
                     networkValidationStatus));
             mNetworkValidationStatus = networkValidationStatus;
-            notifyPreciseDataConnectionState();
         }
+
+        mDataNetworkValidationStats.onUpdateNetworkValidationState(
+                mNetworkValidationStatus, getDataNetworkType());
     }
 
     /**
@@ -3775,77 +3864,54 @@
      * @param reason Data deactivation reason.
      * @return The deactivation reason in string format.
      */
-    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:
-                return "SIM_REMOVAL";
-            case TEAR_DOWN_REASON_AIRPLANE_MODE_ON:
-                return "AIRPLANE_MODE_ON";
-            case TEAR_DOWN_REASON_DATA_DISABLED:
-                return "DATA_DISABLED";
-            case TEAR_DOWN_REASON_NO_LIVE_REQUEST:
-                return "TEAR_DOWN_REASON_NO_LIVE_REQUEST";
-            case TEAR_DOWN_REASON_RAT_NOT_ALLOWED:
-                return "TEAR_DOWN_REASON_RAT_NOT_ALLOWED";
-            case TEAR_DOWN_REASON_ROAMING_DISABLED:
-                return "TEAR_DOWN_REASON_ROAMING_DISABLED";
-            case TEAR_DOWN_REASON_CONCURRENT_VOICE_DATA_NOT_ALLOWED:
-                return "TEAR_DOWN_REASON_CONCURRENT_VOICE_DATA_NOT_ALLOWED";
-            case TEAR_DOWN_REASON_SERVICE_OPTION_NOT_SUPPORTED:
-                return "TEAR_DOWN_REASON_SERVICE_OPTION_NOT_SUPPORTED";
-            case TEAR_DOWN_REASON_DATA_SERVICE_NOT_READY:
-                return "TEAR_DOWN_REASON_DATA_SERVICE_NOT_READY";
-            case TEAR_DOWN_REASON_POWER_OFF_BY_CARRIER:
-                return "TEAR_DOWN_REASON_POWER_OFF_BY_CARRIER";
-            case TEAR_DOWN_REASON_DATA_STALL:
-                return "TEAR_DOWN_REASON_DATA_STALL";
-            case TEAR_DOWN_REASON_HANDOVER_FAILED:
-                return "TEAR_DOWN_REASON_HANDOVER_FAILED";
-            case TEAR_DOWN_REASON_HANDOVER_NOT_ALLOWED:
-                return "TEAR_DOWN_REASON_HANDOVER_NOT_ALLOWED";
-            case TEAR_DOWN_REASON_VCN_REQUESTED:
-                return "TEAR_DOWN_REASON_VCN_REQUESTED";
-            case TEAR_DOWN_REASON_VOPS_NOT_SUPPORTED:
-                return "TEAR_DOWN_REASON_VOPS_NOT_SUPPORTED";
-            case TEAR_DOWN_REASON_DEFAULT_DATA_UNSELECTED:
-                return "TEAR_DOWN_REASON_DEFAULT_DATA_UNSELECTED";
-            case TEAR_DOWN_REASON_NOT_IN_SERVICE:
-                return "TEAR_DOWN_REASON_NOT_IN_SERVICE";
-            case TEAR_DOWN_REASON_DATA_CONFIG_NOT_READY:
-                return "TEAR_DOWN_REASON_DATA_CONFIG_NOT_READY";
-            case TEAR_DOWN_REASON_PENDING_TEAR_DOWN_ALL:
-                return "TEAR_DOWN_REASON_PENDING_TEAR_DOWN_ALL";
-            case TEAR_DOWN_REASON_NO_SUITABLE_DATA_PROFILE:
-                return "TEAR_DOWN_REASON_NO_SUITABLE_DATA_PROFILE";
-            case TEAR_DOWN_REASON_CDMA_EMERGENCY_CALLBACK_MODE:
-                return "TEAR_DOWN_REASON_CDMA_EMERGENCY_CALLBACK_MODE";
-            case TEAR_DOWN_REASON_RETRY_SCHEDULED:
-                return "TEAR_DOWN_REASON_RETRY_SCHEDULED";
-            case TEAR_DOWN_REASON_DATA_THROTTLED:
-                return "TEAR_DOWN_REASON_DATA_THROTTLED";
-            case TEAR_DOWN_REASON_DATA_PROFILE_INVALID:
-                return "TEAR_DOWN_REASON_DATA_PROFILE_INVALID";
-            case TEAR_DOWN_REASON_DATA_PROFILE_NOT_PREFERRED:
-                return "TEAR_DOWN_REASON_DATA_PROFILE_NOT_PREFERRED";
-            case TEAR_DOWN_REASON_NOT_ALLOWED_BY_POLICY:
-                return "TEAR_DOWN_REASON_NOT_ALLOWED_BY_POLICY";
-            case TEAR_DOWN_REASON_ILLEGAL_STATE:
-                return "TEAR_DOWN_REASON_ILLEGAL_STATE";
-            case TEAR_DOWN_REASON_ONLY_ALLOWED_SINGLE_NETWORK:
-                return "TEAR_DOWN_REASON_ONLY_ALLOWED_SINGLE_NETWORK";
-            case TEAR_DOWN_REASON_PREFERRED_DATA_SWITCHED:
-                return "TEAR_DOWN_REASON_PREFERRED_DATA_SWITCHED";
-            case TEAR_DOWN_REASON_DATA_LIMIT_REACHED:
-                return "TEAR_DOWN_REASON_DATA_LIMIT_REACHED";
-            case TEAR_DOWN_REASON_DATA_NETWORK_TRANSPORT_NOT_ALLOWED:
-                return "TEAR_DOWN_REASON_DATA_NETWORK_TRANSPORT_NOT_ALLOWED";
-            default:
-                return "UNKNOWN(" + reason + ")";
-        }
+    @NonNull
+    public static String tearDownReasonToString(@TearDownReason int reason) {
+        return switch (reason) {
+            case TEAR_DOWN_REASON_NONE -> "NONE";
+            case TEAR_DOWN_REASON_CONNECTIVITY_SERVICE_UNWANTED -> "CONNECTIVITY_SERVICE_UNWANTED";
+            case TEAR_DOWN_REASON_SIM_REMOVAL -> "SIM_REMOVAL";
+            case TEAR_DOWN_REASON_AIRPLANE_MODE_ON -> "AIRPLANE_MODE_ON";
+            case TEAR_DOWN_REASON_DATA_DISABLED -> "DATA_DISABLED";
+            case TEAR_DOWN_REASON_NO_LIVE_REQUEST -> "TEAR_DOWN_REASON_NO_LIVE_REQUEST";
+            case TEAR_DOWN_REASON_RAT_NOT_ALLOWED -> "TEAR_DOWN_REASON_RAT_NOT_ALLOWED";
+            case TEAR_DOWN_REASON_ROAMING_DISABLED -> "TEAR_DOWN_REASON_ROAMING_DISABLED";
+            case TEAR_DOWN_REASON_CONCURRENT_VOICE_DATA_NOT_ALLOWED ->
+                    "TEAR_DOWN_REASON_CONCURRENT_VOICE_DATA_NOT_ALLOWED";
+            case TEAR_DOWN_REASON_SERVICE_OPTION_NOT_SUPPORTED ->
+                    "TEAR_DOWN_REASON_SERVICE_OPTION_NOT_SUPPORTED";
+            case TEAR_DOWN_REASON_DATA_SERVICE_NOT_READY ->
+                    "TEAR_DOWN_REASON_DATA_SERVICE_NOT_READY";
+            case TEAR_DOWN_REASON_POWER_OFF_BY_CARRIER -> "TEAR_DOWN_REASON_POWER_OFF_BY_CARRIER";
+            case TEAR_DOWN_REASON_DATA_STALL -> "TEAR_DOWN_REASON_DATA_STALL";
+            case TEAR_DOWN_REASON_HANDOVER_FAILED -> "TEAR_DOWN_REASON_HANDOVER_FAILED";
+            case TEAR_DOWN_REASON_HANDOVER_NOT_ALLOWED -> "TEAR_DOWN_REASON_HANDOVER_NOT_ALLOWED";
+            case TEAR_DOWN_REASON_VCN_REQUESTED -> "TEAR_DOWN_REASON_VCN_REQUESTED";
+            case TEAR_DOWN_REASON_VOPS_NOT_SUPPORTED -> "TEAR_DOWN_REASON_VOPS_NOT_SUPPORTED";
+            case TEAR_DOWN_REASON_DEFAULT_DATA_UNSELECTED ->
+                    "TEAR_DOWN_REASON_DEFAULT_DATA_UNSELECTED";
+            case TEAR_DOWN_REASON_NOT_IN_SERVICE -> "TEAR_DOWN_REASON_NOT_IN_SERVICE";
+            case TEAR_DOWN_REASON_DATA_CONFIG_NOT_READY -> "TEAR_DOWN_REASON_DATA_CONFIG_NOT_READY";
+            case TEAR_DOWN_REASON_PENDING_TEAR_DOWN_ALL -> "TEAR_DOWN_REASON_PENDING_TEAR_DOWN_ALL";
+            case TEAR_DOWN_REASON_NO_SUITABLE_DATA_PROFILE ->
+                    "TEAR_DOWN_REASON_NO_SUITABLE_DATA_PROFILE";
+            case TEAR_DOWN_REASON_CDMA_EMERGENCY_CALLBACK_MODE ->
+                    "TEAR_DOWN_REASON_CDMA_EMERGENCY_CALLBACK_MODE";
+            case TEAR_DOWN_REASON_RETRY_SCHEDULED -> "TEAR_DOWN_REASON_RETRY_SCHEDULED";
+            case TEAR_DOWN_REASON_DATA_THROTTLED -> "TEAR_DOWN_REASON_DATA_THROTTLED";
+            case TEAR_DOWN_REASON_DATA_PROFILE_INVALID -> "TEAR_DOWN_REASON_DATA_PROFILE_INVALID";
+            case TEAR_DOWN_REASON_DATA_PROFILE_NOT_PREFERRED ->
+                    "TEAR_DOWN_REASON_DATA_PROFILE_NOT_PREFERRED";
+            case TEAR_DOWN_REASON_NOT_ALLOWED_BY_POLICY -> "TEAR_DOWN_REASON_NOT_ALLOWED_BY_POLICY";
+            case TEAR_DOWN_REASON_ILLEGAL_STATE -> "TEAR_DOWN_REASON_ILLEGAL_STATE";
+            case TEAR_DOWN_REASON_ONLY_ALLOWED_SINGLE_NETWORK ->
+                    "TEAR_DOWN_REASON_ONLY_ALLOWED_SINGLE_NETWORK";
+            case TEAR_DOWN_REASON_PREFERRED_DATA_SWITCHED ->
+                    "TEAR_DOWN_REASON_PREFERRED_DATA_SWITCHED";
+            case TEAR_DOWN_REASON_DATA_LIMIT_REACHED -> "TEAR_DOWN_REASON_DATA_LIMIT_REACHED";
+            case TEAR_DOWN_REASON_DATA_NETWORK_TRANSPORT_NOT_ALLOWED ->
+                    "TEAR_DOWN_REASON_DATA_NETWORK_TRANSPORT_NOT_ALLOWED";
+            default -> "UNKNOWN(" + reason + ")";
+        };
     }
 
     /**
@@ -3854,65 +3920,43 @@
      * @param event The event
      * @return The event in string format.
      */
-    private static @NonNull String eventToString(int event) {
-        switch (event) {
-            case EVENT_DATA_CONFIG_UPDATED:
-                return "EVENT_DATA_CONFIG_UPDATED";
-            case EVENT_ATTACH_NETWORK_REQUEST:
-                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:
-                return "EVENT_SETUP_DATA_NETWORK_RESPONSE";
-            case EVENT_TEAR_DOWN_NETWORK:
-                return "EVENT_TEAR_DOWN_NETWORK";
-            case EVENT_DATA_STATE_CHANGED:
-                return "EVENT_DATA_STATE_CHANGED";
-            case EVENT_SERVICE_STATE_CHANGED:
-                return "EVENT_DATA_NETWORK_TYPE_REG_STATE_CHANGED";
-            case EVENT_DETACH_ALL_NETWORK_REQUESTS:
-                return "EVENT_DETACH_ALL_NETWORK_REQUESTS";
-            case EVENT_BANDWIDTH_ESTIMATE_FROM_MODEM_CHANGED:
-                return "EVENT_BANDWIDTH_ESTIMATE_FROM_MODEM_CHANGED";
-            case EVENT_DISPLAY_INFO_CHANGED:
-                return "EVENT_DISPLAY_INFO_CHANGED";
-            case EVENT_HANDOVER_RESPONSE:
-                return "EVENT_HANDOVER_RESPONSE";
-            case EVENT_SUBSCRIPTION_PLAN_OVERRIDE:
-                return "EVENT_SUBSCRIPTION_PLAN_OVERRIDE";
-            case EVENT_PCO_DATA_RECEIVED:
-                return "EVENT_PCO_DATA_RECEIVED";
-            case EVENT_CARRIER_PRIVILEGED_UIDS_CHANGED:
-                return "EVENT_CARRIER_PRIVILEGED_UIDS_CHANGED";
-            case EVENT_DEACTIVATE_DATA_NETWORK_RESPONSE:
-                return "EVENT_DEACTIVATE_DATA_NETWORK_RESPONSE";
-            case EVENT_STUCK_IN_TRANSIENT_STATE:
-                return "EVENT_STUCK_IN_TRANSIENT_STATE";
-            case EVENT_WAITING_FOR_TEARING_DOWN_CONDITION_MET:
-                return "EVENT_WAITING_FOR_TEARING_DOWN_CONDITION_MET";
-            case EVENT_VOICE_CALL_STARTED:
-                return "EVENT_VOICE_CALL_STARTED";
-            case EVENT_VOICE_CALL_ENDED:
-                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";
-            case EVENT_DATA_NETWORK_VALIDATION_REQUESTED:
-                return "EVENT_DATA_NETWORK_VALIDATION_REQUESTED";
-            case EVENT_DATA_NETWORK_VALIDATION_RESPONSE:
-                return "EVENT_DATA_NETWORK_VALIDATION_RESPONSE";
-            default:
-                return "Unknown(" + event + ")";
-        }
+    @NonNull
+    private static String eventToString(int event) {
+        return switch (event) {
+            case EVENT_DATA_CONFIG_UPDATED -> "EVENT_DATA_CONFIG_UPDATED";
+            case EVENT_ATTACH_NETWORK_REQUEST -> "EVENT_ATTACH_NETWORK_REQUEST";
+            case EVENT_DETACH_NETWORK_REQUEST -> "EVENT_DETACH_NETWORK_REQUEST";
+            case EVENT_RADIO_NOT_AVAILABLE -> "EVENT_RADIO_NOT_AVAILABLE";
+            case EVENT_ALLOCATE_PDU_SESSION_ID_RESPONSE -> "EVENT_ALLOCATE_PDU_SESSION_ID_RESPONSE";
+            case EVENT_SETUP_DATA_NETWORK_RESPONSE -> "EVENT_SETUP_DATA_NETWORK_RESPONSE";
+            case EVENT_TEAR_DOWN_NETWORK -> "EVENT_TEAR_DOWN_NETWORK";
+            case EVENT_DATA_STATE_CHANGED -> "EVENT_DATA_STATE_CHANGED";
+            case EVENT_SERVICE_STATE_CHANGED -> "EVENT_DATA_NETWORK_TYPE_REG_STATE_CHANGED";
+            case EVENT_DETACH_ALL_NETWORK_REQUESTS -> "EVENT_DETACH_ALL_NETWORK_REQUESTS";
+            case EVENT_BANDWIDTH_ESTIMATE_FROM_MODEM_CHANGED ->
+                    "EVENT_BANDWIDTH_ESTIMATE_FROM_MODEM_CHANGED";
+            case EVENT_CANCEL_HANDOVER_NO_RESPONSE -> "EVENT_CANCEL_HANDOVER_NO_RESPONSE";
+            case EVENT_DISPLAY_INFO_CHANGED -> "EVENT_DISPLAY_INFO_CHANGED";
+            case EVENT_HANDOVER_RESPONSE -> "EVENT_HANDOVER_RESPONSE";
+            case EVENT_SUBSCRIPTION_PLAN_OVERRIDE -> "EVENT_SUBSCRIPTION_PLAN_OVERRIDE";
+            case EVENT_PCO_DATA_RECEIVED -> "EVENT_PCO_DATA_RECEIVED";
+            case EVENT_CARRIER_PRIVILEGED_UIDS_CHANGED -> "EVENT_CARRIER_PRIVILEGED_UIDS_CHANGED";
+            case EVENT_DEACTIVATE_DATA_NETWORK_RESPONSE -> "EVENT_DEACTIVATE_DATA_NETWORK_RESPONSE";
+            case EVENT_STUCK_IN_TRANSIENT_STATE -> "EVENT_STUCK_IN_TRANSIENT_STATE";
+            case EVENT_WAITING_FOR_TEARING_DOWN_CONDITION_MET ->
+                    "EVENT_WAITING_FOR_TEARING_DOWN_CONDITION_MET";
+            case EVENT_VOICE_CALL_STARTED -> "EVENT_VOICE_CALL_STARTED";
+            case EVENT_VOICE_CALL_ENDED -> "EVENT_VOICE_CALL_ENDED";
+            case EVENT_CSS_INDICATOR_CHANGED -> "EVENT_CSS_INDICATOR_CHANGED";
+            case EVENT_NOTIFY_HANDOVER_STARTED -> "EVENT_NOTIFY_HANDOVER_STARTED";
+            case EVENT_NOTIFY_HANDOVER_STARTED_RESPONSE -> "EVENT_NOTIFY_HANDOVER_STARTED_RESPONSE";
+            case EVENT_NOTIFY_HANDOVER_CANCELLED_RESPONSE ->
+                    "EVENT_NOTIFY_HANDOVER_CANCELLED_RESPONSE";
+            case EVENT_DATA_NETWORK_VALIDATION_REQUESTED ->
+                    "EVENT_DATA_NETWORK_VALIDATION_REQUESTED";
+            case EVENT_DATA_NETWORK_VALIDATION_RESPONSE -> "EVENT_DATA_NETWORK_VALIDATION_RESPONSE";
+            default -> "Unknown(" + event + ")";
+        };
     }
 
     @Override
@@ -3925,7 +3969,8 @@
     /**
      * @return The short name of the data network (e.g. DN-C-1)
      */
-    public @NonNull String name() {
+    @NonNull
+    public String name() {
         return mLogTag;
     }
 
diff --git a/src/java/com/android/internal/telephony/data/DataNetworkController.java b/src/java/com/android/internal/telephony/data/DataNetworkController.java
index 5a688ca..a720e03 100644
--- a/src/java/com/android/internal/telephony/data/DataNetworkController.java
+++ b/src/java/com/android/internal/telephony/data/DataNetworkController.java
@@ -17,6 +17,7 @@
 package com.android.internal.telephony.data;
 
 import android.annotation.CallbackExecutor;
+import android.annotation.ElapsedRealtimeLong;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -38,6 +39,7 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
+import android.os.SystemClock;
 import android.telecom.TelecomManager;
 import android.telephony.AccessNetworkConstants;
 import android.telephony.AccessNetworkConstants.AccessNetworkType;
@@ -244,32 +246,46 @@
             TimeUnit.SECONDS.toMillis(1);
 
     /**
-     * The delay in milliseconds to re-evaluate existing data networks for bootstrap sim data usage
-     * limit.
+     * The guard timer in milliseconds to limit querying the data usage api stats frequently
      */
-    private static final long REEVALUATE_BOOTSTRAP_SIM_DATA_USAGE_MILLIS =
-            TimeUnit.SECONDS.toMillis(60);
+    private static final long GUARD_TIMER_INTERVAL_TO_QUERY_DATA_USAGE_API_STATS_MILLIS =
+            TimeUnit.SECONDS.toMillis(1);
 
     /**
      * bootstrap sim total data usage bytes
      */
     private long mBootStrapSimTotalDataUsageBytes = 0L;
 
+    /**
+     * bootstrap sim last data usage query time
+     */
+    @ElapsedRealtimeLong
+    private long mBootstrapSimLastDataUsageQueryTime = 0L;
+
     private final Phone mPhone;
     private final String mLogTag;
     private final LocalLog mLocalLog = new LocalLog(128);
 
-    private final @NonNull DataConfigManager mDataConfigManager;
-    private final @NonNull DataSettingsManager mDataSettingsManager;
-    private final @NonNull DataProfileManager mDataProfileManager;
-    private final @NonNull DataStallRecoveryManager mDataStallRecoveryManager;
-    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<>();
+    @NonNull
+    private final DataConfigManager mDataConfigManager;
+    @NonNull
+    private final DataSettingsManager mDataSettingsManager;
+    @NonNull
+    private final DataProfileManager mDataProfileManager;
+    @NonNull
+    private final DataStallRecoveryManager mDataStallRecoveryManager;
+    @NonNull
+    private final AccessNetworksManager mAccessNetworksManager;
+    @NonNull
+    private final DataRetryManager mDataRetryManager;
+    @NonNull
+    private final ImsManager mImsManager;
+    @NonNull
+    private final TelecomManager mTelecomManager;
+    @NonNull
+    private final NetworkPolicyManager mNetworkPolicyManager;
+    @NonNull
+    private final SparseArray<DataServiceManager> mDataServiceManagers = new SparseArray<>();
 
     /** The subscription index associated with this data network controller. */
     private int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
@@ -277,35 +293,41 @@
     /** The current service state of the device. */
     // Note that keeping a copy here instead of directly using ServiceStateTracker.getServiceState()
     // is intended for detecting the delta.
-    private @NonNull ServiceState mServiceState;
+    @NonNull
+    private ServiceState mServiceState;
 
     /** The list of SubscriptionPlans, updated when initialized and when plans are changed. */
-    private final @NonNull List<SubscriptionPlan> mSubscriptionPlans = new ArrayList<>();
+    @NonNull
+    private final List<SubscriptionPlan> mSubscriptionPlans = new ArrayList<>();
 
     /**
      * The set of network types an unmetered override applies to, set by onSubscriptionOverride
      * and cleared when the device is rebooted or the override expires.
      */
-    private final @NonNull @NetworkType Set<Integer> mUnmeteredOverrideNetworkTypes =
-            new ArraySet<>();
+    @NonNull
+    @NetworkType
+    private final Set<Integer> mUnmeteredOverrideNetworkTypes = new ArraySet<>();
 
     /**
      * The set of network types a congested override applies to, set by onSubscriptionOverride
      * and cleared when the device is rebooted or the override expires.
      */
-    private final @NonNull @NetworkType Set<Integer> mCongestedOverrideNetworkTypes =
-            new ArraySet<>();
+    @NonNull
+    @NetworkType
+    private final Set<Integer> mCongestedOverrideNetworkTypes = new ArraySet<>();
 
     /**
      * The list of all network requests.
      */
-    private final @NonNull NetworkRequestList mAllNetworkRequestList = new NetworkRequestList();
+    @NonNull
+    private final NetworkRequestList mAllNetworkRequestList = new NetworkRequestList();
 
     /**
      * The current data network list, including the ones that are connected, connecting, or
      * disconnecting.
      */
-    private final @NonNull List<DataNetwork> mDataNetworkList = new ArrayList<>();
+    @NonNull
+    private final List<DataNetwork> mDataNetworkList = new ArrayList<>();
 
     /** {@code true} indicating at least one data network exists. */
     private boolean mAnyDataNetworkExisting;
@@ -313,27 +335,33 @@
     /**
      * Contain the last 10 data networks that were connected. This is for debugging purposes only.
      */
-    private final @NonNull List<DataNetwork> mPreviousConnectedDataNetworkList = new ArrayList<>();
+    @NonNull
+    private final List<DataNetwork> mPreviousConnectedDataNetworkList = new ArrayList<>();
 
     /**
      * The internet data network state. Note that this is the best effort if more than one
      * data network supports internet.
      */
-    private @DataState int mInternetDataNetworkState = TelephonyManager.DATA_DISCONNECTED;
+    @DataState
+    private int mInternetDataNetworkState = TelephonyManager.DATA_DISCONNECTED;
 
     /** All the current connected/handover internet networks.  */
-    @NonNull private Set<DataNetwork> mConnectedInternetNetworks = new HashSet<>();
+    @NonNull
+    private Set<DataNetwork> mConnectedInternetNetworks = new HashSet<>();
 
     /**
      * The IMS data network state. For now this is just for debugging purposes.
      */
-    private @DataState int mImsDataNetworkState = TelephonyManager.DATA_DISCONNECTED;
+    @DataState
+    private int mImsDataNetworkState = TelephonyManager.DATA_DISCONNECTED;
 
     /** Overall aggregated link status from internet data networks. */
-    private @LinkStatus int mInternetLinkStatus = DataCallResponse.LINK_STATUS_UNKNOWN;
+    @LinkStatus
+    private int mInternetLinkStatus = DataCallResponse.LINK_STATUS_UNKNOWN;
 
     /** Data network controller callbacks. */
-    private final @NonNull Set<DataNetworkControllerCallback> mDataNetworkControllerCallbacks =
+    @NonNull
+    private final Set<DataNetworkControllerCallback> mDataNetworkControllerCallbacks =
             new ArraySet<>();
 
     /** Indicates if packet switch data is restricted by the cellular network. */
@@ -349,54 +377,59 @@
      * Indicates if the data services are bound. Key if the transport type, and value is the boolean
      * indicating service is bound or not.
      */
-    private final @NonNull SparseBooleanArray mDataServiceBound = new SparseBooleanArray();
+    @NonNull
+    private final SparseBooleanArray mDataServiceBound = new SparseBooleanArray();
 
     /** SIM state. */
-    private @SimState int mSimState = TelephonyManager.SIM_STATE_UNKNOWN;
+    @SimState
+    private int mSimState = TelephonyManager.SIM_STATE_UNKNOWN;
 
     /** Data activity. */
-    private @DataActivityType int mDataActivity = TelephonyManager.DATA_ACTIVITY_NONE;
+    @DataActivityType
+    private int mDataActivity = TelephonyManager.DATA_ACTIVITY_NONE;
 
     /**
      * IMS state callbacks. Key is the IMS feature, value is the callback.
      */
-    private final @NonNull SparseArray<ImsStateCallback> mImsStateCallbacks = new SparseArray<>();
+    @NonNull
+    private final SparseArray<ImsStateCallback> mImsStateCallbacks = new SparseArray<>();
 
     /** Registered IMS features. Unregistered IMS features are removed from the set. */
-    private final @NonNull Set<Integer> mRegisteredImsFeatures = new ArraySet<>();
+    @NonNull
+    private final Set<Integer> mRegisteredImsFeatures = new ArraySet<>();
 
     /** IMS feature package names. Key is the IMS feature, value is the package name. */
-    private final @NonNull SparseArray<String> mImsFeaturePackageName = new SparseArray<>();
+    @NonNull
+    private final SparseArray<String> mImsFeaturePackageName = new SparseArray<>();
 
     /**
      * Networks that are pending IMS de-registration. Key is the data network, value is the function
      * to tear down the network.
      */
-    private final @NonNull Map<DataNetwork, Runnable> mPendingImsDeregDataNetworks =
-            new ArrayMap<>();
+    @NonNull
+    private final Map<DataNetwork, Runnable> mPendingImsDeregDataNetworks = new ArrayMap<>();
 
     /**
      * IMS feature registration callback. The key is the IMS feature, the value is the registration
      * callback. When new SIM inserted, the old callbacks associated with the old subscription index
      * will be unregistered.
      */
-    private final @NonNull SparseArray<RegistrationManager.RegistrationCallback>
+    @NonNull
+    private final SparseArray<RegistrationManager.RegistrationCallback>
             mImsFeatureRegistrationCallbacks = new SparseArray<>();
 
     /** The counter to detect back to back release/request IMS network. */
-    private @NonNull SlidingWindowEventCounter mImsThrottleCounter;
+    @NonNull
+    private SlidingWindowEventCounter mImsThrottleCounter;
     /** Event counter for unwanted network within time window, is used to trigger anomaly report. */
-    private @NonNull SlidingWindowEventCounter mNetworkUnwantedCounter;
+    @NonNull
+    private SlidingWindowEventCounter mNetworkUnwantedCounter;
     /** Event counter for WLAN setup data failure within time window to trigger anomaly report. */
-    private @NonNull SlidingWindowEventCounter mSetupDataCallWlanFailureCounter;
+    @NonNull
+    private SlidingWindowEventCounter mSetupDataCallWlanFailureCounter;
     /** Event counter for WWAN setup data failure within time window to trigger anomaly report. */
-    private @NonNull SlidingWindowEventCounter mSetupDataCallWwanFailureCounter;
-
-    /**
-     * {@code true} if {@link #tearDownAllDataNetworks(int)} was invoked and waiting for all
-     * networks torn down.
-     */
-    private boolean mPendingTearDownAllNetworks = false;
+    @NonNull
+    private SlidingWindowEventCounter mSetupDataCallWwanFailureCounter;
 
     /**
      * The capabilities of the latest released IMS request. To detect back to back release/request
@@ -407,7 +440,8 @@
     /** True after try to release an IMS network; False after try to request an IMS network. */
     private boolean mLastImsOperationIsRelease;
 
-    private final @NonNull FeatureFlags mFeatureFlags;
+    @NonNull
+    private final FeatureFlags mFeatureFlags;
 
     /** The broadcast receiver. */
     private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
@@ -436,7 +470,7 @@
     /**
      * The sorted network request list by priority. The highest priority network request stays at
      * the head of the list. The highest priority is 100, the lowest is 0.
-     *
+     * <p>
      * Note this list is not thread-safe. Do not access the list from different threads.
      */
     @VisibleForTesting
@@ -519,7 +553,8 @@
          * @return The first network request in the list that contains all the provided
          * capabilities.
          */
-        public @Nullable TelephonyNetworkRequest get(@NonNull @NetCapability int[] netCaps) {
+        @Nullable
+        public TelephonyNetworkRequest get(@NonNull @NetCapability int[] netCaps) {
             int index = 0;
             while (index < size()) {
                 TelephonyNetworkRequest networkRequest = get(index);
@@ -528,7 +563,7 @@
                         .boxed()
                         .collect(Collectors.toSet())
                         .containsAll(Arrays.stream(netCaps).boxed()
-                                .collect(Collectors.toList()))) {
+                                .toList())) {
                     return networkRequest;
                 }
                 index++;
@@ -706,19 +741,26 @@
         private static final String RULE_TAG_ROAMING = "roaming";
 
         /** Handover rule type. */
-        public final @HandoverRuleType int type;
+        @HandoverRuleType
+        public final int type;
 
         /** The applicable source access networks for handover. */
-        public final @NonNull @RadioAccessNetworkType Set<Integer> sourceAccessNetworks;
+        @NonNull
+        @RadioAccessNetworkType
+        public final Set<Integer> sourceAccessNetworks;
 
         /** The applicable target access networks for handover. */
-        public final @NonNull @RadioAccessNetworkType Set<Integer> targetAccessNetworks;
+        @NonNull
+        @RadioAccessNetworkType
+        public final Set<Integer> targetAccessNetworks;
 
         /**
          * The network capabilities to any of which this handover rule applies.
          * If is empty, then capability is ignored as a rule matcher.
          */
-        public final @NonNull @NetCapability Set<Integer> networkCapabilities;
+        @NonNull
+        @NetCapability
+        public final Set<Integer> networkCapabilities;
 
         /** {@code true} indicates this policy is only applicable when the device is roaming. */
         public final boolean isOnlyForRoaming;
@@ -1195,7 +1237,7 @@
                 mSubscriptionPlans.clear();
                 mSubscriptionPlans.addAll(Arrays.asList(plans));
                 mDataNetworkControllerCallbacks.forEach(cb -> cb.invokeFromExecutor(
-                        () -> cb.onSubscriptionPlanOverride()));
+                        cb::onSubscriptionPlanOverride));
                 break;
             case EVENT_SUBSCRIPTION_OVERRIDE:
                 int overrideMask = msg.arg1;
@@ -1215,7 +1257,7 @@
                         }
                     }
                     mDataNetworkControllerCallbacks.forEach(cb -> cb.invokeFromExecutor(
-                            () -> cb.onSubscriptionPlanOverride()));
+                            cb::onSubscriptionPlanOverride));
                 } else if (overrideMask == NetworkPolicyManager.SUBSCRIPTION_OVERRIDE_CONGESTED) {
                     log("Congested subscription override: override=" + override
                             + ", networkTypes=" + Arrays.stream(networkTypes)
@@ -1229,7 +1271,7 @@
                         }
                     }
                     mDataNetworkControllerCallbacks.forEach(cb -> cb.invokeFromExecutor(
-                            () -> cb.onSubscriptionPlanOverride()));
+                            cb::onSubscriptionPlanOverride));
                 } else {
                     loge("Unknown override mask: " + overrideMask);
                 }
@@ -1368,28 +1410,6 @@
             // When reaching here, it means this data network can satisfy all the network requests.
             logv("Found a compatible data network " + dataNetwork + ". Attaching "
                     + requestList);
-
-            // If WLAN preferred, see whether a more suitable data profile shall be used to satisfy
-            // a short-lived request that doesn't perform handover.
-            int capability = requestList.getFirst().getApnTypeNetworkCapability();
-            int preferredTransport = mAccessNetworksManager
-                    .getPreferredTransportByNetworkCapability(capability);
-            if (capability == NetworkCapabilities.NET_CAPABILITY_MMS
-                    && preferredTransport != dataNetwork.getTransport()
-                    && preferredTransport == AccessNetworkConstants.TRANSPORT_TYPE_WLAN) {
-                DataProfile candidate = mDataProfileManager
-                        .getDataProfileForNetworkRequest(requestList.getFirst(),
-                                TelephonyManager.NETWORK_TYPE_IWLAN,
-                                mServiceState.isUsingNonTerrestrialNetwork(),
-                                isEsimBootStrapProvisioningActivated(),
-                                false/*ignorePermanentFailure*/);
-                if (candidate != null && !dataNetwork.getDataProfile().equals(candidate)) {
-                    logv("But skipped because found better data profile " + candidate
-                            + DataUtils.networkCapabilityToString(capability) + " preferred on "
-                            + AccessNetworkConstants.transportTypeToString(preferredTransport));
-                    continue;
-                }
-            }
             return dataNetwork.attachNetworkRequests(requestList);
         }
         return false;
@@ -1519,7 +1539,8 @@
      * @return List of the reasons why internet data is not allowed. An empty list if internet
      * is allowed.
      */
-    public @NonNull List<DataDisallowedReason> getInternetDataDisallowedReasons() {
+    @NonNull
+    public List<DataDisallowedReason> getInternetDataDisallowedReasons() {
         TelephonyNetworkRequest internetRequest = new TelephonyNetworkRequest(
                 new NetworkRequest.Builder()
                         .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
@@ -1537,7 +1558,8 @@
      * @param reason The reason for evaluation.
      * @return The data evaluation result.
      */
-    private @NonNull DataEvaluation evaluateNetworkRequest(
+    @NonNull
+    private DataEvaluation evaluateNetworkRequest(
             @NonNull TelephonyNetworkRequest networkRequest, DataEvaluationReason reason) {
         DataEvaluation evaluation = new DataEvaluation(reason);
         int transport = mAccessNetworksManager.getPreferredTransportByNetworkCapability(
@@ -1631,7 +1653,7 @@
         }
 
         // Check if there are pending tear down all networks request.
-        if (mPendingTearDownAllNetworks) {
+        if (mPhone.getServiceStateTracker().isPendingRadioPowerOffAfterDataOff()) {
             evaluation.addDataDisallowedReason(DataDisallowedReason.PENDING_TEAR_DOWN_ALL);
         }
 
@@ -1755,7 +1777,7 @@
         networkRequest.setEvaluation(evaluation);
         // EXTERNAL_QUERY generates too many log spam.
         if (reason != DataEvaluationReason.EXTERNAL_QUERY) {
-            log(evaluation.toString() + ", network type="
+            log(evaluation + ", network type="
                     + TelephonyManager.getNetworkTypeName(getDataNetworkType(transport))
                     + ", reg state="
                     + NetworkRegistrationInfo.registrationStateToString(
@@ -1770,7 +1792,8 @@
      *  - At evaluation network request and evaluation data network determines, if
      *    bootstrap sim current data usage reached bootstrap sim max data limit allowed set
      *    at {@link DataConfigManager#getEsimBootStrapMaxDataLimitBytes()}
-     *  - Query the current data usage at {@link #getDataUsage()}
+     *  - Query the current data usage at {@link #getDataUsage()}, if last data usage query guarding
+     *    interval as expired.
      *
      * @return true, if bootstrap sim data limit is reached
      *         else false, if bootstrap sim max data limit allowed set is -1(Unlimited) or current
@@ -1785,13 +1808,15 @@
             return false;
         }
 
-        log("current bootstrap sim data Usage: " + mBootStrapSimTotalDataUsageBytes);
-        if (mBootStrapSimTotalDataUsageBytes >= esimBootStrapMaxDataLimitBytes) {
-            return true;
-        } else {
+        if (mBootStrapSimTotalDataUsageBytes < esimBootStrapMaxDataLimitBytes
+                && (mBootstrapSimLastDataUsageQueryTime == 0
+                || SystemClock.elapsedRealtime() - mBootstrapSimLastDataUsageQueryTime
+                > GUARD_TIMER_INTERVAL_TO_QUERY_DATA_USAGE_API_STATS_MILLIS)) {
             mBootStrapSimTotalDataUsageBytes = getDataUsage();
-            return mBootStrapSimTotalDataUsageBytes >= esimBootStrapMaxDataLimitBytes;
+            log("current bootstrap sim data usage: " + mBootStrapSimTotalDataUsageBytes);
+            mBootstrapSimLastDataUsageQueryTime =  SystemClock.elapsedRealtime();
         }
+        return mBootStrapSimTotalDataUsageBytes >= esimBootStrapMaxDataLimitBytes;
     }
 
     /**
@@ -1811,8 +1836,7 @@
 
             if (!TextUtils.isEmpty(subscriberId)) {
                 builder.setSubscriberIds(Set.of(subscriberId));
-                // Consider data usage calculation of only metered network.
-                // Emergency data usage is excluded.
+                // Consider data usage calculation of only metered capabilities / data network
                 builder.setMeteredness(android.net.NetworkStats.METERED_YES);
                 NetworkTemplate template = builder.build();
                 final NetworkStats.Bucket ret = networkStatsManager
@@ -1827,7 +1851,8 @@
      * @return The grouped unsatisfied network requests. The network requests that have the same
      * network capabilities is grouped into one {@link NetworkRequestList}.
      */
-    private @NonNull List<NetworkRequestList> getGroupedUnsatisfiedNetworkRequests() {
+    @NonNull
+    private List<NetworkRequestList> getGroupedUnsatisfiedNetworkRequests() {
         NetworkRequestList networkRequestList = new NetworkRequestList();
         for (TelephonyNetworkRequest networkRequest : mAllNetworkRequestList) {
             if (networkRequest.getState() == TelephonyNetworkRequest.REQUEST_STATE_UNSATISFIED) {
@@ -1881,7 +1906,8 @@
      *
      * @return The data evaluation result.
      */
-    private @NonNull DataEvaluation evaluateDataNetwork(@NonNull DataNetwork dataNetwork,
+    @NonNull
+    private DataEvaluation evaluateDataNetwork(@NonNull DataNetwork dataNetwork,
             @NonNull DataEvaluationReason reason) {
         DataEvaluation evaluation = new DataEvaluation(reason);
         // Bypass all checks for emergency data network.
@@ -1935,7 +1961,7 @@
                 if (!hasMessages(EVENT_REEVALUATE_EXISTING_DATA_NETWORKS)) {
                     sendMessageDelayed(obtainMessage(EVENT_REEVALUATE_EXISTING_DATA_NETWORKS,
                             DataEvaluationReason.CHECK_DATA_USAGE),
-                            REEVALUATE_BOOTSTRAP_SIM_DATA_USAGE_MILLIS);
+                            mDataConfigManager.getReevaluateBootstrapSimDataUsageMillis());
                 } else {
                     log("skip scheduling evaluating existing data networks since already"
                             + "scheduled");
@@ -2220,7 +2246,8 @@
      *
      * @see CarrierConfigManager#KEY_IWLAN_HANDOVER_POLICY_STRING_ARRAY
      */
-    private @NonNull DataEvaluation evaluateDataNetworkHandover(@NonNull DataNetwork dataNetwork) {
+    @NonNull
+    private DataEvaluation evaluateDataNetworkHandover(@NonNull DataNetwork dataNetwork) {
         DataEvaluation dataEvaluation = new DataEvaluation(DataEvaluationReason.DATA_HANDOVER);
         if (!dataNetwork.isConnecting() && !dataNetwork.isConnected()) {
             dataEvaluation.addDataDisallowedReason(DataDisallowedReason.ILLEGAL_STATE);
@@ -2278,10 +2305,7 @@
                     sourceNetworkType);
             NetworkRegistrationInfo nri = mServiceState.getNetworkRegistrationInfo(
                     NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
-            boolean isWwanInService = false;
-            if (nri != null && nri.isInService()) {
-                isWwanInService = true;
-            }
+            boolean isWwanInService = nri != null && nri.isInService();
             // If WWAN is inService, use the real roaming state reported by modem instead of
             // using the overridden roaming state, otherwise get last known roaming state stored
             // in data network.
@@ -2343,7 +2367,8 @@
      * {@link #evaluateDataNetwork(DataNetwork, DataEvaluationReason)}.
      * @return The tear down reason.
      */
-    private static @TearDownReason int getTearDownReason(@NonNull DataEvaluation dataEvaluation) {
+    @TearDownReason
+    private static int getTearDownReason(@NonNull DataEvaluation dataEvaluation) {
         if (dataEvaluation.containsDisallowedReasons()) {
             switch (dataEvaluation.getDataDisallowedReasons().get(0)) {
                 case DATA_DISABLED:
@@ -2544,14 +2569,14 @@
         RegistrationManager.RegistrationCallback callback =
                 new RegistrationManager.RegistrationCallback() {
                     @Override
-                    public void onRegistered(ImsRegistrationAttributes attributes) {
+                    public void onRegistered(@NonNull ImsRegistrationAttributes attributes) {
                         log("IMS " + DataUtils.imsFeatureToString(imsFeature)
                                 + " registered. Attributes=" + attributes);
                         mRegisteredImsFeatures.add(imsFeature);
                     }
 
                     @Override
-                    public void onUnregistered(ImsReasonInfo info) {
+                    public void onUnregistered(@NonNull ImsReasonInfo info) {
                         log("IMS " + DataUtils.imsFeatureToString(imsFeature)
                                 + " deregistered. Info=" + info);
                         mRegisteredImsFeatures.remove(imsFeature);
@@ -2759,8 +2784,8 @@
      * @param dataProfile The data profile.
      * @return The network requests list.
      */
-    private @NonNull NetworkRequestList findSatisfiableNetworkRequests(
-            @NonNull DataProfile dataProfile) {
+    @NonNull
+    private NetworkRequestList findSatisfiableNetworkRequests(@NonNull DataProfile dataProfile) {
         return new NetworkRequestList(mAllNetworkRequestList.stream()
                 .filter(request -> request.getState()
                         == TelephonyNetworkRequest.REQUEST_STATE_UNSATISFIED)
@@ -2937,7 +2962,6 @@
         mDataNetworkList.remove(dataNetwork);
         trackSetupDataCallFailure(dataNetwork.getTransport(), cause);
         if (mAnyDataNetworkExisting && mDataNetworkList.isEmpty()) {
-            mPendingTearDownAllNetworks = false;
             mAnyDataNetworkExisting = false;
             mDataNetworkControllerCallbacks.forEach(callback -> callback.invokeFromExecutor(
                     () -> callback.onAnyDataNetworkExistingChanged(mAnyDataNetworkExisting)));
@@ -3038,7 +3062,7 @@
         if (isEsimBootStrapProvisioningActivated()) {
             sendMessageDelayed(obtainMessage(EVENT_REEVALUATE_EXISTING_DATA_NETWORKS,
                     DataEvaluationReason.CHECK_DATA_USAGE),
-                    REEVALUATE_BOOTSTRAP_SIM_DATA_USAGE_MILLIS);
+                    mDataConfigManager.getReevaluateBootstrapSimDataUsageMillis());
         }
     }
 
@@ -3215,7 +3239,13 @@
             return;
         }
 
-        if (dataNetwork.isInternetSupported()) {
+        // Only track the networks that require validation.
+        // The criteria is base on NetworkMonitorUtils.java.
+        NetworkCapabilities capabilities = dataNetwork.getNetworkCapabilities();
+        if (capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+                && capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
+                && capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
+                && capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)) {
             if (status == NetworkAgent.VALIDATION_STATUS_NOT_VALID
                     && (dataNetwork.getCurrentState() == null || dataNetwork.isDisconnected())) {
                 log("Ignoring invalid validation status for disconnected DataNetwork");
@@ -3272,7 +3302,6 @@
 
         if (mAnyDataNetworkExisting && mDataNetworkList.isEmpty()) {
             log("All data networks disconnected now.");
-            mPendingTearDownAllNetworks = false;
             mAnyDataNetworkExisting = false;
             mDataNetworkControllerCallbacks.forEach(callback -> callback.invokeFromExecutor(
                     () -> callback.onAnyDataNetworkExistingChanged(mAnyDataNetworkExisting)));
@@ -3706,17 +3735,13 @@
         DataSpecificRegistrationInfo newDsri = newNri.getDataSpecificInfo();
 
         if (newDsri == null) return false;
-        if ((oldDsri == null || oldDsri.getVopsSupportInfo() == null
+        // If previously VoPS was supported (or does not exist), and now the network reports
+        // VoPS not supported, we should evaluate existing data networks to see if they need
+        // to be torn down.
+        return (oldDsri == null || oldDsri.getVopsSupportInfo() == null
                 || oldDsri.getVopsSupportInfo().isVopsSupported())
                 && (newDsri.getVopsSupportInfo() != null && !newDsri.getVopsSupportInfo()
-                .isVopsSupported())) {
-            // If previously VoPS was supported (or does not exist), and now the network reports
-            // VoPS not supported, we should evaluate existing data networks to see if they need
-            // to be torn down.
-            return true;
-        }
-
-        return false;
+                .isVopsSupported());
     }
 
     /**
@@ -3763,17 +3788,13 @@
         DataSpecificRegistrationInfo newDsri = newPsNri.getDataSpecificInfo();
 
         if (oldDsri == null) return false;
-        if ((newDsri == null || newDsri.getVopsSupportInfo() == null
+        // If previously VoPS was not supported, and now the network reports
+        // VoPS supported (or does not report), we should evaluate the unsatisfied network
+        // request to see if the can be satisfied again.
+        return (newDsri == null || newDsri.getVopsSupportInfo() == null
                 || newDsri.getVopsSupportInfo().isVopsSupported())
                 && (oldDsri.getVopsSupportInfo() != null && !oldDsri.getVopsSupportInfo()
-                .isVopsSupported())) {
-            // If previously VoPS was not supported, and now the network reports
-            // VoPS supported (or does not report), we should evaluate the unsatisfied network
-            // request to see if the can be satisfied again.
-            return true;
-        }
-
-        return false;
+                .isVopsSupported());
     }
 
     /**
@@ -3884,28 +3905,32 @@
     /**
      * @return Data config manager instance.
      */
-    public @NonNull DataConfigManager getDataConfigManager() {
+    @NonNull
+    public DataConfigManager getDataConfigManager() {
         return mDataConfigManager;
     }
 
     /**
      * @return Data profile manager instance.
      */
-    public @NonNull DataProfileManager getDataProfileManager() {
+    @NonNull
+    public DataProfileManager getDataProfileManager() {
         return mDataProfileManager;
     }
 
     /**
      * @return Data settings manager instance.
      */
-    public @NonNull DataSettingsManager getDataSettingsManager() {
+    @NonNull
+    public DataSettingsManager getDataSettingsManager() {
         return mDataSettingsManager;
     }
 
     /**
      * @return Data retry manager instance.
      */
-    public @NonNull DataRetryManager getDataRetryManager() {
+    @NonNull
+    public DataRetryManager getDataRetryManager() {
         return mDataRetryManager;
     }
 
@@ -3913,7 +3938,8 @@
      * @return The list of SubscriptionPlans
      */
     @VisibleForTesting
-    public @NonNull List<SubscriptionPlan> getSubscriptionPlans() {
+    @NonNull
+    public List<SubscriptionPlan> getSubscriptionPlans() {
         return mSubscriptionPlans;
     }
 
@@ -3921,7 +3947,9 @@
      * @return The set of network types an unmetered override applies to
      */
     @VisibleForTesting
-    public @NonNull @NetworkType Set<Integer> getUnmeteredOverrideNetworkTypes() {
+    @NonNull
+    @NetworkType
+    public Set<Integer> getUnmeteredOverrideNetworkTypes() {
         return mUnmeteredOverrideNetworkTypes;
     }
 
@@ -3929,7 +3957,9 @@
      * @return The set of network types a congested override applies to
      */
     @VisibleForTesting
-    public @NonNull @NetworkType Set<Integer> getCongestedOverrideNetworkTypes() {
+    @NonNull
+    @NetworkType
+    public Set<Integer> getCongestedOverrideNetworkTypes() {
         return mCongestedOverrideNetworkTypes;
     }
 
@@ -3939,7 +3969,8 @@
      * @param transport The transport.
      * @return The current network type.
      */
-    private @NetworkType int getDataNetworkType(@TransportType int transport) {
+    @NetworkType
+    private int getDataNetworkType(@TransportType int transport) {
         NetworkRegistrationInfo nri = mServiceState.getNetworkRegistrationInfo(
                 NetworkRegistrationInfo.DOMAIN_PS, transport);
         if (nri != null) {
@@ -3955,8 +3986,8 @@
      * @param transport The transport.
      * @return The registration state.
      */
-    private @RegistrationState int getDataRegistrationState(@NonNull ServiceState ss,
-            @TransportType int transport) {
+    @RegistrationState
+    private int getDataRegistrationState(@NonNull ServiceState ss, @TransportType int transport) {
         NetworkRegistrationInfo nri = ss.getNetworkRegistrationInfo(
                 NetworkRegistrationInfo.DOMAIN_PS, transport);
         if (nri != null) {
@@ -3968,7 +3999,8 @@
     /**
      * @return The data activity. Note this is only updated when screen is on.
      */
-    public @DataActivityType int getDataActivity() {
+    @DataActivityType
+    public int getDataActivity() {
         return mDataActivity;
     }
 
@@ -4006,14 +4038,13 @@
      *
      * @param reason The reason to tear down.
      */
-    public void onTearDownAllDataNetworks(@TearDownReason int reason) {
+    private void onTearDownAllDataNetworks(@TearDownReason int reason) {
         log("onTearDownAllDataNetworks: reason=" + DataNetwork.tearDownReasonToString(reason));
         if (mDataNetworkList.isEmpty()) {
             log("tearDownAllDataNetworks: No pending networks. All disconnected now.");
             return;
         }
 
-        mPendingTearDownAllNetworks = true;
         for (DataNetwork dataNetwork : mDataNetworkList) {
             if (!dataNetwork.isDisconnecting()) {
                 tearDownGracefully(dataNetwork, reason);
@@ -4115,14 +4146,16 @@
      *
      * @return The data network state.
      */
-    public @DataState int getInternetDataNetworkState() {
+    @DataState
+    public int getInternetDataNetworkState() {
         return mInternetDataNetworkState;
     }
 
     /**
      * @return List of bound data service packages name on WWAN and WLAN.
      */
-    public @NonNull List<String> getDataServicePackages() {
+    @NonNull
+    public List<String> getDataServicePackages() {
         List<String> packages = new ArrayList<>();
         for (int i = 0; i < mDataServiceManagers.size(); i++) {
             packages.add(mDataServiceManagers.valueAt(i).getDataServicePackageName());
@@ -4132,8 +4165,8 @@
 
     /**
      * Request network validation.
-     *
-     * Nnetwork validation request is sent to the DataNetwork that matches the network capability
+     * <p>
+     * Network validation request is sent to the DataNetwork that matches the network capability
      * in the list of DataNetwork owned by the DNC.
      *
      * @param capability network capability {@link NetCapability}
diff --git a/src/java/com/android/internal/telephony/data/DataProfileManager.java b/src/java/com/android/internal/telephony/data/DataProfileManager.java
index 0da220b..c50d50a 100644
--- a/src/java/com/android/internal/telephony/data/DataProfileManager.java
+++ b/src/java/com/android/internal/telephony/data/DataProfileManager.java
@@ -45,6 +45,7 @@
 import android.util.ArraySet;
 import android.util.IndentingPrintWriter;
 import android.util.LocalLog;
+import android.util.LruCache;
 
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.data.DataConfigManager.DataConfigManagerCallback;
@@ -81,42 +82,51 @@
     private final LocalLog mLocalLog = new LocalLog(128);
 
     /** Data network controller. */
-    private final @NonNull DataNetworkController mDataNetworkController;
+    @NonNull
+    private final DataNetworkController mDataNetworkController;
 
     /** Data config manager. */
-    private final @NonNull DataConfigManager mDataConfigManager;
+    @NonNull
+    private final DataConfigManager mDataConfigManager;
 
     /** Cellular data service. */
-    private final @NonNull DataServiceManager mWwanDataServiceManager;
+    @NonNull
+    private final DataServiceManager mWwanDataServiceManager;
 
     /**
      * 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<>();
+    @NonNull
+    private final List<DataProfile> mAllDataProfiles = new ArrayList<>();
 
     /** The data profile used for initial attach. */
-    private @Nullable DataProfile mInitialAttachDataProfile = null;
+    @Nullable
+    private DataProfile mInitialAttachDataProfile = null;
 
     /** The preferred data profile used for internet. */
-    private @Nullable DataProfile mPreferredDataProfile = null;
+    @Nullable
+    private DataProfile mPreferredDataProfile = null;
 
-    /** The last data profile that's successful for internet connection. */
-    private @Nullable DataProfile mLastInternetDataProfile = null;
+    /** The last data profile that's successful for internet connection by subscription id. */
+    @NonNull
+    private final LruCache<Integer, DataProfile> mLastInternetDataProfiles = new LruCache<>(256);
 
     /** Preferred data profile set id. */
     private int mPreferredDataProfileSetId = Telephony.Carriers.NO_APN_SET_ID;
 
     /** Data profile manager callbacks. */
-    private final @NonNull Set<DataProfileManagerCallback> mDataProfileManagerCallbacks =
-            new ArraySet<>();
+    @NonNull
+    private final Set<DataProfileManagerCallback> mDataProfileManagerCallbacks = new ArraySet<>();
 
     /** SIM state. */
-    private @SimState int mSimState = TelephonyManager.SIM_STATE_UNKNOWN;
+    @SimState
+    private int mSimState = TelephonyManager.SIM_STATE_UNKNOWN;
 
     /** Feature flags controlling which feature is enabled. */
-    private final @NonNull FeatureFlags mFeatureFlags;
+    @NonNull
+    private final FeatureFlags mFeatureFlags;
 
     /**
      * Data profile manager callback. This should be only used by {@link DataNetworkController}.
@@ -452,9 +462,11 @@
             }
         }
 
-        // Update a working internet data profile as a future candidate for preferred data profile
-        // after APNs are reset to default
-        mLastInternetDataProfile = defaultProfile;
+        // Update a working internet data profile by subid as a future candidate for preferred
+        // data profile after APNs are reset to default
+        if (defaultProfile != null) {
+            mLastInternetDataProfiles.put(mPhone.getSubId(), defaultProfile);
+        }
 
         // 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
@@ -473,7 +485,8 @@
      *
      * @return The preferred data profile.
      */
-    private @Nullable DataProfile getPreferredDataProfileFromDb() {
+    @Nullable
+    private DataProfile getPreferredDataProfileFromDb() {
         Cursor cursor = mPhone.getContext().getContentResolver().query(
                 Uri.withAppendedPath(Telephony.Carriers.PREFERRED_APN_URI,
                         String.valueOf(mPhone.getSubId())), null, null, null,
@@ -498,7 +511,8 @@
     /**
      * @return The preferred data profile from carrier config.
      */
-    private @Nullable DataProfile getPreferredDataProfileFromConfig() {
+    @Nullable
+    private DataProfile getPreferredDataProfileFromConfig() {
         // Check if there is configured default preferred data profile.
         String defaultPreferredApn = mDataConfigManager.getDefaultPreferredApn();
         if (!TextUtils.isEmpty(defaultPreferredApn)) {
@@ -542,7 +556,8 @@
      */
     private boolean updatePreferredDataProfile() {
         DataProfile preferredDataProfile;
-        if (SubscriptionManager.isValidSubscriptionId(mPhone.getSubId())) {
+        int subId = mPhone.getSubId();
+        if (SubscriptionManager.isValidSubscriptionId(subId)) {
             preferredDataProfile = getPreferredDataProfileFromDb();
             if (preferredDataProfile == null) {
                 preferredDataProfile = getPreferredDataProfileFromConfig();
@@ -551,7 +566,8 @@
                     setPreferredDataProfile(preferredDataProfile);
                 } else {
                     preferredDataProfile = mAllDataProfiles.stream()
-                            .filter(dp -> areDataProfilesSharingApn(dp, mLastInternetDataProfile))
+                            .filter(dp -> areDataProfilesSharingApn(dp,
+                                    mLastInternetDataProfiles.get(subId)))
                             .findFirst()
                             .orElse(null);
                     if (preferredDataProfile != null) {
@@ -580,10 +596,10 @@
 
     /**
      * Update the data profile used for initial attach.
-     *
+     * <p>
      * Note that starting from Android 13 only APNs that supports "IA" type will be used for
      * initial attach. Please update APN configuration file if needed.
-     *
+     * <p>
      * 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}.
@@ -597,7 +613,7 @@
         // Sort the data profiles so the preferred data profile is at the beginning.
         List<DataProfile> allDataProfiles = mAllDataProfiles.stream()
                 .sorted(Comparator.comparing((DataProfile dp) -> !dp.equals(mPreferredDataProfile)))
-                .collect(Collectors.toList());
+                .toList();
         // Search in the order. "IA" type should be the first from getAllowedInitialAttachApnTypes.
         for (int apnType : mDataConfigManager.getAllowedInitialAttachApnTypes()) {
             initialAttachDataProfile = allDataProfiles.stream()
@@ -636,7 +652,8 @@
      * @param apnTypeBitmask APN type
      * @return The APN setting
      */
-    private @NonNull ApnSetting buildDefaultApnSetting(@NonNull String entry,
+    @NonNull
+    private ApnSetting buildDefaultApnSetting(@NonNull String entry,
             @NonNull String apn, @Annotation.ApnType int apnTypeBitmask) {
         return new ApnSetting.Builder()
                 .setEntryName(entry)
@@ -659,7 +676,8 @@
      * 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(
+    @Nullable
+    public DataProfile getDataProfileForNetworkRequest(
             @NonNull TelephonyNetworkRequest networkRequest, @NetworkType int networkType,
             boolean isNtn, boolean isEsimBootstrapProvisioning, boolean ignorePermanentFailure) {
         ApnSetting apnSetting = null;
@@ -730,7 +748,8 @@
      * 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(
+    @Nullable
+    private ApnSetting getApnSettingForNetworkRequest(
             @NonNull TelephonyNetworkRequest networkRequest, @NetworkType int networkType,
             boolean isNtn, boolean isEsimBootStrapProvisioning, boolean ignorePermanentFailure) {
         if (!networkRequest.hasAttribute(
@@ -793,7 +812,7 @@
             logv("Satisfied profile: " + dataProfile + ", last setup="
                     + DataUtils.elapsedTimeToString(dataProfile.getLastSetupTimestamp()));
         }
-        if (dataProfiles.size() == 0) {
+        if (dataProfiles.isEmpty()) {
             log("Can't find any data profile that can satisfy " + networkRequest);
             return null;
         }
@@ -810,16 +829,14 @@
                                 ApnSetting.INFRASTRUCTURE_SATELLITE)) {
                             return false;
                         }
-                        if (!isNtn && !dp.getApnSetting().isForInfrastructure(
-                                ApnSetting.INFRASTRUCTURE_CELLULAR)) {
-                            return false;
-                        }
+                        return isNtn || dp.getApnSetting().isForInfrastructure(
+                                ApnSetting.INFRASTRUCTURE_CELLULAR);
                     }
 
                     return true;
                 })
                 .collect(Collectors.toList());
-        if (dataProfiles.size() == 0) {
+        if (dataProfiles.isEmpty()) {
             String ntnReason = "";
             if (mFeatureFlags.carrierEnabledSatelliteFlag()) {
                 ntnReason = " and infrastructure for "
@@ -837,7 +854,7 @@
                         == Telephony.Carriers.MATCH_ALL_APN_SET_ID
                         || dp.getApnSetting().getApnSetId() == mPreferredDataProfileSetId))
                 .collect(Collectors.toList());
-        if (dataProfiles.size() == 0) {
+        if (dataProfiles.isEmpty()) {
             log("Can't find any data profile has APN set id matched. mPreferredDataProfileSetId="
                     + mPreferredDataProfileSetId);
             return null;
@@ -845,9 +862,10 @@
 
         // Check if data profiles are permanently failed.
         dataProfiles = dataProfiles.stream()
-                .filter(dp -> ignorePermanentFailure || !dp.getApnSetting().getPermanentFailed())
+                .filter(dp -> ignorePermanentFailure || (dp.getApnSetting() != null
+                        && !dp.getApnSetting().getPermanentFailed()))
                 .collect(Collectors.toList());
-        if (dataProfiles.size() == 0) {
+        if (dataProfiles.isEmpty()) {
             log("The suitable data profiles are all in permanent failed state.");
             return null;
         }
@@ -939,7 +957,8 @@
      * @param setting The Apn setting to be checked.
      */
     private void checkApnSetting(@NonNull ApnSetting setting) {
-        if (setting.canHandleType(ApnSetting.TYPE_MMS)) {
+        if (setting.canHandleType(ApnSetting.TYPE_MMS)
+                && setting.getEditedStatus() == Telephony.Carriers.UNEDITED) {
             if (setting.getMmsc() == null) {
                 reportAnomaly("MMS is supported but no MMSC configured " + setting,
                         "9af73e18-b523-4dc5-adab-19d86c6a3685");
@@ -965,7 +984,7 @@
     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 (a == null || a.getEditedStatus() != Telephony.Carriers.UNEDITED) continue;
             if (// Lingering network is not the default and doesn't cover all the regular networks
                     (int) TelephonyManager.NETWORK_TYPE_BITMASK_UNKNOWN
                     != a.getLingeringNetworkTypeBitmask()
@@ -979,23 +998,6 @@
                                 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");
-                }
-            }
         }
     }
 
@@ -1007,7 +1009,8 @@
      *
      * @return The merged data profile. {@code null} if merging is not possible.
      */
-    private static @Nullable DataProfile mergeDataProfiles(
+    @Nullable
+    private static DataProfile mergeDataProfiles(
             @NonNull DataProfile dp1, @NonNull DataProfile dp2) {
         Objects.requireNonNull(dp1);
         Objects.requireNonNull(dp2);
@@ -1106,10 +1109,6 @@
      * @return {@code true} if the provided data profile can be still used in current environment.
      */
     public boolean isDataProfileCompatible(@NonNull DataProfile dataProfile) {
-        if (dataProfile == null) {
-            return false;
-        }
-
         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.
@@ -1224,7 +1223,10 @@
         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("Last internet data profile for=");
+        pw.increaseIndent();
+        mLastInternetDataProfiles.snapshot().forEach((key, value) -> pw.println(key + ":" + value));
+        pw.decreaseIndent();
         pw.println("Initial attach data profile=" + mInitialAttachDataProfile);
         pw.println("isTetheringDataProfileExisting=" + isTetheringDataProfileExisting(
                 TelephonyManager.NETWORK_TYPE_LTE));
diff --git a/src/java/com/android/internal/telephony/data/DataRetryManager.java b/src/java/com/android/internal/telephony/data/DataRetryManager.java
index 1fdc182..d35e9e8 100644
--- a/src/java/com/android/internal/telephony/data/DataRetryManager.java
+++ b/src/java/com/android/internal/telephony/data/DataRetryManager.java
@@ -150,53 +150,67 @@
     private static final int RESET_REASON_TAC_CHANGED = 6;
 
     /** The phone instance. */
-    private final @NonNull Phone mPhone;
+    @NonNull
+    private final Phone mPhone;
 
     /** Featureflags. */
-    private final @NonNull FeatureFlags mFlags;
+    @NonNull
+    private final FeatureFlags mFlags;
 
     /** The RIL instance. */
-    private final @NonNull CommandsInterface mRil;
+    @NonNull
+    private final CommandsInterface mRil;
 
     /** Logging tag. */
-    private final @NonNull String mLogTag;
+    @NonNull
+    private final String mLogTag;
 
     /** Local log. */
-    private final @NonNull LocalLog mLocalLog = new LocalLog(128);
+    @NonNull
+    private final LocalLog mLocalLog = new LocalLog(128);
 
     /** Alarm Manager used to schedule long set up or handover retries. */
-    private final @NonNull AlarmManager mAlarmManager;
+    @NonNull
+    private final AlarmManager mAlarmManager;
 
     /**
      * The data retry callback. This is only used to notify {@link DataNetworkController} to retry
      * setup data network.
      */
-    private @NonNull Set<DataRetryManagerCallback> mDataRetryManagerCallbacks = new ArraySet<>();
+    @NonNull
+    private final Set<DataRetryManagerCallback> mDataRetryManagerCallbacks = new ArraySet<>();
 
     /** Data service managers. */
-    private @NonNull SparseArray<DataServiceManager> mDataServiceManagers;
+    @NonNull
+    private final SparseArray<DataServiceManager> mDataServiceManagers;
 
     /** Data config manager instance. */
-    private final @NonNull DataConfigManager mDataConfigManager;
+    @NonNull
+    private final DataConfigManager mDataConfigManager;
 
     /** Data profile manager. */
-    private final @NonNull DataProfileManager mDataProfileManager;
+    @NonNull
+    private final DataProfileManager mDataProfileManager;
 
     /** Data setup retry rule list. */
-    private @NonNull List<DataSetupRetryRule> mDataSetupRetryRuleList = new ArrayList<>();
+    @NonNull
+    private List<DataSetupRetryRule> mDataSetupRetryRuleList = new ArrayList<>();
 
     /** Data handover retry rule list. */
-    private @NonNull List<DataHandoverRetryRule> mDataHandoverRetryRuleList = new ArrayList<>();
+    @NonNull
+    private List<DataHandoverRetryRule> mDataHandoverRetryRuleList = new ArrayList<>();
 
     /** Data retry entries. */
-    private final @NonNull List<DataRetryEntry> mDataRetryEntries = new ArrayList<>();
+    @NonNull
+    private final List<DataRetryEntry> mDataRetryEntries = new ArrayList<>();
 
     /**
      * Data throttling entries. Note this only stores throttling requested by networks. We intended
      * not to store frameworks-initiated throttling because they are not explicit/strong throttling
      * requests.
      */
-    private final @NonNull List<DataThrottlingEntry> mDataThrottlingEntries = new ArrayList<>();
+    @NonNull
+    private final List<DataThrottlingEntry> mDataThrottlingEntries = new ArrayList<>();
 
     /**
      * Represent a single data setup/handover throttling reported by networks.
@@ -205,31 +219,37 @@
         /**
          * The data profile that is being throttled for setup/handover retry.
          */
-        public final @NonNull DataProfile dataProfile;
+        @NonNull
+        public final DataProfile dataProfile;
 
         /**
          * The associated network request list when throttling happened. Should be {@code null} when
          * retry type is {@link ThrottleStatus#RETRY_TYPE_HANDOVER}.
          */
-        public final @Nullable NetworkRequestList networkRequestList;
+        @Nullable
+        public final NetworkRequestList networkRequestList;
 
         /**
-         * @param dataNetwork The data network that is being throttled for handover retry. Should be
+         * The data network that is being throttled for handover retry. Should be
          * {@code null} when retryType is {@link ThrottleStatus#RETRY_TYPE_NEW_CONNECTION}.
          */
-        public final @Nullable DataNetwork dataNetwork;
+        @Nullable
+        public final DataNetwork dataNetwork;
 
         /** The transport that the data profile has been throttled on. */
-        public final @TransportType int transport;
+        @TransportType
+        public final int transport;
 
         /** The retry type when throttling expires. */
-        public final @RetryType int retryType;
+        @RetryType
+        public final int retryType;
 
         /**
          * The expiration time of data throttling. This is the time retrieved from
          * {@link SystemClock#elapsedRealtime()}.
          */
-        public final @ElapsedRealtimeLong long expirationTimeMillis;
+        @ElapsedRealtimeLong
+        public final long expirationTimeMillis;
 
         /**
          * Constructor.
@@ -257,7 +277,8 @@
         }
 
         @Override
-        public @NonNull String toString() {
+        @NonNull
+        public String toString() {
             return "[DataThrottlingEntry: dataProfile=" + dataProfile + ", request list="
                     + networkRequestList + ", dataNetwork=" + dataNetwork + ", transport="
                     + AccessNetworkConstants.transportTypeToString(transport) + ", expiration time="
@@ -293,13 +314,17 @@
          * capabilities specified here, then retry will happen. Empty set indicates the retry rule
          * is not using network capabilities.
          */
-        protected @NonNull @NetCapability Set<Integer> mNetworkCapabilities = new ArraySet<>();
+        @NonNull
+        @NetCapability
+        protected Set<Integer> mNetworkCapabilities = new ArraySet<>();
 
         /**
          * The fail causes. If data setup failed with certain fail causes, then retry will happen.
          * Empty set indicates the retry rule is not using the fail causes.
          */
-        protected @NonNull @DataFailureCause Set<Integer> mFailCauses = new ArraySet<>();
+        @NonNull
+        @DataFailureCause
+        protected Set<Integer> mFailCauses = new ArraySet<>();
 
         public DataRetryRule(@NonNull String ruleString) {
             if (TextUtils.isEmpty(ruleString)) {
@@ -353,7 +378,8 @@
          * @return The data network setup retry intervals in milliseconds. If this is empty, then
          * {@link #getMaxRetries()} must return 0.
          */
-        public @NonNull List<Long> getRetryIntervalsMillis() {
+        @NonNull
+        public List<Long> getRetryIntervalsMillis() {
             return mRetryIntervalsMillis;
         }
 
@@ -372,43 +398,44 @@
          * happen. Empty set indicates the retry rule is not using the fail causes.
          */
         @VisibleForTesting
-        public @NonNull @DataFailureCause Set<Integer> getFailCauses() {
+        @NonNull
+        @DataFailureCause
+        public Set<Integer> getFailCauses() {
             return mFailCauses;
         }
     }
 
     /**
      * Represent a rule for data setup retry.
-     *
+     * <p>
      * The syntax of the retry rule:
      * 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]"
-     *
+     * <p>
      * 2. Retry based on {@link DataFailCause}
      * "fail_causes=[cause1|cause2|cause3|..], [retry_interval=n1|n2|n3|n4...], [maximum_retries=n]"
-     *
+     * <p>
      * 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]"
-     *
+     * <p>
      * 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"
-     *
+     * <p>
      * 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.
-     *
+     * <p>
      * "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.
-     *
      */
     public static class DataSetupRetryRule extends DataRetryRule {
         private static final String RULE_TAG_PERMANENT_FAIL_CAUSES = "permanent_fail_causes";
@@ -469,7 +496,9 @@
          * capabilities.
          */
         @VisibleForTesting
-        public @NonNull @NetCapability Set<Integer> getNetworkCapabilities() {
+        @NonNull
+        @NetCapability
+        public Set<Integer> getNetworkCapabilities() {
             return mNetworkCapabilities;
         }
 
@@ -487,7 +516,7 @@
          * @param cause Fail cause from previous setup data request.
          * @return {@code true} if the retry rule can be matched.
          */
-        public boolean canBeMatched(@NonNull @NetCapability int networkCapability,
+        public boolean canBeMatched(@NetCapability int networkCapability,
                 @DataFailureCause int cause) {
             if (!mFailCauses.isEmpty() && !mFailCauses.contains(cause)) {
                 return false;
@@ -510,22 +539,22 @@
 
     /**
      * Represent a handover data network retry rule.
-     *
+     * <p>
      * The syntax of the retry rule:
      * 1. Retry when handover fails.
      * "retry_interval=[n1|n2|n3|...], [maximum_retries=n]"
-     *
+     * <p>
      * For example,
      * "retry_interval=1000|3000|5000, maximum_retries=10" means handover retry will happen in 1s,
      * 3s, 5s, 5s, 5s....up to 10 times.
-     *
+     * <p>
      * 2. Retry when handover fails with certain fail causes.
      * "retry_interval=[n1|n2|n3|...], fail_causes=[cause1|cause2|cause3|...], [maximum_retries=n]
-     *
+     * <p>
      * For example,
      * "retry_interval=1000, maximum_retries=3, fail_causes=5" means handover retry every 1 second
      * for up to 3 times when handover fails with the cause 5.
-     *
+     * <p>
      * "maximum_retries=0, fail_causes=6|10|67" means handover retry should not happen for those
      * causes.
      */
@@ -573,7 +602,8 @@
         public @interface DataRetryState {}
 
         /** The rule used for this data retry. {@code null} if the retry is requested by network. */
-        public final @Nullable DataRetryRule appliedDataRetryRule;
+        @Nullable
+        public final DataRetryRule appliedDataRetryRule;
 
         /** The retry delay in milliseconds. */
         public final long retryDelayMillis;
@@ -582,13 +612,15 @@
          * Retry elapsed time. This is the system elapsed time retrieved from
          * {@link SystemClock#elapsedRealtime()}.
          */
-        public final @ElapsedRealtimeLong long retryElapsedTime;
+        @ElapsedRealtimeLong
+        public final long retryElapsedTime;
 
         /** The retry state. */
         protected int mRetryState = RETRY_STATE_NOT_RETRIED;
 
         /** Timestamp when a state is set. For debugging purposes only. */
-        protected @ElapsedRealtimeLong long mRetryStateTimestamp = 0;
+        @ElapsedRealtimeLong
+        protected long mRetryStateTimestamp;
 
         /**
          * Constructor
@@ -617,7 +649,8 @@
         /**
          * @return Get the retry state.
          */
-        public @DataRetryState int getState() {
+        @DataRetryState
+        public int getState() {
             return mRetryState;
         }
 
@@ -628,13 +661,13 @@
          * @return Retry state in string format.
          */
         public static String retryStateToString(@DataRetryState int retryState) {
-            switch (retryState) {
-                case RETRY_STATE_NOT_RETRIED: return "NOT_RETRIED";
-                case RETRY_STATE_FAILED: return "FAILED";
-                case RETRY_STATE_SUCCEEDED: return "SUCCEEDED";
-                case RETRY_STATE_CANCELLED: return "CANCELLED";
-                default: return "Unknown(" + retryState + ")";
-            }
+            return switch (retryState) {
+                case RETRY_STATE_NOT_RETRIED -> "NOT_RETRIED";
+                case RETRY_STATE_FAILED -> "FAILED";
+                case RETRY_STATE_SUCCEEDED -> "SUCCEEDED";
+                case RETRY_STATE_CANCELLED -> "CANCELLED";
+                default -> "Unknown(" + retryState + ")";
+            };
         }
 
         /**
@@ -649,7 +682,8 @@
             protected long mRetryDelayMillis = TimeUnit.SECONDS.toMillis(5);
 
             /** The applied data retry rule. */
-            protected @Nullable DataRetryRule mAppliedDataRetryRule;
+            @Nullable
+            protected DataRetryRule mAppliedDataRetryRule;
 
             /**
              * Set the data retry delay.
@@ -657,7 +691,8 @@
              * @param retryDelayMillis The retry delay in milliseconds.
              * @return This builder.
              */
-            public @NonNull T setRetryDelay(long retryDelayMillis) {
+            @NonNull
+            public T setRetryDelay(long retryDelayMillis) {
                 mRetryDelayMillis = retryDelayMillis;
                 return (T) this;
             }
@@ -668,7 +703,8 @@
              * @param dataRetryRule The rule that used for this data retry.
              * @return This builder.
              */
-            public @NonNull T setAppliedRetryRule(@NonNull DataRetryRule dataRetryRule) {
+            @NonNull
+            public T setAppliedRetryRule(@NonNull DataRetryRule dataRetryRule) {
                 mAppliedDataRetryRule = dataRetryRule;
                 return (T) this;
             }
@@ -703,16 +739,20 @@
         public @interface SetupRetryType {}
 
         /** Setup retry type. Could be retry by same data profile or same capability. */
-        public final @SetupRetryType int setupRetryType;
+        @SetupRetryType
+        public final int setupRetryType;
 
         /** The network requests to satisfy when retry happens. */
-        public final @NonNull NetworkRequestList networkRequestList;
+        @NonNull
+        public final NetworkRequestList networkRequestList;
 
         /** The data profile that will be used for retry. */
-        public final @Nullable DataProfile dataProfile;
+        @Nullable
+        public final DataProfile dataProfile;
 
         /** The transport to retry data setup. */
-        public final @TransportType int transport;
+        @TransportType
+        public final int transport;
 
         /**
          * Constructor
@@ -743,11 +783,12 @@
          * @return Retry type in string format.
          */
         private static String retryTypeToString(@SetupRetryType int setupRetryType) {
-            switch (setupRetryType) {
-                case RETRY_TYPE_DATA_PROFILE: return "BY_PROFILE";
-                case RETRY_TYPE_NETWORK_REQUESTS: return "BY_NETWORK_REQUESTS";
-                default: return "Unknown(" + setupRetryType + ")";
-            }
+            return switch (setupRetryType) {
+                case RETRY_TYPE_DATA_PROFILE -> "BY_PROFILE";
+                case RETRY_TYPE_NETWORK_REQUESTS -> "BY_NETWORK_REQUESTS";
+                case RETRY_TYPE_UNKNOWN -> "UNKNOWN";
+                default -> "Unknown(" + setupRetryType + ")";
+            };
         }
 
         @Override
@@ -768,16 +809,20 @@
          */
         public static class Builder<T extends Builder<T>> extends DataRetryEntry.Builder<T> {
             /** Data setup retry type. Could be retry by same data profile or same capabilities. */
-            private @SetupRetryType int mSetupRetryType = RETRY_TYPE_UNKNOWN;
+            @SetupRetryType
+            private int mSetupRetryType = RETRY_TYPE_UNKNOWN;
 
             /** The network requests to satisfy when retry happens. */
-            private @NonNull NetworkRequestList mNetworkRequestList;
+            @NonNull
+            private NetworkRequestList mNetworkRequestList;
 
             /** The data profile that will be used for retry. */
-            private @Nullable DataProfile mDataProfile;
+            @Nullable
+            private DataProfile mDataProfile;
 
             /** The transport to retry data setup. */
-            private @TransportType int mTransport = AccessNetworkConstants.TRANSPORT_TYPE_INVALID;
+            @TransportType
+            private int mTransport = AccessNetworkConstants.TRANSPORT_TYPE_INVALID;
 
             /**
              * Set the data retry type.
@@ -786,7 +831,8 @@
              * capabilities.
              * @return This builder.
              */
-            public @NonNull Builder<T> setSetupRetryType(@SetupRetryType int setupRetryType) {
+            @NonNull
+            public Builder<T> setSetupRetryType(@SetupRetryType int setupRetryType) {
                 mSetupRetryType = setupRetryType;
                 return this;
             }
@@ -797,7 +843,8 @@
              * @param networkRequestList The network requests to satisfy when retry happens.
              * @return This builder.
              */
-            public @NonNull Builder<T> setNetworkRequestList(
+            @NonNull
+            public Builder<T> setNetworkRequestList(
                     @NonNull NetworkRequestList networkRequestList) {
                 mNetworkRequestList = networkRequestList;
                 return this;
@@ -809,7 +856,8 @@
              * @param dataProfile The data profile that will be used for retry.
              * @return This builder.
              */
-            public @NonNull Builder<T> setDataProfile(@NonNull DataProfile dataProfile) {
+            @NonNull
+            public Builder<T> setDataProfile(@NonNull DataProfile dataProfile) {
                 mDataProfile = dataProfile;
                 return this;
             }
@@ -820,7 +868,8 @@
              * @param transport The transport to retry data setup.
              * @return This builder.
              */
-            public @NonNull Builder<T> setTransport(@TransportType int transport) {
+            @NonNull
+            public Builder<T> setTransport(@TransportType int transport) {
                 mTransport = transport;
                 return this;
             }
@@ -830,7 +879,8 @@
              *
              * @return The instance of {@link DataSetupRetryEntry}.
              */
-            public @NonNull DataSetupRetryEntry build() {
+            @NonNull
+            public DataSetupRetryEntry build() {
                 if (mNetworkRequestList == null) {
                     throw new IllegalArgumentException("network request list is not specified.");
                 }
@@ -854,7 +904,8 @@
      */
     public static class DataHandoverRetryEntry extends DataRetryEntry {
         /** The data network to be retried for handover. */
-        public final @NonNull DataNetwork dataNetwork;
+        @NonNull
+        public final DataNetwork dataNetwork;
 
         /**
          * Constructor.
@@ -886,7 +937,8 @@
          */
         public static class Builder<T extends Builder<T>> extends DataRetryEntry.Builder<T> {
             /** The data network to be retried for handover. */
-            public @NonNull DataNetwork mDataNetwork;
+            @NonNull
+            public DataNetwork mDataNetwork;
 
             /**
              * Set the data retry type.
@@ -895,7 +947,8 @@
              *
              * @return This builder.
              */
-            public @NonNull Builder<T> setDataNetwork(@NonNull DataNetwork dataNetwork) {
+            @NonNull
+            public Builder<T> setDataNetwork(@NonNull DataNetwork dataNetwork) {
                 mDataNetwork = dataNetwork;
                 return this;
             }
@@ -905,7 +958,8 @@
              *
              * @return The instance of {@link DataHandoverRetryEntry}.
              */
-            public @NonNull DataHandoverRetryEntry build() {
+            @NonNull
+            public DataHandoverRetryEntry build() {
                 return new DataHandoverRetryEntry(mDataNetwork,
                         (DataHandoverRetryRule) mAppliedDataRetryRule, mRetryDelayMillis);
             }
@@ -1168,7 +1222,7 @@
             // when unthrottling happens, we still want to retry and we'll need
             // a type there so we know what to retry. Using RETRY_TYPE_NONE
             // ThrottleStatus is just for API backwards compatibility reason.
-            updateThrottleStatus(dataProfile, requestList, null,
+            throttleDataProfile(dataProfile, requestList, null,
                     ThrottleStatus.RETRY_TYPE_NEW_CONNECTION, transport, Long.MAX_VALUE);
             return;
         } else if (retryDelayMillis != DataCallResponse.RETRY_DURATION_UNDEFINED) {
@@ -1180,7 +1234,7 @@
                     .setDataProfile(dataProfile)
                     .setTransport(transport)
                     .build();
-            updateThrottleStatus(dataProfile, requestList, null,
+            throttleDataProfile(dataProfile, requestList, null,
                     ThrottleStatus.RETRY_TYPE_NEW_CONNECTION, transport,
                     dataSetupRetryEntry.retryElapsedTime);
             schedule(dataSetupRetryEntry);
@@ -1298,7 +1352,7 @@
             // when unthrottling happens, we still want to retry and we'll need
             // a type there so we know what to retry. Using RETRY_TYPE_NONE
             // ThrottleStatus is just for API backwards compatibility reason.
-            updateThrottleStatus(dataNetwork.getDataProfile(),
+            throttleDataProfile(dataNetwork.getDataProfile(),
                     dataNetwork.getAttachedNetworkRequestList(), dataNetwork,
                     ThrottleStatus.RETRY_TYPE_HANDOVER, targetTransport, Long.MAX_VALUE);
         } else if (retryDelayMillis != DataCallResponse.RETRY_DURATION_UNDEFINED) {
@@ -1308,7 +1362,7 @@
                     .setDataNetwork(dataNetwork)
                     .build();
 
-            updateThrottleStatus(dataNetwork.getDataProfile(),
+            throttleDataProfile(dataNetwork.getDataProfile(),
                     dataNetwork.getAttachedNetworkRequestList(), dataNetwork,
                     ThrottleStatus.RETRY_TYPE_HANDOVER, targetTransport,
                     dataHandoverRetryEntry.retryElapsedTime);
@@ -1534,7 +1588,7 @@
      * @param expirationTime The expiration time of data throttling. This is the time retrieved from
      * {@link SystemClock#elapsedRealtime()}.
      */
-    private void updateThrottleStatus(@NonNull DataProfile dataProfile,
+    private void throttleDataProfile(@NonNull DataProfile dataProfile,
             @Nullable NetworkRequestList networkRequestList,
             @Nullable DataNetwork dataNetwork, @RetryType int retryType,
             @TransportType int transport, @ElapsedRealtimeLong long expirationTime) {
@@ -1565,21 +1619,7 @@
                 ? ThrottleStatus.RETRY_TYPE_NONE : retryType;
 
         // Report to the clients.
-        final List<ThrottleStatus> throttleStatusList = new ArrayList<>();
-        if (dataProfile.getApnSetting() != null) {
-            throttleStatusList.addAll(dataProfile.getApnSetting().getApnTypes().stream()
-                    .map(apnType -> new ThrottleStatus.Builder()
-                            .setApnType(apnType)
-                            .setRetryType(dataRetryType)
-                            .setSlotIndex(mPhone.getPhoneId())
-                            .setThrottleExpiryTimeMillis(expirationTime)
-                            .setTransportType(transport)
-                            .build())
-                    .collect(Collectors.toList()));
-        }
-
-        mDataRetryManagerCallbacks.forEach(callback -> callback.invokeFromExecutor(
-                () -> callback.onThrottleStatusChanged(throttleStatusList)));
+        notifyThrottleStatus(dataProfile, expirationTime, dataRetryType, transport);
     }
 
     /**
@@ -1650,23 +1690,9 @@
         // Make it final so it can be used in the lambda function below.
         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)
-                            .setSlotIndex(mPhone.getPhoneId())
-                            .setNoThrottle()
-                            .setRetryType(dataRetryType)
-                            .setTransportType(transport)
-                            .build())
-                    .collect(Collectors.toList()));
-        }
-
-        mDataRetryManagerCallbacks.forEach(callback -> callback.invokeFromExecutor(
-                () -> callback.onThrottleStatusChanged(throttleStatusList)));
-
         if (unthrottledProfile != null) {
+            notifyThrottleStatus(unthrottledProfile, ThrottleStatus.Builder.NO_THROTTLE_EXPIRY_TIME,
+                    dataRetryType, transport);
             // cancel pending retries since we will soon schedule an immediate retry
             cancelRetriesForDataProfile(unthrottledProfile, transport);
         }
@@ -1799,6 +1825,60 @@
                         && ((DataHandoverRetryEntry) entry).dataNetwork == dataNetwork
                         && entry.getState() == DataRetryEntry.RETRY_STATE_NOT_RETRIED)
                 .forEach(entry -> entry.setState(DataRetryEntry.RETRY_STATE_CANCELLED));
+
+        long now = SystemClock.elapsedRealtime();
+        DataThrottlingEntry dataUnThrottlingEntry = mDataThrottlingEntries.stream()
+                .filter(entry -> dataNetwork == entry.dataNetwork
+                        && entry.expirationTimeMillis > now).findAny().orElse(null);
+        if (dataUnThrottlingEntry == null) {
+            return;
+        }
+        log("onCancelPendingHandoverRetry removed throttling entry:" + dataUnThrottlingEntry);
+        DataProfile unThrottledProfile =
+                dataUnThrottlingEntry.dataNetwork.getDataProfile();
+        final int transport = dataUnThrottlingEntry.transport;
+
+        notifyThrottleStatus(unThrottledProfile, ThrottleStatus.Builder.NO_THROTTLE_EXPIRY_TIME,
+                ThrottleStatus.RETRY_TYPE_HANDOVER, transport);
+        mDataThrottlingEntries.removeIf(entry -> dataNetwork == entry.dataNetwork);
+    }
+
+    /**
+     * Notify listeners of throttle status for a given data profile
+     *
+     * @param dataProfile Data profile for this throttling status notification
+     * @param expirationTime Expiration time of throttling status. {@link
+     * ThrottleStatus.Builder#NO_THROTTLE_EXPIRY_TIME} indicates un-throttling.
+     * @param dataRetryType Retry type of this throttling notification.
+     * @param transportType Transport type of this throttling notification.
+     */
+    private void notifyThrottleStatus(
+            @NonNull DataProfile dataProfile, long expirationTime, @RetryType int dataRetryType,
+            @TransportType int transportType) {
+        if (dataProfile.getApnSetting() != null) {
+            final boolean unThrottled =
+                    expirationTime == ThrottleStatus.Builder.NO_THROTTLE_EXPIRY_TIME;
+            if (unThrottled) {
+                dataProfile.getApnSetting().setPermanentFailed(false);
+            }
+            final List<ThrottleStatus> throttleStatusList = new ArrayList<>(
+                    dataProfile.getApnSetting().getApnTypes().stream()
+                            .map(apnType -> {
+                                ThrottleStatus.Builder builder = new ThrottleStatus.Builder()
+                                        .setApnType(apnType)
+                                        .setSlotIndex(mPhone.getPhoneId())
+                                        .setRetryType(dataRetryType)
+                                        .setTransportType(transportType);
+                                if (unThrottled) {
+                                    builder.setNoThrottle();
+                                } else {
+                                    builder.setThrottleExpiryTimeMillis(expirationTime);
+                                }
+                                return builder.build();
+                            }).toList());
+            mDataRetryManagerCallbacks.forEach(callback -> callback.invokeFromExecutor(
+                    () -> callback.onThrottleStatusChanged(throttleStatusList)));
+        }
     }
 
     /**
@@ -1839,23 +1919,17 @@
      * @param reason The reason
      * @return The reason in string format.
      */
-    private static @NonNull String resetReasonToString(int reason) {
-        switch (reason) {
-            case RESET_REASON_DATA_PROFILES_CHANGED:
-                return "DATA_PROFILES_CHANGED";
-            case RESET_REASON_RADIO_ON:
-                return "RADIO_ON";
-            case RESET_REASON_MODEM_RESTART:
-                return "MODEM_RESTART";
-            case RESET_REASON_DATA_SERVICE_BOUND:
-                return "DATA_SERVICE_BOUND";
-            case RESET_REASON_DATA_CONFIG_CHANGED:
-                return "DATA_CONFIG_CHANGED";
-            case RESET_REASON_TAC_CHANGED:
-                return "TAC_CHANGED";
-            default:
-                return "UNKNOWN(" + reason + ")";
-        }
+    @NonNull
+    private static String resetReasonToString(int reason) {
+        return switch (reason) {
+            case RESET_REASON_DATA_PROFILES_CHANGED -> "DATA_PROFILES_CHANGED";
+            case RESET_REASON_RADIO_ON -> "RADIO_ON";
+            case RESET_REASON_MODEM_RESTART -> "MODEM_RESTART";
+            case RESET_REASON_DATA_SERVICE_BOUND -> "DATA_SERVICE_BOUND";
+            case RESET_REASON_DATA_CONFIG_CHANGED -> "DATA_CONFIG_CHANGED";
+            case RESET_REASON_TAC_CHANGED -> "TAC_CHANGED";
+            default -> "UNKNOWN(" + reason + ")";
+        };
     }
 
     /**
diff --git a/src/java/com/android/internal/telephony/data/DataServiceManager.java b/src/java/com/android/internal/telephony/data/DataServiceManager.java
index ee66a6a..a71bd10 100644
--- a/src/java/com/android/internal/telephony/data/DataServiceManager.java
+++ b/src/java/com/android/internal/telephony/data/DataServiceManager.java
@@ -16,7 +16,6 @@
 
 package com.android.internal.telephony.data;
 
-import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
 import static android.text.format.DateUtils.SECOND_IN_MILLIS;
 
 import android.annotation.NonNull;
@@ -61,6 +60,7 @@
 import com.android.internal.telephony.util.TelephonyUtils;
 import com.android.telephony.Rlog;
 
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
@@ -82,17 +82,12 @@
 
     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;
 
@@ -102,8 +97,6 @@
 
     private IDataService mIDataService;
 
-    private DataServiceManagerDeathRecipient mDeathRecipient;
-
     private final RegistrantList mServiceBindingChangedRegistrants = new RegistrantList();
 
     private final Map<IBinder, Message> mMessageMap = new ConcurrentHashMap<>();
@@ -118,7 +111,7 @@
 
     private String mLastBoundPackageName;
 
-    private List<DataCallResponse> mLastDataCallResponseList = Collections.EMPTY_LIST;
+    private List<DataCallResponse> mLastDataCallResponseList = new ArrayList<>();
 
     private class DataServiceManagerDeathRecipient implements IBinder.DeathRecipient {
         @Override
@@ -137,7 +130,7 @@
             mMessageMap.clear();
 
             // Tear down all connections
-            mLastDataCallResponseList = Collections.EMPTY_LIST;
+            mLastDataCallResponseList = new ArrayList<>();
             mDataCallListChangedRegistrants.notifyRegistrants(
                     new AsyncResult(null, Collections.EMPTY_LIST, null));
         }
@@ -209,13 +202,11 @@
         public void onServiceConnected(ComponentName name, IBinder service) {
             if (DBG) log("onServiceConnected: " + name);
             mIDataService = IDataService.Stub.asInterface(service);
-            mDeathRecipient = new DataServiceManagerDeathRecipient();
             mBound = true;
             mLastBoundPackageName = getDataServicePackageName();
-            removeMessages(EVENT_WATCHDOG_TIMEOUT);
 
             try {
-                service.linkToDeath(mDeathRecipient, 0);
+                service.linkToDeath(new DataServiceManagerDeathRecipient(), 0);
                 mIDataService.createDataServiceProvider(mPhone.getPhoneId());
                 mIDataService.registerForDataCallListChanged(mPhone.getPhoneId(),
                         new DataServiceCallbackWrapper("dataCallListChanged"));
@@ -230,7 +221,6 @@
         @Override
         public void onServiceDisconnected(ComponentName name) {
             if (DBG) log("onServiceDisconnected");
-            removeMessages(EVENT_WATCHDOG_TIMEOUT);
             mIDataService = null;
             mBound = false;
             mServiceBindingChangedRegistrants.notifyResult(false);
@@ -257,7 +247,6 @@
                 log("onSetupDataCallComplete. resultCode = " + resultCode + ", response = "
                         + response);
             }
-            removeMessages(EVENT_WATCHDOG_TIMEOUT, DataServiceCallbackWrapper.this);
             Message msg = mMessageMap.remove(asBinder());
             if (msg != null) {
                 msg.getData().putParcelable(DATA_CALL_RESPONSE, response);
@@ -270,7 +259,6 @@
         @Override
         public void onDeactivateDataCallComplete(@DataServiceCallback.ResultCode int resultCode) {
             if (DBG) log("onDeactivateDataCallComplete. resultCode = " + resultCode);
-            removeMessages(EVENT_WATCHDOG_TIMEOUT, DataServiceCallbackWrapper.this);
             Message msg = mMessageMap.remove(asBinder());
             sendCompleteMessage(msg, resultCode);
         }
@@ -326,7 +314,7 @@
         @Override
         public void onDataCallListChanged(List<DataCallResponse> dataCallList) {
             mLastDataCallResponseList =
-                    dataCallList != null ? dataCallList : Collections.EMPTY_LIST;
+                    dataCallList != null ? dataCallList : new ArrayList<>();
             mDataCallListChangedRegistrants.notifyRegistrants(
                     new AsyncResult(null, dataCallList, null));
         }
@@ -334,7 +322,6 @@
         @Override
         public void onHandoverStarted(@DataServiceCallback.ResultCode int resultCode) {
             if (DBG) log("onHandoverStarted. resultCode = " + resultCode);
-            removeMessages(EVENT_WATCHDOG_TIMEOUT, DataServiceCallbackWrapper.this);
             Message msg = mMessageMap.remove(asBinder());
             sendCompleteMessage(msg, resultCode);
         }
@@ -342,7 +329,6 @@
         @Override
         public void onHandoverCancelled(@DataServiceCallback.ResultCode int resultCode) {
             if (DBG) log("onHandoverCancelled. resultCode = " + resultCode);
-            removeMessages(EVENT_WATCHDOG_TIMEOUT, DataServiceCallbackWrapper.this);
             Message msg = mMessageMap.remove(asBinder());
             sendCompleteMessage(msg, resultCode);
         }
@@ -383,8 +369,8 @@
         mTag = "DSM-" + (mTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WWAN ? "C-"
                 : "I-") + mPhone.getPhoneId();
         mBound = false;
-        mCarrierConfigManager = (CarrierConfigManager) phone.getContext().getSystemService(
-                Context.CARRIER_CONFIG_SERVICE);
+        CarrierConfigManager carrierConfigManager = phone.getContext().getSystemService(
+                CarrierConfigManager.class);
         // 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
@@ -393,17 +379,18 @@
         mAppOps = (AppOpsManager) phone.getContext().getSystemService(Context.APP_OPS_SERVICE);
 
         // 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();
-                    }
-                });
-
+        if (carrierConfigManager != null) {
+            carrierConfigManager.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);
 
@@ -417,29 +404,13 @@
      */
     @Override
     public void handleMessage(Message msg) {
-        switch (msg.what) {
-            case EVENT_BIND_DATA_SERVICE:
-                rebindDataService();
-                break;
-            case EVENT_WATCHDOG_TIMEOUT:
-                handleRequestUnresponded((DataServiceCallbackWrapper) msg.obj);
-                break;
-            default:
-                loge("Unhandled event " + msg.what);
+        if (msg.what == EVENT_BIND_DATA_SERVICE) {
+            rebindDataService();
+        } else {
+            loge("Unhandled event " + msg.what);
         }
     }
 
-    private void handleRequestUnresponded(DataServiceCallbackWrapper 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();
@@ -473,7 +444,7 @@
             return;
         }
 
-        Intent intent = null;
+        Intent intent;
         String className = getDataServiceClassName();
         if (TextUtils.isEmpty(className)) {
             intent = new Intent(DataService.SERVICE_INTERFACE);
@@ -512,7 +483,8 @@
         bindDataService(packageName);
     }
 
-    private @NonNull Set<String> getAllDataServicePackageNames() {
+    @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
@@ -540,7 +512,7 @@
 
     /**
      * Get the data service package by transport type.
-     *
+     * <p>
      * 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.
@@ -552,21 +524,19 @@
         String packageName;
         int resourceId;
         String carrierConfig;
-
         switch (transportType) {
-            case AccessNetworkConstants.TRANSPORT_TYPE_WWAN:
+            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:
+            }
+            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));
+            }
+            default -> throw new IllegalStateException("Transport type not WWAN or WLAN. type="
+                    + AccessNetworkConstants.transportTypeToString(mTransportType));
         }
 
         // Read package name from resource overlay
@@ -604,19 +574,18 @@
         int resourceId;
         String carrierConfig;
         switch (transportType) {
-            case AccessNetworkConstants.TRANSPORT_TYPE_WWAN:
+            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:
+            }
+            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);
+            }
+            default -> throw new IllegalStateException("Transport type not WWAN or WLAN. type="
+                    + transportType);
         }
 
         // Read package name from resource overlay
@@ -680,8 +649,6 @@
             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);
@@ -720,8 +687,6 @@
             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.");
@@ -732,13 +697,13 @@
 
     /**
      * Indicates that a handover has begun.  This is called on the source transport.
-     *
+     * <p>
      * Any resources being transferred cannot be released while a
      * handover is underway.
-     *
+     * <p>
      * If a handover was unsuccessful, then the framework calls DataServiceManager#cancelHandover.
      * The target transport retains ownership over any of the resources being transferred.
-     *
+     * <p>
      * 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.
@@ -756,13 +721,9 @@
 
         DataServiceCallbackWrapper callback =
                 new DataServiceCallbackWrapper("startHandover");
-        if (onCompleteMessage != null) {
-            mMessageMap.put(callback.asBinder(), onCompleteMessage);
-        }
+        mMessageMap.put(callback.asBinder(), onCompleteMessage);
 
         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.");
@@ -774,7 +735,7 @@
     /**
      * Indicates that a handover was cancelled after a call to DataServiceManager#startHandover.
      * This is called on the source transport.
-     *
+     * <p>
      * Since the handover was unsuccessful, the source transport retains ownership over any of
      * the resources being transferred and is still responsible for releasing them.
      *
@@ -791,13 +752,9 @@
 
         DataServiceCallbackWrapper callback =
                 new DataServiceCallbackWrapper("cancelHandover");
-        if (onCompleteMessage != null) {
-            mMessageMap.put(callback.asBinder(), onCompleteMessage);
-        }
+        mMessageMap.put(callback.asBinder(), onCompleteMessage);
 
         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.");
@@ -894,9 +851,7 @@
             mIDataService.requestDataCallList(mPhone.getPhoneId(), callback);
         } catch (RemoteException e) {
             loge("Cannot invoke requestDataCallList on data service.");
-            if (callback != null) {
-                mMessageMap.remove(callback.asBinder());
-            }
+            mMessageMap.remove(callback.asBinder());
             sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE);
         }
     }
@@ -937,17 +892,6 @@
     }
 
     /**
-     * Unregister for apn unthrottled event
-     *
-     * @param h The handler
-     */
-    public void unregisterForApnUnthrottled(Handler h) {
-        if (h != null) {
-            mApnUnthrottledRegistrants.remove(h);
-        }
-    }
-
-    /**
      * Request data network validation.
      *
      * <p>Validates a given data network to ensure that the network can work properly.
@@ -981,9 +925,7 @@
             mIDataService.requestNetworkValidation(mPhone.getPhoneId(), cid, callback);
         } catch (RemoteException e) {
             loge("Cannot invoke requestNetworkValidation on data service.");
-            if (callback != null) {
-                mMessageMap.remove(callback.asBinder());
-            }
+            mMessageMap.remove(callback.asBinder());
             sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE);
         }
     }
@@ -1005,17 +947,6 @@
         }
     }
 
-    /**
-     * Unregister for data service binding status changed event.
-     *
-     * @param h The handler
-     */
-    public void unregisterForServiceBindingChanged(Handler h) {
-        if (h != null) {
-            mServiceBindingChangedRegistrants.remove(h);
-        }
-    }
-
     private void log(String s) {
         Rlog.d(mTag, s);
     }
diff --git a/src/java/com/android/internal/telephony/data/DataSettingsManager.java b/src/java/com/android/internal/telephony/data/DataSettingsManager.java
index 51e5b7d..36e7e19 100644
--- a/src/java/com/android/internal/telephony/data/DataSettingsManager.java
+++ b/src/java/com/android/internal/telephony/data/DataSettingsManager.java
@@ -91,7 +91,8 @@
     private static final int EVENT_INITIALIZE = 11;
 
     private final Phone mPhone;
-    private final @NonNull FeatureFlags mFeatureFlags;
+    @NonNull
+    private final FeatureFlags mFeatureFlags;
     private final ContentResolver mResolver;
     private final SettingsObserver mSettingsObserver;
     private final String mLogTag;
@@ -100,11 +101,12 @@
     private int mSubId;
 
     /** Data config manager */
-    private final @NonNull DataConfigManager mDataConfigManager;
+    @NonNull
+    private final DataConfigManager mDataConfigManager;
 
     /** Data settings manager callbacks. */
-    private final @NonNull Set<DataSettingsManagerCallback> mDataSettingsManagerCallbacks =
-            new ArraySet<>();
+    @NonNull
+    private final Set<DataSettingsManagerCallback> mDataSettingsManagerCallbacks = new ArraySet<>();
 
     /** Mapping of {@link TelephonyManager.DataEnabledReason} to data enabled values. */
     private final Map<Integer, Boolean> mDataEnabledSettings = new ArrayMap<>();
@@ -718,8 +720,9 @@
      * @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) {
+    @NonNull
+    @MobileDataPolicy
+    public Set<Integer> getMobileDataPolicyEnabled(@NonNull String policies) {
         Set<Integer> mobileDataPolicies = new HashSet<>();
         String[] rulesString = policies.trim().split("\\s*,\\s*");
         for (String rule : rulesString) {
@@ -741,7 +744,8 @@
      * @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) {
+    @MobileDataPolicy
+    private int parsePolicyFrom(@NonNull String policy) {
         int dataPolicy;
         try {
             // parse as new override policy
@@ -810,20 +814,14 @@
 
     private static String dataEnabledChangedReasonToString(
             @TelephonyManager.DataEnabledChangedReason int reason) {
-        switch (reason) {
-            case TelephonyManager.DATA_ENABLED_REASON_USER:
-                return "USER";
-            case TelephonyManager.DATA_ENABLED_REASON_POLICY:
-                return "POLICY";
-            case TelephonyManager.DATA_ENABLED_REASON_CARRIER:
-                return "CARRIER";
-            case TelephonyManager.DATA_ENABLED_REASON_THERMAL:
-                return "THERMAL";
-            case TelephonyManager.DATA_ENABLED_REASON_OVERRIDE:
-                return "OVERRIDE";
-            default:
-                return "UNKNOWN";
-        }
+        return switch (reason) {
+            case TelephonyManager.DATA_ENABLED_REASON_USER -> "USER";
+            case TelephonyManager.DATA_ENABLED_REASON_POLICY -> "POLICY";
+            case TelephonyManager.DATA_ENABLED_REASON_CARRIER -> "CARRIER";
+            case TelephonyManager.DATA_ENABLED_REASON_THERMAL -> "THERMAL";
+            case TelephonyManager.DATA_ENABLED_REASON_OVERRIDE -> "OVERRIDE";
+            default -> "UNKNOWN";
+        };
     }
 
     @Override
diff --git a/src/java/com/android/internal/telephony/data/DataStallRecoveryManager.java b/src/java/com/android/internal/telephony/data/DataStallRecoveryManager.java
index ee8890a..b9b60a0 100644
--- a/src/java/com/android/internal/telephony/data/DataStallRecoveryManager.java
+++ b/src/java/com/android/internal/telephony/data/DataStallRecoveryManager.java
@@ -73,12 +73,11 @@
             value = {
                 RECOVERY_ACTION_GET_DATA_CALL_LIST,
                 RECOVERY_ACTION_CLEANUP,
-                RECOVERY_ACTION_REREGISTER,
                 RECOVERY_ACTION_RADIO_RESTART,
                 RECOVERY_ACTION_RESET_MODEM
             })
     @Retention(RetentionPolicy.SOURCE)
-    public @interface RecoveryAction {};
+    public @interface RecoveryAction {}
 
     /* DataStallRecoveryManager queries RIL for link properties (IP addresses, DNS server addresses
      * etc) using RIL_REQUEST_GET_DATA_CALL_LIST.  This will help in cases where the data stall
@@ -92,16 +91,6 @@
      */
     public static final int RECOVERY_ACTION_CLEANUP = 1;
 
-    /**
-     * Add the RECOVERY_ACTION_REREGISTER to align the RecoveryActions between
-     * DataStallRecoveryManager and atoms.proto. In Android T, This action will not process because
-     * the boolean array for skip recovery action is default true in carrier config setting.
-     *
-     * @deprecated Do not use.
-     */
-    @java.lang.Deprecated
-    public static final int RECOVERY_ACTION_REREGISTER = 2;
-
     /* DataStallRecoveryManager will request ServiceStateTracker to send RIL_REQUEST_RADIO_POWER
      * to restart radio. It will restart the radio and re-attch to the network.
      */
@@ -121,9 +110,9 @@
                 RECOVERED_REASON_USER
             })
     @Retention(RetentionPolicy.SOURCE)
-    public @interface RecoveredReason {};
+    public @interface RecoveredReason {}
 
-    /** The reason when data stall recovered. */
+    // The reason when data stall recovered.
     /** The data stall not recovered yet. */
     private static final int RECOVERED_REASON_NONE = 0;
     /** The data stall recovered by our DataStallRecoveryManager. */
@@ -151,24 +140,33 @@
     /** Event for duration milliseconds changed. */
     private static final int EVENT_CONTENT_DSRM_DURATION_MILLIS_CHANGED = 5;
 
-    private final @NonNull Phone mPhone;
-    private final @NonNull String mLogTag;
-    private final @NonNull LocalLog mLocalLog = new LocalLog(128);
-    private final @NonNull FeatureFlags mFeatureFlags;
+    @NonNull
+    private final Phone mPhone;
+    @NonNull
+    private final String mLogTag;
+    @NonNull
+    private final LocalLog mLocalLog = new LocalLog(128);
+    @NonNull
+    private final FeatureFlags mFeatureFlags;
 
     /** Data network controller */
-    private final @NonNull DataNetworkController mDataNetworkController;
+    @NonNull
+    private final DataNetworkController mDataNetworkController;
 
     /** Data config manager */
-    private final @NonNull DataConfigManager mDataConfigManager;
+    @NonNull
+    private final DataConfigManager mDataConfigManager;
 
     /** Cellular data service */
-    private final @NonNull DataServiceManager mWwanDataServiceManager;
+    @NonNull
+    private final DataServiceManager mWwanDataServiceManager;
 
     /** The data stall recovery action. */
-    private @RecoveryAction int mRecoveryAction;
+    @RecoveryAction
+    private int mRecoveryAction;
     /** The elapsed real time of last recovery attempted */
-    private @ElapsedRealtimeLong long mTimeLastRecoveryStartMs;
+    @ElapsedRealtimeLong
+    private long mTimeLastRecoveryStartMs;
     /** Whether current network is good or not */
     private boolean mIsValidNetwork;
     /** Whether data stall recovery is triggered or not */
@@ -179,11 +177,14 @@
     private boolean mLastActionReported;
     /** The real time for data stall start. */
     @VisibleForTesting
-    public @ElapsedRealtimeLong long mDataStallStartMs;
+    @ElapsedRealtimeLong
+    public long mDataStallStartMs;
     /** Last data stall recovery action. */
-    private @RecoveryAction int mLastAction;
+    @RecoveryAction
+    private int mLastAction;
     /** Last radio power state. */
-    private @RadioPowerState int mRadioPowerState;
+    @RadioPowerState
+    private int mRadioPowerState;
     /** Whether the NetworkCheckTimer start. */
     private boolean mNetworkCheckTimerStarted = false;
     /** Whether radio state changed during data stall. */
@@ -197,15 +198,18 @@
     /** Whether internet network that require validation is connected. */
     private boolean mIsInternetNetworkConnected;
     /** The durations for current recovery action */
-    private @ElapsedRealtimeLong long mTimeElapsedOfCurrentAction;
+    @ElapsedRealtimeLong
+    private long mTimeElapsedOfCurrentAction;
     /** Tracks the total number of validation duration a data stall */
     private int mValidationCount;
     /** Tracks the number of validation for current action during a data stall */
     private int mActionValidationCount;
     /** The array for the timers between recovery actions. */
-    private @NonNull long[] mDataStallRecoveryDelayMillisArray;
+    @NonNull
+    private long[] mDataStallRecoveryDelayMillisArray;
     /** The boolean array for the flags. They are used to skip the recovery actions if needed. */
-    private @NonNull boolean[] mSkipRecoveryActionArray;
+    @NonNull
+    private boolean[] mSkipRecoveryActionArray;
 
     /**
      * The content URI for the DSRM recovery actions.
@@ -224,12 +228,13 @@
             Settings.Global.DSRM_DURATION_MILLIS);
 
 
-    private DataStallRecoveryManagerCallback mDataStallRecoveryManagerCallback;
+    private final DataStallRecoveryManagerCallback mDataStallRecoveryManagerCallback;
 
     private final DataStallRecoveryStats mStats;
 
     /** The number of milliseconds to wait for the DSRM prediction to complete. */
-    private @ElapsedRealtimeLong long mPredictWaitingMillis = 0L;
+    @ElapsedRealtimeLong
+    private long mPredictWaitingMillis = 0L;
 
     /**
      * The data stall recovery manager callback. Note this is only used for passing information
@@ -476,9 +481,8 @@
 
             // Copy the values from the durationMillisArray array to the
             // mDataStallRecoveryDelayMillisArray array.
-            for (int i = 0; i < minLength; i++) {
-                mDataStallRecoveryDelayMillisArray[i] = durationMillisArray[i];
-            }
+            System.arraycopy(durationMillisArray, 0, mDataStallRecoveryDelayMillisArray,
+                    0, minLength);
             log("DataStallRecoveryDelayMillis: "
                     + Arrays.toString(mDataStallRecoveryDelayMillisArray));
             mPredictWaitingMillis = DSRM_PREDICT_WAITING_MILLIS;
@@ -574,7 +578,7 @@
         if (isValid) {
             if (mFeatureFlags.dsrsDiagnosticsEnabled()) {
                 // Broadcast intent that data stall recovered.
-                broadcastDataStallDetected(getRecoveryAction());
+                broadcastDataStallDetected(mLastAction);
             }
             reset();
         } else if (isRecoveryNeeded(true)) {
@@ -695,7 +699,7 @@
         // Get the information for DSRS state
         final boolean isRecovered = !mDataStalled;
         final int duration = (int) (SystemClock.elapsedRealtime() - mDataStallStartMs);
-        final @RecoveredReason int reason = getRecoveredReason(mIsValidNetwork);
+        @RecoveredReason final int reason = getRecoveredReason(mIsValidNetwork);
         final int durationOfAction = (int) getDurationOfCurrentRecoveryMs();
         if (mFeatureFlags.dsrsDiagnosticsEnabled()) {
             log("mValidationCount=" + mValidationCount
@@ -723,7 +727,7 @@
     private void cleanUpDataNetwork() {
         log("cleanUpDataNetwork: notify clean up data network");
         mDataStallRecoveryManagerCallback.invokeFromExecutor(
-                () -> mDataStallRecoveryManagerCallback.onDataStallReestablishInternet());
+                mDataStallRecoveryManagerCallback::onDataStallReestablishInternet);
     }
 
     /** Recovery Action: RECOVERY_ACTION_RADIO_RESTART */
@@ -828,7 +832,7 @@
     private void setNetworkValidationState(boolean isValid) {
         boolean isLogNeeded = false;
         int timeDuration = 0;
-        int timeDurationOfCurrentAction = 0;
+        int timeDurationOfCurrentAction;
         boolean isFirstDataStall = false;
         boolean isFirstValidationAfterDoRecovery = false;
         @RecoveredReason int reason = getRecoveredReason(isValid);
@@ -872,7 +876,7 @@
                     isFirstValidationAfterDoRecovery, timeDurationOfCurrentAction);
             logl(
                     "data stall: "
-                    + (isFirstDataStall == true ? "start" : isValid == false ? "in process" : "end")
+                    + (isFirstDataStall ? "start" : !isValid ? "in process" : "end")
                     + ", lastaction="
                     + recoveryActionToString(mLastAction)
                     + ", isRecovered="
@@ -902,9 +906,6 @@
             if (mLastAction <= RECOVERY_ACTION_CLEANUP) {
                 ret = RECOVERED_REASON_MODEM;
             }
-            if (mLastAction > RECOVERY_ACTION_CLEANUP) {
-                ret = RECOVERED_REASON_DSRM;
-            }
             if (mIsAirPlaneModeEnableDuringDataStall) {
                 ret = RECOVERED_REASON_USER;
             }
@@ -964,19 +965,15 @@
      * @param reason The recovered reason.
      * @return The recovered reason in string format.
      */
-    private static @NonNull String recoveredReasonToString(@RecoveredReason int reason) {
-        switch (reason) {
-            case RECOVERED_REASON_NONE:
-                return "RECOVERED_REASON_NONE";
-            case RECOVERED_REASON_DSRM:
-                return "RECOVERED_REASON_DSRM";
-            case RECOVERED_REASON_MODEM:
-                return "RECOVERED_REASON_MODEM";
-            case RECOVERED_REASON_USER:
-                return "RECOVERED_REASON_USER";
-            default:
-                return "Unknown(" + reason + ")";
-        }
+    @NonNull
+    private static String recoveredReasonToString(@RecoveredReason int reason) {
+        return switch (reason) {
+            case RECOVERED_REASON_NONE -> "RECOVERED_REASON_NONE";
+            case RECOVERED_REASON_DSRM -> "RECOVERED_REASON_DSRM";
+            case RECOVERED_REASON_MODEM -> "RECOVERED_REASON_MODEM";
+            case RECOVERED_REASON_USER -> "RECOVERED_REASON_USER";
+            default -> "Unknown(" + reason + ")";
+        };
     }
 
     /**
@@ -985,17 +982,14 @@
      * @param state The radio power state
      * @return The radio power state in string format.
      */
-    private static @NonNull String radioPowerStateToString(@RadioPowerState int state) {
-        switch (state) {
-            case TelephonyManager.RADIO_POWER_OFF:
-                return "RADIO_POWER_OFF";
-            case TelephonyManager.RADIO_POWER_ON:
-                return "RADIO_POWER_ON";
-            case TelephonyManager.RADIO_POWER_UNAVAILABLE:
-                return "RADIO_POWER_UNAVAILABLE";
-            default:
-                return "Unknown(" + state + ")";
-        }
+    @NonNull
+    private static String radioPowerStateToString(@RadioPowerState int state) {
+        return switch (state) {
+            case TelephonyManager.RADIO_POWER_OFF -> "RADIO_POWER_OFF";
+            case TelephonyManager.RADIO_POWER_ON -> "RADIO_POWER_ON";
+            case TelephonyManager.RADIO_POWER_UNAVAILABLE -> "RADIO_POWER_UNAVAILABLE";
+            default -> "Unknown(" + state + ")";
+        };
     }
 
     /**
@@ -1004,19 +998,15 @@
      * @param action The recovery action
      * @return The recovery action in string format.
      */
-    private static @NonNull String recoveryActionToString(@RecoveryAction int action) {
-        switch (action) {
-            case RECOVERY_ACTION_GET_DATA_CALL_LIST:
-                return "RECOVERY_ACTION_GET_DATA_CALL_LIST";
-            case RECOVERY_ACTION_CLEANUP:
-                return "RECOVERY_ACTION_CLEANUP";
-            case RECOVERY_ACTION_RADIO_RESTART:
-                return "RECOVERY_ACTION_RADIO_RESTART";
-            case RECOVERY_ACTION_RESET_MODEM:
-                return "RECOVERY_ACTION_RESET_MODEM";
-            default:
-                return "Unknown(" + action + ")";
-        }
+    @NonNull
+    private static String recoveryActionToString(@RecoveryAction int action) {
+        return switch (action) {
+            case RECOVERY_ACTION_GET_DATA_CALL_LIST -> "RECOVERY_ACTION_GET_DATA_CALL_LIST";
+            case RECOVERY_ACTION_CLEANUP -> "RECOVERY_ACTION_CLEANUP";
+            case RECOVERY_ACTION_RADIO_RESTART -> "RECOVERY_ACTION_RADIO_RESTART";
+            case RECOVERY_ACTION_RESET_MODEM -> "RECOVERY_ACTION_RESET_MODEM";
+            default -> "Unknown(" + action + ")";
+        };
     }
 
     /**
diff --git a/src/java/com/android/internal/telephony/data/DataUtils.java b/src/java/com/android/internal/telephony/data/DataUtils.java
index 6814516..8b95913 100644
--- a/src/java/com/android/internal/telephony/data/DataUtils.java
+++ b/src/java/com/android/internal/telephony/data/DataUtils.java
@@ -37,7 +37,6 @@
 import android.telephony.data.ApnSetting.ApnType;
 import android.telephony.data.DataCallResponse;
 import android.telephony.data.DataCallResponse.LinkStatus;
-import android.telephony.data.DataProfile;
 import android.telephony.ims.feature.ImsFeature;
 import android.util.ArrayMap;
 
@@ -50,7 +49,6 @@
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.Comparator;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
@@ -72,32 +70,30 @@
      * @param capabilityString The capability in string format
      * @return The network capability. -1 if not found.
      */
-    public static @NetCapability int getNetworkCapabilityFromString(
-            @NonNull String capabilityString) {
-        switch (capabilityString.toUpperCase(Locale.ROOT)) {
-            case "MMS": return NetworkCapabilities.NET_CAPABILITY_MMS;
-            case "SUPL": return NetworkCapabilities.NET_CAPABILITY_SUPL;
-            case "DUN": return NetworkCapabilities.NET_CAPABILITY_DUN;
-            case "FOTA": return NetworkCapabilities.NET_CAPABILITY_FOTA;
-            case "IMS": return NetworkCapabilities.NET_CAPABILITY_IMS;
-            case "CBS": return NetworkCapabilities.NET_CAPABILITY_CBS;
-            case "XCAP": return NetworkCapabilities.NET_CAPABILITY_XCAP;
-            case "EIMS": return NetworkCapabilities.NET_CAPABILITY_EIMS;
-            case "INTERNET": return NetworkCapabilities.NET_CAPABILITY_INTERNET;
-            case "MCX": return NetworkCapabilities.NET_CAPABILITY_MCX;
-            case "VSIM": return NetworkCapabilities.NET_CAPABILITY_VSIM;
-            case "BIP" : return NetworkCapabilities.NET_CAPABILITY_BIP;
-            case "ENTERPRISE": return NetworkCapabilities.NET_CAPABILITY_ENTERPRISE;
-            case "PRIORITIZE_BANDWIDTH":
-                return NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_BANDWIDTH;
-            case "PRIORITIZE_LATENCY":
-                return NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_LATENCY;
-            case "RCS":
-                return NetworkCapabilities.NET_CAPABILITY_RCS;
-            default:
+    @NetCapability
+    public static int getNetworkCapabilityFromString(@NonNull String capabilityString) {
+        return switch (capabilityString.toUpperCase(Locale.ROOT)) {
+            case "MMS" -> NetworkCapabilities.NET_CAPABILITY_MMS;
+            case "SUPL" -> NetworkCapabilities.NET_CAPABILITY_SUPL;
+            case "DUN" -> NetworkCapabilities.NET_CAPABILITY_DUN;
+            case "FOTA" -> NetworkCapabilities.NET_CAPABILITY_FOTA;
+            case "IMS" -> NetworkCapabilities.NET_CAPABILITY_IMS;
+            case "CBS" -> NetworkCapabilities.NET_CAPABILITY_CBS;
+            case "XCAP" -> NetworkCapabilities.NET_CAPABILITY_XCAP;
+            case "EIMS" -> NetworkCapabilities.NET_CAPABILITY_EIMS;
+            case "INTERNET" -> NetworkCapabilities.NET_CAPABILITY_INTERNET;
+            case "MCX" -> NetworkCapabilities.NET_CAPABILITY_MCX;
+            case "VSIM" -> NetworkCapabilities.NET_CAPABILITY_VSIM;
+            case "BIP" -> NetworkCapabilities.NET_CAPABILITY_BIP;
+            case "ENTERPRISE" -> NetworkCapabilities.NET_CAPABILITY_ENTERPRISE;
+            case "PRIORITIZE_BANDWIDTH" -> NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_BANDWIDTH;
+            case "PRIORITIZE_LATENCY" -> NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_LATENCY;
+            case "RCS" -> NetworkCapabilities.NET_CAPABILITY_RCS;
+            default -> {
                 loge("Illegal network capability: " + capabilityString);
-                return -1;
-        }
+                yield -1;
+            }
+        };
     }
 
     /**
@@ -108,7 +104,8 @@
      * @param capabilitiesString capability strings joined by {@code |}
      * @return Set of capabilities
      */
-    public static @NetCapability Set<Integer> getNetworkCapabilitiesFromString(
+    @NetCapability
+    public static Set<Integer> getNetworkCapabilitiesFromString(
             @NonNull String capabilitiesString) {
         // e.g. "IMS|" is not allowed
         if (!capabilitiesString.matches("(\\s*[a-zA-Z_]+\\s*)(\\|\\s*[a-zA-Z_]+\\s*)*")) {
@@ -122,58 +119,57 @@
 
     /**
      * Convert a network capability to string.
-     *
+     * <p>
      * This is for debugging and logging purposes only.
      *
      * @param netCap Network capability.
      * @return Network capability in string format.
      */
-    public static @NonNull String networkCapabilityToString(@NetCapability int netCap) {
-        switch (netCap) {
-            case NetworkCapabilities.NET_CAPABILITY_MMS:                  return "MMS";
-            case NetworkCapabilities.NET_CAPABILITY_SUPL:                 return "SUPL";
-            case NetworkCapabilities.NET_CAPABILITY_DUN:                  return "DUN";
-            case NetworkCapabilities.NET_CAPABILITY_FOTA:                 return "FOTA";
-            case NetworkCapabilities.NET_CAPABILITY_IMS:                  return "IMS";
-            case NetworkCapabilities.NET_CAPABILITY_CBS:                  return "CBS";
-            case NetworkCapabilities.NET_CAPABILITY_WIFI_P2P:             return "WIFI_P2P";
-            case NetworkCapabilities.NET_CAPABILITY_IA:                   return "IA";
-            case NetworkCapabilities.NET_CAPABILITY_RCS:                  return "RCS";
-            case NetworkCapabilities.NET_CAPABILITY_XCAP:                 return "XCAP";
-            case NetworkCapabilities.NET_CAPABILITY_EIMS:                 return "EIMS";
-            case NetworkCapabilities.NET_CAPABILITY_NOT_METERED:          return "NOT_METERED";
-            case NetworkCapabilities.NET_CAPABILITY_INTERNET:             return "INTERNET";
-            case NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED:       return "NOT_RESTRICTED";
-            case NetworkCapabilities.NET_CAPABILITY_TRUSTED:              return "TRUSTED";
-            case NetworkCapabilities.NET_CAPABILITY_NOT_VPN:              return "NOT_VPN";
-            case NetworkCapabilities.NET_CAPABILITY_VALIDATED:            return "VALIDATED";
-            case NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL:       return "CAPTIVE_PORTAL";
-            case NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING:          return "NOT_ROAMING";
-            case NetworkCapabilities.NET_CAPABILITY_FOREGROUND:           return "FOREGROUND";
-            case NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED:        return "NOT_CONGESTED";
-            case NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED:        return "NOT_SUSPENDED";
-            case NetworkCapabilities.NET_CAPABILITY_OEM_PAID:             return "OEM_PAID";
-            case NetworkCapabilities.NET_CAPABILITY_MCX:                  return "MCX";
-            case NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY:
-                return "PARTIAL_CONNECTIVITY";
-            case NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED:
-                return "TEMPORARILY_NOT_METERED";
-            case NetworkCapabilities.NET_CAPABILITY_OEM_PRIVATE:          return "OEM_PRIVATE";
-            case NetworkCapabilities.NET_CAPABILITY_VEHICLE_INTERNAL:     return "VEHICLE_INTERNAL";
-            case NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED:      return "NOT_VCN_MANAGED";
-            case NetworkCapabilities.NET_CAPABILITY_ENTERPRISE:           return "ENTERPRISE";
-            case NetworkCapabilities.NET_CAPABILITY_VSIM:                 return "VSIM";
-            case NetworkCapabilities.NET_CAPABILITY_BIP:                  return "BIP";
-            case NetworkCapabilities.NET_CAPABILITY_HEAD_UNIT:            return "HEAD_UNIT";
-            case NetworkCapabilities.NET_CAPABILITY_MMTEL:                return "MMTEL";
-            case NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_LATENCY:
-                return "PRIORITIZE_LATENCY";
-            case NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_BANDWIDTH:
-                return "PRIORITIZE_BANDWIDTH";
-            default:
+    @NonNull
+    public static String networkCapabilityToString(@NetCapability int netCap) {
+        return switch (netCap) {
+            case NetworkCapabilities.NET_CAPABILITY_MMS -> "MMS";
+            case NetworkCapabilities.NET_CAPABILITY_SUPL -> "SUPL";
+            case NetworkCapabilities.NET_CAPABILITY_DUN -> "DUN";
+            case NetworkCapabilities.NET_CAPABILITY_FOTA -> "FOTA";
+            case NetworkCapabilities.NET_CAPABILITY_IMS -> "IMS";
+            case NetworkCapabilities.NET_CAPABILITY_CBS -> "CBS";
+            case NetworkCapabilities.NET_CAPABILITY_WIFI_P2P -> "WIFI_P2P";
+            case NetworkCapabilities.NET_CAPABILITY_IA -> "IA";
+            case NetworkCapabilities.NET_CAPABILITY_RCS -> "RCS";
+            case NetworkCapabilities.NET_CAPABILITY_XCAP -> "XCAP";
+            case NetworkCapabilities.NET_CAPABILITY_EIMS -> "EIMS";
+            case NetworkCapabilities.NET_CAPABILITY_NOT_METERED -> "NOT_METERED";
+            case NetworkCapabilities.NET_CAPABILITY_INTERNET -> "INTERNET";
+            case NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED -> "NOT_RESTRICTED";
+            case NetworkCapabilities.NET_CAPABILITY_TRUSTED -> "TRUSTED";
+            case NetworkCapabilities.NET_CAPABILITY_NOT_VPN -> "NOT_VPN";
+            case NetworkCapabilities.NET_CAPABILITY_VALIDATED -> "VALIDATED";
+            case NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL -> "CAPTIVE_PORTAL";
+            case NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING -> "NOT_ROAMING";
+            case NetworkCapabilities.NET_CAPABILITY_FOREGROUND -> "FOREGROUND";
+            case NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED -> "NOT_CONGESTED";
+            case NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED -> "NOT_SUSPENDED";
+            case NetworkCapabilities.NET_CAPABILITY_OEM_PAID -> "OEM_PAID";
+            case NetworkCapabilities.NET_CAPABILITY_MCX -> "MCX";
+            case NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY -> "PARTIAL_CONNECTIVITY";
+            case NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED ->
+                    "TEMPORARILY_NOT_METERED";
+            case NetworkCapabilities.NET_CAPABILITY_OEM_PRIVATE -> "OEM_PRIVATE";
+            case NetworkCapabilities.NET_CAPABILITY_VEHICLE_INTERNAL -> "VEHICLE_INTERNAL";
+            case NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED -> "NOT_VCN_MANAGED";
+            case NetworkCapabilities.NET_CAPABILITY_ENTERPRISE -> "ENTERPRISE";
+            case NetworkCapabilities.NET_CAPABILITY_VSIM -> "VSIM";
+            case NetworkCapabilities.NET_CAPABILITY_BIP -> "BIP";
+            case NetworkCapabilities.NET_CAPABILITY_HEAD_UNIT -> "HEAD_UNIT";
+            case NetworkCapabilities.NET_CAPABILITY_MMTEL -> "MMTEL";
+            case NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_LATENCY -> "PRIORITIZE_LATENCY";
+            case NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_BANDWIDTH -> "PRIORITIZE_BANDWIDTH";
+            default -> {
                 loge("Unknown network capability(" + netCap + ")");
-                return "Unknown(" + netCap + ")";
-        }
+                yield "Unknown(" + netCap + ")";
+            }
+        };
     }
 
     /**
@@ -216,13 +212,14 @@
 
     /**
      * Convert network capabilities to string.
-     *
+     * <p>
      * This is for debugging and logging purposes only.
      *
      * @param netCaps Network capabilities.
      * @return Network capabilities in string format.
      */
-    public static @NonNull String networkCapabilitiesToString(
+    @NonNull
+    public static String networkCapabilitiesToString(
             @NetCapability @Nullable Collection<Integer> netCaps) {
         if (netCaps == null || netCaps.isEmpty()) return "";
         return "[" + netCaps.stream()
@@ -232,13 +229,14 @@
 
     /**
      * Convert network capabilities to string.
-     *
+     * <p>
      * This is for debugging and logging purposes only.
      *
      * @param netCaps Network capabilities.
      * @return Network capabilities in string format.
      */
-    public static @NonNull String networkCapabilitiesToString(@NetCapability int[] netCaps) {
+    @NonNull
+    public static String networkCapabilitiesToString(@NetCapability int[] netCaps) {
         if (netCaps == null) return "";
         return "[" + Arrays.stream(netCaps)
                 .mapToObj(DataUtils::networkCapabilityToString)
@@ -251,14 +249,16 @@
      * @param status The validation status.
      * @return The validation status in string format.
      */
-    public static @NonNull String validationStatusToString(@ValidationStatus int status) {
-        switch (status) {
-            case NetworkAgent.VALIDATION_STATUS_VALID: return "VALID";
-            case NetworkAgent.VALIDATION_STATUS_NOT_VALID: return "INVALID";
-            default:
+    @NonNull
+    public static String validationStatusToString(@ValidationStatus int status) {
+        return switch (status) {
+            case NetworkAgent.VALIDATION_STATUS_VALID -> "VALID";
+            case NetworkAgent.VALIDATION_STATUS_NOT_VALID -> "INVALID";
+            default -> {
                 loge("Unknown validation status(" + status + ")");
-                return "UNKNOWN(" + status + ")";
-        }
+                yield "UNKNOWN(" + status + ")";
+            }
+        };
     }
 
     /**
@@ -267,41 +267,26 @@
      * @param networkCapability Network capability.
      * @return APN type.
      */
-    public static @ApnType int networkCapabilityToApnType(@NetCapability int networkCapability) {
-        switch (networkCapability) {
-            case NetworkCapabilities.NET_CAPABILITY_MMS:
-                return ApnSetting.TYPE_MMS;
-            case NetworkCapabilities.NET_CAPABILITY_SUPL:
-                return ApnSetting.TYPE_SUPL;
-            case NetworkCapabilities.NET_CAPABILITY_DUN:
-                return ApnSetting.TYPE_DUN;
-            case NetworkCapabilities.NET_CAPABILITY_FOTA:
-                return ApnSetting.TYPE_FOTA;
-            case NetworkCapabilities.NET_CAPABILITY_IMS:
-                return ApnSetting.TYPE_IMS;
-            case NetworkCapabilities.NET_CAPABILITY_CBS:
-                return ApnSetting.TYPE_CBS;
-            case NetworkCapabilities.NET_CAPABILITY_XCAP:
-                return ApnSetting.TYPE_XCAP;
-            case NetworkCapabilities.NET_CAPABILITY_EIMS:
-                return ApnSetting.TYPE_EMERGENCY;
-            case NetworkCapabilities.NET_CAPABILITY_INTERNET:
-                return ApnSetting.TYPE_DEFAULT;
-            case NetworkCapabilities.NET_CAPABILITY_MCX:
-                return ApnSetting.TYPE_MCX;
-            case NetworkCapabilities.NET_CAPABILITY_IA:
-                return ApnSetting.TYPE_IA;
-            case NetworkCapabilities.NET_CAPABILITY_ENTERPRISE:
-                return ApnSetting.TYPE_ENTERPRISE;
-            case NetworkCapabilities.NET_CAPABILITY_VSIM:
-                return ApnSetting.TYPE_VSIM;
-            case NetworkCapabilities.NET_CAPABILITY_BIP:
-                return ApnSetting.TYPE_BIP;
-            case NetworkCapabilities.NET_CAPABILITY_RCS:
-                return ApnSetting.TYPE_RCS;
-            default:
-                return ApnSetting.TYPE_NONE;
-        }
+    @ApnType
+    public static int networkCapabilityToApnType(@NetCapability int networkCapability) {
+        return switch (networkCapability) {
+            case NetworkCapabilities.NET_CAPABILITY_MMS -> ApnSetting.TYPE_MMS;
+            case NetworkCapabilities.NET_CAPABILITY_SUPL -> ApnSetting.TYPE_SUPL;
+            case NetworkCapabilities.NET_CAPABILITY_DUN -> ApnSetting.TYPE_DUN;
+            case NetworkCapabilities.NET_CAPABILITY_FOTA -> ApnSetting.TYPE_FOTA;
+            case NetworkCapabilities.NET_CAPABILITY_IMS -> ApnSetting.TYPE_IMS;
+            case NetworkCapabilities.NET_CAPABILITY_CBS -> ApnSetting.TYPE_CBS;
+            case NetworkCapabilities.NET_CAPABILITY_XCAP -> ApnSetting.TYPE_XCAP;
+            case NetworkCapabilities.NET_CAPABILITY_EIMS -> ApnSetting.TYPE_EMERGENCY;
+            case NetworkCapabilities.NET_CAPABILITY_INTERNET -> ApnSetting.TYPE_DEFAULT;
+            case NetworkCapabilities.NET_CAPABILITY_MCX -> ApnSetting.TYPE_MCX;
+            case NetworkCapabilities.NET_CAPABILITY_IA -> ApnSetting.TYPE_IA;
+            case NetworkCapabilities.NET_CAPABILITY_ENTERPRISE -> ApnSetting.TYPE_ENTERPRISE;
+            case NetworkCapabilities.NET_CAPABILITY_VSIM -> ApnSetting.TYPE_VSIM;
+            case NetworkCapabilities.NET_CAPABILITY_BIP -> ApnSetting.TYPE_BIP;
+            case NetworkCapabilities.NET_CAPABILITY_RCS -> ApnSetting.TYPE_RCS;
+            default -> ApnSetting.TYPE_NONE;
+        };
     }
 
     /**
@@ -310,41 +295,26 @@
      * @param apnType APN type.
      * @return Network capability.
      */
-    public static @NetCapability int apnTypeToNetworkCapability(@ApnType int apnType) {
-        switch (apnType) {
-            case ApnSetting.TYPE_MMS:
-                return NetworkCapabilities.NET_CAPABILITY_MMS;
-            case ApnSetting.TYPE_SUPL:
-                return NetworkCapabilities.NET_CAPABILITY_SUPL;
-            case ApnSetting.TYPE_DUN:
-                return NetworkCapabilities.NET_CAPABILITY_DUN;
-            case ApnSetting.TYPE_FOTA:
-                return NetworkCapabilities.NET_CAPABILITY_FOTA;
-            case ApnSetting.TYPE_IMS:
-                return NetworkCapabilities.NET_CAPABILITY_IMS;
-            case ApnSetting.TYPE_CBS:
-                return NetworkCapabilities.NET_CAPABILITY_CBS;
-            case ApnSetting.TYPE_XCAP:
-                return NetworkCapabilities.NET_CAPABILITY_XCAP;
-            case ApnSetting.TYPE_EMERGENCY:
-                return NetworkCapabilities.NET_CAPABILITY_EIMS;
-            case ApnSetting.TYPE_DEFAULT:
-                return NetworkCapabilities.NET_CAPABILITY_INTERNET;
-            case ApnSetting.TYPE_MCX:
-                return NetworkCapabilities.NET_CAPABILITY_MCX;
-            case ApnSetting.TYPE_IA:
-                return NetworkCapabilities.NET_CAPABILITY_IA;
-            case ApnSetting.TYPE_BIP:
-                return NetworkCapabilities.NET_CAPABILITY_BIP;
-            case ApnSetting.TYPE_VSIM:
-                return NetworkCapabilities.NET_CAPABILITY_VSIM;
-            case ApnSetting.TYPE_ENTERPRISE:
-                return NetworkCapabilities.NET_CAPABILITY_ENTERPRISE;
-            case ApnSetting.TYPE_RCS:
-                return NetworkCapabilities.NET_CAPABILITY_RCS;
-            default:
-                return -1;
-        }
+    @NetCapability
+    public static int apnTypeToNetworkCapability(@ApnType int apnType) {
+        return switch (apnType) {
+            case ApnSetting.TYPE_MMS -> NetworkCapabilities.NET_CAPABILITY_MMS;
+            case ApnSetting.TYPE_SUPL -> NetworkCapabilities.NET_CAPABILITY_SUPL;
+            case ApnSetting.TYPE_DUN -> NetworkCapabilities.NET_CAPABILITY_DUN;
+            case ApnSetting.TYPE_FOTA -> NetworkCapabilities.NET_CAPABILITY_FOTA;
+            case ApnSetting.TYPE_IMS -> NetworkCapabilities.NET_CAPABILITY_IMS;
+            case ApnSetting.TYPE_CBS -> NetworkCapabilities.NET_CAPABILITY_CBS;
+            case ApnSetting.TYPE_XCAP -> NetworkCapabilities.NET_CAPABILITY_XCAP;
+            case ApnSetting.TYPE_EMERGENCY -> NetworkCapabilities.NET_CAPABILITY_EIMS;
+            case ApnSetting.TYPE_DEFAULT -> NetworkCapabilities.NET_CAPABILITY_INTERNET;
+            case ApnSetting.TYPE_MCX -> NetworkCapabilities.NET_CAPABILITY_MCX;
+            case ApnSetting.TYPE_IA -> NetworkCapabilities.NET_CAPABILITY_IA;
+            case ApnSetting.TYPE_BIP -> NetworkCapabilities.NET_CAPABILITY_BIP;
+            case ApnSetting.TYPE_VSIM -> NetworkCapabilities.NET_CAPABILITY_VSIM;
+            case ApnSetting.TYPE_ENTERPRISE -> NetworkCapabilities.NET_CAPABILITY_ENTERPRISE;
+            case ApnSetting.TYPE_RCS -> NetworkCapabilities.NET_CAPABILITY_RCS;
+            default -> -1;
+        };
     }
 
     /**
@@ -353,37 +323,26 @@
      * @param networkType The network type.
      * @return The access network type.
      */
-    public static @RadioAccessNetworkType int networkTypeToAccessNetworkType(
-            @NetworkType int networkType) {
-        switch (networkType) {
-            case TelephonyManager.NETWORK_TYPE_GPRS:
-            case TelephonyManager.NETWORK_TYPE_EDGE:
-            case TelephonyManager.NETWORK_TYPE_GSM:
-                return AccessNetworkType.GERAN;
-            case TelephonyManager.NETWORK_TYPE_UMTS:
-            case TelephonyManager.NETWORK_TYPE_HSDPA:
-            case TelephonyManager.NETWORK_TYPE_HSPAP:
-            case TelephonyManager.NETWORK_TYPE_HSUPA:
-            case TelephonyManager.NETWORK_TYPE_HSPA:
-            case TelephonyManager.NETWORK_TYPE_TD_SCDMA:
-                return AccessNetworkType.UTRAN;
-            case TelephonyManager.NETWORK_TYPE_CDMA:
-            case TelephonyManager.NETWORK_TYPE_EVDO_0:
-            case TelephonyManager.NETWORK_TYPE_EVDO_A:
-            case TelephonyManager.NETWORK_TYPE_EVDO_B:
-            case TelephonyManager.NETWORK_TYPE_1xRTT:
-            case TelephonyManager.NETWORK_TYPE_EHRPD:
-                return AccessNetworkType.CDMA2000;
-            case TelephonyManager.NETWORK_TYPE_LTE:
-            case TelephonyManager.NETWORK_TYPE_LTE_CA:
-                return AccessNetworkType.EUTRAN;
-            case TelephonyManager.NETWORK_TYPE_IWLAN:
-                return AccessNetworkType.IWLAN;
-            case TelephonyManager.NETWORK_TYPE_NR:
-                return AccessNetworkType.NGRAN;
-            default:
-                return AccessNetworkType.UNKNOWN;
-        }
+    @RadioAccessNetworkType
+    public static int networkTypeToAccessNetworkType(@NetworkType int networkType) {
+        return switch (networkType) {
+            case TelephonyManager.NETWORK_TYPE_GPRS, TelephonyManager.NETWORK_TYPE_EDGE,
+                    TelephonyManager.NETWORK_TYPE_GSM ->
+                    AccessNetworkType.GERAN;
+            case TelephonyManager.NETWORK_TYPE_UMTS, TelephonyManager.NETWORK_TYPE_HSDPA,
+                    TelephonyManager.NETWORK_TYPE_HSPAP, TelephonyManager.NETWORK_TYPE_HSUPA,
+                    TelephonyManager.NETWORK_TYPE_HSPA, TelephonyManager.NETWORK_TYPE_TD_SCDMA ->
+                    AccessNetworkType.UTRAN;
+            case TelephonyManager.NETWORK_TYPE_CDMA, TelephonyManager.NETWORK_TYPE_EVDO_0,
+                    TelephonyManager.NETWORK_TYPE_EVDO_A, TelephonyManager.NETWORK_TYPE_EVDO_B,
+                    TelephonyManager.NETWORK_TYPE_1xRTT, TelephonyManager.NETWORK_TYPE_EHRPD ->
+                    AccessNetworkType.CDMA2000;
+            case TelephonyManager.NETWORK_TYPE_LTE, TelephonyManager.NETWORK_TYPE_LTE_CA ->
+                    AccessNetworkType.EUTRAN;
+            case TelephonyManager.NETWORK_TYPE_IWLAN -> AccessNetworkType.IWLAN;
+            case TelephonyManager.NETWORK_TYPE_NR -> AccessNetworkType.NGRAN;
+            default -> AccessNetworkType.UNKNOWN;
+        };
     }
 
     /**
@@ -392,7 +351,8 @@
      * @param elapsedTime The elapsed time retrieved from {@link SystemClock#elapsedRealtime()}.
      * @return The string format time.
      */
-    public static @NonNull String elapsedTimeToString(@ElapsedRealtimeLong long elapsedTime) {
+    @NonNull
+    public static String elapsedTimeToString(@ElapsedRealtimeLong long elapsedTime) {
         return (elapsedTime != 0) ? systemTimeToString(System.currentTimeMillis()
                 - SystemClock.elapsedRealtime() + elapsedTime) : "never";
     }
@@ -403,7 +363,8 @@
      * @param systemTime The system time retrieved from {@link System#currentTimeMillis()}.
      * @return The string format time.
      */
-    public static @NonNull String systemTimeToString(@CurrentTimeMillisLong long systemTime) {
+    @NonNull
+    public static String systemTimeToString(@CurrentTimeMillisLong long systemTime) {
         return (systemTime != 0) ? TIME_FORMAT.format(systemTime) : "never";
     }
 
@@ -413,33 +374,16 @@
      * @param imsFeature IMS feature.
      * @return IMS feature in string format.
      */
-    public static @NonNull String imsFeatureToString(@ImsFeature.FeatureType int imsFeature) {
-        switch (imsFeature) {
-            case ImsFeature.FEATURE_MMTEL: return "MMTEL";
-            case ImsFeature.FEATURE_RCS: return "RCS";
-            default:
+    @NonNull
+    public static String imsFeatureToString(@ImsFeature.FeatureType int imsFeature) {
+        return switch (imsFeature) {
+            case ImsFeature.FEATURE_MMTEL -> "MMTEL";
+            case ImsFeature.FEATURE_RCS -> "RCS";
+            default -> {
                 loge("Unknown IMS feature(" + imsFeature + ")");
-                return "Unknown(" + imsFeature + ")";
-        }
-    }
-
-    /**
-     * Get the highest priority supported network capability from the specified data profile.
-     *
-     * @param dataConfigManager The data config that contains network priority information.
-     * @param dataProfile The data profile
-     * @return The highest priority network capability. -1 if cannot find one.
-     */
-    public static @NetCapability int getHighestPriorityNetworkCapabilityFromDataProfile(
-            @NonNull DataConfigManager dataConfigManager, @NonNull DataProfile dataProfile) {
-        if (dataProfile.getApnSetting() == null
-                || dataProfile.getApnSetting().getApnTypes().isEmpty()) return -1;
-        return dataProfile.getApnSetting().getApnTypes().stream()
-                .map(DataUtils::apnTypeToNetworkCapability)
-                .sorted(Comparator.comparing(dataConfigManager::getNetworkCapabilityPriority)
-                        .reversed())
-                .collect(Collectors.toList())
-                .get(0);
+                yield "Unknown(" + imsFeature + ")";
+            }
+        };
     }
 
     /**
@@ -450,7 +394,8 @@
      *
      * @return The network requests after grouping.
      */
-    public static @NonNull List<NetworkRequestList> getGroupedNetworkRequestList(
+    @NonNull
+    public static List<NetworkRequestList> getGroupedNetworkRequestList(
             @NonNull NetworkRequestList networkRequestList, @NonNull FeatureFlags featureFlags) {
         List<NetworkRequestList> requests = new ArrayList<>();
         if (featureFlags.satelliteInternet()) {
@@ -513,41 +458,31 @@
      * @param sourceTransport The source transport.
      * @return The target transport.
      */
-    public static @TransportType int getTargetTransport(@TransportType int sourceTransport) {
+    @TransportType
+    public static int getTargetTransport(@TransportType int sourceTransport) {
         return sourceTransport == AccessNetworkConstants.TRANSPORT_TYPE_WWAN
                 ? AccessNetworkConstants.TRANSPORT_TYPE_WLAN
                 : AccessNetworkConstants.TRANSPORT_TYPE_WWAN;
     }
 
     /**
-     * Get the source transport from target transport. This is only used for handover between
-     * IWLAN and cellular scenario.
-     *
-     * @param targetTransport The target transport.
-     * @return The source transport.
-     */
-    public static @TransportType int getSourceTransport(@TransportType int targetTransport) {
-        return targetTransport == AccessNetworkConstants.TRANSPORT_TYPE_WWAN
-                ? AccessNetworkConstants.TRANSPORT_TYPE_WLAN
-                : AccessNetworkConstants.TRANSPORT_TYPE_WWAN;
-    }
-
-    /**
      * Convert link status to string.
      *
      * @param linkStatus The link status.
      * @return The link status in string format.
      */
-    public static @NonNull String linkStatusToString(@LinkStatus int linkStatus) {
-        switch (linkStatus) {
-            case DataCallResponse.LINK_STATUS_UNKNOWN: return "UNKNOWN";
-            case DataCallResponse.LINK_STATUS_INACTIVE: return "INACTIVE";
-            case DataCallResponse.LINK_STATUS_ACTIVE: return "ACTIVE";
-            case DataCallResponse.LINK_STATUS_DORMANT: return "DORMANT";
-            default:
+    @NonNull
+    public static String linkStatusToString(@LinkStatus int linkStatus) {
+        return switch (linkStatus) {
+            case DataCallResponse.LINK_STATUS_UNKNOWN -> "UNKNOWN";
+            case DataCallResponse.LINK_STATUS_INACTIVE -> "INACTIVE";
+            case DataCallResponse.LINK_STATUS_ACTIVE -> "ACTIVE";
+            case DataCallResponse.LINK_STATUS_DORMANT -> "DORMANT";
+            default -> {
                 loge("Unknown link status(" + linkStatus + ")");
-                return "UNKNOWN(" + linkStatus + ")";
-        }
+                yield "UNKNOWN(" + linkStatus + ")";
+            }
+        };
     }
 
     /**
@@ -557,17 +492,12 @@
      * @return {@code true} if the access network type is valid.
      */
     public static boolean isValidAccessNetwork(@RadioAccessNetworkType int accessNetworkType) {
-        switch (accessNetworkType) {
-            case AccessNetworkType.GERAN:
-            case AccessNetworkType.UTRAN:
-            case AccessNetworkType.EUTRAN:
-            case AccessNetworkType.CDMA2000:
-            case AccessNetworkType.IWLAN:
-            case AccessNetworkType.NGRAN:
-                return true;
-            default:
-                return false;
-        }
+        return switch (accessNetworkType) {
+            case AccessNetworkType.GERAN, AccessNetworkType.UTRAN, AccessNetworkType.EUTRAN,
+                    AccessNetworkType.CDMA2000, AccessNetworkType.IWLAN, AccessNetworkType.NGRAN ->
+                    true;
+            default -> false;
+        };
     }
 
     /**
@@ -576,17 +506,19 @@
      * @param dataActivity The data activity.
      * @return The data activity in string format.
      */
-    public static @NonNull String dataActivityToString(@DataActivityType int dataActivity) {
-        switch (dataActivity) {
-            case TelephonyManager.DATA_ACTIVITY_NONE: return "NONE";
-            case TelephonyManager.DATA_ACTIVITY_IN: return "IN";
-            case TelephonyManager.DATA_ACTIVITY_OUT: return "OUT";
-            case TelephonyManager.DATA_ACTIVITY_INOUT: return "INOUT";
-            case TelephonyManager.DATA_ACTIVITY_DORMANT: return "DORMANT";
-            default:
+    @NonNull
+    public static String dataActivityToString(@DataActivityType int dataActivity) {
+        return switch (dataActivity) {
+            case TelephonyManager.DATA_ACTIVITY_NONE -> "NONE";
+            case TelephonyManager.DATA_ACTIVITY_IN -> "IN";
+            case TelephonyManager.DATA_ACTIVITY_OUT -> "OUT";
+            case TelephonyManager.DATA_ACTIVITY_INOUT -> "INOUT";
+            case TelephonyManager.DATA_ACTIVITY_DORMANT -> "DORMANT";
+            default -> {
                 loge("Unknown data activity(" + dataActivity + ")");
-                return "UNKNOWN(" + dataActivity + ")";
-        }
+                yield "UNKNOWN(" + dataActivity + ")";
+            }
+        };
     }
 
     private static void loge(String msg) {
diff --git a/src/java/com/android/internal/telephony/data/KeepaliveStatus.java b/src/java/com/android/internal/telephony/data/KeepaliveStatus.java
index 818de96..7b6c38d 100644
--- a/src/java/com/android/internal/telephony/data/KeepaliveStatus.java
+++ b/src/java/com/android/internal/telephony/data/KeepaliveStatus.java
@@ -23,7 +23,7 @@
 /**
  * This class serves to pass around the parameters of Keepalive session
  * status within the telephony framework.
- *
+ * <p>
  * {@hide}
  */
 public class KeepaliveStatus implements Parcelable {
@@ -54,7 +54,8 @@
      * A status code indicating whether this Keepalive session is
      * active, inactive, or pending activation
      */
-    public final @KeepaliveStatusCode int statusCode;
+    @KeepaliveStatusCode
+    public final int statusCode;
 
     /** An error code indicating a lower layer failure, if any */
     public final int errorCode;
@@ -98,7 +99,7 @@
     }
 
     public static final Parcelable.Creator<KeepaliveStatus> CREATOR =
-            new Parcelable.Creator<KeepaliveStatus>() {
+            new Parcelable.Creator<>() {
                 @Override
                 public KeepaliveStatus createFromParcel(Parcel source) {
                     return new KeepaliveStatus(source);
diff --git a/src/java/com/android/internal/telephony/data/KeepaliveTracker.java b/src/java/com/android/internal/telephony/data/KeepaliveTracker.java
index f9139ec..f221779 100644
--- a/src/java/com/android/internal/telephony/data/KeepaliveTracker.java
+++ b/src/java/com/android/internal/telephony/data/KeepaliveTracker.java
@@ -56,19 +56,24 @@
     private static final int EVENT_UNREGISTER_FOR_KEEPALIVE_STATUS = 5;
 
     /** The phone instance. */
-    private final @NonNull Phone mPhone;
+    @NonNull
+    private final Phone mPhone;
 
     /** The parent data network. */
-    private final @NonNull DataNetwork mDataNetwork;
+    @NonNull
+    private final DataNetwork mDataNetwork;
 
     /** The associated network agent. */
-    private final @NonNull TelephonyNetworkAgent mNetworkAgent;
+    @NonNull
+    private final TelephonyNetworkAgent mNetworkAgent;
 
     /** The log tag. */
-    private final @NonNull String mLogTag;
+    @NonNull
+    private final String mLogTag;
 
     /** The keepalive records. */
-    private final @NonNull SparseArray<KeepaliveRecord> mKeepalives = new SparseArray<>();
+    @NonNull
+    private final SparseArray<KeepaliveRecord> mKeepalives = new SparseArray<>();
 
     /**
      * Keepalive session record
@@ -78,7 +83,8 @@
         public int slotIndex;
 
         /** The current status. */
-        public @KeepaliveStatusCode int currentStatus;
+        @KeepaliveStatusCode
+        public int currentStatus;
 
         /**
          * Constructor
@@ -254,17 +260,12 @@
      * @return The socket alive error.
      */
     private 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;
-        }
+        return switch (error) {
+            case KeepaliveStatus.ERROR_NONE -> SocketKeepalive.SUCCESS;
+            case KeepaliveStatus.ERROR_UNSUPPORTED -> SocketKeepalive.ERROR_UNSUPPORTED;
+            case KeepaliveStatus.ERROR_NO_RESOURCES -> SocketKeepalive.ERROR_INSUFFICIENT_RESOURCES;
+            default -> SocketKeepalive.ERROR_HARDWARE_ERROR;
+        };
     }
 
     /**
diff --git a/src/java/com/android/internal/telephony/data/LinkBandwidthEstimator.java b/src/java/com/android/internal/telephony/data/LinkBandwidthEstimator.java
index de8c48c..b92a590 100644
--- a/src/java/com/android/internal/telephony/data/LinkBandwidthEstimator.java
+++ b/src/java/com/android/internal/telephony/data/LinkBandwidthEstimator.java
@@ -30,6 +30,7 @@
 import android.os.AsyncResult;
 import android.os.Handler;
 import android.os.HandlerExecutor;
+import android.os.Looper;
 import android.os.Message;
 import android.os.OutcomeReceiver;
 import android.preference.PreferenceManager;
@@ -165,7 +166,6 @@
     private final Phone mPhone;
     private final TelephonyFacade mTelephonyFacade;
     private final TelephonyManager mTelephonyManager;
-    private final ConnectivityManager mConnectivityManager;
     private final LocalLog mLocalLog = new LocalLog(512);
     private boolean mScreenOn = false;
     private boolean mIsOnDefaultRoute = false;
@@ -178,29 +178,29 @@
     private long mRxBytesDeltaAcc;
 
     private ModemActivityInfo mLastModemActivityInfo = null;
-    private final TelephonyCallback mTelephonyCallback = new TelephonyCallbackImpl();
     private int mSignalStrengthDbm;
     private int mSignalLevel;
     private int mDataRat = TelephonyManager.NETWORK_TYPE_UNKNOWN;
     private int mTac;
     @NonNull private String mPlmn = UNKNOWN_PLMN;
     private NetworkCapabilities mNetworkCapabilities;
-    private NetworkBandwidth mPlaceholderNetwork;
+    private final NetworkBandwidth mPlaceholderNetwork;
     private long mFilterUpdateTimeMs;
 
     private int mBandwidthUpdateSignalDbm = -1;
     private int mBandwidthUpdateSignalLevel = -1;
     private int mBandwidthUpdateDataRat = TelephonyManager.NETWORK_TYPE_UNKNOWN;
     private String mBandwidthUpdatePlmn = UNKNOWN_PLMN;
-    private BandwidthState mTxState = new BandwidthState(LINK_TX);
-    private BandwidthState mRxState = new BandwidthState(LINK_RX);
+    private final BandwidthState mTxState = new BandwidthState(LINK_TX);
+    private final BandwidthState mRxState = new BandwidthState(LINK_RX);
     private long mLastPlmnOrRatChangeTimeMs;
     private long mLastDrsOrRatChangeTimeMs;
 
     private int mDataActivity = TelephonyManager.DATA_ACTIVITY_NONE;
 
     /** Link bandwidth estimator callbacks. */
-    private final @NonNull Set<LinkBandwidthEstimatorCallback> mLinkBandwidthEstimatorCallbacks =
+    @NonNull
+    private final Set<LinkBandwidthEstimatorCallback> mLinkBandwidthEstimatorCallbacks =
             new ArraySet<>();
 
     /**
@@ -270,14 +270,14 @@
 
     private final OutcomeReceiver<ModemActivityInfo, TelephonyManager.ModemActivityInfoException>
             mOutcomeReceiver =
-            new OutcomeReceiver<ModemActivityInfo, TelephonyManager.ModemActivityInfoException>() {
+            new OutcomeReceiver<>() {
                 @Override
                 public void onResult(ModemActivityInfo result) {
                     obtainMessage(MSG_MODEM_ACTIVITY_RETURNED, result).sendToTarget();
                 }
 
                 @Override
-                public void onError(TelephonyManager.ModemActivityInfoException e) {
+                public void onError(@NonNull TelephonyManager.ModemActivityInfoException e) {
                     Rlog.e(TAG, "error reading modem stats:" + e);
                     obtainMessage(MSG_MODEM_ACTIVITY_RETURNED, null).sendToTarget();
                 }
@@ -296,20 +296,27 @@
                 }
             };
 
-    public LinkBandwidthEstimator(Phone phone, TelephonyFacade telephonyFacade) {
+    public LinkBandwidthEstimator(Phone phone, Looper looper, TelephonyFacade telephonyFacade) {
+        super(looper);
         mPhone = phone;
         mTelephonyFacade = telephonyFacade;
         mTelephonyManager = phone.getContext()
                 .getSystemService(TelephonyManager.class)
                 .createForSubscriptionId(phone.getSubId());
-        mConnectivityManager = phone.getContext().getSystemService(ConnectivityManager.class);
         DisplayManager dm = (DisplayManager) phone.getContext().getSystemService(
                 Context.DISPLAY_SERVICE);
-        dm.registerDisplayListener(mDisplayListener, null);
+        if (dm != null) {
+            dm.registerDisplayListener(mDisplayListener, null);
+        }
         handleScreenStateChanged(isScreenOn());
-        mConnectivityManager.registerDefaultNetworkCallback(mDefaultNetworkCallback, this);
+
+        ConnectivityManager cm = phone.getContext()
+                .getSystemService(ConnectivityManager.class);
+        if (cm != null) {
+            cm.registerDefaultNetworkCallback(mDefaultNetworkCallback, this);
+        }
         mTelephonyManager.registerTelephonyCallback(new HandlerExecutor(this),
-                mTelephonyCallback);
+                new TelephonyCallbackImpl());
         mPlaceholderNetwork = new NetworkBandwidth(UNKNOWN_PLMN);
         initAvgBwPerRatTable();
         registerNrStateFrequencyChange();
@@ -382,6 +389,7 @@
         // the screen is turned off transiently such as due to the proximity sensor.
         final DisplayManager dm = (DisplayManager) mPhone.getContext().getSystemService(
                 Context.DISPLAY_SERVICE);
+        if (dm == null) return false;
         Display[] displays = dm.getDisplays();
 
         if (displays != null) {
@@ -432,6 +440,7 @@
         handleTrafficStatsPollConditionChanged();
     }
 
+    @SuppressWarnings("unchecked")
     private void handleDrsOrRatChanged(AsyncResult ar) {
         Pair<Integer, Integer> drsRatPair = (Pair<Integer, Integer>) ar.result;
         logd("DrsOrRatChanged dataRegState " + drsRatPair.first + " rilRat " + drsRatPair.second);
@@ -729,7 +738,7 @@
                 return;
             }
             long filterOutKbps = (long) mFilterKbps * alpha
-                    + filterInKbps * FILTER_SCALE - filterInKbps * alpha;
+                    + (long) filterInKbps * FILTER_SCALE - (long) filterInKbps * alpha;
             filterOutKbps = filterOutKbps / FILTER_SCALE;
             mFilterKbps = (int) Math.min(filterOutKbps, Integer.MAX_VALUE);
 
@@ -877,8 +886,8 @@
 
             StringBuilder sb = new StringBuilder();
             logd(sb.append(mLink)
-                    .append(" sampKbps ").append(mBwSampleKbps)
-                    .append(" filtKbps ").append(mFilterKbps)
+                    .append(" sampleKbps ").append(mBwSampleKbps)
+                    .append(" filterKbps ").append(mFilterKbps)
                     .append(" reportKbps ").append(mLastReportedBwKbps)
                     .append(" avgUsedKbps ").append(mAvgUsedKbps)
                     .append(" csKbps ").append(mStaticBwKbps)
@@ -944,7 +953,8 @@
     /**
      * @return The data activity.
      */
-    public @DataActivityType int getDataActivity() {
+    @DataActivityType
+    public int getDataActivity() {
         return mDataActivity;
     }
 
@@ -1048,7 +1058,7 @@
         /* ss should always be non-null */
         if (!TextUtils.isEmpty(ss.getOperatorNumeric())) {
             plmn = ss.getOperatorNumeric();
-        } else if (cellIdentity != null && !TextUtils.isEmpty(cellIdentity.getPlmn())) {
+        } else if (!TextUtils.isEmpty(cellIdentity.getPlmn())) {
             plmn = cellIdentity.getPlmn();
         } else {
             plmn = UNKNOWN_PLMN;
@@ -1059,7 +1069,6 @@
             mPlmn = plmn;
         }
 
-        boolean updatedRat = false;
         NetworkRegistrationInfo nri = getDataNri();
         if (nri != null) {
             int dataRat = nri.getAccessNetworkTechnology();
@@ -1133,7 +1142,7 @@
         }
         @Override
         public boolean equals(@Nullable Object o) {
-            if (o == null || !(o instanceof NetworkKey) || hashCode() != o.hashCode()) {
+            if (!(o instanceof NetworkKey that) || hashCode() != o.hashCode()) {
                 return false;
             }
 
@@ -1141,7 +1150,6 @@
                 return true;
             }
 
-            NetworkKey that = (NetworkKey) o;
             return mPlmn.equals(that.mPlmn)
                     && mTac == that.mTac
                     && mDataRat.equals(that.mDataRat);
@@ -1153,11 +1161,7 @@
         }
         @Override
         public String toString() {
-            StringBuilder sb = new StringBuilder();
-            sb.append("Plmn").append(mPlmn)
-                    .append("Rat").append(mDataRat)
-                    .append("Tac").append(mTac);
-            return sb.toString();
+            return "Plmn" + mPlmn + "Rat" + mDataRat + "Tac" + mTac;
         }
     }
 
@@ -1216,11 +1220,7 @@
         }
 
         private String getDataKey(int link, int level) {
-            StringBuilder sb = new StringBuilder();
-            return sb.append(mKey)
-                    .append("Link").append(link)
-                    .append("Level").append(level)
-                    .toString();
+            return mKey + "Link" + link + "Level" + level;
         }
 
         /** Get the accumulated bandwidth value */
diff --git a/src/java/com/android/internal/telephony/data/PhoneSwitcher.java b/src/java/com/android/internal/telephony/data/PhoneSwitcher.java
index 5c1d0e1..27b4331 100644
--- a/src/java/com/android/internal/telephony/data/PhoneSwitcher.java
+++ b/src/java/com/android/internal/telephony/data/PhoneSwitcher.java
@@ -32,7 +32,6 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.compat.annotation.UnsupportedAppUsage;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -46,7 +45,6 @@
 import android.net.NetworkSpecifier;
 import android.net.TelephonyNetworkSpecifier;
 import android.os.AsyncResult;
-import android.os.Build;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
@@ -55,7 +53,6 @@
 import android.os.RegistrantList;
 import android.os.RemoteException;
 import android.telephony.CarrierConfigManager;
-import android.telephony.PhoneCapability;
 import android.telephony.PhoneStateListener;
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
@@ -68,6 +65,7 @@
 import android.util.ArrayMap;
 import android.util.LocalLog;
 import android.util.Log;
+import android.util.SparseIntArray;
 
 import com.android.ims.ImsException;
 import com.android.ims.ImsManager;
@@ -84,6 +82,7 @@
 import com.android.internal.telephony.data.DataNetworkController.NetworkRequestList;
 import com.android.internal.telephony.data.DataSettingsManager.DataSettingsManagerCallback;
 import com.android.internal.telephony.flags.FeatureFlags;
+import com.android.internal.telephony.imsphone.ImsPhone;
 import com.android.internal.telephony.metrics.TelephonyMetrics;
 import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent;
 import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.DataSwitch;
@@ -103,11 +102,12 @@
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.Executor;
 
 /**
  * Utility singleton to monitor subscription changes and incoming NetworkRequests
  * and determine which phone/phones are active.
- *
+ * <p>
  * Manages the ALLOW_DATA calls to modems and notifies phones about changes to
  * the active phones.  Note we don't wait for data attach (which may not happen anyway).
  */
@@ -182,10 +182,12 @@
         }
     }
 
-    private final @NonNull NetworkRequestList mNetworkRequestList = new NetworkRequestList();
+    @NonNull
+    private final NetworkRequestList mNetworkRequestList = new NetworkRequestList();
     protected final RegistrantList mActivePhoneRegistrants;
     private final SubscriptionManagerService mSubscriptionManagerService;
-    private final @NonNull FeatureFlags mFlags;
+    @NonNull
+    private final FeatureFlags mFlags;
     protected final Context mContext;
     private final LocalLog mLocalLog;
     protected PhoneState[] mPhoneStates;
@@ -214,7 +216,6 @@
                 }
             };
 
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     // How many phones (correspondingly logical modems) are allowed for PS attach. This is used
     // when we specifically use setDataAllowed to initiate on-demand PS(data) attach for each phone.
     protected int mMaxDataAttachModemCount;
@@ -310,7 +311,7 @@
     private static final int MAX_LOCAL_LOG_LINES = 256;
 
     // Default timeout value of network validation in millisecond.
-    private final static int DEFAULT_VALIDATION_EXPIRATION_TIME = 2000;
+    private static final int DEFAULT_VALIDATION_EXPIRATION_TIME = 2000;
 
     /** Controller that tracks {@link TelephonyManager#MOBILE_DATA_POLICY_AUTO_DATA_SWITCH} */
     @NonNull private final AutoDataSwitchController mAutoDataSwitchController;
@@ -318,20 +319,23 @@
     @NonNull private final AutoDataSwitchController.AutoDataSwitchControllerCallback
             mAutoDataSwitchCallback;
 
-    private ConnectivityManager mConnectivityManager;
+    private final ConnectivityManager mConnectivityManager;
     private int mImsRegistrationTech = REGISTRATION_TECH_NONE;
-
-    private List<Set<CommandException.Error>> mCurrentDdsSwitchFailure;
+    @VisibleForTesting
+    public final SparseIntArray mImsRegistrationRadioTechMap = new SparseIntArray();
+    @NonNull
+    private final List<Set<CommandException.Error>> mCurrentDdsSwitchFailure;
 
     /** Data settings manager callback. Key is the phone id. */
-    private final @NonNull Map<Integer, DataSettingsManagerCallback> mDataSettingsManagerCallbacks =
+    @NonNull
+    private final Map<Integer, DataSettingsManagerCallback> mDataSettingsManagerCallbacks =
             new ArrayMap<>();
 
     private class DefaultNetworkCallback extends ConnectivityManager.NetworkCallback {
         public int mExpectedSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
         public int mSwitchReason = TelephonyEvent.DataSwitch.Reason.DATA_SWITCH_REASON_UNKNOWN;
         @Override
-        public void onCapabilitiesChanged(Network network,
+        public void onCapabilitiesChanged(@NonNull Network network,
                 NetworkCapabilities networkCapabilities) {
             if (networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) {
                 if (SubscriptionManager.isValidSubscriptionId(mExpectedSubId)
@@ -349,15 +353,15 @@
         }
 
         @Override
-        public void onLost(Network network) {
+        public void onLost(@NonNull Network network) {
             mAutoDataSwitchController.updateDefaultNetworkCapabilities(null);
         }
     }
 
-    private RegistrationManager.RegistrationCallback mRegistrationCallback =
+    private final RegistrationManager.RegistrationCallback mRegistrationCallback =
             new RegistrationManager.RegistrationCallback() {
         @Override
-        public void onRegistered(ImsRegistrationAttributes attributes) {
+        public void onRegistered(@NonNull ImsRegistrationAttributes attributes) {
             int imsRegistrationTech = attributes.getRegistrationTechnology();
             if (imsRegistrationTech != mImsRegistrationTech) {
                 mImsRegistrationTech = imsRegistrationTech;
@@ -366,7 +370,7 @@
         }
 
         @Override
-        public void onUnregistered(ImsReasonInfo info) {
+        public void onUnregistered(@NonNull ImsReasonInfo info) {
             if (mImsRegistrationTech != REGISTRATION_TECH_NONE) {
                 mImsRegistrationTech = REGISTRATION_TECH_NONE;
                 sendMessage(obtainMessage(EVENT_IMS_RADIO_TECH_CHANGED));
@@ -390,6 +394,21 @@
             (context, phoneId) -> ImsManager.getInstance(context, phoneId).getRegistrationTech();
 
     /**
+     * Interface to register RegistrationCallback. It's a wrapper of
+     * ImsManager#addRegistrationCallback, to make it mock-able in unittests.
+     */
+    public interface ImsRegisterCallback {
+        /** Set RegistrationCallback. */
+        void setCallback(Context context, int phoneId, RegistrationManager.RegistrationCallback cb,
+                Executor executor) throws ImsException;
+    }
+
+    @VisibleForTesting
+    public ImsRegisterCallback mImsRegisterCallback =
+            (context, phoneId, cb, executor)-> ImsManager.getInstance(context, phoneId)
+                    .addRegistrationCallback(cb, executor);
+
+    /**
      * Method to get singleton instance.
      */
     public static PhoneSwitcher getInstance() {
@@ -432,8 +451,7 @@
 
     private void registerForImsRadioTechChange(Context context, int phoneId) {
         try {
-            ImsManager.getInstance(context, phoneId).addRegistrationCallback(
-                    mRegistrationCallback, this::post);
+            mImsRegisterCallback.setCallback(context, phoneId, mRegistrationCallback, this::post);
             mIsRegisteredForImsRadioTechChange = true;
         } catch (ImsException imsException) {
             mIsRegisteredForImsRadioTechChange = false;
@@ -476,7 +494,7 @@
         mRadioConfig = RadioConfig.getInstance();
         mValidator = CellularNetworkValidator.getInstance();
 
-        mCurrentDdsSwitchFailure = new ArrayList<Set<CommandException.Error>>();
+        mCurrentDdsSwitchFailure = new ArrayList<>();
         IntentFilter filter = new IntentFilter();
         filter.addAction(TelephonyManager.ACTION_SIM_APPLICATION_STATE_CHANGED);
         mContext.registerReceiver(mSimStateIntentReceiver, filter);
@@ -494,6 +512,14 @@
                 if (phone.getImsPhone() != null) {
                     phone.getImsPhone().registerForPreciseCallStateChanged(
                             this, EVENT_PRECISE_CALL_STATE_CHANGED, null);
+                    if (mFlags.changeMethodOfObtainingImsRegistrationRadioTech()) {
+                        // Initialize IMS registration tech
+                        mImsRegistrationRadioTechMap.put(phoneId, REGISTRATION_TECH_NONE);
+                        ((ImsPhone) phone.getImsPhone()).registerForImsRegistrationChanges(
+                                this, EVENT_IMS_RADIO_TECH_CHANGED, null);
+
+                        log("register handler to receive IMS registration : " + phoneId);
+                    }
                 }
                 mDataSettingsManagerCallbacks.computeIfAbsent(phoneId,
                         v -> new DataSettingsManagerCallback(this::post) {
@@ -511,9 +537,12 @@
                             }});
                 phone.getDataSettingsManager().registerCallback(
                         mDataSettingsManagerCallbacks.get(phoneId));
-                registerForImsRadioTechChange(context, phoneId);
+
+                if (!mFlags.changeMethodOfObtainingImsRegistrationRadioTech()) {
+                    registerForImsRadioTechChange(context, phoneId);
+                }
             }
-            Set<CommandException.Error> ddsFailure = new HashSet<CommandException.Error>();
+            Set<CommandException.Error> ddsFailure = new HashSet<>();
             mCurrentDdsSwitchFailure.add(ddsFailure);
         }
 
@@ -620,7 +649,7 @@
         }
     };
 
-    private BroadcastReceiver mSimStateIntentReceiver = new BroadcastReceiver() {
+    private final BroadcastReceiver mSimStateIntentReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
             String action = intent.getAction();
@@ -721,7 +750,18 @@
             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 (!mFlags.changeMethodOfObtainingImsRegistrationRadioTech()) {
+                    registerForImsRadioTechChange();
+                } else {
+                    if (msg.obj == null) {
+                        log("EVENT_IMS_RADIO_TECH_CHANGED but parameter is not available");
+                        break;
+                    }
+                    if (!onImsRadioTechChanged((AsyncResult) (msg.obj))) {
+                        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()) {
@@ -733,7 +773,9 @@
             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 (!mFlags.changeMethodOfObtainingImsRegistrationRadioTech()) {
+                    registerForImsRadioTechChange();
+                }
 
                 // If the phoneId in voice call didn't change, do nothing.
                 if (!updatesIfPhoneInVoiceCallChanged()) {
@@ -829,10 +871,8 @@
                             mEmergencyOverride.mOverrideCompleteFuture.complete(false);
                         }
                     }
-                    mEmergencyOverride = req;
-                } else {
-                    mEmergencyOverride = req;
                 }
+                mEmergencyOverride = req;
 
                 logl("new emergency override - " + mEmergencyOverride);
                 // a new request has been created, remove any previous override complete scheduled.
@@ -885,6 +925,41 @@
         }
     }
 
+    /**
+     * Only provide service for the handler of PhoneSwitcher.
+     * @return true if the radio tech changed, otherwise false
+     */
+    private boolean onImsRadioTechChanged(@NonNull AsyncResult asyncResult) {
+        ImsPhone.ImsRegistrationRadioTechInfo imsRegistrationRadioTechInfo =
+                (ImsPhone.ImsRegistrationRadioTechInfo) asyncResult.result;
+        if (imsRegistrationRadioTechInfo == null
+                || imsRegistrationRadioTechInfo.phoneId() == INVALID_PHONE_INDEX
+                || imsRegistrationRadioTechInfo.imsRegistrationState()
+                == RegistrationManager.REGISTRATION_STATE_REGISTERING) {
+            // Ignore REGISTERING state, handle only REGISTERED and NOT_REGISTERED
+            log("onImsRadioTechChanged : result is not available");
+            return false;
+        }
+
+        int phoneId = imsRegistrationRadioTechInfo.phoneId();
+        int subId = SubscriptionManager.getSubscriptionId(phoneId);
+        int tech = imsRegistrationRadioTechInfo.imsRegistrationTech();
+        log("onImsRadioTechChanged phoneId : " + phoneId + " subId : " + subId + " old tech : "
+                + mImsRegistrationRadioTechMap.get(phoneId, REGISTRATION_TECH_NONE)
+                + " new tech : " + tech);
+
+        if (mImsRegistrationRadioTechMap.get(phoneId, REGISTRATION_TECH_NONE) == tech) {
+            // Registration tech not changed
+            return false;
+        }
+
+        mImsRegistrationRadioTechMap.put(phoneId, tech);
+
+        // Need to update the cached IMS registration tech but no need to do any of the
+        // following. When the SIM removed, REGISTRATION_STATE_NOT_REGISTERED is notified.
+        return subId != INVALID_SUBSCRIPTION_ID;
+    }
+
     private synchronized void onMultiSimConfigChanged(int activeModemCount) {
         // No change.
         if (mActiveModemCount == activeModemCount) return;
@@ -911,6 +986,14 @@
             if (phone.getImsPhone() != null) {
                 phone.getImsPhone().registerForPreciseCallStateChanged(
                         this, EVENT_PRECISE_CALL_STATE_CHANGED, null);
+                if (mFlags.changeMethodOfObtainingImsRegistrationRadioTech()) {
+                    // Initialize IMS registration tech for new phoneId
+                    mImsRegistrationRadioTechMap.put(phoneId, REGISTRATION_TECH_NONE);
+                    ((ImsPhone) phone.getImsPhone()).registerForImsRegistrationChanges(
+                            this, EVENT_IMS_RADIO_TECH_CHANGED, null);
+
+                    log("register handler to receive IMS registration : " + phoneId);
+                }
             }
 
             mDataSettingsManagerCallbacks.computeIfAbsent(phone.getPhoneId(),
@@ -931,9 +1014,12 @@
             phone.getDataSettingsManager().registerCallback(
                     mDataSettingsManagerCallbacks.get(phone.getPhoneId()));
 
-            Set<CommandException.Error> ddsFailure = new HashSet<CommandException.Error>();
+            Set<CommandException.Error> ddsFailure = new HashSet<>();
             mCurrentDdsSwitchFailure.add(ddsFailure);
-            registerForImsRadioTechChange(mContext, phoneId);
+
+            if (!mFlags.changeMethodOfObtainingImsRegistrationRadioTech()) {
+                registerForImsRadioTechChange(mContext, phoneId);
+            }
         }
 
         mAutoDataSwitchController.onMultiSimConfigChanged(activeModemCount);
@@ -977,7 +1063,7 @@
         }
 
         @Override
-        protected void needNetworkFor(NetworkRequest networkRequest) {
+        protected void needNetworkFor(@NonNull NetworkRequest networkRequest) {
             if (VDBG) log("needNetworkFor " + networkRequest);
             Message msg = mPhoneSwitcher.obtainMessage(EVENT_REQUEST_NETWORK);
             msg.obj = networkRequest;
@@ -985,7 +1071,7 @@
         }
 
         @Override
-        protected void releaseNetworkFor(NetworkRequest networkRequest) {
+        protected void releaseNetworkFor(@NonNull NetworkRequest networkRequest) {
             if (VDBG) log("releaseNetworkFor " + networkRequest);
             Message msg = mPhoneSwitcher.obtainMessage(EVENT_RELEASE_NETWORK);
             msg.obj = networkRequest;
@@ -1062,7 +1148,7 @@
     protected static final boolean REQUESTS_UNCHANGED = false;
     /**
      * Re-evaluate things. Do nothing if nothing's changed.
-     *
+     * <p>
      * Otherwise, go through the requests in priority order adding their phone until we've added up
      * to the max allowed.  Then go through shutting down phones that aren't in the active phone
      * list. Finally, activate all phones in the active phone list.
@@ -1100,10 +1186,14 @@
                     mAutoSelectedDataSubId = DEFAULT_SUBSCRIPTION_ID;
                 }
                 mPhoneSubscriptions[i] = sub;
-                // Listen to IMS radio tech change for new sub
-                if (SubscriptionManager.isValidSubscriptionId(sub)) {
-                    registerForImsRadioTechChange(mContext, i);
+
+                if (!mFlags.changeMethodOfObtainingImsRegistrationRadioTech()) {
+                    // Listen to IMS radio tech change for new sub
+                    if (SubscriptionManager.isValidSubscriptionId(sub)) {
+                        registerForImsRadioTechChange(mContext, i);
+                    }
                 }
+
                 diffDetected = true;
                 mAutoDataSwitchController.notifySubscriptionsMappingChanged();
             }
@@ -1152,13 +1242,11 @@
                 }
                 sendRilCommands(mPreferredDataPhoneId);
             } else {
-                List<Integer> newActivePhones = new ArrayList<Integer>();
+                List<Integer> newActivePhones = new ArrayList<>();
 
-                /**
-                 * If all phones can have PS attached, activate all.
-                 * Otherwise, choose to activate phones according to requests. And
-                 * if list is not full, add mPreferredDataPhoneId.
-                 */
+                // If all phones can have PS attached, activate all.
+                // Otherwise, choose to activate phones according to requests. And
+                // if list is not full, add mPreferredDataPhoneId.
                 if (mMaxDataAttachModemCount == mActiveModemCount) {
                     for (int i = 0; i < mMaxDataAttachModemCount; i++) {
                         newActivePhones.add(i);
@@ -1216,12 +1304,10 @@
         public long lastRequested = 0;
     }
 
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     protected void activate(int phoneId) {
         switchPhone(phoneId, true);
     }
 
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     protected void deactivate(int phoneId) {
         switchPhone(phoneId, false);
     }
@@ -1248,7 +1334,7 @@
 
     /**
      * Switch the Default data for the context of an 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
@@ -1294,16 +1380,6 @@
         }
     }
 
-    private void onPhoneCapabilityChangedInternal(PhoneCapability capability) {
-        int newMaxDataAttachModemCount = TelephonyManager.getDefault()
-                .getNumberOfModemsWithSimultaneousDataConnections();
-        if (mMaxDataAttachModemCount != newMaxDataAttachModemCount) {
-            mMaxDataAttachModemCount = newMaxDataAttachModemCount;
-            logl("Max active phones changed to " + mMaxDataAttachModemCount);
-            onEvaluate(REQUESTS_UNCHANGED, "phoneCfgChanged");
-        }
-    }
-
     private int phoneIdForRequest(TelephonyNetworkRequest networkRequest) {
         NetworkRequest netRequest = networkRequest.getNativeNetworkRequest();
         int subId = getSubIdFromNetworkSpecifier(netRequest.getNetworkSpecifier());
@@ -1492,10 +1568,6 @@
         r.notifyRegistrant();
     }
 
-    public void unregisterForActivePhoneSwitch(Handler h) {
-        mActivePhoneRegistrants.remove(h);
-    }
-
     /**
      * Set opportunistic data subscription. It's an indication to switch Internet data to this
      * subscription. It has to be an active subscription, and PhoneSwitcher will try to validate
@@ -1684,7 +1756,7 @@
 
     /**
      * Notify PhoneSwitcher to try to switch data to an opportunistic subscription.
-     *
+     * <p>
      * Set opportunistic data subscription. It's an indication to switch Internet data to this
      * subscription. It has to be an active subscription, and PhoneSwitcher will try to validate
      * it first if needed. If subId is DEFAULT_SUBSCRIPTION_ID, it means we are un-setting
@@ -1735,7 +1807,6 @@
      * Log debug messages and also log into the local log.
      * @param l debug messages
      */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     protected void logl(String l) {
         log(l);
         mLocalLog.log(l);
@@ -1764,20 +1835,16 @@
      * @param reason The switch reason.
      * @return The switch reason in string format.
      */
-    private static @NonNull String switchReasonToString(int reason) {
-        switch(reason) {
-            case TelephonyEvent.DataSwitch.Reason.DATA_SWITCH_REASON_UNKNOWN:
-                return "UNKNOWN";
-            case TelephonyEvent.DataSwitch.Reason.DATA_SWITCH_REASON_MANUAL:
-                return "MANUAL";
-            case TelephonyEvent.DataSwitch.Reason.DATA_SWITCH_REASON_IN_CALL:
-                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 + ")";
-        }
+    @NonNull
+    private static String switchReasonToString(int reason) {
+        return switch (reason) {
+            case DataSwitch.Reason.DATA_SWITCH_REASON_UNKNOWN -> "UNKNOWN";
+            case DataSwitch.Reason.DATA_SWITCH_REASON_MANUAL -> "MANUAL";
+            case DataSwitch.Reason.DATA_SWITCH_REASON_IN_CALL -> "IN_CALL";
+            case DataSwitch.Reason.DATA_SWITCH_REASON_CBRS -> "CBRS";
+            case DataSwitch.Reason.DATA_SWITCH_REASON_AUTO -> "AUTO";
+            default -> "UNKNOWN(" + reason + ")";
+        };
     }
 
     /**
@@ -1786,16 +1853,14 @@
      * @param state The switching state.
      * @return The switching state in string format.
      */
-    private static @NonNull String switchStateToString(int state) {
-        switch(state) {
-            case TelephonyEvent.EventState.EVENT_STATE_UNKNOWN:
-                return "UNKNOWN";
-            case TelephonyEvent.EventState.EVENT_STATE_START:
-                return "START";
-            case TelephonyEvent.EventState.EVENT_STATE_END:
-                return "END";
-            default: return "UNKNOWN(" + state + ")";
-        }
+    @NonNull
+    private static String switchStateToString(int state) {
+        return switch (state) {
+            case TelephonyEvent.EventState.EVENT_STATE_UNKNOWN -> "UNKNOWN";
+            case TelephonyEvent.EventState.EVENT_STATE_START -> "START";
+            case TelephonyEvent.EventState.EVENT_STATE_END -> "END";
+            default -> "UNKNOWN(" + state + ")";
+        };
     }
 
     /**
@@ -1838,12 +1903,6 @@
         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) {
-        onPhoneCapabilityChangedInternal(capability);
-    }
-
     public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
         final IndentingPrintWriter pw = new IndentingPrintWriter(writer, "  ");
         pw.println("PhoneSwitcher:");
diff --git a/src/java/com/android/internal/telephony/data/QosCallbackTracker.java b/src/java/com/android/internal/telephony/data/QosCallbackTracker.java
index ac04627..a1ac379 100644
--- a/src/java/com/android/internal/telephony/data/QosCallbackTracker.java
+++ b/src/java/com/android/internal/telephony/data/QosCallbackTracker.java
@@ -53,13 +53,18 @@
     private static final int DEDICATED_BEARER_EVENT_STATE_MODIFIED = 2;
     private static final int DEDICATED_BEARER_EVENT_STATE_DELETED = 3;
 
-    private final @NonNull String mLogTag;
-    private final @NonNull TelephonyNetworkAgent mNetworkAgent;
-    private final @NonNull Map<Integer, QosBearerSession> mQosBearerSessions;
-    private final @NonNull RcsStats mRcsStats;
+    @NonNull
+    private final String mLogTag;
+    @NonNull
+    private final TelephonyNetworkAgent mNetworkAgent;
+    @NonNull
+    private final Map<Integer, QosBearerSession> mQosBearerSessions;
+    @NonNull
+    private final RcsStats mRcsStats;
 
     // We perform an exact match on the address
-    private final @NonNull Map<Integer, IFilter> mCallbacksToFilter;
+    @NonNull
+    private final Map<Integer, IFilter> mCallbacksToFilter;
 
     private final int mPhoneId;
 
@@ -139,11 +144,6 @@
                                     }
                                 });
                     }
-
-                    @Override
-                    public void onQosCallbackUnregistered(int qosCallbackId) {
-
-                    }
                 });
     }
 
@@ -255,7 +255,6 @@
                         // The filter matches which means it was previously available, and now is
                         // lost
                         if (doFiltersMatch(existingSession, filter)) {
-                            bearerState = DEDICATED_BEARER_EVENT_STATE_DELETED;
                             sendSessionLost(callbackId, existingSession);
                             notifyMetricDedicatedBearerEvent(existingSession, bearerState, true);
                             sessionsReportedToMetric.add(sessionId);
@@ -281,13 +280,13 @@
         });
     }
 
-    private boolean doFiltersMatch(final @NonNull QosBearerSession qosBearerSession,
-            final @NonNull IFilter filter) {
+    private boolean doFiltersMatch(@NonNull final QosBearerSession qosBearerSession,
+                                   @NonNull final IFilter filter) {
         return getMatchingQosBearerFilter(qosBearerSession, filter) != null;
     }
 
-    private boolean matchesByLocalAddress(final @NonNull QosBearerFilter sessionFilter,
-            final @NonNull IFilter filter) {
+    private boolean matchesByLocalAddress(@NonNull final QosBearerFilter sessionFilter,
+                                          @NonNull final IFilter filter) {
         int portStart;
         int portEnd;
         if (sessionFilter.getLocalPortRange() == null) {
@@ -316,7 +315,7 @@
     }
 
     private boolean matchesByRemoteAddress(@NonNull QosBearerFilter sessionFilter,
-            final @NonNull IFilter filter) {
+                                           @NonNull final IFilter filter) {
         int portStart;
         int portEnd;
         boolean result = false;
@@ -346,8 +345,8 @@
     }
 
     private boolean matchesByProtocol(@NonNull QosBearerFilter sessionFilter,
-            final @NonNull IFilter filter, boolean hasMatchedFilter) {
-        boolean result = false;
+                                      @NonNull final IFilter filter, boolean hasMatchedFilter) {
+        boolean result;
         int protocol = sessionFilter.getProtocol();
         if (protocol == QosBearerFilter.QOS_PROTOCOL_TCP
                 || protocol == QosBearerFilter.QOS_PROTOCOL_UDP) {
@@ -367,8 +366,9 @@
                 ? sessionFilter : qosFilter;
     }
 
-    private @Nullable QosBearerFilter getMatchingQosBearerFilter(
-            @NonNull QosBearerSession qosBearerSession, final @NonNull IFilter filter) {
+    @Nullable
+    private QosBearerFilter getMatchingQosBearerFilter(
+            @NonNull QosBearerSession qosBearerSession, @NonNull final IFilter filter) {
         QosBearerFilter qosFilter = null;
 
         for (final QosBearerFilter sessionFilter : qosBearerSession.getQosBearerFilterList()) {
@@ -406,11 +406,11 @@
         return qosFilter;
     }
 
-    private void sendSessionAvailable(final int callbackId, final @NonNull QosBearerSession session,
-            @NonNull IFilter filter) {
+    private void sendSessionAvailable(final int callbackId, @NonNull final QosBearerSession session,
+                                      @NonNull IFilter filter) {
         QosBearerFilter qosBearerFilter = getMatchingQosBearerFilter(session, filter);
         List<InetSocketAddress> remoteAddresses = new ArrayList<>();
-        if (qosBearerFilter.getRemoteAddresses().size() > 0
+        if (qosBearerFilter != null && !qosBearerFilter.getRemoteAddresses().isEmpty()
                 && qosBearerFilter.getRemotePortRange() != null) {
             remoteAddresses.add(
                     new InetSocketAddress(qosBearerFilter.getRemoteAddresses().get(0).getAddress(),
@@ -455,17 +455,16 @@
     }
 
     private void notifyMetricDedicatedBearerListenerAdded(final int callbackId,
-            final @NonNull QosBearerSession session) {
+                                                          @NonNull final QosBearerSession session) {
 
-        final int slotId = mPhoneId;
         final int rat = getRatInfoFromSessionInfo(session);
         final int qci = getQCIFromSessionInfo(session);
 
-        mRcsStats.onImsDedicatedBearerListenerAdded(callbackId, slotId, rat, qci);
+        mRcsStats.onImsDedicatedBearerListenerAdded(callbackId, mPhoneId, rat, qci);
     }
 
     private void notifyMetricDedicatedBearerListenerBearerUpdateSession(
-            final int callbackId, final @NonNull QosBearerSession session) {
+            final int callbackId, @NonNull final QosBearerSession session) {
         mRcsStats.onImsDedicatedBearerListenerUpdateSession(callbackId, mPhoneId,
                 getRatInfoFromSessionInfo(session), getQCIFromSessionInfo(session), true);
     }
@@ -518,13 +517,12 @@
 
     private void notifyMetricDedicatedBearerEvent(final QosBearerSession session,
             final int bearerState, final boolean hasListener) {
-        final int slotId = mPhoneId;
         int ratAtEnd = getRatInfoFromSessionInfo(session);
         int qci = getQCIFromSessionInfo(session);
         boolean localConnectionInfoReceived = doesLocalConnectionInfoExist(session);
         boolean remoteConnectionInfoReceived = doesRemoteConnectionInfoExist(session);
 
-        mRcsStats.onImsDedicatedBearerEvent(slotId, ratAtEnd, qci, bearerState,
+        mRcsStats.onImsDedicatedBearerEvent(mPhoneId, ratAtEnd, qci, bearerState,
                 localConnectionInfoReceived, remoteConnectionInfoReceived, hasListener);
     }
 
diff --git a/src/java/com/android/internal/telephony/data/TelephonyNetworkAgent.java b/src/java/com/android/internal/telephony/data/TelephonyNetworkAgent.java
index 5ed09e6..5a81f19 100644
--- a/src/java/com/android/internal/telephony/data/TelephonyNetworkAgent.java
+++ b/src/java/com/android/internal/telephony/data/TelephonyNetworkAgent.java
@@ -54,10 +54,8 @@
     private static final int NETWORK_AGENT_TEARDOWN_DELAY_MS = 5_000;
 
     /** The parent data network. */
-    private final @NonNull DataNetwork mDataNetwork;
-
-    /** Network agent config. For unit test use only. */
-    private final @NonNull NetworkAgentConfig mNetworkAgentConfig;
+    @NonNull
+    private final DataNetwork mDataNetwork;
 
     /** This is the id from {@link NetworkAgent#register()}. */
     private final int mId;
@@ -72,7 +70,8 @@
      * The callbacks that are used to pass information to {@link DataNetwork} and
      * {@link QosCallbackTracker}.
      */
-    private final @NonNull Set<TelephonyNetworkAgentCallback> mTelephonyNetworkAgentCallbacks =
+    @NonNull
+    private final Set<TelephonyNetworkAgentCallback> mTelephonyNetworkAgentCallbacks =
             new ArraySet<>();
 
     /**
@@ -112,7 +111,7 @@
 
         /**
          * Called when a qos callback is registered with a filter.
-         *
+         * <p>
          * Any QoS events that are sent with the same callback id after this method is called are a
          * no-op.
          *
@@ -165,7 +164,6 @@
                 config, provider);
         register();
         mDataNetwork = dataNetwork;
-        mNetworkAgentConfig = config;
         mTelephonyNetworkAgentCallbacks.add(callback);
         mId = getNetwork().getNetId();
         mLogTag = "TNA-" + mId;
@@ -270,7 +268,7 @@
      * @param filter the filter being registered
      */
     @Override
-    public void onQosCallbackRegistered(final int qosCallbackId, final @NonNull QosFilter filter) {
+    public void onQosCallbackRegistered(final int qosCallbackId, @NonNull final QosFilter filter) {
         if (mAbandoned) {
             log("The agent is already abandoned. Ignored onQosCallbackRegistered.");
             return;
@@ -281,7 +279,7 @@
 
     /**
      * Called when a qos callback is registered with a filter.
-     *
+     * <p>
      * Any QoS events that are sent with the same callback id after this method is called are a
      * no-op.
      *
@@ -345,15 +343,6 @@
     }
 
     /**
-     * Log debug messages and also log into the local log.
-     * @param s debug messages
-     */
-    private void logl(@NonNull String s) {
-        log(s);
-        mLocalLog.log(s);
-    }
-
-    /**
      * Dump the state of TelephonyNetworkAgent
      *
      * @param fd File descriptor
diff --git a/src/java/com/android/internal/telephony/data/TelephonyNetworkFactory.java b/src/java/com/android/internal/telephony/data/TelephonyNetworkFactory.java
index 877d7b8..ca34ca7 100644
--- a/src/java/com/android/internal/telephony/data/TelephonyNetworkFactory.java
+++ b/src/java/com/android/internal/telephony/data/TelephonyNetworkFactory.java
@@ -72,14 +72,15 @@
 
     private final Phone mPhone;
 
-    private AccessNetworksManager mAccessNetworksManager;
+    private final AccessNetworksManager mAccessNetworksManager;
 
     private int mSubscriptionId;
 
     @VisibleForTesting
     public final Handler mInternalHandler;
 
-    private final @NonNull FeatureFlags mFlags;
+    @NonNull
+    private final FeatureFlags mFlags;
 
 
     /**
@@ -109,20 +110,19 @@
                 null);
 
         mSubscriptionId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
-        SubscriptionManager.from(mPhone.getContext()).addOnSubscriptionsChangedListener(
-                mSubscriptionsChangedListener);
+        SubscriptionManager.OnSubscriptionsChangedListener subscriptionsChangedListener =
+                new SubscriptionManager.OnSubscriptionsChangedListener() {
+                    @Override
+                    public void onSubscriptionsChanged() {
+                        mInternalHandler.sendEmptyMessage(EVENT_SUBSCRIPTION_CHANGED);
+                    }};
+
+        mPhone.getContext().getSystemService(SubscriptionManager.class)
+                .addOnSubscriptionsChangedListener(subscriptionsChangedListener);
 
         register();
     }
 
-    private final SubscriptionManager.OnSubscriptionsChangedListener mSubscriptionsChangedListener =
-            new SubscriptionManager.OnSubscriptionsChangedListener() {
-                @Override
-                public void onSubscriptionsChanged() {
-                    mInternalHandler.sendEmptyMessage(EVENT_SUBSCRIPTION_CHANGED);
-                }
-            };
-
     private NetworkCapabilities makeNetworkFilterByPhoneId(int phoneId) {
         return makeNetworkFilter(SubscriptionManager.getSubscriptionId(phoneId));
     }
@@ -265,13 +265,13 @@
     }
 
     @Override
-    public void needNetworkFor(NetworkRequest networkRequest) {
+    public void needNetworkFor(@NonNull NetworkRequest networkRequest) {
         Message msg = mInternalHandler.obtainMessage(EVENT_NETWORK_REQUEST);
         msg.obj = networkRequest;
         msg.sendToTarget();
     }
 
-    private void onNeedNetworkFor(Message msg) {
+    private void onNeedNetworkFor(@NonNull Message msg) {
         TelephonyNetworkRequest networkRequest =
                 new TelephonyNetworkRequest((NetworkRequest) msg.obj, mPhone, mFlags);
         boolean shouldApply = mPhoneSwitcher.shouldApplyNetworkRequest(
@@ -291,13 +291,13 @@
     }
 
     @Override
-    public void releaseNetworkFor(NetworkRequest networkRequest) {
+    public void releaseNetworkFor(@NonNull NetworkRequest networkRequest) {
         Message msg = mInternalHandler.obtainMessage(EVENT_NETWORK_RELEASE);
         msg.obj = networkRequest;
         msg.sendToTarget();
     }
 
-    private void onReleaseNetworkFor(Message msg) {
+    private void onReleaseNetworkFor(@NonNull Message msg) {
         TelephonyNetworkRequest networkRequest =
                 new TelephonyNetworkRequest((NetworkRequest) msg.obj, mPhone, mFlags);
         boolean applied = mNetworkRequests.get(networkRequest)
diff --git a/src/java/com/android/internal/telephony/data/TelephonyNetworkRequest.java b/src/java/com/android/internal/telephony/data/TelephonyNetworkRequest.java
index 2be3556..117eb36 100644
--- a/src/java/com/android/internal/telephony/data/TelephonyNetworkRequest.java
+++ b/src/java/com/android/internal/telephony/data/TelephonyNetworkRequest.java
@@ -138,19 +138,22 @@
     );
 
     /** The phone instance. */
-    private final @NonNull Phone mPhone;
+    @NonNull
+    private final Phone mPhone;
 
     /**
      * Native network request from the clients. See {@link NetworkRequest};
      */
-    private final @NonNull NetworkRequest mNativeNetworkRequest;
+    @NonNull
+    private final NetworkRequest mNativeNetworkRequest;
 
     /**
      * The attributes of the network capabilities in this network request. This describes how this
      * network request can be translated to different fields in {@link DataProfile} or perform
      * special actions in telephony.
      */
-    private final @NetCapabilityAttribute int mCapabilitiesAttributes;
+    @NetCapabilityAttribute
+    private final int mCapabilitiesAttributes;
 
     /**
      * Priority of the network request. The network request has higher priority will be satisfied
@@ -161,13 +164,15 @@
     /**
      * Data config manager for retrieving data config.
      */
-    private final @NonNull DataConfigManager mDataConfigManager;
+    @NonNull
+    private final DataConfigManager mDataConfigManager;
 
     /**
      * The attached data network. Note that the data network could be in any state. {@code null}
      * indicates this network request is not satisfied.
      */
-    private @Nullable DataNetwork mAttachedDataNetwork;
+    @Nullable
+    private DataNetwork mAttachedDataNetwork;
 
     /**
      * The state of the network request.
@@ -176,16 +181,20 @@
      * @see #REQUEST_STATE_SATISFIED
      */
     // This is not a boolean because there might be more states in the future.
-    private @RequestState int mState;
+    @RequestState
+    private int mState;
 
     /** The timestamp when this network request enters telephony. */
-    private final @ElapsedRealtimeLong long mCreatedTimeMillis;
+    @ElapsedRealtimeLong
+    private final long mCreatedTimeMillis;
 
     /** The data evaluation result. */
-    private @Nullable DataEvaluation mEvaluation;
+    @Nullable
+    private DataEvaluation mEvaluation;
 
     /** Feature flag. */
-    private final @NonNull FeatureFlags mFeatureFlags;
+    @NonNull
+    private final FeatureFlags mFeatureFlags;
 
     /**
      * Constructor
@@ -220,14 +229,17 @@
     /**
      * @see NetworkRequest#getNetworkSpecifier()
      */
-    public @Nullable NetworkSpecifier getNetworkSpecifier() {
+    @Nullable
+    public NetworkSpecifier getNetworkSpecifier() {
         return mNativeNetworkRequest.getNetworkSpecifier();
     }
 
     /**
      * @see NetworkRequest#getCapabilities()
      */
-    public @NonNull @NetCapability int[] getCapabilities() {
+    @NonNull
+    @NetCapability
+    public int[] getCapabilities() {
         return mNativeNetworkRequest.getCapabilities();
     }
 
@@ -372,7 +384,8 @@
      * @return The highest priority APN type based network capability from this network request. -1
      * if there is no APN type capabilities in this network request.
      */
-    public @NetCapability int getApnTypeNetworkCapability() {
+    @NetCapability
+    public int getApnTypeNetworkCapability() {
         if (!hasAttribute(CAPABILITY_ATTRIBUTE_APN_SETTING)) return -1;
         return Arrays.stream(getCapabilities()).boxed()
                 .filter(cap -> DataUtils.networkCapabilityToApnType(cap) != ApnSetting.TYPE_NONE)
@@ -382,7 +395,8 @@
     /**
      * @return The native network request.
      */
-    public @NonNull NetworkRequest getNativeNetworkRequest() {
+    @NonNull
+    public NetworkRequest getNativeNetworkRequest() {
         return mNativeNetworkRequest;
     }
 
@@ -391,7 +405,7 @@
      *
      * @param dataNetwork The data network.
      */
-    public void setAttachedNetwork(@NonNull DataNetwork dataNetwork) {
+    public void setAttachedNetwork(@Nullable DataNetwork dataNetwork) {
         mAttachedDataNetwork = dataNetwork;
     }
 
@@ -399,7 +413,8 @@
      * @return The attached network. {@code null} indicates the request is not attached to any
      * network (i.e. the request is unsatisfied).
      */
-    public @Nullable DataNetwork getAttachedNetwork() {
+    @Nullable
+    public DataNetwork getAttachedNetwork() {
         return mAttachedDataNetwork;
     }
 
@@ -415,7 +430,8 @@
     /**
      * @return The state of the network request.
      */
-    public @RequestState int getState() {
+    @RequestState
+    public int getState() {
         return mState;
     }
 
@@ -458,7 +474,8 @@
      * @return Os/App id. {@code null} if the request does not have traffic descriptor based network
      * capabilities.
      */
-    public @Nullable OsAppId getOsAppId() {
+    @Nullable
+    public OsAppId getOsAppId() {
         if (!hasAttribute(CAPABILITY_ATTRIBUTE_TRAFFIC_DESCRIPTOR_OS_APP_ID)) return null;
 
         // We do not support multiple network capabilities translated to Os/App id at this time.
@@ -489,18 +506,19 @@
      * @param state The request state.
      * @return The request state in string format.
      */
-    private static @NonNull String requestStateToString(
+    @NonNull
+    private static String requestStateToString(
             @TelephonyNetworkRequest.RequestState int state) {
-        switch (state) {
-            case TelephonyNetworkRequest.REQUEST_STATE_UNSATISFIED: return "UNSATISFIED";
-            case TelephonyNetworkRequest.REQUEST_STATE_SATISFIED: return "SATISFIED";
-            default: return "UNKNOWN(" + state + ")";
-        }
+        return switch (state) {
+            case TelephonyNetworkRequest.REQUEST_STATE_UNSATISFIED -> "UNSATISFIED";
+            case TelephonyNetworkRequest.REQUEST_STATE_SATISFIED -> "SATISFIED";
+            default -> "UNKNOWN(" + state + ")";
+        };
     }
 
     @Override
     public String toString() {
-        return "[" + mNativeNetworkRequest.toString() + ", mPriority=" + mPriority
+        return "[" + mNativeNetworkRequest + ", mPriority=" + mPriority
                 + ", state=" + requestStateToString(mState)
                 + ", mAttachedDataNetwork=" + (mAttachedDataNetwork != null
                 ? mAttachedDataNetwork.name() : null) + ", isMetered=" + isMeteredRequest()
diff --git a/src/java/com/android/internal/telephony/euicc/EuiccCardController.java b/src/java/com/android/internal/telephony/euicc/EuiccCardController.java
index 6e1c8dd..e511e8f 100644
--- a/src/java/com/android/internal/telephony/euicc/EuiccCardController.java
+++ b/src/java/com/android/internal/telephony/euicc/EuiccCardController.java
@@ -31,8 +31,10 @@
 import android.content.pm.ComponentInfo;
 import android.content.pm.PackageManager;
 import android.os.Binder;
+import android.os.Build;
 import android.os.Handler;
 import android.os.RemoteException;
+import android.os.SystemProperties;
 import android.preference.PreferenceManager;
 import android.provider.Settings;
 import android.service.euicc.EuiccProfileInfo;
@@ -75,6 +77,7 @@
     private UiccController mUiccController;
     private FeatureFlags mFeatureFlags;
     private PackageManager mPackageManager;
+    private final int mVendorApiLevel;
 
     private static EuiccCardController sInstance;
 
@@ -143,6 +146,8 @@
         mEuiccController = euiccController;
         mFeatureFlags = featureFlags;
         mPackageManager = context.getPackageManager();
+        mVendorApiLevel = SystemProperties.getInt(
+                "ro.vendor.api_level", Build.VERSION.DEVICE_INITIAL_SDK_INT);
 
         if (isBootUp(mContext)) {
             mSimSlotStatusChangeReceiver = new SimSlotStatusChangedBroadcastReceiver();
@@ -1541,7 +1546,11 @@
 
         if (!mFeatureFlags.enforceTelephonyFeatureMappingForPublicApis()
                 || !CompatChanges.isChangeEnabled(ENABLE_FEATURE_MAPPING, callingPackage,
-                Binder.getCallingUserHandle())) {
+                Binder.getCallingUserHandle())
+                || mVendorApiLevel < Build.VERSION_CODES.VANILLA_ICE_CREAM) {
+            // Skip to check associated telephony feature,
+            // if compatibility change is not enabled for the current process or
+            // the SDK version of vendor partition is less than Android V.
             return;
         }
 
diff --git a/src/java/com/android/internal/telephony/euicc/EuiccController.java b/src/java/com/android/internal/telephony/euicc/EuiccController.java
index 18b4b14..1a5b99e 100644
--- a/src/java/com/android/internal/telephony/euicc/EuiccController.java
+++ b/src/java/com/android/internal/telephony/euicc/EuiccController.java
@@ -34,7 +34,9 @@
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.os.Binder;
+import android.os.Build;
 import android.os.Bundle;
+import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.Settings;
@@ -124,6 +126,7 @@
     private final AppOpsManager mAppOpsManager;
     private final PackageManager mPackageManager;
     private final FeatureFlags mFeatureFlags;
+    private final int mVendorApiLevel;
 
     // These values should be set or updated upon 1) system boot, 2) EuiccService/LPA is bound to
     // the phone process, 3) values are updated remotely by server flags.
@@ -172,6 +175,8 @@
         mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
         mPackageManager = context.getPackageManager();
         mFeatureFlags = featureFlags;
+        mVendorApiLevel = SystemProperties.getInt(
+                "ro.vendor.api_level", Build.VERSION.DEVICE_INITIAL_SDK_INT);
     }
 
     /**
@@ -615,25 +620,32 @@
     void downloadSubscription(int cardId, int portIndex, DownloadableSubscription subscription,
             boolean switchAfterDownload, String callingPackage, boolean forceDeactivateSim,
             Bundle resolvedBundle, PendingIntent callbackIntent) {
-        boolean callerCanWriteEmbeddedSubscriptions = callerCanWriteEmbeddedSubscriptions();
-        boolean callerCanDownloadAdminManagedSubscription =
-                Flags.esimManagementEnabled()
-                        && callerCanManageDevicePolicyManagedSubscriptions(callingPackage);
+        mAppOpsManager.checkPackage(Binder.getCallingUid(), callingPackage);
+
+        boolean callerHasAdminPrivileges = false;
         if (Flags.esimManagementEnabled()) {
-            if (mContext
-                    .getSystemService(UserManager.class)
-                    .hasUserRestriction(UserManager.DISALLOW_SIM_GLOBALLY)
-                    && !callerCanDownloadAdminManagedSubscription) {
+            callerHasAdminPrivileges = callerCanManageDevicePolicyManagedSubscriptions(
+                    callingPackage);
+            if (callerHasAdminPrivileges && (switchAfterDownload && !shouldAllowSwitchAfterDownload(
+                    callingPackage))) {
+                // Throw error if calling admin does not have privileges to enable
+                // subscription silently after download but switchAfterDownload is passed as true.
+                sendResult(callbackIntent, ERROR, null);
+                return;
+            }
+            if (mContext.getSystemService(UserManager.class).hasUserRestriction(
+                    UserManager.DISALLOW_SIM_GLOBALLY) && !callerHasAdminPrivileges) {
                 // Only admin managed subscriptions are allowed, but the caller is not authorised to
                 // download admin managed subscriptions. Abort.
-                throw new SecurityException("Caller is not authorized to download subscriptions");
+                sendResult(callbackIntent, ERROR, null);
+                return;
             }
         }
-        mAppOpsManager.checkPackage(Binder.getCallingUid(), callingPackage);
         // Don't try to resolve the port index for apps which are not targeting on T for backward
         // compatibility. instead always use default port 0.
         boolean shouldResolvePortIndex = isCompatChangeEnabled(callingPackage,
                 EuiccManager.SHOULD_RESOLVE_PORT_INDEX_FOR_APPS);
+        boolean callerCanWriteEmbeddedSubscriptions = callerCanWriteEmbeddedSubscriptions();
 
         long token = Binder.clearCallingIdentity();
         try {
@@ -646,26 +658,19 @@
                 isConsentNeededToResolvePortIndex = (portIndex
                         == TelephonyManager.INVALID_PORT_INDEX);
             }
-            // Caller has admin privileges if they can download admin managed subscription,
-            // and are not switching the subscription after download (admins cannot silently
-            // enable the subscription).
-            boolean hasAdminPrivileges =
-                    callerCanDownloadAdminManagedSubscription && !switchAfterDownload;
             Log.d(TAG, " downloadSubscription cardId: " + cardId + " switchAfterDownload: "
-                    + switchAfterDownload + " portIndex: " + portIndex
-                    + " forceDeactivateSim: " + forceDeactivateSim + " callingPackage: "
-                    + callingPackage
+                    + switchAfterDownload + " portIndex: " + portIndex + " forceDeactivateSim: "
+                    + forceDeactivateSim + " callingPackage: " + callingPackage
                     + " isConsentNeededToResolvePortIndex: " + isConsentNeededToResolvePortIndex
                     + " shouldResolvePortIndex:" + shouldResolvePortIndex
-                    + " hasAdminPrivileges:" + hasAdminPrivileges);
-            if (!isConsentNeededToResolvePortIndex
-                    && (callerCanWriteEmbeddedSubscriptions
-                                    || hasAdminPrivileges)) {
+                    + " callerHasAdminPrivileges:" + callerHasAdminPrivileges);
+            if (!isConsentNeededToResolvePortIndex && (callerCanWriteEmbeddedSubscriptions
+                    || callerHasAdminPrivileges)) {
                 // With WRITE_EMBEDDED_SUBSCRIPTIONS, we can skip profile-specific permission checks
                 // and move straight to the profile download.
                 downloadSubscriptionPrivileged(cardId, portIndex, token, subscription,
                         switchAfterDownload, forceDeactivateSim, callingPackage, resolvedBundle,
-                        callbackIntent, callerCanDownloadAdminManagedSubscription,
+                        callbackIntent, callerHasAdminPrivileges,
                         getCurrentEmbeddedSubscriptionIds(cardId));
                 return;
             }
@@ -862,8 +867,11 @@
                                             cardId,
                                             existingSubscriptions);
                                     return;
+                                } else if (markAsOwnedByAdmin) {
+                                    refreshSubscriptionsOwnership(true, callingPackage, cardId,
+                                            existingSubscriptions);
                                 }
-                                break;
+                            break;
                             case EuiccService.RESULT_MUST_DEACTIVATE_SIM:
                                 resultCode = RESOLVABLE_ERROR;
                                 addResolutionIntentWithPort(extrasIntent,
@@ -1728,22 +1736,27 @@
         SubscriptionManagerService.getInstance().updateEmbeddedSubscriptions(
                 List.of(mTelephonyManager.getCardIdForDefaultEuicc()),
                 () -> {
-                    if (Flags.esimManagementEnabled() && isCallerAdmin) {
-                        // Mark the newly downloaded subscriptions as being owned by an admin so
-                        // that actions for that subscription can be restricted,
-                        // and the admin is limited to effecting only these subscriptions.
-                        Set<Integer> subscriptionsAfter = getCurrentEmbeddedSubscriptionIds(cardId);
-                        subscriptionsAfter.removeAll(subscriptionsBefore);
-                        for (int subId: subscriptionsAfter) {
-                            SubscriptionManagerService
-                                    .getInstance().setGroupOwner(subId, callingPackage);
-                        }
-                    }
+                    refreshSubscriptionsOwnership(isCallerAdmin, callingPackage, cardId,
+                            subscriptionsBefore);
                     sendResult(callbackIntent, resultCode, extrasIntent);
                 });
 
     }
 
+    private void refreshSubscriptionsOwnership(boolean isCallerAdmin, String callingPackage,
+            int cardId, Set<Integer> subscriptionsBefore) {
+        if (Flags.esimManagementEnabled() && isCallerAdmin) {
+            // Mark the newly downloaded subscriptions as being owned by an admin so
+            // that actions for that subscription can be restricted,
+            // and the admin is limited to effecting only these subscriptions.
+            Set<Integer> subscriptionsAfter = getCurrentEmbeddedSubscriptionIds(cardId);
+            subscriptionsAfter.removeAll(subscriptionsBefore);
+            for (int subId : subscriptionsAfter) {
+                SubscriptionManagerService.getInstance().setGroupOwner(subId, callingPackage);
+            }
+        }
+    }
+
     /** Dispatch the given callback intent with the given result code and data. */
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
     public void sendResult(PendingIntent callbackIntent, int resultCode, Intent extrasIntent) {
@@ -2181,20 +2194,31 @@
     }
 
     private boolean callerCanManageDevicePolicyManagedSubscriptions(String callingPackage) {
-        // isProfileOwner/isDeviceOwner needs to callers user, so create device policy manager
-        // with the correct context associated with the caller.
+        DevicePolicyManager devicePolicyManager = getDevicePolicyManager();
+        boolean isAdmin =
+                devicePolicyManager != null && (devicePolicyManager.isProfileOwnerApp(
+                        callingPackage)
+                        || devicePolicyManager.isDeviceOwnerApp(callingPackage));
+        return isAdmin || mContext.checkCallingOrSelfPermission(
+                Manifest.permission.MANAGE_DEVICE_POLICY_MANAGED_SUBSCRIPTIONS)
+                == PackageManager.PERMISSION_GRANTED;
+    }
+
+    private boolean shouldAllowSwitchAfterDownload(String callingPackage) {
+        DevicePolicyManager devicePolicyManager = getDevicePolicyManager();
+        return devicePolicyManager != null && (devicePolicyManager.isDeviceOwnerApp(callingPackage)
+                || (devicePolicyManager.isProfileOwnerApp(callingPackage)
+                && devicePolicyManager.isOrganizationOwnedDeviceWithManagedProfile()));
+    }
+
+    private DevicePolicyManager getDevicePolicyManager() {
+        // create device policy manager with the correct context associated with the caller.
         DevicePolicyManager devicePolicyManager =
                 retrieveDevicePolicyManagerFromUserContext(Binder.getCallingUserHandle());
         if (devicePolicyManager == null) {
             Log.w(TAG, "Unable to get device policy manager");
-            return false;
         }
-        boolean isAdmin =
-                devicePolicyManager.isProfileOwnerApp(callingPackage)
-                        || devicePolicyManager.isDeviceOwnerApp(callingPackage);
-        return isAdmin || mContext.checkCallingOrSelfPermission(
-                Manifest.permission.MANAGE_DEVICE_POLICY_MANAGED_SUBSCRIPTIONS)
-                == PackageManager.PERMISSION_GRANTED;
+        return devicePolicyManager;
     }
 
     @Override
@@ -2351,7 +2375,11 @@
 
         if (!mFeatureFlags.enforceTelephonyFeatureMappingForPublicApis()
                 || !CompatChanges.isChangeEnabled(ENABLE_FEATURE_MAPPING, callingPackage,
-                Binder.getCallingUserHandle())) {
+                Binder.getCallingUserHandle())
+                || mVendorApiLevel < Build.VERSION_CODES.VANILLA_ICE_CREAM) {
+            // Skip to check associated telephony feature,
+            // if compatibility change is not enabled for the current process or
+            // the SDK version of vendor partition is less than Android V.
             return;
         }
 
diff --git a/src/java/com/android/internal/telephony/gsm/GsmMmiCode.java b/src/java/com/android/internal/telephony/gsm/GsmMmiCode.java
index 9de3ee9..c003405 100644
--- a/src/java/com/android/internal/telephony/gsm/GsmMmiCode.java
+++ b/src/java/com/android/internal/telephony/gsm/GsmMmiCode.java
@@ -200,6 +200,12 @@
          10 = dialing number
 */
 
+    /**
+     * An FAC code is of the following format:
+     * #FAC#MSISDN*
+     */
+    static Pattern sFac = Pattern.compile("^\\#\\d+\\#[^*]+\\*$");
+
     static final int MATCH_GROUP_POUND_STRING = 1;
 
     static final int MATCH_GROUP_ACTION = 2;
@@ -274,7 +280,9 @@
                 // in India operator(Mumbai MTNL)
                 ret = new GsmMmiCode(phone, app);
                 ret.mPoundString = dialString;
-            } else if (ret.isFacToDial()) {
+            } else if (ret.isFacToDial(dialString)) {
+                // Note: we had to pass in the dial string above because the full dial string is not
+                // in the MmiCode class (or even needed there).
                 // This is a FAC (feature access code) to dial as a normal call.
                 ret = null;
             }
@@ -963,12 +971,28 @@
     }
 
     /**
+     * Determines if a full dial string matches the general format of a FAC code.
+     * Ie. #FAC#MSIDN*
+     * @param dialString The full dialed number.
+     * @return {@code true} if the dialed number has the general format of a FAC code.
+     */
+    private static boolean isFacFormatNumber(String dialString) {
+        Matcher m = sFac.matcher(dialString);
+        return m.matches();
+    }
+
+    /**
      * Returns true if the Service Code is FAC to dial as a normal call.
      *
      * FAC stands for feature access code and it is special patterns of characters
      * to invoke certain features.
      */
-    private boolean isFacToDial() {
+    private boolean isFacToDial(String dialString) {
+        if (!isFacFormatNumber(dialString)) {
+            // If the full dial string doesn't conform to the required format for a FAC, we will
+            // bail early. It is likely a true USSD which shares the same code as the FAC.
+            return false;
+        }
         CarrierConfigManager configManager = (CarrierConfigManager)
                 mPhone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
         PersistableBundle b = configManager.getConfigForSubId(mPhone.getSubId());
diff --git a/src/java/com/android/internal/telephony/ims/ImsResolver.java b/src/java/com/android/internal/telephony/ims/ImsResolver.java
index 49b7e62..eb389b7 100644
--- a/src/java/com/android/internal/telephony/ims/ImsResolver.java
+++ b/src/java/com/android/internal/telephony/ims/ImsResolver.java
@@ -60,6 +60,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.os.SomeArgs;
 import com.android.internal.telephony.PhoneConfigurationManager;
+import com.android.internal.telephony.flags.FeatureFlags;
 import com.android.internal.util.IndentingPrintWriter;
 
 import java.io.FileDescriptor;
@@ -139,11 +140,12 @@
      * Create the ImsResolver Service singleton instance.
      */
     public static void make(Context context, String defaultMmTelPackageName,
-            String defaultRcsPackageName, int numSlots, ImsFeatureBinderRepository repo) {
+            String defaultRcsPackageName, int numSlots, ImsFeatureBinderRepository repo,
+            FeatureFlags featureFlags) {
         if (sInstance == null) {
             sHandlerThread.start();
             sInstance = new ImsResolver(context, defaultMmTelPackageName, defaultRcsPackageName,
-                    numSlots, repo, sHandlerThread.getLooper());
+                    numSlots, repo, sHandlerThread.getLooper(), featureFlags);
         }
     }
 
@@ -372,7 +374,7 @@
          */
         ImsServiceController create(Context context, ComponentName componentName,
                 ImsServiceController.ImsServiceControllerCallbacks callbacks,
-                ImsFeatureBinderRepository repo);
+                ImsFeatureBinderRepository repo, FeatureFlags featureFlags);
     }
 
     private ImsServiceControllerFactory mImsServiceControllerFactory =
@@ -386,8 +388,9 @@
         @Override
         public ImsServiceController create(Context context, ComponentName componentName,
                 ImsServiceController.ImsServiceControllerCallbacks callbacks,
-                ImsFeatureBinderRepository repo) {
-            return new ImsServiceController(context, componentName, callbacks, repo);
+                ImsFeatureBinderRepository repo, FeatureFlags featureFlags) {
+                    return new ImsServiceController(context, componentName, callbacks, repo,
+                            featureFlags);
         }
     };
 
@@ -410,7 +413,7 @@
                 @Override
                 public ImsServiceController create(Context context, ComponentName componentName,
                         ImsServiceController.ImsServiceControllerCallbacks callbacks,
-                        ImsFeatureBinderRepository repo) {
+                        ImsFeatureBinderRepository repo, FeatureFlags featureFlags) {
                     return new ImsServiceControllerCompat(context, componentName, callbacks, repo);
                 }
             };
@@ -445,6 +448,9 @@
     // Synchronize all events on a handler to ensure that the cache includes the most recent
     // version of the installed ImsServices.
     private final Handler mHandler;
+
+    private final FeatureFlags mFeatureFlags;
+
     private class ResolverHandler extends Handler {
 
         ResolverHandler(Looper looper) {
@@ -581,7 +587,7 @@
 
     public ImsResolver(Context context, String defaultMmTelPackageName,
             String defaultRcsPackageName, int numSlots, ImsFeatureBinderRepository repo,
-            Looper looper) {
+            Looper looper, FeatureFlags featureFlags) {
         Log.i(TAG, "device MMTEL package: " + defaultMmTelPackageName + ", device RCS package:"
                 + defaultRcsPackageName);
         mContext = context;
@@ -591,6 +597,7 @@
 
         mHandler = new ResolverHandler(looper);
         mRunnableExecutor = new HandlerExecutor(mHandler);
+        mFeatureFlags = featureFlags;
         mCarrierServices = new SparseArray<>(mNumSlots);
         setDeviceConfiguration(defaultMmTelPackageName, ImsFeature.FEATURE_EMERGENCY_MMTEL);
         setDeviceConfiguration(defaultMmTelPackageName, ImsFeature.FEATURE_MMTEL);
@@ -1233,7 +1240,8 @@
                     Log.w(TAG, "bindImsService: error=" + e.getMessage());
                 }
             } else {
-                controller = info.controllerFactory.create(mContext, info.name, this, mRepo);
+                controller = info.controllerFactory.create(mContext, info.name, this, mRepo,
+                        mFeatureFlags);
                 Log.i(TAG, "Binding ImsService: " + controller.getComponentName()
                         + " with features: " + features);
                 controller.bind(features, slotIdToSubIdMap);
diff --git a/src/java/com/android/internal/telephony/ims/ImsServiceController.java b/src/java/com/android/internal/telephony/ims/ImsServiceController.java
index 6af7a08..ea8399f 100644
--- a/src/java/com/android/internal/telephony/ims/ImsServiceController.java
+++ b/src/java/com/android/internal/telephony/ims/ImsServiceController.java
@@ -49,6 +49,7 @@
 import com.android.ims.internal.IImsFeatureStatusCallback;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.ExponentialBackoff;
+import com.android.internal.telephony.flags.FeatureFlags;
 import com.android.internal.telephony.util.TelephonyUtils;
 
 import java.io.PrintWriter;
@@ -265,6 +266,7 @@
     private final HandlerThread mHandlerThread = new HandlerThread("ImsServiceControllerHandler");
     private final Handler mHandler;
     private final LegacyPermissionManager mPermissionManager;
+    private final FeatureFlags mFeatureFlags;
     private ImsFeatureBinderRepository mRepo;
     private ImsServiceControllerCallbacks mCallbacks;
     private ExponentialBackoff mBackoff;
@@ -353,7 +355,8 @@
     };
 
     public ImsServiceController(Context context, ComponentName componentName,
-            ImsServiceControllerCallbacks callbacks, ImsFeatureBinderRepository repo) {
+            ImsServiceControllerCallbacks callbacks, ImsFeatureBinderRepository repo,
+            FeatureFlags featureFlags) {
         mContext = context;
         mComponentName = componentName;
         mCallbacks = callbacks;
@@ -369,6 +372,7 @@
                 Context.LEGACY_PERMISSION_SERVICE);
         mRepo = repo;
         mImsEnablementTracker = new ImsEnablementTracker(mHandlerThread.getLooper(), componentName);
+        mFeatureFlags = featureFlags;
         mPackageManager = mContext.getPackageManager();
         if (mPackageManager != null) {
             mChangedPackages = mPackageManager.getChangedPackages(mLastSequenceNumber);
@@ -383,7 +387,7 @@
     // testing, use a handler supplied by the testing system.
     public ImsServiceController(Context context, ComponentName componentName,
             ImsServiceControllerCallbacks callbacks, Handler handler, RebindRetry rebindRetry,
-            ImsFeatureBinderRepository repo) {
+            ImsFeatureBinderRepository repo, FeatureFlags featureFlags) {
         mContext = context;
         mComponentName = componentName;
         mCallbacks = callbacks;
@@ -396,6 +400,7 @@
                 mRestartImsServiceRunnable);
         mPermissionManager = null;
         mRepo = repo;
+        mFeatureFlags = featureFlags;
         mImsEnablementTracker = new ImsEnablementTracker(handler.getLooper(), componentName);
     }
 
@@ -493,6 +498,12 @@
         synchronized (mLock) {
             HashSet<Integer> slotIDs = newImsFeatures.stream().map(e -> e.slotId).collect(
                     Collectors.toCollection(HashSet::new));
+
+            // Set the number of slot for IMS enable for each slot
+            if (mFeatureFlags.setNumberOfSimForImsEnable()) {
+                mImsEnablementTracker.setNumOfSlots(slotIDs.size());
+            }
+
             // detect which subIds have changed on a per-slot basis
             SparseIntArray changedSubIds = new SparseIntArray(slotIDs.size());
             for (Integer slotID : slotIDs) {
diff --git a/src/java/com/android/internal/telephony/ims/ImsServiceControllerCompat.java b/src/java/com/android/internal/telephony/ims/ImsServiceControllerCompat.java
index 778bd0e..440d784 100644
--- a/src/java/com/android/internal/telephony/ims/ImsServiceControllerCompat.java
+++ b/src/java/com/android/internal/telephony/ims/ImsServiceControllerCompat.java
@@ -38,6 +38,7 @@
 import com.android.ims.internal.IImsMMTelFeature;
 import com.android.ims.internal.IImsServiceController;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.flags.FeatureFlagsImpl;
 
 /**
  * Manages the Binding lifecycle of one ImsService as well as the relevant ImsFeatures that the
@@ -76,7 +77,7 @@
     public ImsServiceControllerCompat(Context context, ComponentName componentName,
             ImsServiceController.ImsServiceControllerCallbacks callbacks,
             ImsFeatureBinderRepository repo) {
-        super(context, componentName, callbacks, repo);
+        super(context, componentName, callbacks, repo, new FeatureFlagsImpl());
         mMmTelFeatureFactory = MmTelFeatureCompatAdapter::new;
     }
 
@@ -84,7 +85,8 @@
     public ImsServiceControllerCompat(Context context, ComponentName componentName,
             ImsServiceControllerCallbacks callbacks, Handler handler, RebindRetry rebindRetry,
             ImsFeatureBinderRepository repo, MmTelFeatureCompatFactory factory) {
-        super(context, componentName, callbacks, handler, rebindRetry, repo);
+        super(context, componentName, callbacks, handler, rebindRetry, repo,
+                new FeatureFlagsImpl());
         mMmTelFeatureFactory = factory;
     }
 
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhone.java b/src/java/com/android/internal/telephony/imsphone/ImsPhone.java
index a6bb1d6..10cbe77 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhone.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhone.java
@@ -21,6 +21,7 @@
 import static android.telephony.ims.ImsManager.EXTRA_WFC_REGISTRATION_FAILURE_TITLE;
 import static android.telephony.ims.RegistrationManager.REGISTRATION_STATE_NOT_REGISTERED;
 import static android.telephony.ims.RegistrationManager.REGISTRATION_STATE_REGISTERED;
+import static android.telephony.ims.RegistrationManager.REGISTRATION_STATE_REGISTERING;
 import static android.telephony.ims.RegistrationManager.SUGGESTED_ACTION_NONE;
 import static android.telephony.ims.RegistrationManager.SUGGESTED_ACTION_TRIGGER_CLEAR_RAT_BLOCKS;
 import static android.telephony.ims.RegistrationManager.SUGGESTED_ACTION_TRIGGER_PLMN_BLOCK;
@@ -75,6 +76,7 @@
 import android.os.UserHandle;
 import android.preference.PreferenceManager;
 import android.sysprop.TelephonyProperties;
+import android.telecom.VideoProfile;
 import android.telephony.AccessNetworkConstants;
 import android.telephony.CarrierConfigManager;
 import android.telephony.NetworkRegistrationInfo;
@@ -263,6 +265,14 @@
         }
     }
 
+    /**
+     * Container to transfer IMS registration radio tech.
+     * This will be used as result value of AsyncResult to the handler that called
+     * {@link #registerForImsRegistrationChanges(Handler, int, Object)}
+     */
+    public record ImsRegistrationRadioTechInfo(int phoneId, int imsRegistrationTech,
+                                                int imsRegistrationState) {}
+
     // Instance Variables
     Phone mDefaultPhone;
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@@ -802,7 +812,11 @@
             try {
                 if (getRingingCall().getState() != ImsPhoneCall.State.IDLE) {
                     if (DBG) logd("MmiCode 2: accept ringing call");
-                    mCT.acceptCall(ImsCallProfile.CALL_TYPE_VOICE);
+                    if (mFeatureFlags.answerAudioOnlyWhenAnsweringViaMmiCode()) {
+                        mCT.acceptCall(VideoProfile.STATE_AUDIO_ONLY);
+                    } else {
+                        mCT.acceptCall(ImsCallProfile.CALL_TYPE_VOICE);
+                    }
                 } else if (getBackgroundCall().getState() == ImsPhoneCall.State.HOLDING) {
                     // If there's an active ongoing call as well, hold it and the background one
                     // should automatically unhold. Otherwise just unhold the background call.
@@ -2504,7 +2518,15 @@
             updateImsRegistrationInfo(REGISTRATION_STATE_REGISTERED,
                     attributes.getRegistrationTechnology(), SUGGESTED_ACTION_NONE,
                     imsTransportType);
-            AsyncResult ar = new AsyncResult(null, null, null);
+
+            AsyncResult ar;
+            if (mFeatureFlags.changeMethodOfObtainingImsRegistrationRadioTech()) {
+                ar = new AsyncResult(null, new ImsRegistrationRadioTechInfo(mPhoneId,
+                        attributes.getRegistrationTechnology(), REGISTRATION_STATE_REGISTERED),
+                        null);
+            } else {
+                ar = new AsyncResult(null, null, null);
+            }
             mImsRegistrationUpdateRegistrants.notifyRegistrants(ar);
         }
 
@@ -2521,7 +2543,15 @@
             mMetrics.writeOnImsConnectionState(mPhoneId, ImsConnectionState.State.PROGRESSING,
                     null);
             mImsStats.onImsRegistering(imsRadioTech);
-            AsyncResult ar = new AsyncResult(null, null, null);
+
+            AsyncResult ar;
+            if (mFeatureFlags.changeMethodOfObtainingImsRegistrationRadioTech()) {
+                ar = new AsyncResult(null, new ImsRegistrationRadioTechInfo(mPhoneId,
+                        imsRadioTech, REGISTRATION_STATE_REGISTERING),
+                        null);
+            } else {
+                ar = new AsyncResult(null, null, null);
+            }
             mImsRegistrationUpdateRegistrants.notifyRegistrants(ar);
         }
 
@@ -2564,7 +2594,15 @@
                 setCurrentSubscriberUris(null);
                 clearPhoneNumberForSourceIms();
             }
-            AsyncResult ar = new AsyncResult(null, null, null);
+
+            AsyncResult ar;
+            if (mFeatureFlags.changeMethodOfObtainingImsRegistrationRadioTech()) {
+                ar = new AsyncResult(null, new ImsRegistrationRadioTechInfo(mPhoneId,
+                        REGISTRATION_TECH_NONE, REGISTRATION_STATE_NOT_REGISTERED),
+                        null);
+            } else {
+                ar = new AsyncResult(null, null, null);
+            }
             mImsRegistrationUpdateRegistrants.notifyRegistrants(ar);
         }
 
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java
index a71355d..aee8867 100755
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java
@@ -157,6 +157,11 @@
      */
     private boolean mIsHeldByRemote = false;
 
+    /**
+     * Used to indicate if both the user and carrier config have enabled the business composer.
+     */
+    private boolean mIsBusinessComposerFeatureEnabled = false;
+
     //***** Event Constants
     private static final int EVENT_DTMF_DONE = 1;
     private static final int EVENT_PAUSE_DONE = 2;
@@ -230,6 +235,10 @@
         mCreateTime = System.currentTimeMillis();
         mUusInfo = null;
 
+        if (com.android.server.telecom.flags.Flags.businessCallComposer()) {
+            setIsBusinessComposerFeatureEnabled(phone);
+        }
+
         // Ensure any extras set on the ImsCallProfile at the start of the call are cached locally
         // in the ImsPhoneConnection.  This isn't going to inform any listeners (since the original
         // connection is not likely to be associated with a TelephonyConnection yet).
@@ -1389,10 +1398,18 @@
      * ImsCallProfile.EXTRA_ASSERTED_DISPLAY_NAME). This helper notifies Telecom of the business
      * composer values which will then be injected into the android.telecom.Call object.
      */
-    private void maybeInjectBusinessComposerExtras(Bundle extras) {
+    @VisibleForTesting
+    public void maybeInjectBusinessComposerExtras(Bundle extras) {
         if (extras == null) {
             return;
         }
+        // Telephony should check that the business composer features is on BEFORE
+        // propagating the business call extras.  This prevents the user from getting
+        // business call info when they turned the feature off.
+        if (!mIsBusinessComposerFeatureEnabled) {
+            Rlog.i(LOG_TAG, "mIBCE: business composer feature is NOT enabled");
+            return;
+        }
         try {
             if (extras.containsKey(ImsCallProfile.EXTRA_IS_BUSINESS_CALL)
                     && !extras.containsKey(android.telecom.Call.EXTRA_IS_BUSINESS_CALL)) {
@@ -1413,6 +1430,60 @@
         }
     }
 
+    @VisibleForTesting
+    public boolean getIsBusinessComposerFeatureEnabled() {
+        return mIsBusinessComposerFeatureEnabled;
+    }
+
+    @VisibleForTesting
+    public void setIsBusinessComposerFeatureEnabled(Phone phone) {
+        mIsBusinessComposerFeatureEnabled = isBusinessComposerEnabledByConfig(phone)
+                && isBusinessOnlyCallComposerEnabledByUser(phone);
+        Rlog.i(LOG_TAG, String.format(
+                "setIsBusinessComposerFeatureEnabled:  mIsBusinessComposerFeatureEnabled=[%b], "
+                        + "phone=[%s]", mIsBusinessComposerFeatureEnabled, phone));
+    }
+
+    /**
+     * Returns whether the carrier supports and has enabled the business composer
+     */
+    @VisibleForTesting
+    public boolean isBusinessComposerEnabledByConfig(Phone phone) {
+        PersistableBundle b = null;
+        CarrierConfigManager configMgr = phone.getContext().getSystemService(
+                CarrierConfigManager.class);
+
+        if (configMgr != null) {
+            // If an invalid subId is used, this bundle will contain default values.
+            b = configMgr.getConfigForSubId(phone.getSubId());
+        }
+        if (b != null) {
+            return b.getBoolean(CarrierConfigManager.KEY_SUPPORTS_BUSINESS_CALL_COMPOSER_BOOL);
+        } else {
+            // Return static default defined in CarrierConfigManager.
+            return CarrierConfigManager.getDefaultConfig()
+                    .getBoolean(CarrierConfigManager.KEY_SUPPORTS_BUSINESS_CALL_COMPOSER_BOOL);
+        }
+    }
+
+    /**
+     * Returns whether the user has enabled the business composer
+     */
+    @VisibleForTesting
+    public boolean isBusinessOnlyCallComposerEnabledByUser(Phone phone) {
+        if (phone == null || phone.getContext() == null) {
+            return false;
+        }
+        TelephonyManager tm = (TelephonyManager)
+                phone.getContext().getSystemService(Context.TELEPHONY_SERVICE);
+        if (tm == null) {
+            Rlog.e(LOG_TAG, "isBusinessOnlyCallComposerEnabledByUser: TelephonyManager is null");
+            return false;
+        }
+        return tm.getCallComposerStatus() == TelephonyManager.CALL_COMPOSER_STATUS_BUSINESS_ONLY
+                || tm.getCallComposerStatus() == TelephonyManager.CALL_COMPOSER_STATUS_ON;
+    }
+
     private static boolean areBundlesEqual(Bundle extras, Bundle newExtras) {
         if (extras == null || newExtras == null) {
             return extras == newExtras;
diff --git a/src/java/com/android/internal/telephony/metrics/DataCallSessionStats.java b/src/java/com/android/internal/telephony/metrics/DataCallSessionStats.java
index 2eadf17..13fb826 100644
--- a/src/java/com/android/internal/telephony/metrics/DataCallSessionStats.java
+++ b/src/java/com/android/internal/telephony/metrics/DataCallSessionStats.java
@@ -79,8 +79,9 @@
     }
 
     /** Creates a new ongoing atom when data call is set up. */
-    public synchronized void onSetupDataCall(@ApnType int apnTypeBitMask) {
-        mDataCallSession = getDefaultProto(apnTypeBitMask);
+    public synchronized void onSetupDataCall(@ApnType int apnTypeBitMask,
+            boolean isSatellite) {
+        mDataCallSession = getDefaultProto(apnTypeBitMask, isSatellite);
         mStartTime = getTimeMillis();
         PhoneFactory.getMetricsCollector().registerOngoingDataCallStat(this);
     }
@@ -307,11 +308,13 @@
         copy.isNonDds = call.isNonDds;
         copy.isIwlanCrossSim = call.isIwlanCrossSim;
         copy.isNtn = call.isNtn;
+        copy.isSatelliteTransport = call.isSatelliteTransport;
         return copy;
     }
 
     /** Creates a proto for a normal {@code DataCallSession} with default values. */
-    private DataCallSession getDefaultProto(@ApnType int apnTypeBitmask) {
+    private DataCallSession getDefaultProto(@ApnType int apnTypeBitmask,
+            boolean isSatellite) {
         DataCallSession proto = new DataCallSession();
         proto.dimension = RANDOM.nextInt();
         proto.isMultiSim = SimSlotState.isMultiSim();
@@ -335,6 +338,7 @@
         proto.isIwlanCrossSim = false;
         proto.isNtn = mSatelliteController != null
                 ? mSatelliteController.isInSatelliteModeForCarrierRoaming(mPhone) : false;
+        proto.isSatelliteTransport = isSatellite;
         return proto;
     }
 
diff --git a/src/java/com/android/internal/telephony/metrics/DataConnectionStateTracker.java b/src/java/com/android/internal/telephony/metrics/DataConnectionStateTracker.java
index c7ef625..079ff03 100644
--- a/src/java/com/android/internal/telephony/metrics/DataConnectionStateTracker.java
+++ b/src/java/com/android/internal/telephony/metrics/DataConnectionStateTracker.java
@@ -16,9 +16,12 @@
 
 package com.android.internal.telephony.metrics;
 
+import static com.android.internal.telephony.flags.Flags.dataRatMetricEnabled;
+
 import android.os.Handler;
 import android.os.HandlerExecutor;
 import android.os.HandlerThread;
+import android.telephony.PhysicalChannelConfig;
 import android.telephony.PreciseDataConnectionState;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyCallback;
@@ -27,6 +30,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.TelephonyStatsLog;
 
 import java.util.HashMap;
 import java.util.List;
@@ -47,7 +51,10 @@
     private int mSubId;
     private HashMap<Integer, PreciseDataConnectionState> mLastPreciseDataConnectionState =
             new HashMap<>();
-    private PreciseDataConnectionStateListenerImpl mDataConnectionStateListener;
+    private TelephonyListenerImpl mTelephonyListener;
+    private int mActiveDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+    private int mChannelCountEnum = TelephonyStatsLog
+            .CONNECTED_CHANNEL_CHANGED__CONNECTED_CHANNEL_COUNT__CHANNEL_COUNT_UNSPECIFIED;
 
     private final SubscriptionManager.OnSubscriptionsChangedListener mSubscriptionsChangedListener =
             new SubscriptionManager.OnSubscriptionsChangedListener() {
@@ -132,15 +139,15 @@
         TelephonyManager telephonyManager =
                 mPhone.getContext().getSystemService(TelephonyManager.class);
         if (telephonyManager != null) {
-            mDataConnectionStateListener = new PreciseDataConnectionStateListenerImpl(mExecutor);
-            mDataConnectionStateListener.register(telephonyManager.createForSubscriptionId(subId));
+            mTelephonyListener = new TelephonyListenerImpl(mExecutor);
+            mTelephonyListener.register(telephonyManager.createForSubscriptionId(subId));
         }
     }
 
     private void unregisterTelephonyListener() {
-        if (mDataConnectionStateListener != null) {
-            mDataConnectionStateListener.unregister();
-            mDataConnectionStateListener = null;
+        if (mTelephonyListener != null) {
+            mTelephonyListener.unregister();
+            mTelephonyListener = null;
         }
     }
 
@@ -156,12 +163,46 @@
         mPhone.getVoiceCallSessionStats().onPreciseDataConnectionStateChanged(connectionState);
     }
 
-    private class PreciseDataConnectionStateListenerImpl extends TelephonyCallback
-            implements TelephonyCallback.PreciseDataConnectionStateListener {
+    static int getActiveDataSubId() {
+        if (sDataConnectionStateTracker.size() == 0) {
+            return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+        }
+        return sDataConnectionStateTracker.valueAt(0).mActiveDataSubId;
+    }
+
+    /**
+     * Log RAT if the active data subId changes to another subId with a different RAT.
+     *
+     * @param subId the current active data subId
+     */
+    private void logRATChanges(int subId) {
+        if (mSubId == subId && mActiveDataSubId != subId) {
+            int newDataRat = mPhone.getServiceStateTracker()
+                    .getServiceStateStats().getCurrentDataRat();
+            for (int i = 0; i < sDataConnectionStateTracker.size(); i++) {
+                DataConnectionStateTracker dataConnectionStateTracker =
+                        sDataConnectionStateTracker.valueAt(0);
+                if (dataConnectionStateTracker.mSubId == mActiveDataSubId) {
+                    int previousDataRat = dataConnectionStateTracker.mPhone
+                            .getServiceStateTracker().getServiceStateStats()
+                            .getCurrentDataRat();
+                    if (newDataRat != previousDataRat) {
+                        TelephonyStatsLog.write(TelephonyStatsLog.DATA_RAT_STATE_CHANGED,
+                                newDataRat);
+                    }
+                }
+            }
+        }
+    }
+
+    private class TelephonyListenerImpl extends TelephonyCallback
+            implements TelephonyCallback.PreciseDataConnectionStateListener,
+            TelephonyCallback.ActiveDataSubscriptionIdListener,
+            TelephonyCallback.PhysicalChannelConfigListener {
         private final Executor mExecutor;
         private TelephonyManager mTelephonyManager = null;
 
-        PreciseDataConnectionStateListenerImpl(Executor executor) {
+        TelephonyListenerImpl(Executor executor) {
             mExecutor = executor;
         }
 
@@ -185,5 +226,58 @@
                 PreciseDataConnectionState connectionState) {
             notifyDataConnectionStateChanged(connectionState);
         }
+
+        @Override
+        public void onActiveDataSubscriptionIdChanged(int subId) {
+            if (dataRatMetricEnabled()) {
+                logRATChanges(subId);
+            }
+            mActiveDataSubId = subId;
+        }
+
+        @Override
+        public void onPhysicalChannelConfigChanged(List<PhysicalChannelConfig> configs) {
+            logChannelChange(configs);
+        }
+
+        /** Log channel number if it changes for active data subscription*/
+        private void logChannelChange(List<PhysicalChannelConfig> configs) {
+            int connectedChannelCount = configs.size();
+            int channelCountEnum = TelephonyStatsLog
+                    .CONNECTED_CHANNEL_CHANGED__CONNECTED_CHANNEL_COUNT__CHANNEL_COUNT_UNSPECIFIED;
+            switch(connectedChannelCount) {
+                case 0:
+                    channelCountEnum = TelephonyStatsLog
+                            .CONNECTED_CHANNEL_CHANGED__CONNECTED_CHANNEL_COUNT__CHANNEL_COUNT_ONE;
+                    break;
+                case 1:
+                    channelCountEnum = TelephonyStatsLog
+                            .CONNECTED_CHANNEL_CHANGED__CONNECTED_CHANNEL_COUNT__CHANNEL_COUNT_ONE;
+                    break;
+                case 2:
+                    channelCountEnum = TelephonyStatsLog
+                            .CONNECTED_CHANNEL_CHANGED__CONNECTED_CHANNEL_COUNT__CHANNEL_COUNT_TWO;
+                    break;
+                case 3:
+                    channelCountEnum = TelephonyStatsLog
+                            .CONNECTED_CHANNEL_CHANGED__CONNECTED_CHANNEL_COUNT__CHANNEL_COUNT_THREE;
+                    break;
+                case 4:
+                    channelCountEnum = TelephonyStatsLog
+                            .CONNECTED_CHANNEL_CHANGED__CONNECTED_CHANNEL_COUNT__CHANNEL_COUNT_FOUR;
+                    break;
+                // Greater than 4
+                default:
+                    channelCountEnum = TelephonyStatsLog
+                            .CONNECTED_CHANNEL_CHANGED__CONNECTED_CHANNEL_COUNT__CHANNEL_COUNT_FIVE;
+            }
+            if (mChannelCountEnum != channelCountEnum) {
+                if (mSubId != mActiveDataSubId) {
+                    TelephonyStatsLog.write(TelephonyStatsLog.CONNECTED_CHANNEL_CHANGED,
+                            channelCountEnum);
+                }
+                mChannelCountEnum = channelCountEnum;
+            }
+        }
     }
 }
diff --git a/src/java/com/android/internal/telephony/metrics/DataNetworkValidationStats.java b/src/java/com/android/internal/telephony/metrics/DataNetworkValidationStats.java
new file mode 100644
index 0000000..fdac834
--- /dev/null
+++ b/src/java/com/android/internal/telephony/metrics/DataNetworkValidationStats.java
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2024 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.metrics;
+
+import android.annotation.ElapsedRealtimeLong;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.SystemClock;
+import android.telephony.Annotation.ApnType;
+import android.telephony.Annotation.NetworkType;
+import android.telephony.PreciseDataConnectionState;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneFactory;
+import com.android.internal.telephony.TelephonyStatsLog;
+import com.android.internal.telephony.nano.PersistAtomsProto.DataNetworkValidation;
+
+/**
+ * DataNetworkValidationStats logs the atoms for response after a validation request from the
+ * DataNetwork in framework.
+ */
+public class DataNetworkValidationStats {
+
+    private static final String TAG = DataNetworkValidationStats.class.getSimpleName();
+
+    @NonNull
+    private final Phone mPhone;
+
+    @NonNull
+    private final PersistAtomsStorage mAtomsStorage =
+            PhoneFactory.getMetricsCollector().getAtomsStorage();
+
+    @Nullable
+    private DataNetworkValidation mDataNetworkValidation;
+
+    @ElapsedRealtimeLong
+    private long mRequestedTimeInMillis;
+
+    /** constructor */
+    public DataNetworkValidationStats(@NonNull Phone phone) {
+        mPhone = phone;
+    }
+
+
+    /**
+     * Create a new ongoing atom when NetworkValidation requested.
+     *
+     * Create a data network validation proto for a new atom record and write the start time to
+     * calculate the elapsed time required.
+     *
+     * @param apnTypeBitMask APN type bitmask of DataNetwork.
+     */
+    public void onRequestNetworkValidation(@ApnType int apnTypeBitMask) {
+        if (mDataNetworkValidation == null) {
+            mDataNetworkValidation = getDefaultProto(apnTypeBitMask);
+            mRequestedTimeInMillis = getTimeMillis();
+        }
+    }
+
+    /** Mark the Handover Attempt field as true if validation was requested */
+    public void onHandoverAttempted() {
+        if (mDataNetworkValidation != null) {
+            mDataNetworkValidation.handoverAttempted = true;
+        }
+    }
+
+    /**
+     * Called when data network is disconnected.
+     *
+     * Since network validation is based on the data network, validation must also end when the data
+     * network is disconnected. At this time, validation has not been completed, save an atom as
+     * unspecified. and clear.
+     *
+     * @param networkType Current Network Type of the Data Network.
+     */
+    public void onDataNetworkDisconnected(@NetworkType int networkType) {
+        // Nothing to do, if never requested validation
+        if (mDataNetworkValidation == null) {
+            return;
+        }
+
+        // Set data for and atom.
+        calcElapsedTime();
+        mDataNetworkValidation.networkType = networkType;
+        mDataNetworkValidation.signalStrength = mPhone.getSignalStrength().getLevel();
+        mDataNetworkValidation.validationResult = TelephonyStatsLog
+                .DATA_NETWORK_VALIDATION__VALIDATION_RESULT__VALIDATION_RESULT_UNSPECIFIED;
+
+        // Store.
+        mAtomsStorage.addDataNetworkValidation(mDataNetworkValidation);
+
+        // clear all values.
+        clear();
+    }
+
+    /**
+     * Store an atom by updated state.
+     *
+     * Called when the validation status is updated, and saves the atom when a failure or success
+     * result is received.
+     *
+     * @param status Data Network Validation Status.
+     * @param networkType Current Network Type of the Data Network.
+     */
+    public void onUpdateNetworkValidationState(
+            @PreciseDataConnectionState.NetworkValidationStatus int status,
+            @NetworkType int networkType) {
+        // Nothing to do, if never requested validation
+        if (mDataNetworkValidation == null) {
+            return;
+        }
+
+        switch (status) {
+            // Immediately after requesting validation, these messages may occur. In this case,
+            // ignore it and wait for the next update.
+            case PreciseDataConnectionState.NETWORK_VALIDATION_NOT_REQUESTED: // fall-through
+            case PreciseDataConnectionState.NETWORK_VALIDATION_IN_PROGRESS:
+                return;
+            // If status is unsupported, NetworkValidation should not be requested initially. logs
+            // this for abnormal tracking.
+            case PreciseDataConnectionState.NETWORK_VALIDATION_UNSUPPORTED:
+                mDataNetworkValidation.validationResult = TelephonyStatsLog
+                    .DATA_NETWORK_VALIDATION__VALIDATION_RESULT__VALIDATION_RESULT_NOT_SUPPORTED;
+                break;
+            // Success or failure corresponds to the result, store an atom.
+            case PreciseDataConnectionState.NETWORK_VALIDATION_SUCCESS:
+            case PreciseDataConnectionState.NETWORK_VALIDATION_FAILURE:
+                mDataNetworkValidation.validationResult = status;
+                break;
+        }
+
+        // Set data for and atom.
+        calcElapsedTime();
+        mDataNetworkValidation.networkType = networkType;
+        mDataNetworkValidation.signalStrength = mPhone.getSignalStrength().getLevel();
+
+        // Store.
+        mAtomsStorage.addDataNetworkValidation(mDataNetworkValidation);
+
+        // clear all values.
+        clear();
+    }
+
+    /**
+     * Calculate the current time required based on when network validation is requested.
+     */
+    private void calcElapsedTime() {
+        if (mDataNetworkValidation != null && mRequestedTimeInMillis != 0) {
+            mDataNetworkValidation.elapsedTimeInMillis = getTimeMillis() - mRequestedTimeInMillis;
+        }
+    }
+
+    /**
+     * Returns current time in millis from boot.
+     */
+    @VisibleForTesting
+    @ElapsedRealtimeLong
+    protected long getTimeMillis() {
+        return SystemClock.elapsedRealtime();
+    }
+
+    /**
+     * Clear all values.
+     */
+    private void clear() {
+        mDataNetworkValidation = null;
+        mRequestedTimeInMillis = 0;
+    }
+
+
+    /** Creates a DataNetworkValidation proto with default values. */
+    @NonNull
+    private DataNetworkValidation getDefaultProto(@ApnType int apnTypeBitmask) {
+        DataNetworkValidation proto = new DataNetworkValidation();
+        proto.networkType =
+                TelephonyStatsLog.DATA_NETWORK_VALIDATION__NETWORK_TYPE__NETWORK_TYPE_UNKNOWN;
+        proto.apnTypeBitmask = apnTypeBitmask;
+        proto.signalStrength =
+                TelephonyStatsLog
+                        .DATA_NETWORK_VALIDATION__SIGNAL_STRENGTH__SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+        proto.validationResult =
+                TelephonyStatsLog
+                        .DATA_NETWORK_VALIDATION__VALIDATION_RESULT__VALIDATION_RESULT_UNSPECIFIED;
+        proto.elapsedTimeInMillis = 0;
+        proto.handoverAttempted = false;
+        proto.networkValidationCount = 1;
+        return proto;
+    }
+}
+
diff --git a/src/java/com/android/internal/telephony/metrics/DeviceStateHelper.java b/src/java/com/android/internal/telephony/metrics/DeviceStateHelper.java
index 29729c8..9ab52fb 100644
--- a/src/java/com/android/internal/telephony/metrics/DeviceStateHelper.java
+++ b/src/java/com/android/internal/telephony/metrics/DeviceStateHelper.java
@@ -41,7 +41,7 @@
                 .registerCallback(
                         new HandlerExecutor(new Handler(mHandlerThread.getLooper())),
                         state -> {
-                            updateFoldState(state);
+                            updateFoldState(state.getIdentifier());
                         });
     }
 
diff --git a/src/java/com/android/internal/telephony/metrics/MetricsCollector.java b/src/java/com/android/internal/telephony/metrics/MetricsCollector.java
index f174b25..49afd54 100644
--- a/src/java/com/android/internal/telephony/metrics/MetricsCollector.java
+++ b/src/java/com/android/internal/telephony/metrics/MetricsCollector.java
@@ -22,6 +22,7 @@
 import static com.android.internal.telephony.TelephonyStatsLog.CELLULAR_DATA_SERVICE_SWITCH;
 import static com.android.internal.telephony.TelephonyStatsLog.CELLULAR_SERVICE_STATE;
 import static com.android.internal.telephony.TelephonyStatsLog.DATA_CALL_SESSION;
+import static com.android.internal.telephony.TelephonyStatsLog.DATA_NETWORK_VALIDATION;
 import static com.android.internal.telephony.TelephonyStatsLog.DEVICE_TELEPHONY_PROPERTIES;
 import static com.android.internal.telephony.TelephonyStatsLog.EMERGENCY_NUMBERS_INFO;
 import static com.android.internal.telephony.TelephonyStatsLog.GBA_EVENT;
@@ -78,6 +79,7 @@
 import com.android.internal.telephony.nano.PersistAtomsProto.CellularDataServiceSwitch;
 import com.android.internal.telephony.nano.PersistAtomsProto.CellularServiceState;
 import com.android.internal.telephony.nano.PersistAtomsProto.DataCallSession;
+import com.android.internal.telephony.nano.PersistAtomsProto.DataNetworkValidation;
 import com.android.internal.telephony.nano.PersistAtomsProto.EmergencyNumbersInfo;
 import com.android.internal.telephony.nano.PersistAtomsProto.GbaEvent;
 import com.android.internal.telephony.nano.PersistAtomsProto.ImsDedicatedBearerEvent;
@@ -146,14 +148,6 @@
     private static final long MIN_COOLDOWN_MILLIS =
             DBG ? 10L * MILLIS_PER_SECOND : 23L * MILLIS_PER_HOUR;
 
-    /**
-     * Sets atom pull cool down to 4 minutes for userdebug build.
-     *
-     * <p>Applies to certain atoms: CellularServiceState.
-     */
-    private static final long CELL_SERVICE_MIN_COOLDOWN_MILLIS =
-            DBG ? 10L * MILLIS_PER_SECOND :
-                    IS_DEBUGGABLE ? 4L * MILLIS_PER_MINUTE : 23L * MILLIS_PER_HOUR;
 
     /**
      * Buckets with less than these many calls will be dropped.
@@ -174,6 +168,13 @@
     private static final long CELL_SERVICE_DURATION_BUCKET_MILLIS =
             DBG || IS_DEBUGGABLE ? 2L * MILLIS_PER_SECOND : 5L * MILLIS_PER_MINUTE;
 
+    /**
+     * Sets atom pull cool down to 4 minutes for userdebug build, 5 hours for user build.
+     *
+     * <p>Applies to certain atoms: CellularServiceState, DataCallSession,
+     * ImsRegistrationTermination.
+     */
+    private final long mPowerCorrelatedMinCooldownMillis;
     private final PersistAtomsStorage mStorage;
     private final DeviceStateHelper mDeviceStateHelper;
     private final StatsManager mStatsManager;
@@ -236,6 +237,7 @@
             registerAtom(SATELLITE_OUTGOING_DATAGRAM);
             registerAtom(SATELLITE_PROVISION);
             registerAtom(SATELLITE_SOS_MESSAGE_RECOMMENDER);
+            registerAtom(DATA_NETWORK_VALIDATION);
             registerAtom(CARRIER_ROAMING_SATELLITE_SESSION);
             registerAtom(CARRIER_ROAMING_SATELLITE_CONTROLLER_STATS);
             registerAtom(SATELLITE_ENTITLEMENT);
@@ -246,7 +248,10 @@
         }
 
         mAirplaneModeStats = new AirplaneModeStats(context);
-        mDefaultNetworkMonitor = defaultNetworkMonitor;
+        mDefaultNetworkMonitor = new DefaultNetworkMonitor(context, featureFlags);
+        mPowerCorrelatedMinCooldownMillis = DBG ? 10L * MILLIS_PER_SECOND :
+                IS_DEBUGGABLE ? 4L * MILLIS_PER_MINUTE : (long) context.getResources().getInteger(
+                com.android.internal.R.integer.config_metrics_pull_cooldown_millis);
     }
 
     /**
@@ -331,6 +336,8 @@
                 return pullSatelliteProvision(data);
             case SATELLITE_SOS_MESSAGE_RECOMMENDER:
                 return pullSatelliteSosMessageRecommender(data);
+            case DATA_NETWORK_VALIDATION:
+                return pullDataNetworkValidation(data);
             case CARRIER_ROAMING_SATELLITE_SESSION:
                 return pullCarrierRoamingSatelliteSession(data);
             case CARRIER_ROAMING_SATELLITE_CONTROLLER_STATS:
@@ -537,7 +544,8 @@
     private int pullDataCallSession(List<StatsEvent> data) {
         // Include ongoing data call segments
         concludeDataCallSessionStats();
-        DataCallSession[] dataCallSessions = mStorage.getDataCallSessions(MIN_COOLDOWN_MILLIS);
+        DataCallSession[] dataCallSessions = mStorage.getDataCallSessions(
+                mPowerCorrelatedMinCooldownMillis);
         if (dataCallSessions != null) {
             Arrays.stream(dataCallSessions)
                     .forEach(dataCall -> data.add(buildStatsEvent(dataCall)));
@@ -565,8 +573,8 @@
     private int pullCellularServiceState(List<StatsEvent> data) {
         // Include the latest durations
         concludeServiceStateStats();
-        CellularServiceState[] persistAtoms =
-                mStorage.getCellularServiceStates(CELL_SERVICE_MIN_COOLDOWN_MILLIS);
+        CellularServiceState[] persistAtoms = mStorage.getCellularServiceStates(
+                mPowerCorrelatedMinCooldownMillis);
         if (persistAtoms != null) {
             // list is already shuffled when instances were inserted
             Arrays.stream(persistAtoms)
@@ -594,8 +602,8 @@
     }
 
     private int pullImsRegistrationTermination(List<StatsEvent> data) {
-        ImsRegistrationTermination[] persistAtoms =
-                mStorage.getImsRegistrationTerminations(MIN_COOLDOWN_MILLIS);
+        ImsRegistrationTermination[] persistAtoms = mStorage.getImsRegistrationTerminations(
+                mPowerCorrelatedMinCooldownMillis);
         if (persistAtoms != null) {
             // list is already shuffled when instances were inserted
             Arrays.stream(persistAtoms)
@@ -958,6 +966,19 @@
         }
     }
 
+    private int pullDataNetworkValidation(@NonNull List<StatsEvent> data) {
+        DataNetworkValidation[] dataNetworkValidations =
+                mStorage.getDataNetworkValidation(mPowerCorrelatedMinCooldownMillis);
+        if (dataNetworkValidations != null) {
+            Arrays.stream(dataNetworkValidations)
+                    .forEach(d -> data.add(buildStatsEvent(d)));
+            return StatsManager.PULL_SUCCESS;
+        } else {
+            Rlog.w(TAG, "DATA_NETWORK_VALIDATION pull too frequent, skipping");
+            return StatsManager.PULL_SKIP;
+        }
+    }
+
     private int pullCarrierRoamingSatelliteSession(List<StatsEvent> data) {
         CarrierRoamingSatelliteSession[] carrierRoamingSatelliteSessionAtoms =
                 mStorage.getCarrierRoamingSatelliteSessionStats(MIN_COOLDOWN_MILLIS);
@@ -1184,7 +1205,8 @@
                 dataCallSession.handoverFailureRat,
                 dataCallSession.isNonDds,
                 dataCallSession.isIwlanCrossSim,
-                dataCallSession.isNtn);
+                dataCallSession.isNtn,
+                dataCallSession.isSatelliteTransport);
     }
 
     private static StatsEvent buildStatsEvent(ImsRegistrationStats stats) {
@@ -1489,6 +1511,18 @@
                 stats.isSatelliteAllowedInCurrentLocation);
     }
 
+    private static StatsEvent buildStatsEvent(DataNetworkValidation stats) {
+        return TelephonyStatsLog.buildStatsEvent(
+                DATA_NETWORK_VALIDATION,
+                stats.networkType,
+                stats.apnTypeBitmask,
+                stats.signalStrength,
+                stats.validationResult,
+                stats.elapsedTimeInMillis,
+                stats.handoverAttempted,
+                stats.networkValidationCount);
+    }
+
     private static StatsEvent buildStatsEvent(CarrierRoamingSatelliteSession stats) {
         return TelephonyStatsLog.buildStatsEvent(
                 CARRIER_ROAMING_SATELLITE_SESSION,
diff --git a/src/java/com/android/internal/telephony/metrics/NetworkRequestsStats.java b/src/java/com/android/internal/telephony/metrics/NetworkRequestsStats.java
index 26c28f0..6f4a1a0 100644
--- a/src/java/com/android/internal/telephony/metrics/NetworkRequestsStats.java
+++ b/src/java/com/android/internal/telephony/metrics/NetworkRequestsStats.java
@@ -59,6 +59,47 @@
             networkRequestsTemplate.capability = NetworkRequestsV2.NetworkCapability.ENTERPRISE;
             storage.addNetworkRequestsV2(networkRequestsTemplate);
         }
+
+        if (networkRequest.hasTransport(NetworkCapabilities.TRANSPORT_SATELLITE)
+                && !networkRequest.hasCapability(
+                        NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)) {
+
+            if (networkRequest.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) {
+                networkRequestsTemplate.capability =
+                        NetworkRequestsV2.NetworkCapability.SATELLITE_INTERNET_RESTRICTED;
+                storage.addNetworkRequestsV2(networkRequestsTemplate);
+            }
+
+            if (networkRequest.hasCapability(NetworkCapabilities.NET_CAPABILITY_MMS)) {
+                networkRequestsTemplate.capability =
+                        NetworkRequestsV2.NetworkCapability.SATELLITE_MMS_RESTRICTED;
+                storage.addNetworkRequestsV2(networkRequestsTemplate);
+            }
+
+            if (networkRequest.hasCapability(NetworkCapabilities.NET_CAPABILITY_IMS)) {
+                networkRequestsTemplate.capability =
+                        NetworkRequestsV2.NetworkCapability.SATELLITE_IMS_RESTRICTED;
+                storage.addNetworkRequestsV2(networkRequestsTemplate);
+            }
+
+            if (networkRequest.hasCapability(NetworkCapabilities.NET_CAPABILITY_XCAP)) {
+                networkRequestsTemplate.capability =
+                        NetworkRequestsV2.NetworkCapability.SATELLITE_XCAP_RESTRICTED;
+                storage.addNetworkRequestsV2(networkRequestsTemplate);
+            }
+
+            if (networkRequest.hasCapability(NetworkCapabilities.NET_CAPABILITY_EIMS)) {
+                networkRequestsTemplate.capability =
+                        NetworkRequestsV2.NetworkCapability.SATELLITE_EIMS_RESTRICTED;
+                storage.addNetworkRequestsV2(networkRequestsTemplate);
+            }
+
+            if (networkRequest.hasCapability(NetworkCapabilities.NET_CAPABILITY_SUPL)) {
+                networkRequestsTemplate.capability =
+                        NetworkRequestsV2.NetworkCapability.SATELLITE_SUPL_RESTRICTED;
+                storage.addNetworkRequestsV2(networkRequestsTemplate);
+            }
+        }
     }
 
     /** Returns the carrier ID of the given subscription id. */
diff --git a/src/java/com/android/internal/telephony/metrics/PersistAtomsStorage.java b/src/java/com/android/internal/telephony/metrics/PersistAtomsStorage.java
index 68cca3c..975750e 100644
--- a/src/java/com/android/internal/telephony/metrics/PersistAtomsStorage.java
+++ b/src/java/com/android/internal/telephony/metrics/PersistAtomsStorage.java
@@ -35,6 +35,7 @@
 import com.android.internal.telephony.nano.PersistAtomsProto.CellularDataServiceSwitch;
 import com.android.internal.telephony.nano.PersistAtomsProto.CellularServiceState;
 import com.android.internal.telephony.nano.PersistAtomsProto.DataCallSession;
+import com.android.internal.telephony.nano.PersistAtomsProto.DataNetworkValidation;
 import com.android.internal.telephony.nano.PersistAtomsProto.GbaEvent;
 import com.android.internal.telephony.nano.PersistAtomsProto.ImsDedicatedBearerEvent;
 import com.android.internal.telephony.nano.PersistAtomsProto.ImsDedicatedBearerListenerEvent;
@@ -178,6 +179,9 @@
     private final int mMaxNumSatelliteControllerStats = 1;
     private final int mMaxNumCarrierRoamingSatelliteSessionStats = 1;
 
+    /** Maximum number of data network validation to store during pulls. */
+    private final int mMaxNumDataNetworkValidation;
+
     /** Stores persist atoms and persist states of the puller. */
     @VisibleForTesting protected PersistAtoms mAtoms;
 
@@ -228,6 +232,7 @@
             mMaxNumGbaEventStats = 5;
             mMaxOutgoingShortCodeSms = 5;
             mMaxNumSatelliteStats = 5;
+            mMaxNumDataNetworkValidation = 5;
         } else {
             mMaxNumVoiceCallSessions = 50;
             mMaxNumSms = 25;
@@ -252,6 +257,7 @@
             mMaxNumGbaEventStats = 10;
             mMaxOutgoingShortCodeSms = 10;
             mMaxNumSatelliteStats = 15;
+            mMaxNumDataNetworkValidation = 15;
         }
 
         mAtoms = loadAtomsFromFile();
@@ -812,6 +818,25 @@
         saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS);
     }
 
+    /** Adds a data network validation to the storage. */
+    public synchronized void addDataNetworkValidation(DataNetworkValidation dataNetworkValidation) {
+        DataNetworkValidation existingStats = find(dataNetworkValidation);
+        if (existingStats != null) {
+            int count = existingStats.networkValidationCount
+                    + dataNetworkValidation.networkValidationCount;
+            long elapsedTime = ((dataNetworkValidation.elapsedTimeInMillis
+                    * dataNetworkValidation.networkValidationCount) + (
+                    existingStats.elapsedTimeInMillis * existingStats.networkValidationCount))
+                    / count;
+            existingStats.networkValidationCount = count;
+            existingStats.elapsedTimeInMillis = elapsedTime;
+        } else {
+            mAtoms.dataNetworkValidation = insertAtRandomPlace(
+                    mAtoms.dataNetworkValidation, dataNetworkValidation, mMaxNumDataCallSessions);
+        }
+        saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS);
+    }
+
     /** Adds a new {@link CarrierRoamingSatelliteSession} to the storage. */
     public synchronized void addCarrierRoamingSatelliteSessionStats(
             CarrierRoamingSatelliteSession stats) {
@@ -1528,6 +1553,24 @@
     }
 
     /**
+     * Returns and clears the data network validation if last pulled longer than {@code
+     * minIntervalMillis} ago, otherwise returns {@code null}.
+     */
+    @Nullable
+    public synchronized DataNetworkValidation[] getDataNetworkValidation(long minIntervalMillis) {
+        long wallTime = getWallTimeMillis();
+        if (wallTime - mAtoms.dataNetworkValidationPullTimestampMillis > minIntervalMillis) {
+            mAtoms.dataNetworkValidationPullTimestampMillis = wallTime;
+            DataNetworkValidation[] previousDataNetworkValidation = mAtoms.dataNetworkValidation;
+            mAtoms.dataNetworkValidation = new DataNetworkValidation[0];
+            saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS);
+            return previousDataNetworkValidation;
+        } else {
+            return null;
+        }
+    }
+
+    /**
      * Returns and clears the {@link CarrierRoamingSatelliteSession} stats if last pulled
      * longer than {@code minIntervalMillis} ago, otherwise returns {@code null}.
      */
@@ -1755,6 +1798,12 @@
             atoms.satelliteSosMessageRecommender = sanitizeAtoms(
                     atoms.satelliteSosMessageRecommender, SatelliteSosMessageRecommender.class,
                     mMaxNumSatelliteStats);
+            atoms.dataNetworkValidation =
+                    sanitizeAtoms(
+                            atoms.dataNetworkValidation,
+                            DataNetworkValidation.class,
+                            mMaxNumDataNetworkValidation
+                    );
             atoms.carrierRoamingSatelliteSession = sanitizeAtoms(
                     atoms.carrierRoamingSatelliteSession, CarrierRoamingSatelliteSession.class,
                     mMaxNumSatelliteStats);
@@ -1827,6 +1876,8 @@
                     sanitizeTimestamp(atoms.satelliteProvisionPullTimestampMillis);
             atoms.satelliteSosMessageRecommenderPullTimestampMillis =
                     sanitizeTimestamp(atoms.satelliteSosMessageRecommenderPullTimestampMillis);
+            atoms.dataNetworkValidationPullTimestampMillis =
+                    sanitizeTimestamp(atoms.dataNetworkValidationPullTimestampMillis);
             atoms.carrierRoamingSatelliteSessionPullTimestampMillis = sanitizeTimestamp(
                     atoms.carrierRoamingSatelliteSessionPullTimestampMillis);
             atoms.carrierRoamingSatelliteControllerStatsPullTimestampMillis = sanitizeTimestamp(
@@ -2271,6 +2322,23 @@
     }
 
     /**
+     * Returns SatelliteOutgoingDatagram atom that has same values or {@code null}
+     * if it does not exist.
+     */
+    private @Nullable DataNetworkValidation find(DataNetworkValidation key) {
+        for (DataNetworkValidation stats : mAtoms.dataNetworkValidation) {
+            if (stats.networkType == key.networkType
+                    && stats.apnTypeBitmask == key.apnTypeBitmask
+                    && stats.signalStrength == key.signalStrength
+                    && stats.validationResult == key.validationResult
+                    && stats.handoverAttempted == key.handoverAttempted) {
+                return stats;
+            }
+        }
+        return null;
+    }
+
+    /**
      * Returns SatelliteEntitlement atom that has same values or {@code null} if it does not exist.
      */
     private @Nullable SatelliteEntitlement find(SatelliteEntitlement key) {
@@ -2556,6 +2624,7 @@
         atoms.satelliteOutgoingDatagramPullTimestampMillis = currentTime;
         atoms.satelliteProvisionPullTimestampMillis = currentTime;
         atoms.satelliteSosMessageRecommenderPullTimestampMillis = currentTime;
+        atoms.dataNetworkValidationPullTimestampMillis = currentTime;
         atoms.carrierRoamingSatelliteSessionPullTimestampMillis = currentTime;
         atoms.carrierRoamingSatelliteControllerStatsPullTimestampMillis = currentTime;
         atoms.satelliteEntitlementPullTimestampMillis = currentTime;
diff --git a/src/java/com/android/internal/telephony/metrics/ServiceStateStats.java b/src/java/com/android/internal/telephony/metrics/ServiceStateStats.java
index d4830ae..3f24968 100644
--- a/src/java/com/android/internal/telephony/metrics/ServiceStateStats.java
+++ b/src/java/com/android/internal/telephony/metrics/ServiceStateStats.java
@@ -21,6 +21,7 @@
 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_CS;
 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_IMS;
 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_UNKNOWN;
+import static com.android.internal.telephony.flags.Flags.dataRatMetricEnabled;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -31,6 +32,7 @@
 import android.telephony.NetworkRegistrationInfo;
 import android.telephony.ServiceState;
 import android.telephony.ServiceState.RoamingType;
+import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.telephony.ims.stub.ImsRegistrationImplBase;
 
@@ -38,6 +40,7 @@
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneFactory;
 import com.android.internal.telephony.ServiceStateTracker;
+import com.android.internal.telephony.TelephonyStatsLog;
 import com.android.internal.telephony.data.DataNetwork;
 import com.android.internal.telephony.data.DataNetworkController;
 import com.android.internal.telephony.data.DataNetworkController.DataNetworkControllerCallback;
@@ -62,6 +65,8 @@
     private final PersistAtomsStorage mStorage;
     private final DeviceStateHelper mDeviceStateHelper;
     private boolean mExistAnyConnectedInternetPdn;
+    private int mCurrentDataRat =
+            TelephonyStatsLog.DATA_RAT_STATE_CHANGED__DATA_RAT__DATA_RAT_UNSPECIFIED;
 
     public ServiceStateStats(Phone phone) {
         super(Runnable::run);
@@ -144,6 +149,10 @@
             addServiceStateAndSwitch(
                     prevState, now, getDataServiceSwitch(prevState.mServiceState, newState));
         }
+
+        if (dataRatMetricEnabled()) {
+            writeDataRatAtom(serviceState);
+        }
     }
 
     /** Updates the fold state of the device for the current service state. */
@@ -461,6 +470,68 @@
                 || isNetworkRoaming(ss, NetworkRegistrationInfo.DOMAIN_PS);
     }
 
+    /** Collect data Rat metric. */
+    private void writeDataRatAtom(@NonNull ServiceState serviceState) {
+        if (DataConnectionStateTracker.getActiveDataSubId() != mPhone.getSubId()) {
+            return;
+        }
+        NetworkRegistrationInfo wwanRegInfo = serviceState.getNetworkRegistrationInfo(
+                NetworkRegistrationInfo.DOMAIN_PS,
+                AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+        if (wwanRegInfo == null) {
+            return;
+        }
+        int dataRat = wwanRegInfo.getAccessNetworkTechnology();
+        int nrFrequency = serviceState.getNrFrequencyRange();
+        int nrState = serviceState.getNrState();
+        int translatedDataRat =
+                TelephonyStatsLog.DATA_RAT_STATE_CHANGED__DATA_RAT__DATA_RAT_UNSPECIFIED;
+        if (!SubscriptionManager.isValidSubscriptionId(mPhone.getSubId())) {
+            translatedDataRat = TelephonyStatsLog.DATA_RAT_STATE_CHANGED__DATA_RAT__NO_SIM;
+        } else if (dataRat == TelephonyManager.NETWORK_TYPE_EHRPD
+                || dataRat == TelephonyManager.NETWORK_TYPE_HSPAP
+                || dataRat == TelephonyManager.NETWORK_TYPE_UMTS
+                || dataRat == TelephonyManager.NETWORK_TYPE_HSDPA
+                || dataRat == TelephonyManager.NETWORK_TYPE_HSUPA
+                || dataRat == TelephonyManager.NETWORK_TYPE_HSPA
+                || dataRat == TelephonyManager.NETWORK_TYPE_EVDO_0
+                || dataRat == TelephonyManager.NETWORK_TYPE_EVDO_A
+                || dataRat == TelephonyManager.NETWORK_TYPE_EVDO_B) {
+            translatedDataRat = TelephonyStatsLog.DATA_RAT_STATE_CHANGED__DATA_RAT__DATA_RAT_3G;
+        } else if (dataRat == TelephonyManager.NETWORK_TYPE_1xRTT
+                || dataRat == TelephonyManager.NETWORK_TYPE_GPRS
+                || dataRat == TelephonyManager.NETWORK_TYPE_EDGE
+                || dataRat == TelephonyManager.NETWORK_TYPE_CDMA
+                || dataRat == TelephonyManager.NETWORK_TYPE_GSM) {
+            translatedDataRat = TelephonyStatsLog.DATA_RAT_STATE_CHANGED__DATA_RAT__DATA_RAT_2G;
+        } else if (dataRat == TelephonyManager.NETWORK_TYPE_NR) {
+            translatedDataRat = nrFrequency != ServiceState.FREQUENCY_RANGE_MMWAVE
+                    ? TelephonyStatsLog.DATA_RAT_STATE_CHANGED__DATA_RAT__DATA_RAT_5G_SA_FR1 :
+                    TelephonyStatsLog.DATA_RAT_STATE_CHANGED__DATA_RAT__DATA_RAT_5G_SA_FR2;
+        } else if (dataRat == TelephonyManager.NETWORK_TYPE_LTE) {
+            if (nrState == NetworkRegistrationInfo.NR_STATE_CONNECTED) {
+                translatedDataRat = nrFrequency != ServiceState.FREQUENCY_RANGE_MMWAVE
+                    ? TelephonyStatsLog.DATA_RAT_STATE_CHANGED__DATA_RAT__DATA_RAT_5G_NSA_FR1 :
+                    TelephonyStatsLog.DATA_RAT_STATE_CHANGED__DATA_RAT__DATA_RAT_5G_NSA_FR2;
+            } else if (nrState == NetworkRegistrationInfo.NR_STATE_NOT_RESTRICTED) {
+                translatedDataRat =
+                        TelephonyStatsLog.DATA_RAT_STATE_CHANGED__DATA_RAT__DATA_RAT_5G_NSA_LTE;
+            } else {
+                translatedDataRat =
+                        TelephonyStatsLog.DATA_RAT_STATE_CHANGED__DATA_RAT__DATA_RAT_4G_LTE;
+            }
+        }
+
+        if (translatedDataRat != mCurrentDataRat) {
+            TelephonyStatsLog.write(TelephonyStatsLog.DATA_RAT_STATE_CHANGED, translatedDataRat);
+            mCurrentDataRat = translatedDataRat;
+        }
+    }
+
+    int getCurrentDataRat() {
+        return mCurrentDataRat;
+    }
+
     @VisibleForTesting
     protected long getTimeMillis() {
         return SystemClock.elapsedRealtime();
diff --git a/src/java/com/android/internal/telephony/metrics/VoiceCallSessionStats.java b/src/java/com/android/internal/telephony/metrics/VoiceCallSessionStats.java
index 8be9e83..c4ad93a 100644
--- a/src/java/com/android/internal/telephony/metrics/VoiceCallSessionStats.java
+++ b/src/java/com/android/internal/telephony/metrics/VoiceCallSessionStats.java
@@ -797,11 +797,8 @@
                 }
             }
         }
-        if (bearer == VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_IMS) {
-            return TelephonyManager.NETWORK_TYPE_UNKNOWN;
-        } else {
-            return ServiceStateStats.getRat(state, NetworkRegistrationInfo.DOMAIN_CS);
-        }
+
+        return ServiceStateStats.getRat(state, NetworkRegistrationInfo.DOMAIN_CS);
     }
 
     /** Resets the list of codecs used for the connection with only the codec currently in use. */
diff --git a/src/java/com/android/internal/telephony/security/CellularNetworkSecuritySafetySource.java b/src/java/com/android/internal/telephony/security/CellularNetworkSecuritySafetySource.java
index 34f26e3..2d4776c 100644
--- a/src/java/com/android/internal/telephony/security/CellularNetworkSecuritySafetySource.java
+++ b/src/java/com/android/internal/telephony/security/CellularNetworkSecuritySafetySource.java
@@ -25,11 +25,14 @@
 import android.app.PendingIntent;
 import android.content.Context;
 import android.content.Intent;
+import android.content.res.Resources;
+import android.net.Uri;
 import android.safetycenter.SafetyCenterManager;
 import android.safetycenter.SafetyEvent;
 import android.safetycenter.SafetySourceData;
 import android.safetycenter.SafetySourceIssue;
 import android.safetycenter.SafetySourceStatus;
+import android.text.format.DateFormat;
 
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
@@ -39,8 +42,10 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.time.Instant;
-import java.util.Date;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
 import java.util.HashMap;
+import java.util.Locale;
 import java.util.Objects;
 import java.util.Optional;
 import java.util.stream.Stream;
@@ -62,10 +67,6 @@
 
     private static final Intent CELLULAR_NETWORK_SECURITY_SETTINGS_INTENT =
             new Intent("android.settings.CELLULAR_NETWORK_SECURITY");
-    // TODO(b/321999913): direct to a help page URL e.g.
-    //                    new Intent(Intent.ACTION_VIEW, Uri.parse("https://..."));
-    private static final Intent LEARN_MORE_INTENT = new Intent();
-
     static final int NULL_CIPHER_STATE_ENCRYPTED = 0;
     static final int NULL_CIPHER_STATE_NOTIFY_ENCRYPTED = 1;
     static final int NULL_CIPHER_STATE_NOTIFY_NON_ENCRYPTED = 2;
@@ -123,6 +124,13 @@
     }
 
     /**
+     * Clears issue state for the identified subscription
+     */
+    public synchronized  void clearNullCipherState(Context context, int subId) {
+        mNullCipherStates.remove(subId);
+        updateSafetyCenter(context);
+    }
+    /**
      * Enables or disables the identifier disclosure issue and clears any current issues if the
      * enable state is changed.
      */
@@ -209,6 +217,7 @@
         SubscriptionInfoInternal subInfo =
                 mSubscriptionManagerService.getSubscriptionInfoInternal(subId);
         final SafetySourceIssue.Builder builder;
+        final SafetySourceIssue.Notification customNotification;
         switch (state) {
             case NULL_CIPHER_STATE_ENCRYPTED:
                 return Optional.empty();
@@ -216,43 +225,70 @@
                 builder = new SafetySourceIssue.Builder(
                         NULL_CIPHER_ISSUE_NON_ENCRYPTED_ID + "_" + subId,
                         context.getString(
-                            R.string.scNullCipherIssueNonEncryptedTitle, subInfo.getDisplayName()),
-                        context.getString(R.string.scNullCipherIssueNonEncryptedSummary),
+                                R.string.scNullCipherIssueNonEncryptedTitle),
+                        context.getString(
+                              R.string.scNullCipherIssueNonEncryptedSummary,
+                              subInfo.getDisplayName()),
                         SEVERITY_LEVEL_RECOMMENDATION,
                         NULL_CIPHER_ISSUE_NON_ENCRYPTED_ID);
+                customNotification =
+                         new SafetySourceIssue.Notification.Builder(
+                                context.getString(R.string.scNullCipherIssueNonEncryptedTitle),
+                                context.getString(
+                                        R.string.scNullCipherIssueNonEncryptedSummaryNotification,
+                                        subInfo.getDisplayName()))
+                        .build();
                 break;
             case NULL_CIPHER_STATE_NOTIFY_ENCRYPTED:
                 builder = new SafetySourceIssue.Builder(
                         NULL_CIPHER_ISSUE_NON_ENCRYPTED_ID + "_" + subId,
                         context.getString(
-                            R.string.scNullCipherIssueEncryptedTitle, subInfo.getDisplayName()),
-                        context.getString(R.string.scNullCipherIssueEncryptedSummary),
+                                R.string.scNullCipherIssueEncryptedTitle,
+                                subInfo.getDisplayName()),
+                        context.getString(
+                                R.string.scNullCipherIssueEncryptedSummary,
+                                subInfo.getDisplayName()),
                         SEVERITY_LEVEL_INFORMATION,
                         NULL_CIPHER_ISSUE_ENCRYPTED_ID);
+                customNotification =
+                        new SafetySourceIssue.Notification.Builder(
+                                context.getString(
+                                      R.string.scNullCipherIssueEncryptedTitle,
+                                      subInfo.getDisplayName()),
+                                context.getString(
+                                      R.string.scNullCipherIssueEncryptedSummary,
+                                      subInfo.getDisplayName()))
+                        .build();
                 break;
             default:
                 throw new AssertionError();
         }
-
-        return Optional.of(
-                builder
-                    .setNotificationBehavior(SafetySourceIssue.NOTIFICATION_BEHAVIOR_IMMEDIATELY)
-                    .setIssueCategory(SafetySourceIssue.ISSUE_CATEGORY_DEVICE)
-                    .addAction(
+        builder
+                .setNotificationBehavior(
+                        SafetySourceIssue.NOTIFICATION_BEHAVIOR_IMMEDIATELY)
+                .setIssueCategory(SafetySourceIssue.ISSUE_CATEGORY_DATA)
+                .setCustomNotification(customNotification)
+                .addAction(
                         new SafetySourceIssue.Action.Builder(
                                 NULL_CIPHER_ACTION_SETTINGS_ID,
                                 context.getString(R.string.scNullCipherIssueActionSettings),
                                 mSafetyCenterManagerWrapper.getActivityPendingIntent(
                                         context, CELLULAR_NETWORK_SECURITY_SETTINGS_INTENT))
-                            .build())
-                    .addAction(
-                        new SafetySourceIssue.Action.Builder(
-                                NULL_CIPHER_ACTION_LEARN_MORE_ID,
-                                context.getString(R.string.scNullCipherIssueActionLearnMore),
-                                mSafetyCenterManagerWrapper.getActivityPendingIntent(
-                                        context, LEARN_MORE_INTENT))
-                            .build())
-                    .build());
+                                .build());
+
+        Intent learnMoreIntent = getLearnMoreIntent(context);
+        if (learnMoreIntent != null) {
+            builder.addAction(
+                    new SafetySourceIssue.Action.Builder(
+                            NULL_CIPHER_ACTION_LEARN_MORE_ID,
+                            context.getString(
+                                    R.string.scNullCipherIssueActionLearnMore),
+                            mSafetyCenterManagerWrapper.getActivityPendingIntent(
+                                    context, learnMoreIntent))
+                            .build());
+        }
+
+        return Optional.of(builder.build());
     }
 
     /** Builds the identity disclosure issue if it's enabled and there are disclosures to report. */
@@ -264,35 +300,77 @@
 
         SubscriptionInfoInternal subInfo =
                 mSubscriptionManagerService.getSubscriptionInfoInternal(subId);
-        return Optional.of(
+
+        // Notifications have no buttons
+        final SafetySourceIssue.Notification customNotification =
+                new SafetySourceIssue.Notification.Builder(
+                        context.getString(R.string.scIdentifierDisclosureIssueTitle),
+                        context.getString(
+                                R.string.scIdentifierDisclosureIssueSummaryNotification,
+                                getCurrentTime(),
+                                subInfo.getDisplayName())).build();
+        SafetySourceIssue.Builder builder =
                 new SafetySourceIssue.Builder(
                         IDENTIFIER_DISCLOSURE_ISSUE_ID + "_" + subId,
                         context.getString(R.string.scIdentifierDisclosureIssueTitle),
                         context.getString(
                                 R.string.scIdentifierDisclosureIssueSummary,
-                                disclosure.getDisclosureCount(),
-                                Date.from(disclosure.getWindowStart()),
-                                Date.from(disclosure.getWindowEnd()),
+                                getCurrentTime(),
                                 subInfo.getDisplayName()),
                         SEVERITY_LEVEL_RECOMMENDATION,
                         IDENTIFIER_DISCLOSURE_ISSUE_ID)
-                    .setNotificationBehavior(SafetySourceIssue.NOTIFICATION_BEHAVIOR_IMMEDIATELY)
-                    .setIssueCategory(SafetySourceIssue.ISSUE_CATEGORY_DEVICE)
-                    .addAction(
-                        new SafetySourceIssue.Action.Builder(
-                                NULL_CIPHER_ACTION_SETTINGS_ID,
-                                context.getString(R.string.scNullCipherIssueActionSettings),
-                                mSafetyCenterManagerWrapper.getActivityPendingIntent(
-                                        context, CELLULAR_NETWORK_SECURITY_SETTINGS_INTENT))
-                            .build())
-                    .addAction(
-                        new SafetySourceIssue.Action.Builder(
-                                NULL_CIPHER_ACTION_LEARN_MORE_ID,
-                                context.getString(R.string.scNullCipherIssueActionLearnMore),
-                                mSafetyCenterManagerWrapper.getActivityPendingIntent(
-                                        context, LEARN_MORE_INTENT))
-                            .build())
-                .build());
+                        .setNotificationBehavior(
+                                SafetySourceIssue.NOTIFICATION_BEHAVIOR_IMMEDIATELY)
+                        .setIssueCategory(SafetySourceIssue.ISSUE_CATEGORY_DATA)
+                        .setCustomNotification(customNotification)
+                        .addAction(
+                                new SafetySourceIssue.Action.Builder(
+                                        NULL_CIPHER_ACTION_SETTINGS_ID,
+                                        context.getString(
+                                                R.string.scNullCipherIssueActionSettings),
+                                        mSafetyCenterManagerWrapper.getActivityPendingIntent(
+                                                context,
+                                                CELLULAR_NETWORK_SECURITY_SETTINGS_INTENT))
+                                        .build());
+
+        Intent learnMoreIntent = getLearnMoreIntent(context);
+        if (learnMoreIntent != null) {
+            builder.addAction(
+                    new SafetySourceIssue.Action.Builder(
+                            NULL_CIPHER_ACTION_LEARN_MORE_ID,
+                            context.getString(R.string.scNullCipherIssueActionLearnMore),
+                            mSafetyCenterManagerWrapper.getActivityPendingIntent(
+                                    context, learnMoreIntent)).build()
+            );
+        }
+
+        return Optional.of(builder.build());
+    }
+
+    private String getCurrentTime() {
+        String pattern = DateFormat.getBestDateTimePattern(Locale.getDefault(), "hh:mm");
+        return Instant.now().atZone(ZoneId.systemDefault())
+              .format(DateTimeFormatter.ofPattern(pattern)).toString();
+    }
+
+    /**
+     * Return Intent for learn more action, or null if resource associated with the Intent
+     * uri is
+     * missing or empty.
+     */
+    private Intent getLearnMoreIntent(Context context) {
+        String learnMoreUri;
+        try {
+            learnMoreUri = context.getString(R.string.scCellularNetworkSecurityLearnMore);
+        } catch (Resources.NotFoundException e) {
+            return null;
+        }
+
+        if (learnMoreUri.isEmpty()) {
+            return null;
+        }
+
+        return new Intent(Intent.ACTION_VIEW, Uri.parse(learnMoreUri));
     }
 
     /** A wrapper around {@link SafetyCenterManager} that can be instrumented in tests. */
@@ -337,7 +415,7 @@
         private IdentifierDisclosure(int count, Instant start, Instant end) {
             mDisclosureCount = count;
             mWindowStart = start;
-            mWindowEnd  = end;
+            mWindowEnd = end;
         }
 
         private int getDisclosureCount() {
diff --git a/src/java/com/android/internal/telephony/security/NullCipherNotifier.java b/src/java/com/android/internal/telephony/security/NullCipherNotifier.java
index 3ece701..e13c5b0 100644
--- a/src/java/com/android/internal/telephony/security/NullCipherNotifier.java
+++ b/src/java/com/android/internal/telephony/security/NullCipherNotifier.java
@@ -56,6 +56,7 @@
 
     private final CellularNetworkSecuritySafetySource mSafetySource;
     private final HashMap<Integer, SubscriptionState> mSubscriptionState = new HashMap<>();
+    private final HashMap<Integer, Integer> mActiveSubscriptions = new HashMap<>();
 
     private final Object mEnabledLock = new Object();
     @GuardedBy("mEnabledLock")
@@ -90,31 +91,78 @@
      * Adds a security algorithm update. If appropriate, this will trigger a user notification.
      */
     public void onSecurityAlgorithmUpdate(
-            Context context, int subId, SecurityAlgorithmUpdate update) {
+            Context context, int phoneId, int subId, SecurityAlgorithmUpdate update) {
         Rlog.d(TAG, "Security algorithm update: subId = " + subId + " " + update);
 
         if (shouldIgnoreUpdate(update)) {
             return;
         }
 
+        if (!isEnabled()) {
+            Rlog.i(TAG, "Ignoring onSecurityAlgorithmUpdate. Notifier is disabled.");
+            return;
+        }
+
         try {
             mSerializedWorkQueue.execute(() -> {
-                SubscriptionState subState = mSubscriptionState.get(subId);
-                if (subState == null) {
-                    subState = new SubscriptionState();
-                    mSubscriptionState.put(subId, subState);
-                }
+                try {
+                    maybeUpdateSubscriptionMapping(context, phoneId, subId);
+                    SubscriptionState subState = mSubscriptionState.get(subId);
+                    if (subState == null) {
+                        subState = new SubscriptionState();
+                        mSubscriptionState.put(subId, subState);
+                    }
 
-                @CellularNetworkSecuritySafetySource.NullCipherState int nullCipherState =
-                        subState.update(update);
-                mSafetySource.setNullCipherState(context, subId, nullCipherState);
+                    @CellularNetworkSecuritySafetySource.NullCipherState int nullCipherState =
+                            subState.update(update);
+                    mSafetySource.setNullCipherState(context, subId, nullCipherState);
+                } catch (Throwable t) {
+                    Rlog.e(TAG, "Failed to execute onSecurityAlgorithmUpdate " + t.getMessage());
+                }
             });
         } catch (RejectedExecutionException e) {
-            Rlog.e(TAG, "Failed to schedule onEnableNotifier: " + e.getMessage());
+            Rlog.e(TAG, "Failed to schedule onSecurityAlgorithmUpdate: " + e.getMessage());
         }
     }
 
     /**
+     * Set or update the current phoneId to subId mapping. When a new subId is mapped to a phoneId,
+     * we update the safety source to clear state of the old subId.
+     */
+    public void setSubscriptionMapping(Context context, int phoneId, int subId) {
+
+        if (!isEnabled()) {
+            Rlog.i(TAG, "Ignoring setSubscriptionMapping. Notifier is disabled.");
+        }
+
+        try {
+            mSerializedWorkQueue.execute(() -> {
+                try {
+                    maybeUpdateSubscriptionMapping(context, phoneId, subId);
+                } catch (Throwable t) {
+                    Rlog.e(TAG, "Failed to update subId mapping. phoneId: " + phoneId + " subId: "
+                            + subId + ". " + t.getMessage());
+                }
+            });
+
+        } catch (RejectedExecutionException e) {
+            Rlog.e(TAG, "Failed to schedule setSubscriptionMapping: " + e.getMessage());
+        }
+    }
+
+    private void maybeUpdateSubscriptionMapping(Context context, int phoneId, int subId) {
+        final Integer oldSubId = mActiveSubscriptions.put(phoneId, subId);
+        if (oldSubId == null || oldSubId == subId) {
+            return;
+        }
+
+        // Our subId was updated for this phone, we should clear this subId's state.
+        mSubscriptionState.remove(oldSubId);
+        mSafetySource.clearNullCipherState(context, oldSubId);
+    }
+
+
+    /**
      * Enables null cipher notification; {@code onSecurityAlgorithmUpdate} will start handling
      * security algorithm updates and send notifications to the user when required.
      */
@@ -285,7 +333,7 @@
     /** The state of security algorithms for a network connection. */
     private static final class ConnectionState {
         private static final ConnectionState UNKNOWN =
-                 new ConnectionState(SECURITY_ALGORITHM_UNKNOWN, SECURITY_ALGORITHM_UNKNOWN);
+                new ConnectionState(SECURITY_ALGORITHM_UNKNOWN, SECURITY_ALGORITHM_UNKNOWN);
 
         private final @SecurityAlgorithm int mEncryption;
         private final @SecurityAlgorithm int mIntegrity;
diff --git a/src/java/com/android/internal/telephony/subscription/SubscriptionManagerService.java b/src/java/com/android/internal/telephony/subscription/SubscriptionManagerService.java
index 2e0bd31..0e98a9e 100644
--- a/src/java/com/android/internal/telephony/subscription/SubscriptionManagerService.java
+++ b/src/java/com/android/internal/telephony/subscription/SubscriptionManagerService.java
@@ -318,6 +318,9 @@
     @NonNull
     private final int[] mSimState;
 
+    /** Vendor API level from system property. */
+    private final int mVendorApiLevel;
+
     /**
      * {@code true} if a user profile can only see the SIMs associated with it, unless it possesses
      * no SIMs on the device.
@@ -472,6 +475,8 @@
         mEuiccManager = context.getSystemService(EuiccManager.class);
         mAppOpsManager = context.getSystemService(AppOpsManager.class);
         mPackageManager = context.getPackageManager();
+        mVendorApiLevel = SystemProperties.getInt(
+                "ro.vendor.api_level", Build.VERSION.DEVICE_INITIAL_SDK_INT);
 
         mUiccController = UiccController.getInstance();
         mHandler = new Handler(looper);
@@ -938,9 +943,13 @@
      *
      * @param subId Subscription id.
      * @param groupOwner The group owner to assign to the subscription
+     *
+     * @throws SecurityException if the caller does not have required permissions.
      */
+    @Override
+    @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
     public void setGroupOwner(int subId, @NonNull String groupOwner) {
-        // This can throw IllegalArgumentException if the subscription does not exist.
+        enforcePermissions("setGroupOwner", Manifest.permission.MODIFY_PHONE_STATE);
         try {
             mSubscriptionDatabaseManager.setGroupOwner(
                     subId,
@@ -1443,6 +1452,11 @@
             if (mSlotIndexToSubId.containsKey(phoneId)) {
                 markSubscriptionsInactive(phoneId);
             }
+
+            if (Flags.clearCachedImsPhoneNumberWhenDeviceLostImsRegistration()) {
+                // Clear the cached Ims phone number
+                setNumberFromIms(getSubId(phoneId), new String(""));
+            }
         } else if (simState == TelephonyManager.SIM_STATE_NOT_READY) {
             // Check if this is the final state. Only update the subscription if NOT_READY is a
             // final state.
@@ -1455,6 +1469,11 @@
             } else {
                 logl("updateSubscription: UICC app disabled on slot " + phoneId);
                 markSubscriptionsInactive(phoneId);
+
+                if (Flags.clearCachedImsPhoneNumberWhenDeviceLostImsRegistration()) {
+                    // Clear the cached Ims phone number
+                    setNumberFromIms(getSubId(phoneId), new String(""));
+                }
             }
         } else {
             String iccId = getIccId(phoneId);
@@ -1563,12 +1582,6 @@
                         loge("updateSubscription: ICC card is not available.");
                     }
 
-                    if (Flags.clearCachedImsPhoneNumberWhenDeviceLostImsRegistration()) {
-                        // Clear the cached Ims phone number
-                        // before proceeding with Ims Registration
-                        setNumberFromIms(subId, new String(""));
-                    }
-
                     // Attempt to restore SIM specific settings when SIM is loaded.
                     Bundle result = mContext.getContentResolver().call(
                             SubscriptionManager.SIM_INFO_BACKUP_AND_RESTORE_CONTENT_URI,
@@ -1578,6 +1591,10 @@
                             SubscriptionManager.RESTORE_SIM_SPECIFIC_SETTINGS_DATABASE_UPDATED)) {
                         logl("Sim specific settings changed the database.");
                         mSubscriptionDatabaseManager.reloadDatabaseSync();
+                        if (mFeatureFlags.backupAndRestoreForEnable2g()) {
+                            PhoneFactory.getPhone(phoneId)
+                                    .loadAllowedNetworksFromSubscriptionDatabase();
+                        }
                     }
                 }
 
@@ -3762,42 +3779,114 @@
             "carrier privileges",
     })
     public String getPhoneNumber(int subId, @PhoneNumberSource int source,
-            @NonNull String callingPackage, @Nullable String callingFeatureId) {
+            @NonNull String callingPackage, @Nullable String callingFeatureId /* unused */) {
         TelephonyPermissions.enforceAnyPermissionGrantedOrCarrierPrivileges(
                 mContext, subId, Binder.getCallingUid(), "getPhoneNumber",
                 Manifest.permission.READ_PHONE_NUMBERS,
                 Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
-
         enforceTelephonyFeatureWithException(callingPackage, "getPhoneNumber");
 
-        final long identity = Binder.clearCallingIdentity();
+        if (mFeatureFlags.saferGetPhoneNumber()) {
+            checkPhoneNumberSource(source);
+            subId = checkAndGetSubId(subId);
+            if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) return "";
 
-        SubscriptionInfoInternal subInfo = mSubscriptionDatabaseManager
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                return getPhoneNumberFromSourceInternal(subId, source);
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        } else {
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                SubscriptionInfoInternal subInfo = mSubscriptionDatabaseManager
+                        .getSubscriptionInfoInternal(subId);
+
+                if (subInfo == null) {
+                    loge("Invalid sub id " + subId + ", callingPackage=" + callingPackage);
+                    return "";
+                }
+
+                switch(source) {
+                    case SubscriptionManager.PHONE_NUMBER_SOURCE_UICC:
+                        Phone phone = PhoneFactory.getPhone(getSlotIndex(subId));
+                        if (phone != null) {
+                        return TextUtils.emptyIfNull(phone.getLine1Number());
+                        } else {
+                        return subInfo.getNumber();
+                        }
+                    case SubscriptionManager.PHONE_NUMBER_SOURCE_CARRIER:
+                        return subInfo.getNumberFromCarrier();
+                    case SubscriptionManager.PHONE_NUMBER_SOURCE_IMS:
+                        return subInfo.getNumberFromIms();
+                    default:
+                        throw new IllegalArgumentException("Invalid number source " + source);
+                }
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+    }
+
+    /**
+     * Get a resolved subId based on what the user passed in.
+     *
+     * Only use this before clearing the calling binder. Used for compatibility (only).
+     * Do not use this behavior for new methods.
+     *
+     * @param subId the subId passed in by the user.
+     */
+    private int checkAndGetSubId(int subId) {
+        if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+            // for historical reasons, INVALID_SUB_ID fails gracefully
+            return subId;
+        } else if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
+            return getDefaultSubId();
+        } else if (!SubscriptionManager.isValidSubscriptionId(subId)) {
+            throw new IllegalArgumentException("Invalid SubId=" + subId);
+        } else {
+            return subId;
+        }
+    }
+
+    private void checkPhoneNumberSource(int source) {
+        if (source == SubscriptionManager.PHONE_NUMBER_SOURCE_UICC
+                || source == SubscriptionManager.PHONE_NUMBER_SOURCE_CARRIER
+                || source == SubscriptionManager.PHONE_NUMBER_SOURCE_IMS) {
+            return;
+        }
+
+        throw new IllegalArgumentException("Invalid number source " + source);
+    }
+
+    private @NonNull String getPhoneNumberFromSourceInternal(
+            int subId,
+            @PhoneNumberSource int source) {
+
+        final SubscriptionInfoInternal subInfo = mSubscriptionDatabaseManager
                 .getSubscriptionInfoInternal(subId);
 
         if (subInfo == null) {
-            loge("Invalid sub id " + subId + ", callingPackage=" + callingPackage);
+            loge("No SubscriptionInfo found for subId=" + subId);
             return "";
         }
 
-        try {
-            switch(source) {
-                case SubscriptionManager.PHONE_NUMBER_SOURCE_UICC:
-                    Phone phone = PhoneFactory.getPhone(getSlotIndex(subId));
-                    if (phone != null) {
-                        return TextUtils.emptyIfNull(phone.getLine1Number());
-                    } else {
-                        return subInfo.getNumber();
-                    }
-                case SubscriptionManager.PHONE_NUMBER_SOURCE_CARRIER:
-                    return subInfo.getNumberFromCarrier();
-                case SubscriptionManager.PHONE_NUMBER_SOURCE_IMS:
-                    return subInfo.getNumberFromIms();
-                default:
-                    throw new IllegalArgumentException("Invalid number source " + source);
-            }
-        } finally {
-            Binder.restoreCallingIdentity(identity);
+        switch(source) {
+            case SubscriptionManager.PHONE_NUMBER_SOURCE_UICC:
+                final Phone phone = PhoneFactory.getPhone(getSlotIndex(subId));
+                if (phone != null) {
+                    return TextUtils.emptyIfNull(phone.getLine1Number());
+                } else {
+                    return subInfo.getNumber();
+                }
+            case SubscriptionManager.PHONE_NUMBER_SOURCE_CARRIER:
+                return subInfo.getNumberFromCarrier();
+            case SubscriptionManager.PHONE_NUMBER_SOURCE_IMS:
+                return subInfo.getNumberFromIms();
+            default:
+                loge("No SubscriptionInfo found for subId=" + subId);
+                return "";
         }
     }
 
@@ -3833,25 +3922,51 @@
         enforceTelephonyFeatureWithException(callingPackage,
                 "getPhoneNumberFromFirstAvailableSource");
 
-        String numberFromCarrier = getPhoneNumber(subId,
-                SubscriptionManager.PHONE_NUMBER_SOURCE_CARRIER, callingPackage,
-                callingFeatureId);
-        if (!TextUtils.isEmpty(numberFromCarrier)) {
-            return numberFromCarrier;
+        if (mFeatureFlags.saferGetPhoneNumber()) {
+            subId = checkAndGetSubId(subId);
+            if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) return "";
+
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                String number;
+                number = getPhoneNumberFromSourceInternal(
+                        subId,
+                        SubscriptionManager.PHONE_NUMBER_SOURCE_CARRIER);
+                if (!TextUtils.isEmpty(number)) return number;
+
+                number = getPhoneNumberFromSourceInternal(
+                        subId,
+                        SubscriptionManager.PHONE_NUMBER_SOURCE_UICC);
+                if (!TextUtils.isEmpty(number)) return number;
+
+                number = getPhoneNumberFromSourceInternal(
+                        subId,
+                        SubscriptionManager.PHONE_NUMBER_SOURCE_IMS);
+                return TextUtils.emptyIfNull(number);
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        } else {
+            String numberFromCarrier = getPhoneNumber(subId,
+                    SubscriptionManager.PHONE_NUMBER_SOURCE_CARRIER, callingPackage,
+                    callingFeatureId);
+            if (!TextUtils.isEmpty(numberFromCarrier)) {
+                return numberFromCarrier;
+            }
+            String numberFromUicc = getPhoneNumber(
+                    subId, SubscriptionManager.PHONE_NUMBER_SOURCE_UICC, callingPackage,
+                    callingFeatureId);
+            if (!TextUtils.isEmpty(numberFromUicc)) {
+                return numberFromUicc;
+            }
+            String numberFromIms = getPhoneNumber(
+                    subId, SubscriptionManager.PHONE_NUMBER_SOURCE_IMS, callingPackage,
+                    callingFeatureId);
+            if (!TextUtils.isEmpty(numberFromIms)) {
+                return numberFromIms;
+            }
+            return "";
         }
-        String numberFromUicc = getPhoneNumber(
-                subId, SubscriptionManager.PHONE_NUMBER_SOURCE_UICC, callingPackage,
-                callingFeatureId);
-        if (!TextUtils.isEmpty(numberFromUicc)) {
-            return numberFromUicc;
-        }
-        String numberFromIms = getPhoneNumber(
-                subId, SubscriptionManager.PHONE_NUMBER_SOURCE_IMS, callingPackage,
-                callingFeatureId);
-        if (!TextUtils.isEmpty(numberFromIms)) {
-            return numberFromIms;
-        }
-        return "";
     }
 
     /**
@@ -4199,6 +4314,10 @@
                     SubscriptionManager.RESTORE_SIM_SPECIFIC_SETTINGS_DATABASE_UPDATED)) {
                 logl("Sim specific settings changed the database.");
                 mSubscriptionDatabaseManager.reloadDatabaseSync();
+                if (mFeatureFlags.backupAndRestoreForEnable2g()) {
+                    Arrays.stream(PhoneFactory.getPhones())
+                            .forEach(Phone::loadAllowedNetworksFromSubscriptionDatabase);
+                }
             }
         } finally {
             Binder.restoreCallingIdentity(token);
@@ -4532,7 +4651,11 @@
 
         if (!mFeatureFlags.enforceTelephonyFeatureMappingForPublicApis()
                 || !CompatChanges.isChangeEnabled(ENABLE_FEATURE_MAPPING, callingPackage,
-                Binder.getCallingUserHandle())) {
+                Binder.getCallingUserHandle())
+                || mVendorApiLevel < Build.VERSION_CODES.VANILLA_ICE_CREAM) {
+            // Skip to check associated telephony feature,
+            // if compatibility change is not enabled for the current process or
+            // the SDK version of vendor partition is less than Android V.
             return;
         }
 
@@ -4605,6 +4728,11 @@
                     "config_satellite_sim_spn_identifier", "");
         }
         log("isSatelliteSpn: overlaySpn=" + overlaySpn + ", spn=" + spn);
+
+        if (TextUtils.isEmpty(spn) || TextUtils.isEmpty(overlaySpn)) {
+            return false;
+        }
+
         return TextUtils.equals(spn, overlaySpn);
     }
 
diff --git a/src/java/com/android/internal/telephony/uicc/UiccController.java b/src/java/com/android/internal/telephony/uicc/UiccController.java
index 0459bf6..84e84d9 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccController.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccController.java
@@ -1799,6 +1799,8 @@
         }
         pw.decreaseIndent();
         pw.println();
+        mCarrierServiceBindHelper.dump(fd, pw, args);
+        pw.println();
         pw.println("sLocalLog= ");
         pw.increaseIndent();
         mPinStorage.dump(fd, pw, args);
diff --git a/src/java/com/android/internal/telephony/uicc/UiccPort.java b/src/java/com/android/internal/telephony/uicc/UiccPort.java
index fd8f1c4..9e341ef 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccPort.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccPort.java
@@ -31,6 +31,7 @@
 import com.android.internal.telephony.TelephonyComponentFactory;
 import com.android.internal.telephony.flags.FeatureFlags;
 import com.android.internal.telephony.flags.FeatureFlagsImpl;
+import com.android.internal.telephony.flags.Flags;
 import com.android.telephony.Rlog;
 
 import java.io.FileDescriptor;
@@ -57,8 +58,9 @@
     private int mPortIdx;
     private int mPhysicalSlotIndex;
 
-    // The list of the opened logical channel record. The channels will be closed by us when
-    // detecting client died without closing them in advance.
+    // The list of the opened logical channel record.
+    // The channels will be closed by us when detecting client died without closing them in advance.
+    // The same lock should be used to protect both access of the list and the individual record.
     @GuardedBy("mOpenChannelRecords")
     private final List<OpenLogicalChannelRecord> mOpenChannelRecords = new ArrayList<>();
 
@@ -101,11 +103,13 @@
             }
             mUiccProfile = null;
         }
+        cleanupOpenLogicalChannelRecordsIfNeeded();
     }
 
     @Override
     protected void finalize() {
         if (DBG) log("UiccPort finalized");
+        cleanupOpenLogicalChannelRecordsIfNeeded();
     }
 
     /**
@@ -389,8 +393,10 @@
     public void onLogicalChannelOpened(@NonNull IccLogicalChannelRequest request) {
         OpenLogicalChannelRecord record = new OpenLogicalChannelRecord(request);
         try {
-            request.binder.linkToDeath(record, /*flags=*/ 0);
-            addOpenLogicalChannelRecord(record);
+            synchronized (mOpenChannelRecords) {
+                request.binder.linkToDeath(record, /*flags=*/ 0);
+                mOpenChannelRecords.add(record);
+            }
             if (DBG) log("onLogicalChannelOpened: monitoring client " + record);
         } catch (RemoteException | NullPointerException ex) {
             loge("IccOpenLogicChannel client has died, clean up manually");
@@ -406,11 +412,13 @@
      */
     public void onLogicalChannelClosed(int channelId) {
         OpenLogicalChannelRecord record = getOpenLogicalChannelRecord(channelId);
-        if (record != null && record.mRequest != null && record.mRequest.binder != null) {
-            if (DBG) log("onLogicalChannelClosed: stop monitoring client " + record);
-            record.mRequest.binder.unlinkToDeath(record, /*flags=*/ 0);
-            removeOpenLogicalChannelRecord(record);
-            record.mRequest.binder = null;
+        synchronized (mOpenChannelRecords) {
+            if (record != null && record.mRequest != null && record.mRequest.binder != null) {
+                if (DBG) log("onLogicalChannelClosed: stop monitoring client " + record);
+                record.mRequest.binder.unlinkToDeath(record, /*flags=*/ 0);
+                record.mRequest.binder = null;
+                mOpenChannelRecords.remove(record);
+            }
         }
     }
 
@@ -428,15 +436,21 @@
         return null;
     }
 
-    private void addOpenLogicalChannelRecord(OpenLogicalChannelRecord record) {
-        synchronized (mOpenChannelRecords) {
-            mOpenChannelRecords.add(record);
-        }
-    }
-
-    private void removeOpenLogicalChannelRecord(OpenLogicalChannelRecord record) {
-        synchronized (mOpenChannelRecords) {
-            mOpenChannelRecords.remove(record);
+    /**
+     * Clean up records when logical channels underneath have been released, in cases like SIM
+     * removal or modem reset. The obsoleted records may trigger a redundant release of logical
+     * channel that may have been assigned to other client.
+     */
+    private void cleanupOpenLogicalChannelRecordsIfNeeded() {
+        if (Flags.cleanupOpenLogicalChannelRecordOnDispose()) {
+            synchronized (mOpenChannelRecords) {
+                for (OpenLogicalChannelRecord record : mOpenChannelRecords) {
+                    if (DBG) log("Clean up " + record);
+                    record.mRequest.binder.unlinkToDeath(record, /*flags=*/ 0);
+                    record.mRequest.binder = null;
+                }
+                mOpenChannelRecords.clear();
+            }
         }
     }
 
diff --git a/src/java/com/android/internal/telephony/uicc/UiccProfile.java b/src/java/com/android/internal/telephony/uicc/UiccProfile.java
index 0457971..a22f075 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccProfile.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccProfile.java
@@ -1290,6 +1290,11 @@
             return -1;
         }
 
+        if (mUiccApplications[index] == null) {
+            loge("App index " + index + " is invalid");
+            return -1;
+        }
+
         if (mUiccApplications[index].getType() != expectedAppType
                 && mUiccApplications[index].getType() != altExpectedAppType) {
             loge("App index " + index + " is invalid since it's not "
diff --git a/tests/telephonytests/Android.bp b/tests/telephonytests/Android.bp
index 8547581..a6b47cd 100644
--- a/tests/telephonytests/Android.bp
+++ b/tests/telephonytests/Android.bp
@@ -1,4 +1,5 @@
 package {
+    default_team: "trendy_team_fwk_telephony",
     // See: http://go/android-license-faq
     default_applicable_licenses: [
         "frameworks_opt_telephony_tests_telephonytests_license",
diff --git a/tests/telephonytests/src/com/android/internal/telephony/CarrierKeyDownloadMgrTest.java b/tests/telephonytests/src/com/android/internal/telephony/CarrierKeyDownloadMgrTest.java
index 07482e0..e60e95b 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/CarrierKeyDownloadMgrTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/CarrierKeyDownloadMgrTest.java
@@ -22,6 +22,7 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.anyBoolean;
 import static org.mockito.Mockito.anyInt;
 import static org.mockito.Mockito.times;
@@ -31,6 +32,8 @@
 import android.app.DownloadManager;
 import android.content.Context;
 import android.content.Intent;
+import android.net.ConnectivityManager;
+import android.net.Network;
 import android.os.PersistableBundle;
 import android.telephony.CarrierConfigManager;
 import android.telephony.ImsiEncryptionInfo;
@@ -38,18 +41,18 @@
 import android.telephony.TelephonyManager;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
+import android.text.TextUtils;
 import android.util.Pair;
 
 import androidx.test.filters.SmallTest;
 
-import com.android.internal.telephony.flags.FeatureFlags;
-
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
 import org.mockito.ArgumentMatchers;
+import org.mockito.Mock;
 import org.mockito.Mockito;
 
 import java.security.PublicKey;
@@ -91,7 +94,7 @@
                     + "\"public-key\": \"" + CERT + "\"}]}";
 
     private CarrierConfigManager.CarrierConfigChangeListener mCarrierConfigChangeListener;
-    private FeatureFlags mFeatureFlags;
+
     @Before
     public void setUp() throws Exception {
         logd("CarrierActionAgentTest +Setup!");
@@ -99,15 +102,18 @@
         mBundle = mContextFixture.getCarrierConfigBundle();
         when(mCarrierConfigManager.getConfigForSubId(anyInt(), any())).thenReturn(mBundle);
         when(mUserManager.isUserUnlocked()).thenReturn(true);
+        when(mKeyguardManager.isDeviceLocked()).thenReturn(false);
         // Capture listener to emulate the carrier config change notification used later
         ArgumentCaptor<CarrierConfigManager.CarrierConfigChangeListener> listenerArgumentCaptor =
                 ArgumentCaptor.forClass(CarrierConfigManager.CarrierConfigChangeListener.class);
-        mFeatureFlags = Mockito.mock(FeatureFlags.class);
-        mCarrierKeyDM = new CarrierKeyDownloadManager(mPhone, mFeatureFlags);
+        mCarrierKeyDM = new CarrierKeyDownloadManager(mPhone);
         verify(mCarrierConfigManager).registerCarrierConfigChangeListener(any(),
                 listenerArgumentCaptor.capture());
         mCarrierConfigChangeListener = listenerArgumentCaptor.getAllValues().get(0);
-
+        mConnectivityManager = (ConnectivityManager) mPhone.getContext().getSystemService(
+                Context.CONNECTIVITY_SERVICE);
+        Network network = Mockito.mock(Network.class);
+        when(mConnectivityManager.getActiveNetwork()).thenReturn(network);
         processAllMessages();
         logd("CarrierActionAgentTest -Setup!");
     }
@@ -331,8 +337,8 @@
         expectedCal.add(Calendar.DATE, 1);
         String dateExpected = dt.format(expectedCal.getTime());
 
-        when(mTelephonyManager.getSimOperator(anyInt())).thenReturn("310260");
-        when(mTelephonyManager.getSimCarrierId()).thenReturn(1);
+        when(mPhone.getOperatorNumeric()).thenReturn("310260");
+        when(mPhone.getCarrierId()).thenReturn(1);
         Intent mIntent = new Intent(DownloadManager.ACTION_DOWNLOAD_COMPLETE);
         mIntent.putExtra(DownloadManager.EXTRA_DOWNLOAD_ID, downloadId);
         mContext.sendBroadcast(mIntent);
@@ -355,11 +361,11 @@
         bundle.putInt(CarrierConfigManager.IMSI_KEY_AVAILABILITY_INT, 3);
         bundle.putString(CarrierConfigManager.IMSI_KEY_DOWNLOAD_URL_STRING, mURL);
 
-        when(mTelephonyManager.getSimOperator(anyInt())).thenReturn("310260");
-        when(mTelephonyManager.getSimCarrierId()).thenReturn(1);
+        when(mPhone.getOperatorNumeric()).thenReturn("310260");
+        when(mPhone.getCarrierId()).thenReturn(1);
         mCarrierConfigChangeListener.onCarrierConfigChanged(0 /* slotIndex */,
                 SubscriptionManager.INVALID_SUBSCRIPTION_ID,
-                TelephonyManager.UNKNOWN_CARRIER_ID, TelephonyManager.UNKNOWN_CARRIER_ID);
+                1, TelephonyManager.UNKNOWN_CARRIER_ID);
         processAllMessages();
         assertEquals("310260", mCarrierKeyDM.mMccMncForDownload);
         assertEquals(1, mCarrierKeyDM.mCarrierId);
@@ -369,6 +375,7 @@
     @SmallTest
     public void testCarrierConfigChangedWithUserLocked() {
         when(mUserManager.isUserUnlocked()).thenReturn(false);
+        when(mKeyguardManager.isDeviceLocked()).thenReturn(true);
         CarrierConfigManager carrierConfigManager = (CarrierConfigManager)
                 mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
         int slotId = mPhone.getPhoneId();
@@ -376,14 +383,14 @@
         bundle.putInt(CarrierConfigManager.IMSI_KEY_AVAILABILITY_INT, 3);
         bundle.putString(CarrierConfigManager.IMSI_KEY_DOWNLOAD_URL_STRING, mURL);
 
-        when(mTelephonyManager.getSimOperator(anyInt())).thenReturn("310260");
-        when(mTelephonyManager.getSimCarrierId()).thenReturn(1);
+        when(mPhone.getOperatorNumeric()).thenReturn("310260");
+        when(mPhone.getCarrierId()).thenReturn(1);
         mCarrierConfigChangeListener.onCarrierConfigChanged(0 /* slotIndex */,
                 SubscriptionManager.INVALID_SUBSCRIPTION_ID,
-                TelephonyManager.UNKNOWN_CARRIER_ID, TelephonyManager.UNKNOWN_CARRIER_ID);
+                1, TelephonyManager.UNKNOWN_CARRIER_ID);
         processAllMessages();
-        assertNull(mCarrierKeyDM.mMccMncForDownload);
-        assertEquals(0, mCarrierKeyDM.mCarrierId);
+        assertEquals("310260", mCarrierKeyDM.mMccMncForDownload);
+        assertEquals(1, mCarrierKeyDM.mCarrierId);
     }
 
     @Test
@@ -391,6 +398,7 @@
     public void testUserLockedAfterCarrierConfigChanged() {
         // User is locked at beginning
         when(mUserManager.isUserUnlocked()).thenReturn(false);
+        when(mKeyguardManager.isDeviceLocked()).thenReturn(true);
         CarrierConfigManager carrierConfigManager = (CarrierConfigManager)
                 mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
         int slotId = mPhone.getPhoneId();
@@ -399,17 +407,18 @@
         bundle.putString(CarrierConfigManager.IMSI_KEY_DOWNLOAD_URL_STRING, mURL);
 
         // Carrier config change received
-        when(mTelephonyManager.getSimOperator(anyInt())).thenReturn("310260");
-        when(mTelephonyManager.getSimCarrierId()).thenReturn(1);
+        when(mPhone.getOperatorNumeric()).thenReturn("310260");
+        when(mPhone.getCarrierId()).thenReturn(1);
         mCarrierConfigChangeListener.onCarrierConfigChanged(0 /* slotIndex */,
                 SubscriptionManager.INVALID_SUBSCRIPTION_ID,
-                TelephonyManager.UNKNOWN_CARRIER_ID, TelephonyManager.UNKNOWN_CARRIER_ID);
+                1, TelephonyManager.UNKNOWN_CARRIER_ID);
         processAllMessages();
 
         // User unlocked event received
-        Intent mIntent = new Intent(Intent.ACTION_USER_UNLOCKED);
+        Intent mIntent = new Intent(Intent.ACTION_USER_PRESENT);
         mContext.sendBroadcast(mIntent);
         when(mUserManager.isUserUnlocked()).thenReturn(true);
+        when(mKeyguardManager.isDeviceLocked()).thenReturn(false);
         processAllMessages();
 
         assertEquals("310260", mCarrierKeyDM.mMccMncForDownload);
@@ -432,11 +441,11 @@
 
         mCarrierConfigChangeListener.onCarrierConfigChanged(0 /* slotIndex */,
                 SubscriptionManager.INVALID_SUBSCRIPTION_ID,
-                TelephonyManager.UNKNOWN_CARRIER_ID, TelephonyManager.UNKNOWN_CARRIER_ID);
+                1, TelephonyManager.UNKNOWN_CARRIER_ID);
         processAllMessages();
-        assertNull(mCarrierKeyDM.mMccMncForDownload);
+        assertTrue(TextUtils.isEmpty(mCarrierKeyDM.mMccMncForDownload));
 
-        verify(mPhone).deleteCarrierInfoForImsiEncryption(0);
+        verify(mPhone).deleteCarrierInfoForImsiEncryption(1, "");
     }
 
     /**
@@ -453,8 +462,8 @@
         bundle.putInt(CarrierConfigManager.IMSI_KEY_AVAILABILITY_INT, 3);
         bundle.putString(CarrierConfigManager.IMSI_KEY_DOWNLOAD_URL_STRING, mURL);
 
-        when(mTelephonyManager.getSimOperator(anyInt())).thenReturn("310260");
-        when(mTelephonyManager.getSimCarrierId()).thenReturn(1);
+        when(mPhone.getOperatorNumeric()).thenReturn("310260");
+        when(mPhone.getCarrierId()).thenReturn(1);
         Intent mIntent = new Intent("com.android.internal.telephony.carrier_key_download_alarm");
         mIntent.putExtra(SubscriptionManager.EXTRA_SLOT_INDEX, slotIndex);
         mContext.sendBroadcast(mIntent);
@@ -493,4 +502,4 @@
         assertEquals(CarrierKeyDownloadManager
                 .cleanCertString("Comments before" + CERT + "Comments after"), CERT);
     }
-}
+}
\ No newline at end of file
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java b/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java
index bef8944..552b5a7 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java
@@ -362,6 +362,8 @@
                 return Context.ALARM_SERVICE;
             } else if (serviceClass == DevicePolicyManager.class) {
                 return Context.DEVICE_POLICY_SERVICE;
+            } else if (serviceClass == NotificationManager.class) {
+                return Context.NOTIFICATION_SERVICE;
             }
             return super.getSystemServiceName(serviceClass);
         }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java b/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java
index 614846b..ba08f8b 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java
@@ -3019,7 +3019,63 @@
         processAllMessages();
 
         verify(mNullCipherNotifier, times(1))
-                .onSecurityAlgorithmUpdate(eq(mContext), eq(0), eq(update));
+                .onSecurityAlgorithmUpdate(eq(mContext), eq(0), eq(0), eq(update));
+    }
+
+    @Test
+    public void testUpdateNullCipherNotifier_flagDisabled() {
+        when(mFeatureFlags.enableModemCipherTransparencyUnsolEvents()).thenReturn(false);
+        Phone phoneUT = makeNewPhoneUT();
+        phoneUT.sendMessage(
+                mPhoneUT.obtainMessage(
+                        Phone.EVENT_SUBSCRIPTIONS_CHANGED,
+                        new AsyncResult(null, null, null)));
+        processAllMessages();
+
+        verify(mNullCipherNotifier, never()).setSubscriptionMapping(any(), anyInt(), anyInt());
+    }
+
+    @Test
+    public void testUpdateNullCipherNotifier_activeSubscription() {
+        when(mFeatureFlags.enableModemCipherTransparencyUnsolEvents()).thenReturn(true);
+
+        int subId = 10;
+        SubscriptionInfoInternal subInfo = new SubscriptionInfoInternal.Builder().setSimSlotIndex(
+                0).setId(subId).build();
+        when(mSubscriptionManagerService.getSubscriptionInfoInternal(subId)).thenReturn(
+                subInfo);
+        doReturn(subId).when(mSubscriptionManagerService)
+                .getSubId(anyInt());
+        Phone phoneUT = makeNewPhoneUT();
+
+        phoneUT.sendMessage(
+                mPhoneUT.obtainMessage(
+                        Phone.EVENT_SUBSCRIPTIONS_CHANGED,
+                        new AsyncResult(null, null, null)));
+        processAllMessages();
+
+        verify(mNullCipherNotifier, times(1)).setSubscriptionMapping(eq(mContext), eq(0), eq(10));
+    }
+
+    @Test
+    public void testUpdateNullCipherNotifier_inactiveSubscription() {
+        when(mFeatureFlags.enableModemCipherTransparencyUnsolEvents()).thenReturn(true);
+        int subId = 1;
+        SubscriptionInfoInternal subInfo = new SubscriptionInfoInternal.Builder().setSimSlotIndex(
+                -1).setId(subId).build();
+        when(mSubscriptionManagerService.getSubscriptionInfoInternal(subId)).thenReturn(
+                subInfo);
+        doReturn(subId).when(mSubscriptionManagerService)
+                .getSubId(anyInt());
+        Phone phoneUT = makeNewPhoneUT();
+
+        phoneUT.sendMessage(
+                mPhoneUT.obtainMessage(
+                        Phone.EVENT_SUBSCRIPTIONS_CHANGED,
+                        new AsyncResult(null, null, null)));
+        processAllMessages();
+
+        verify(mNullCipherNotifier, times(1)).setSubscriptionMapping(eq(mContext), eq(0), eq(-1));
     }
 
     @Test
diff --git a/tests/telephonytests/src/com/android/internal/telephony/MultiSimSettingControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/MultiSimSettingControllerTest.java
index d1ab64d..a6b2000 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/MultiSimSettingControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/MultiSimSettingControllerTest.java
@@ -911,6 +911,28 @@
         // This time user data should be disabled on phone1.
         verify(mDataSettingsManagerMock2).setDataEnabled(
                 TelephonyManager.DATA_ENABLED_REASON_USER, false, PHONE_PACKAGE);
+
+        // Remove and insert back SIM before it's loaded.
+        clearInvocations(mSubscriptionManagerService);
+        markSubscriptionInactive(1/*subid*/);
+        sendCarrierConfigChanged(0/*phoneid*/, SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+
+        verify(mSubscriptionManagerService).setDefaultDataSubId(
+                SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+
+        // insert it back, but carrier config not loaded yet
+        clearInvocations(mSubscriptionManagerService);
+        setSimSlotIndex(1/*subid*/, 0/*phoneid*/);
+        mMultiSimSettingControllerUT.notifySubscriptionInfoChanged();
+        sendCarrierConfigChanged(0/*phoneid*/, SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+
+        verify(mSubscriptionManagerService, never()).setDefaultDataSubId(
+                SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+
+        // carrier config loaded
+        clearInvocations(mContext);
+        sendCarrierConfigChanged(0/*phoneid*/, 1/*subid*/);
+        verify(mContext).sendBroadcast(any());
     }
 
     @Test
diff --git a/tests/telephonytests/src/com/android/internal/telephony/PhoneConfigurationManagerTest.java b/tests/telephonytests/src/com/android/internal/telephony/PhoneConfigurationManagerTest.java
index 6743d1c..0e04aff 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/PhoneConfigurationManagerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/PhoneConfigurationManagerTest.java
@@ -57,6 +57,7 @@
 import org.mockito.Mockito;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
@@ -157,6 +158,7 @@
         init(1);
         assertEquals(PhoneCapability.DEFAULT_SSSS_CAPABILITY, mPcm.getStaticPhoneCapability());
 
+        mPcm.updateRadioCapability();
         setAndVerifyStaticCapability(PhoneCapability.DEFAULT_DSDS_CAPABILITY);
     }
 
@@ -171,6 +173,7 @@
                 .setMaxActiveVoiceSubscriptions(2)
                 .build();
 
+        mPcm.updateRadioCapability();
         ArgumentCaptor<Message> captor = ArgumentCaptor.forClass(Message.class);
         verify(mMockRadioConfig).getPhoneCapability(captor.capture());
         Message msg = captor.getValue();
@@ -188,7 +191,7 @@
         init(2);
         mPcm.updateSimultaneousCallingSupport();
 
-        int[] enabledLogicalSlots = {0, 1};
+        List<Integer> enabledLogicalSlots = Arrays.asList(0, 1);
         ArgumentCaptor<Message> captor = ArgumentCaptor.forClass(Message.class);
         verify(mMockRadioConfig).updateSimultaneousCallingSupport(captor.capture());
         Message msg = captor.getValue();
@@ -209,7 +212,7 @@
         mPcm.updateSimultaneousCallingSupport();
 
         // Have the modem send invalid phone slots -1 and 5:
-        int[] invalidEnabledLogicalSlots = {-1, 5};
+        List<Integer> invalidEnabledLogicalSlots = Arrays.asList(-1, 5);
         ArgumentCaptor<Message> captor = ArgumentCaptor.forClass(Message.class);
         verify(mMockRadioConfig).updateSimultaneousCallingSupport(captor.capture());
         Message msg = captor.getValue();
@@ -245,14 +248,14 @@
         mPcm.registerForSimultaneousCellularCallingSlotsChanged(newSlots ->
                 cachedSimultaneousCallingSlots[0] = newSlots);
 
-        mPcm.getStaticPhoneCapability();
+        mPcm.updateRadioCapability();
         setAndVerifyStaticCapability(STATIC_DSDA_CAPABILITY);
         ArgumentCaptor<SubscriptionManager.OnSubscriptionsChangedListener> cBCaptor =
                 ArgumentCaptor.forClass(SubscriptionManager.OnSubscriptionsChangedListener.class);
         verify(mMockRegistryManager).addOnSubscriptionsChangedListener(cBCaptor.capture(), any());
         processAllMessages();
 
-        int[] enabledLogicalSlots = {0, 1};
+        List<Integer> enabledLogicalSlots = Arrays.asList(0, 1);
         HashSet<Integer> expectedSlots = new HashSet<>(2);
         for (int i : enabledLogicalSlots) {
             expectedSlots.add(i);
@@ -296,7 +299,7 @@
 
         // Simultaneous calling enabled
         mPcm.updateSimultaneousCallingSupport();
-        int[] enabledLogicalSlots = {0, 1};
+        List<Integer> enabledLogicalSlots = Arrays.asList(0, 1);
         ArgumentCaptor<Message> captor = ArgumentCaptor.forClass(Message.class);
         verify(mMockRadioConfig).updateSimultaneousCallingSupport(captor.capture());
         Message msg = captor.getValue();
@@ -318,7 +321,7 @@
 
         // Simultaneous Calling Disabled
         mPcm.updateSimultaneousCallingSupport();
-        int[] disabled = {};
+        List<Integer> disabled = List.of();
         captor = ArgumentCaptor.forClass(Message.class);
         verify(mMockRadioConfig, times(2)).updateSimultaneousCallingSupport(captor.capture());
         msg = captor.getAllValues().get(1);
@@ -346,13 +349,13 @@
 
         // Set the capability to DSDA mode to register listener, which will also trigger
         // simultaneous calling evaluation
-        mPcm.getCurrentPhoneCapability();
+        mPcm.updateRadioCapability();
         setAndVerifyStaticCapability(STATIC_DSDA_CAPABILITY);
         ArgumentCaptor<SubscriptionManager.OnSubscriptionsChangedListener> cBCaptor =
                 ArgumentCaptor.forClass(SubscriptionManager.OnSubscriptionsChangedListener.class);
         verify(mMockRegistryManager).addOnSubscriptionsChangedListener(cBCaptor.capture(), any());
 
-        int[] enabledLogicalSlots = {0, 1};
+        List<Integer> enabledLogicalSlots = Arrays.asList(0, 1);
         ArgumentCaptor<Message> captor = ArgumentCaptor.forClass(Message.class);
         verify(mMockRadioConfig).updateSimultaneousCallingSupport(captor.capture());
         Message msg = captor.getValue();
diff --git a/tests/telephonytests/src/com/android/internal/telephony/PhoneNumberUtilsTest.java b/tests/telephonytests/src/com/android/internal/telephony/PhoneNumberUtilsTest.java
index 1c4e43d..bf9ced3 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/PhoneNumberUtilsTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/PhoneNumberUtilsTest.java
@@ -21,7 +21,10 @@
 import static junit.framework.Assert.assertNull;
 import static junit.framework.Assert.assertTrue;
 
+import com.android.internal.telephony.flags.Flags;
+
 import android.net.Uri;
+import android.platform.test.flag.junit.SetFlagsRule;
 import android.telephony.PhoneNumberUtils;
 import android.text.SpannableStringBuilder;
 import android.text.style.TtsSpan;
@@ -32,6 +35,7 @@
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Ignore;
+import org.junit.Rule;
 import org.junit.Test;
 
 public class PhoneNumberUtilsTest {
@@ -40,6 +44,8 @@
 
     private int mOldMinMatch;
 
+    @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
     @Before
     public void setUp() throws Exception {
         mOldMinMatch = PhoneNumberUtils.getMinMatchForTest();
@@ -613,6 +619,60 @@
         assertEquals("+1 650-555-1212", PhoneNumberUtils.formatNumber("+16505551212", "jp"));
     }
 
+    /**
+     * Test to ensure that when international calls to Singapore are being placed the country
+     * code is present with and without the feature flag enabled.
+     */
+    @SmallTest
+    @Test
+    public void testFormatSingaporeInternational() {
+        // Disable feature flag.
+        mSetFlagsRule.disableFlags(Flags.FLAG_REMOVE_COUNTRY_CODE_FROM_LOCAL_SINGAPORE_CALLS);
+
+        // International call from a US iso
+        assertEquals("+65 6521 8000", PhoneNumberUtils.formatNumber("+6565218000", "US"));
+
+        // Lowercase country iso
+        assertEquals("+65 6521 8000", PhoneNumberUtils.formatNumber("+6565218000", "us"));
+
+        // Enable feature flag
+        mSetFlagsRule.enableFlags(Flags.FLAG_REMOVE_COUNTRY_CODE_FROM_LOCAL_SINGAPORE_CALLS);
+
+        // Internal call from a US iso
+        assertEquals("+65 6521 8000", PhoneNumberUtils.formatNumber("+6565218000", "US"));
+
+        // Lowercase country iso
+        assertEquals("+65 6521 8000", PhoneNumberUtils.formatNumber("+6565218000", "us"));
+        mSetFlagsRule.disableFlags(Flags.FLAG_REMOVE_COUNTRY_CODE_FROM_LOCAL_SINGAPORE_CALLS);
+    }
+
+    /**
+     * Test to ensure that when local calls from Singaporean numbers are being placed to other
+     * Singaporean numbers the country code +65 is not being shown.
+     */
+    @SmallTest
+    @Test
+    public void testFormatSingaporeNational() {
+        // Disable feature flag.
+        mSetFlagsRule.disableFlags(Flags.FLAG_REMOVE_COUNTRY_CODE_FROM_LOCAL_SINGAPORE_CALLS);
+
+        // Local call from a Singaporean number to a Singaporean number
+        assertEquals("+65 6521 8000", PhoneNumberUtils.formatNumber("+6565218000", "SG"));
+
+        // Lowercase country iso.
+        assertEquals("+65 6521 8000", PhoneNumberUtils.formatNumber("+6565218000", "sg"));
+
+        // Enable feature flag.
+        mSetFlagsRule.enableFlags(Flags.FLAG_REMOVE_COUNTRY_CODE_FROM_LOCAL_SINGAPORE_CALLS);
+
+        // Local call from a Singaporean number to a Singaporean number.
+        assertEquals("6521 8000", PhoneNumberUtils.formatNumber("+6565218000", "SG"));
+
+        // Lowercase country iso.
+        assertEquals("6521 8000", PhoneNumberUtils.formatNumber("+6565218000", "sg"));
+        mSetFlagsRule.disableFlags(Flags.FLAG_REMOVE_COUNTRY_CODE_FROM_LOCAL_SINGAPORE_CALLS);
+    }
+
     @SmallTest
     @Test
     public void testFormatNumber_LeadingStarAndHash() {
diff --git a/tests/telephonytests/src/com/android/internal/telephony/PhoneSubInfoControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/PhoneSubInfoControllerTest.java
index 1af4a76..d8005e8 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/PhoneSubInfoControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/PhoneSubInfoControllerTest.java
@@ -43,7 +43,8 @@
 import android.os.Build;
 import android.os.RemoteException;
 import android.telephony.TelephonyManager;
-import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.filters.SmallTest;
 
 import com.android.internal.telephony.uicc.IsimUiccRecords;
 import com.android.internal.telephony.uicc.SIMRecords;
@@ -230,7 +231,12 @@
     @Test
     @SmallTest
     @EnableCompatChanges({TelephonyManager.ENABLE_FEATURE_MAPPING})
-    public void testGetNai_EnabledEnforceTelephonyFeatureMappingForPublicApis() {
+    public void testGetNai_EnabledEnforceTelephonyFeatureMappingForPublicApis() throws Exception {
+        // Replace field to set SDK version of vendor partition to Android V
+        int vendorApiLevel = Build.VERSION_CODES.VANILLA_ICE_CREAM;
+        replaceInstance(PhoneSubInfoController.class, "mVendorApiLevel",
+                mPhoneSubInfoControllerUT, vendorApiLevel);
+
         // FeatureFlags enabled, System has required feature
         doReturn(true).when(mFeatureFlags).enforceTelephonyFeatureMappingForPublicApis();
         doReturn(true).when(mPm).hasSystemFeature(
diff --git a/tests/telephonytests/src/com/android/internal/telephony/RILTest.java b/tests/telephonytests/src/com/android/internal/telephony/RILTest.java
index bfe9649..88c5389 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/RILTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/RILTest.java
@@ -330,7 +330,7 @@
         proxies.put(HAL_SERVICE_MODEM, mRadioModemProxy);
         mRILInstance = new RIL(context,
                 RadioAccessFamily.getRafFromNetworkType(RILConstants.PREFERRED_NETWORK_MODE),
-                Phone.PREFERRED_CDMA_SUBSCRIPTION, 0, proxies);
+                Phone.PREFERRED_CDMA_SUBSCRIPTION, 0, proxies, mFeatureFlags);
         mRILUnderTest = spy(mRILInstance);
         doReturn(mRadioProxy).when(mRILUnderTest).getRadioProxy();
         doReturn(mDataProxy).when(mRILUnderTest).getRadioServiceProxy(eq(RadioDataProxy.class));
@@ -354,6 +354,8 @@
             replaceInstance(RIL.class, "mHalVersion", mRILUnderTest, mHalVersionV14);
         } catch (Exception e) {
         }
+
+        doReturn(true).when(mFeatureFlags).combineRilDeathHandle();
     }
 
     @After
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java
index 121136d..fd99ad0 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java
@@ -2605,10 +2605,15 @@
         waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
 
         // PS WLAN
+        int wlanRat = TelephonyManager.NETWORK_TYPE_UNKNOWN;
+        if (wlanState == NetworkRegistrationInfo.REGISTRATION_STATE_HOME
+                || wlanState == NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING) {
+            wlanRat = TelephonyManager.NETWORK_TYPE_IWLAN;
+        }
         NetworkRegistrationInfo dataIwlanResult = new NetworkRegistrationInfo(
                 NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WLAN,
-                wlanState, TelephonyManager.NETWORK_TYPE_IWLAN, 0, false,
-                null, null, "", 1, false, false, false, lteVopsSupportInfo);
+                wlanState, wlanRat, 0, false, null, null,
+                "", 1, false, false, false, lteVopsSupportInfo);
         sst.sendMessage(sst.obtainMessage(
                 ServiceStateTracker.EVENT_POLL_STATE_PS_IWLAN_REGISTRATION,
                 new AsyncResult(sst.mPollingContext, dataIwlanResult, null)));
@@ -2645,6 +2650,47 @@
         verify(mLocaleTracker).updateOperatorNumeric(eq(""));
     }
 
+    /**
+     * Ensure that LocaleTracker is not updated with mcc when only IWLAN is not in-service and the
+     * ims registration status is connected over iwlan,
+     */
+    @Test
+    public void testLocaleTrackerUpdateWithImsRegistrationTechIwlan() {
+        // Start state: Cell data only LTE + IWLAN
+        final String[] OpNamesResult = new String[] { "carrier long", "carrier", "310310" };
+        // Clear invocations for mLocaleTracker as precondition before test case execution & as part
+        // test setup
+        Mockito.clearInvocations(mLocaleTracker);
+
+        // Both Cellular abd Iwlan is in-service.
+        changeRegStateWithIwlanOperatorNumeric(
+                NetworkRegistrationInfo.REGISTRATION_STATE_HOME,
+                TelephonyManager.NETWORK_TYPE_LTE,
+                NetworkRegistrationInfo.REGISTRATION_STATE_HOME, OpNamesResult, true);
+        verify(mLocaleTracker).updateOperatorNumeric(eq(OpNamesResult[2]));
+
+        // Test with Cellular as NOT_REG
+        changeRegStateWithIwlanOperatorNumeric(
+                NetworkRegistrationInfo.REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING,
+                TelephonyManager.NETWORK_TYPE_UNKNOWN,
+                NetworkRegistrationInfo.REGISTRATION_STATE_HOME, OpNamesResult, true);
+        /* cellId based mccmnc */
+        verify(mLocaleTracker).updateOperatorNumeric(eq("00101"));
+
+        // IMS over Iwlan is registered.
+        doReturn(mImsPhone)
+                .when(mPhone).getImsPhone();
+        doReturn(ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN)
+                .when(mImsPhone).getImsRegistrationTech();
+
+        // Test with Iwlan as NOT_REG
+        changeRegStateWithIwlanOperatorNumeric(
+                NetworkRegistrationInfo.REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING,
+                TelephonyManager.NETWORK_TYPE_UNKNOWN,
+                NetworkRegistrationInfo.REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING,
+                OpNamesResult, false);
+        verify(mLocaleTracker).updateOperatorNumeric(eq(""));
+    }
     @Test
     public void testGetServiceProviderNameWithBrandOverride() {
         String brandOverride = "spn from brand override";
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SimultaneousCallingTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/SimultaneousCallingTrackerTest.java
index d3fde34..879b184 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/SimultaneousCallingTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/SimultaneousCallingTrackerTest.java
@@ -172,7 +172,7 @@
     }
 
     private void setAndVerifyStaticCapability(PhoneCapability capability) {
-        mPcm.getCurrentPhoneCapability();
+        mPcm.updateRadioCapability();
         ArgumentCaptor<Message> captor = ArgumentCaptor.forClass(Message.class);
         verify(mMockRadioConfig).getPhoneCapability(captor.capture());
         Message msg = captor.getValue();
@@ -185,7 +185,8 @@
 
     }
 
-    private void setAndVerifySlotsSupportingSimultaneousCellularCalling(int[] enabledLogicalSlots) {
+    private void setAndVerifySlotsSupportingSimultaneousCellularCalling(
+            List<Integer> enabledLogicalSlots) {
         ArgumentCaptor<Message> captor = ArgumentCaptor.forClass(Message.class);
         verify(mMockRadioConfig).updateSimultaneousCallingSupport(captor.capture());
         Message msg = captor.getValue();
@@ -241,7 +242,7 @@
         init(2);
         setAndVerifyStaticCapability(STATIC_DSDA_CAPABILITY);
 
-        int[] enabledLogicalSlots = {0, 1};
+        List<Integer> enabledLogicalSlots = Arrays.asList(0, 1);
         setAndVerifySlotsSupportingSimultaneousCellularCalling(enabledLogicalSlots);
 
         // Trigger onSubscriptionsChanged by updating the subscription ID of a phone slot:
@@ -264,7 +265,7 @@
         setAndVerifyStaticCapability(STATIC_DSDA_CAPABILITY);
 
         // Have the modem inform telephony that only phone slot 0 supports DSDA:
-        int[] enabledLogicalSlots = {0};
+        List<Integer> enabledLogicalSlots = List.of(0);
         setAndVerifySlotsSupportingSimultaneousCellularCalling(enabledLogicalSlots);
 
         // Trigger onSubscriptionsChanged by updating the subscription ID of a phone slot:
@@ -286,7 +287,7 @@
         init(2);
         setAndVerifyStaticCapability(STATIC_DSDA_CAPABILITY);
 
-        int[] enabledLogicalSlots = {0, 1};
+        List<Integer> enabledLogicalSlots = Arrays.asList(0, 1);
         setAndVerifySlotsSupportingSimultaneousCellularCalling(enabledLogicalSlots);
 
         // Trigger onSubscriptionsChanged by updating the subscription ID of a phone slot:
@@ -309,7 +310,7 @@
         init(2);
         setAndVerifyStaticCapability(STATIC_DSDA_CAPABILITY);
 
-        int[] enabledLogicalSlots = {0, 1};
+        List<Integer> enabledLogicalSlots = Arrays.asList(0, 1);
         setAndVerifySlotsSupportingSimultaneousCellularCalling(enabledLogicalSlots);
 
         // Trigger onSubscriptionsChanged by updating the subscription ID of a phone slot:
@@ -356,7 +357,7 @@
         init(2);
         setAndVerifyStaticCapability(STATIC_DSDA_CAPABILITY);
 
-        int[] enabledLogicalSlots = {0, 1};
+        List<Integer> enabledLogicalSlots = Arrays.asList(0, 1);
         setAndVerifySlotsSupportingSimultaneousCellularCalling(enabledLogicalSlots);
 
         // Trigger onSubscriptionsChanged by updating the subscription ID of a phone slot:
@@ -384,7 +385,7 @@
         init(2);
         setAndVerifyStaticCapability(STATIC_DSDA_CAPABILITY);
 
-        int[] enabledLogicalSlots = {0};
+        List<Integer> enabledLogicalSlots = List.of(0);
         setAndVerifySlotsSupportingSimultaneousCellularCalling(enabledLogicalSlots);
 
         // Trigger onSubscriptionsChanged by updating the subscription ID of a phone slot:
@@ -416,7 +417,7 @@
         init(2);
         setAndVerifyStaticCapability(STATIC_DSDA_CAPABILITY);
 
-        int[] enabledLogicalSlots = {0, 1};
+        List<Integer> enabledLogicalSlots = Arrays.asList(0, 1);
         setAndVerifySlotsSupportingSimultaneousCellularCalling(enabledLogicalSlots);
 
         // Trigger onSubscriptionsChanged by updating the subscription ID of a phone slot:
@@ -449,7 +450,7 @@
         init(3);
         setAndVerifyStaticCapability(STATIC_DSDA_CAPABILITY);
 
-        int[] enabledLogicalSlots = {0, 1};
+        List<Integer> enabledLogicalSlots = Arrays.asList(0, 1);
         setAndVerifySlotsSupportingSimultaneousCellularCalling(enabledLogicalSlots);
 
         // Trigger onSubscriptionsChanged by updating the subscription ID of a phone slot:
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SmsControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/SmsControllerTest.java
index f8d1bec..06dbd0b 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/SmsControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/SmsControllerTest.java
@@ -31,6 +31,7 @@
 
 import android.compat.testing.PlatformCompatChangeRule;
 import android.content.pm.PackageManager;
+import android.os.Build;
 import android.telephony.TelephonyManager;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
@@ -295,11 +296,15 @@
 
     @Test
     @EnableCompatChanges({TelephonyManager.ENABLE_FEATURE_MAPPING})
-    public void sendTextForSubscriberTestEnabledTelephonyFeature() {
+    public void sendTextForSubscriberTestEnabledTelephonyFeature() throws Exception {
         int subId = 1;
         doReturn(true).when(mSubscriptionManager)
                 .isSubscriptionAssociatedWithUser(eq(subId), any());
 
+        // Replace field to set SDK version of vendor partition to Android V
+        int vendorApiLevel = Build.VERSION_CODES.VANILLA_ICE_CREAM;
+        replaceInstance(SmsController.class, "mVendorApiLevel", mSmsControllerUT, vendorApiLevel);
+
         // Feature enabled, device does not have required telephony feature.
         doReturn(true).when(mFeatureFlags).enforceTelephonyFeatureMappingForPublicApis();
         doReturn(false).when(mPackageManager).hasSystemFeature(
diff --git a/tests/telephonytests/src/com/android/internal/telephony/TelephonyPermissionsTest.java b/tests/telephonytests/src/com/android/internal/telephony/TelephonyPermissionsTest.java
index 6369825..d4717dd 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/TelephonyPermissionsTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/TelephonyPermissionsTest.java
@@ -73,7 +73,6 @@
 
     // Mocked classes
     private Context mMockContext;
-    private FeatureFlags mMockFeatureFlag;
     private AppOpsManager mMockAppOps;
     private SubscriptionManager mMockSubscriptionManager;
     private ITelephony mMockTelephony;
@@ -92,7 +91,6 @@
     @Before
     public void setUp() throws Exception {
         mMockContext = mock(Context.class);
-        mMockFeatureFlag = mock(FeatureFlags.class);
         mMockAppOps = mock(AppOpsManager.class);
         mMockSubscriptionManager = mock(SubscriptionManager.class);
         mMockTelephony = mock(ITelephony.class);
@@ -135,13 +133,11 @@
         when(mMockContext.checkPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
                 PID, UID)).thenReturn(PackageManager.PERMISSION_DENIED);
 
-        replaceFeatureFlag(mMockFeatureFlag);
         setTelephonyMockAsService();
     }
 
     @After
     public void tearDown() throws Exception {
-        replaceFeatureFlag(mRealFeatureFlagToBeRestored);
         mMockContentResolver = null;
         mFakeSettingsConfigProvider = null;
         mRealFeatureFlagToBeRestored = null;
@@ -554,9 +550,7 @@
     }
 
     @Test
-    public void testCheckSubscriptionAssociatedWithUser_badSub_flag_enabled() {
-        doReturn(true).when(mMockFeatureFlag).rejectBadSubIdInteraction();
-
+    public void testCheckSubscriptionAssociatedWithUser() {
         doThrow(new IllegalArgumentException("has no records on device"))
                 .when(mMockSubscriptionManager).isSubscriptionAssociatedWithUser(SUB_ID,
                         UserHandle.SYSTEM);
@@ -564,19 +558,6 @@
                 UserHandle.SYSTEM));
     }
 
-    @Test
-    public void testCheckSubscriptionAssociatedWithUser_badSub_flag_disabled() {
-        doReturn(false).when(mMockFeatureFlag).rejectBadSubIdInteraction();
-
-        doThrow(new IllegalArgumentException("No records found for sub"))
-                .when(mMockSubscriptionManager).isSubscriptionAssociatedWithUser(SUB_ID,
-                        UserHandle.SYSTEM);
-        assertTrue(TelephonyPermissions.checkSubscriptionAssociatedWithUser(mMockContext, SUB_ID,
-                UserHandle.SYSTEM));
-        assertTrue(TelephonyPermissions.checkSubscriptionAssociatedWithUser(mMockContext,
-                SubscriptionManager.INVALID_SUBSCRIPTION_ID, UserHandle.SYSTEM));
-    }
-
     // Put mMockTelephony into service cache so that TELEPHONY_SUPPLIER will get it.
     private void setTelephonyMockAsService() throws Exception {
         when(mMockTelephonyBinder.queryLocalInterface(anyString())).thenReturn(mMockTelephony);
@@ -666,13 +647,4 @@
         field.setAccessible(true);
         field.set(providerHolder, iContentProvider);
     }
-
-    private synchronized void replaceFeatureFlag(final FeatureFlags newValue)
-            throws Exception {
-        Field field = TelephonyPermissions.class.getDeclaredField("sFeatureFlag");
-        field.setAccessible(true);
-
-        mRealFeatureFlagToBeRestored = (FeatureFlags) field.get(null);
-        field.set(null, newValue);
-    }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java b/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
index 673acbc..38b4f77 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
@@ -35,6 +35,7 @@
 import android.app.IActivityManager;
 import android.app.KeyguardManager;
 import android.app.PropertyInvalidatedCache;
+import android.app.admin.DevicePolicyManager;
 import android.app.usage.NetworkStatsManager;
 import android.content.ContentProvider;
 import android.content.ContentResolver;
@@ -304,6 +305,7 @@
     protected AppOpsManager mAppOpsManager;
     protected CarrierConfigManager mCarrierConfigManager;
     protected UserManager mUserManager;
+    protected DevicePolicyManager mDevicePolicyManager;
     protected KeyguardManager mKeyguardManager;
     protected VcnManager mVcnManager;
     protected NetworkPolicyManager mNetworkPolicyManager;
@@ -630,6 +632,8 @@
         mCarrierConfigManager =
                 (CarrierConfigManager) mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
         mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+        mDevicePolicyManager = (DevicePolicyManager) mContext.getSystemService(
+                Context.DEVICE_POLICY_SERVICE);
         mKeyguardManager = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
         mVcnManager = mContext.getSystemService(VcnManager.class);
         mNetworkPolicyManager = mContext.getSystemService(NetworkPolicyManager.class);
@@ -685,7 +689,7 @@
         doReturn(mEriManager).when(mTelephonyComponentFactory)
                 .makeEriManager(nullable(Phone.class), anyInt());
         doReturn(mLinkBandwidthEstimator).when(mTelephonyComponentFactory)
-                .makeLinkBandwidthEstimator(nullable(Phone.class));
+                .makeLinkBandwidthEstimator(nullable(Phone.class), any(Looper.class));
         doReturn(mDataProfileManager).when(mTelephonyComponentFactory)
                 .makeDataProfileManager(any(Phone.class), any(DataNetworkController.class),
                         any(DataServiceManager.class), any(Looper.class),
diff --git a/tests/telephonytests/src/com/android/internal/telephony/data/AccessNetworksManagerTest.java b/tests/telephonytests/src/com/android/internal/telephony/data/AccessNetworksManagerTest.java
index d1e5066..e45023c 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/AccessNetworksManagerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/AccessNetworksManagerTest.java
@@ -356,7 +356,7 @@
 
         mAccessNetworksManager.registerCallback(mMockedCallback);
 
-        mQnsCallback.onReconnectQualifedNetworkType(ApnSetting.TYPE_IMS | ApnSetting.TYPE_MMS,
+        mQnsCallback.onReconnectQualifiedNetworkType(ApnSetting.TYPE_IMS | ApnSetting.TYPE_MMS,
                 AccessNetworkType.IWLAN);
         processAllMessages();
 
@@ -381,7 +381,7 @@
     @Test
     public void testCallbackForReconnectQualifiedNetworkTypeWithFlagDisabled() throws Exception {
         when(mFeatureFlags.reconnectQualifiedNetwork()).thenReturn(false);
-        mQnsCallback.onReconnectQualifedNetworkType(ApnSetting.TYPE_IMS | ApnSetting.TYPE_MMS,
+        mQnsCallback.onReconnectQualifiedNetworkType(ApnSetting.TYPE_IMS | ApnSetting.TYPE_MMS,
                 AccessNetworkType.IWLAN);
         processAllMessages();
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkControllerTest.java
index 7f42ad0..f418523 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkControllerTest.java
@@ -51,6 +51,7 @@
 import android.net.NetworkCapabilities;
 import android.net.NetworkPolicyManager;
 import android.net.NetworkRequest;
+import android.net.Uri;
 import android.net.vcn.VcnManager.VcnNetworkPolicyChangeListener;
 import android.net.vcn.VcnNetworkPolicyResult;
 import android.os.AsyncResult;
@@ -394,17 +395,6 @@
                             "PRIORITIZE_LATENCY", 1).getBytes()))
             .build();
 
-    private final DataProfile mMmsOnWlanDataProfile = new DataProfile.Builder()
-            .setApnSetting(new ApnSetting.Builder()
-                    .setEntryName("mms_wlan")
-                    .setApnName("mms_wlan")
-                    .setApnTypeBitmask(ApnSetting.TYPE_MMS)
-                    .setCarrierEnabled(true)
-                    .setNetworkTypeBitmask((int) TelephonyManager.NETWORK_TYPE_BITMASK_IWLAN)
-                    .build())
-            .setPreferred(false)
-            .build();
-
     private final DataProfile mNtnDataProfile = new DataProfile.Builder()
             .setApnSetting(new ApnSetting.Builder()
                     .setEntryName("ntn")
@@ -856,6 +846,8 @@
         mContextFixture.putStringArrayResource(com.android.internal.R.array
                 .config_force_cellular_transport_capabilities,
                 new String[] {"ims", "eims", "xcap"});
+        mContextFixture.putIntResource(com.android.internal.R.integer
+                .config_reevaluate_bootstrap_sim_data_usage_millis, 60000);
     }
 
     @Before
@@ -1001,7 +993,7 @@
         List<DataProfile> profiles = List.of(mGeneralPurposeDataProfile,
                 mGeneralPurposeDataProfileAlternative, mImsCellularDataProfile,
                 mImsIwlanDataProfile, mEmergencyDataProfile, mFotaDataProfile,
-                mTetheringDataProfile, mMmsOnWlanDataProfile, mLowLatencyDataProfile,
+                mTetheringDataProfile, mLowLatencyDataProfile,
                 mNtnDataProfile, mEsimBootstrapDataProfile,
                 mEsimBootstrapImsProfile, mEsimBootstrapRcsInfraStructureProfile);
 
@@ -3505,9 +3497,6 @@
         // Verify retry is cleared on this network
         assertThat(mDataNetworkControllerUT.getDataRetryManager()
                 .isAnyHandoverRetryScheduled(network)).isFalse();
-        // Verify the data profile is still throttled
-        assertThat(mDataNetworkControllerUT.getDataRetryManager().isDataProfileThrottled(
-                network.getDataProfile(), AccessNetworkConstants.TRANSPORT_TYPE_WLAN)).isTrue();
     }
 
     @Test
@@ -4174,6 +4163,41 @@
     }
 
     @Test
+    public void testNetworkValidationStatusChangeCallback() throws Exception {
+        // Request not restricted network
+        TelephonyNetworkRequest request = createNetworkRequest(
+                NetworkCapabilities.NET_CAPABILITY_INTERNET);
+        mDataNetworkControllerUT.addNetworkRequest(request);
+        processAllMessages();
+        TelephonyNetworkAgent agent = getPrivateField(getDataNetworks().get(0), "mNetworkAgent",
+                TelephonyNetworkAgent.class);
+        agent.onValidationStatus(1/*status*/, Uri.EMPTY);
+        processAllMessages();
+
+        // Verify notify
+        verify(mMockedDataNetworkControllerCallback)
+                .onInternetDataNetworkValidationStatusChanged(1);
+
+        // Request restricted network
+        mDataNetworkControllerUT.removeNetworkRequest(request);
+        getDataNetworks().get(0).tearDown(DataNetwork.TEAR_DOWN_REASON_NONE);
+        mDataNetworkControllerUT.getDataSettingsManager().setDataEnabled(0, false, "");
+        processAllMessages();
+        clearInvocations(mMockedDataNetworkControllerCallback);
+        mDataNetworkControllerUT.addNetworkRequest(createNetworkRequest(
+                true, NetworkCapabilities.NET_CAPABILITY_INTERNET));
+        processAllMessages();
+        agent = getPrivateField(getDataNetworks().get(0), "mNetworkAgent",
+                TelephonyNetworkAgent.class);
+        agent.onValidationStatus(1/*status*/, Uri.EMPTY);
+        processAllMessages();
+
+        // Verify not notified
+        verify(mMockedDataNetworkControllerCallback, never())
+                .onInternetDataNetworkValidationStatusChanged(anyInt());
+    }
+
+    @Test
     public void testImsGracefulTearDown() throws Exception {
         setImsRegistered(true);
         setRcsRegistered(true);
@@ -5027,34 +5051,6 @@
     }
 
     @Test
-    public void testAllowBringUpWithDifferentDataProfileForWlan() throws Exception {
-        // Mock MMS preferred on WLAN
-        doReturn(AccessNetworkConstants.TRANSPORT_TYPE_WLAN).when(mAccessNetworksManager)
-                .getPreferredTransportByNetworkCapability(NetworkCapabilities.NET_CAPABILITY_MMS);
-
-        // Setup a default cellular network that's capable of MMS
-        mDataNetworkControllerUT.addNetworkRequest(
-                createNetworkRequest(NetworkCapabilities.NET_CAPABILITY_INTERNET));
-        mDataNetworkControllerUT.addNetworkRequest(
-                createNetworkRequest(NetworkCapabilities.NET_CAPABILITY_IMS));
-        processAllMessages();
-        verifyConnectedNetworkHasDataProfile(mGeneralPurposeDataProfile);
-
-        // Mock the designated MMS profile when WLAN is preferred
-        doReturn(mMmsOnWlanDataProfile).when(mDataProfileManager).getDataProfileForNetworkRequest(
-                any(TelephonyNetworkRequest.class), eq(TelephonyManager.NETWORK_TYPE_IWLAN),
-                anyBoolean(), anyBoolean(), anyBoolean());
-        setSuccessfulSetupDataResponse(mMockedWlanDataServiceManager,
-                createDataCallResponse(2, DataCallResponse.LINK_STATUS_ACTIVE));
-
-        // Verify the designated MMS profile is used to satisfy MMS request
-        mDataNetworkControllerUT.addNetworkRequest(
-                createNetworkRequest(NetworkCapabilities.NET_CAPABILITY_MMS));
-        processAllMessages();
-        verifyConnectedNetworkHasDataProfile(mMmsOnWlanDataProfile);
-    }
-
-    @Test
     public void testNonTerrestrialNetwork() throws Exception {
         mIsNonTerrestrialNetwork = true;
         serviceStateChanged(TelephonyManager.NETWORK_TYPE_LTE,
@@ -5379,4 +5375,23 @@
         verify(mMockedWwanDataServiceManager, never()).deactivateDataCall(anyInt(),
                 eq(DataService.REQUEST_REASON_NORMAL), any(Message.class));
     }
+
+    @Test
+    public void testRadioOffTearDown() throws Exception  {
+        testSetupDataNetwork();
+        doReturn(true).when(mSST).isPendingRadioPowerOffAfterDataOff();
+        mDataNetworkControllerUT.tearDownAllDataNetworks(
+                DataNetwork.TEAR_DOWN_REASON_AIRPLANE_MODE_ON);
+        processAllMessages();
+        verifyAllDataDisconnected();
+        verify(mMockedDataNetworkControllerCallback).onAnyDataNetworkExistingChanged(eq(false));
+
+        clearInvocations(mMockedDataNetworkControllerCallback);
+        mDataNetworkControllerUT.addNetworkRequest(
+                createNetworkRequest(NetworkCapabilities.NET_CAPABILITY_INTERNET));
+        processAllMessages();
+        verifyAllDataDisconnected();
+        verify(mMockedDataNetworkControllerCallback, never()).onAnyDataNetworkExistingChanged(
+                anyBoolean());
+    }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkTest.java b/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkTest.java
index 19502f5..d5a52ea 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkTest.java
@@ -256,12 +256,18 @@
 
     private void setSuccessfulSetupDataResponse(DataServiceManager dsm, int cid,
             List<TrafficDescriptor> tds, Qos defaultQos) {
+        setSuccessfulSetupDataResponse(dsm, cid, tds, defaultQos,
+                PreciseDataConnectionState.NETWORK_VALIDATION_UNSUPPORTED);
+    }
+
+    private void setSuccessfulSetupDataResponse(DataServiceManager dsm, int cid,
+            List<TrafficDescriptor> tds, Qos defaultQos, int netwokrValidationResult) {
         doAnswer(invocation -> {
             final Message msg = (Message) invocation.getArguments()[10];
 
             DataCallResponse response = createDataCallResponse(
                     cid, DataCallResponse.LINK_STATUS_ACTIVE, tds, defaultQos,
-                    PreciseDataConnectionState.NETWORK_VALIDATION_UNSUPPORTED);
+                    netwokrValidationResult);
             msg.getData().putParcelable("data_call_response", response);
             msg.arg1 = DataServiceCallback.RESULT_SUCCESS;
             msg.sendToTarget();
@@ -654,6 +660,19 @@
         // The final network should not have NOT_SUSPENDED because the device is OOS.
         assertThat(mDataNetworkUT.getNetworkCapabilities().hasCapability(
                 NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED)).isFalse();
+
+        // Verify recreation triggers notifyDataConnection with new network agent Id.
+        ArgumentCaptor<PreciseDataConnectionState> pdcsCaptor =
+                ArgumentCaptor.forClass(PreciseDataConnectionState.class);
+
+        // 4 times connecting, connected, data state changed, re-create network agent
+        verify(mPhone, times(4)).notifyDataConnection(pdcsCaptor.capture());
+        List<PreciseDataConnectionState> pdcsList = pdcsCaptor.getAllValues();
+        assertThat(pdcsList.get(0).getState()).isEqualTo(TelephonyManager.DATA_CONNECTING);
+        assertThat(pdcsList.get(1).getState()).isEqualTo(TelephonyManager.DATA_CONNECTED);
+        assertThat(pdcsList.get(2).getState()).isEqualTo(TelephonyManager.DATA_SUSPENDED);
+        assertThat(pdcsList.get(3).getNetId())
+                .isNotEqualTo(pdcsList.get(2).getNetId());
     }
 
     @Test
@@ -1233,6 +1252,29 @@
     }
 
     @Test
+    public void testNetworkRequestDetachedBeforeConnected() throws Exception {
+        doReturn(true).when(mFeatureFlags).keepEmptyRequestsNetwork();
+        NetworkRequestList networkRequestList = new NetworkRequestList(new TelephonyNetworkRequest(
+                new NetworkRequest.Builder()
+                        .addCapability(NetworkCapabilities.NET_CAPABILITY_IMS)
+                        .build(), mPhone, mFeatureFlags));
+        mDataNetworkUT = new DataNetwork(mPhone, mFeatureFlags, Looper.myLooper(),
+                mDataServiceManagers, mImsDataProfile, networkRequestList,
+                AccessNetworkConstants.TRANSPORT_TYPE_WWAN, DataAllowedReason.NORMAL,
+                mDataNetworkCallback);
+        replaceInstance(DataNetwork.class, "mDataCallSessionStats",
+                mDataNetworkUT, mDataCallSessionStats);
+
+        // Remove the request before the network connect.
+        mDataNetworkUT.detachNetworkRequest(networkRequestList.getFirst(), false/*should retry*/);
+        setSuccessfulSetupDataResponse(mMockedWwanDataServiceManager, 123);
+        processAllMessages();
+
+        // Verify the request proceed to connected state even without requests.
+        assertThat(mDataNetworkUT.isConnected()).isTrue();
+    }
+
+    @Test
     public void testAdminAndOwnerUids() throws Exception {
         doReturn(ADMIN_UID2).when(mCarrierPrivilegesTracker).getCarrierServicePackageUid();
         setupDataNetwork();
@@ -2245,6 +2287,51 @@
                 .isEqualTo(PreciseDataConnectionState.NETWORK_VALIDATION_UNSUPPORTED);
     }
 
+    @Test
+    public void testHandoverWithSuccessNetworkValidation_FlagEnabled() throws Exception {
+        when(mFeatureFlags.networkValidation()).thenReturn(true);
+
+        setupDataNetwork();
+
+        setSuccessfulSetupDataResponse(
+                mMockedWlanDataServiceManager, 456, Collections.emptyList(), null,
+                PreciseDataConnectionState.NETWORK_VALIDATION_SUCCESS);
+        TelephonyNetworkAgent mockNetworkAgent = Mockito.mock(TelephonyNetworkAgent.class);
+        replaceInstance(DataNetwork.class, "mNetworkAgent",
+                mDataNetworkUT, mockNetworkAgent);
+        // Now handover to IWLAN
+        mDataNetworkUT.startHandover(AccessNetworkConstants.TRANSPORT_TYPE_WLAN, null);
+        processAllMessages();
+
+        verify(mMockedWwanDataServiceManager).startHandover(eq(123), any(Message.class));
+        verify(mLinkBandwidthEstimator).unregisterCallback(any(
+                LinkBandwidthEstimatorCallback.class));
+        assertThat(mDataNetworkUT.getTransport())
+                .isEqualTo(AccessNetworkConstants.TRANSPORT_TYPE_WLAN);
+        assertThat(mDataNetworkUT.getId()).isEqualTo(456);
+        verify(mDataNetworkCallback).onHandoverSucceeded(eq(mDataNetworkUT));
+
+        ArgumentCaptor<PreciseDataConnectionState> pdcsCaptor =
+                ArgumentCaptor.forClass(PreciseDataConnectionState.class);
+        verify(mPhone, times(4)).notifyDataConnection(pdcsCaptor.capture());
+        List<PreciseDataConnectionState> pdcsList = pdcsCaptor.getAllValues();
+
+        assertThat(pdcsList).hasSize(4);
+        assertThat(pdcsList.get(0).getState()).isEqualTo(TelephonyManager.DATA_CONNECTING);
+        assertThat(pdcsList.get(1).getState()).isEqualTo(TelephonyManager.DATA_CONNECTED);
+        assertThat(pdcsList.get(1).getNetworkValidationStatus())
+                .isEqualTo(PreciseDataConnectionState.NETWORK_VALIDATION_UNSUPPORTED);
+        assertThat(pdcsList.get(2).getState())
+                .isEqualTo(TelephonyManager.DATA_HANDOVER_IN_PROGRESS);
+        assertThat(pdcsList.get(2).getNetworkValidationStatus())
+                .isEqualTo(PreciseDataConnectionState.NETWORK_VALIDATION_UNSUPPORTED);
+        assertThat(pdcsList.get(3).getState()).isEqualTo(TelephonyManager.DATA_CONNECTED);
+        assertThat(pdcsList.get(3).getNetworkValidationStatus())
+                .isEqualTo(PreciseDataConnectionState.NETWORK_VALIDATION_SUCCESS);
+
+        verify(mDataNetworkCallback).onHandoverSucceeded(eq(mDataNetworkUT));
+    }
+
     private void setupIwlanDataNetwork() throws Exception {
         // setup iwlan data network over ims
         doReturn(mIwlanNetworkRegistrationInfo).when(mServiceState).getNetworkRegistrationInfo(
@@ -2332,6 +2419,32 @@
         // Now QNS prefers MMS on IWLAN
         doReturn(AccessNetworkConstants.TRANSPORT_TYPE_WLAN).when(mAccessNetworksManager)
                 .getPreferredTransportByNetworkCapability(NetworkCapabilities.NET_CAPABILITY_MMS);
+        // Verify an mms apn that shares the same apn name doesn't count as an alternative.
+        ApnSetting mmsApnWithSameApn = new ApnSetting.Builder()
+                .setId(2164)
+                .setOperatorNumeric("12345")
+                .setEntryName("fake_mms_apn")
+                .setApnName("fake_apn")
+                .setApnTypeBitmask(ApnSetting.TYPE_MMS)
+                .setProtocol(ApnSetting.PROTOCOL_IPV6)
+                .setRoamingProtocol(ApnSetting.PROTOCOL_IP)
+                .setCarrierEnabled(true)
+                .setNetworkTypeBitmask((int) TelephonyManager.NETWORK_TYPE_BITMASK_IWLAN)
+                .build();
+        doReturn(new DataProfile.Builder().setApnSetting(mmsApnWithSameApn)
+                .setTrafficDescriptor(new TrafficDescriptor("fake_apn", null))
+                .build()).when(mDataProfileManager).getDataProfileForNetworkRequest(
+                any(TelephonyNetworkRequest.class),
+                eq(TelephonyManager.NETWORK_TYPE_IWLAN), eq(false), eq(false), eq(false));
+        accessNetworksManagerCallbackArgumentCaptor.getValue()
+                .onPreferredTransportChanged(NetworkCapabilities.NET_CAPABILITY_MMS, false);
+        processAllMessages();
+
+        // Check if MMS capability remains intact.
+        assertThat(mDataNetworkUT.getNetworkCapabilities()
+                .hasCapability(NetworkCapabilities.NET_CAPABILITY_MMS)).isTrue();
+
+        // Verify MMS capability is removed if using a valid MMS alternative APN.
         doReturn(mMmsDataProfile).when(mDataProfileManager).getDataProfileForNetworkRequest(
                 any(TelephonyNetworkRequest.class),
                     eq(TelephonyManager.NETWORK_TYPE_IWLAN), eq(false), eq(false), eq(false));
diff --git a/tests/telephonytests/src/com/android/internal/telephony/data/DataProfileManagerTest.java b/tests/telephonytests/src/com/android/internal/telephony/data/DataProfileManagerTest.java
index 3d6b4f4..30ce46f 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/DataProfileManagerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/DataProfileManagerTest.java
@@ -1119,6 +1119,97 @@
     }
 
     @Test
+    public void testSetPreferredDataProfileBySubscriptionId() {
+        // NetworkRequest.
+        TelephonyNetworkRequest tnr = new TelephonyNetworkRequest(
+                new NetworkRequest.Builder()
+                        .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+                        .build(), mPhone, mFeatureFlags);
+        // DataNetwork for internet
+        DataNetwork internetNetwork = Mockito.mock(DataNetwork.class);
+
+        // Step 1. With sudId 1, connect to internet and set as prefer APN.
+        // Test for SubId 1.
+        doReturn(1).when(mPhone).getSubId();
+
+        // Sim Loaded and loads profiles.
+        changeSimStateTo(TelephonyManager.SIM_STATE_LOADED);
+        mDataProfileManagerUT.obtainMessage(2 /*EVENT_APN_DATABASE_CHANGED*/).sendToTarget();
+        processAllMessages();
+
+        // Find DataProfile based on network request
+        DataProfile dataProfile = mDataProfileManagerUT.getDataProfileForNetworkRequest(
+                tnr, TelephonyManager.NETWORK_TYPE_LTE, false, false, false);
+        assertThat(dataProfile.getApnSetting().getApnName()).isEqualTo(GENERAL_PURPOSE_APN);
+        dataProfile.setLastSetupTimestamp(SystemClock.elapsedRealtime());
+
+        // Connected to internet. At this time, This test expects that dataProfile is stored in
+        // mLastInternetDataProfiles for subid 1.
+        doReturn(dataProfile).when(internetNetwork).getDataProfile();
+        doReturn(new DataNetworkController.NetworkRequestList(List.of(tnr)))
+                .when(internetNetwork).getAttachedNetworkRequestList();
+        mDataNetworkControllerCallback.onConnectedInternetDataNetworksChanged(
+                Set.of(internetNetwork));
+        processAllMessages();
+
+        // After internet connected, preferred APN should be set
+        assertThat(mDataProfileManagerUT.isDataProfilePreferred(dataProfile)).isTrue();
+
+
+        // Step 2. test prefer apn for subId 2.
+        // Test for SubId 2.
+        doReturn(2).when(mPhone).getSubId();
+        // Sim Loaded and loads profiles.
+        changeSimStateTo(TelephonyManager.SIM_STATE_ABSENT);
+        changeSimStateTo(TelephonyManager.SIM_STATE_LOADED);
+        mDataProfileManagerUT.obtainMessage(2 /*EVENT_APN_DATABASE_CHANGED*/).sendToTarget();
+        processAllMessages();
+
+        // Find DataProfile based on network request
+        dataProfile = mDataProfileManagerUT.getDataProfileForNetworkRequest(
+                tnr, TelephonyManager.NETWORK_TYPE_CDMA, false, false, false);
+        assertThat(dataProfile.getApnSetting().getApnName())
+                .isEqualTo(GENERAL_PURPOSE_APN_LEGACY_RAT);
+        dataProfile.setLastSetupTimestamp(SystemClock.elapsedRealtime());
+        doReturn(dataProfile).when(internetNetwork).getDataProfile();
+
+        // APN reset
+        mPreferredApnId = -1;
+        mDataProfileManagerUT.obtainMessage(2 /*EVENT_APN_DATABASE_CHANGED*/).sendToTarget();
+        processAllMessages();
+
+        // Since a new subid has been loaded, preferred APN should be null regardless of the last
+        // internet connection of previous subid.
+        assertThat(mDataProfileManagerUT.isDataProfilePreferred(dataProfile)).isFalse();
+
+
+        // Step 3. try again back to subId 1.
+        // Test for SubId 1.
+        doReturn(1).when(mPhone).getSubId();
+        // Sim Loaded and loads profiles.
+        changeSimStateTo(TelephonyManager.SIM_STATE_ABSENT);
+        changeSimStateTo(TelephonyManager.SIM_STATE_LOADED);
+        mDataProfileManagerUT.obtainMessage(2 /*EVENT_APN_DATABASE_CHANGED*/).sendToTarget();
+        processAllMessages();
+
+        // Find DataProfile based on network request
+        dataProfile = mDataProfileManagerUT.getDataProfileForNetworkRequest(
+                tnr, TelephonyManager.NETWORK_TYPE_LTE, false, false, false);
+        assertThat(dataProfile.getApnSetting().getApnName()).isEqualTo(GENERAL_PURPOSE_APN);
+        dataProfile.setLastSetupTimestamp(SystemClock.elapsedRealtime());
+        doReturn(dataProfile).when(internetNetwork).getDataProfile();
+
+        // APN reset
+        mPreferredApnId = -1;
+        mDataProfileManagerUT.obtainMessage(2 /*EVENT_APN_DATABASE_CHANGED*/).sendToTarget();
+        processAllMessages();
+
+        // Since the old subid has been loaded, Even if preferapn for subid 1 is not in the db, the
+        // preferapn can be loaded by mLastInternetDataProfiles
+        assertThat(mDataProfileManagerUT.isDataProfilePreferred(dataProfile)).isTrue();
+    }
+
+    @Test
     public void testSetInitialAttachDataProfile() {
         ArgumentCaptor<DataProfile> dataProfileCaptor =
                 ArgumentCaptor.forClass(DataProfile.class);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/data/DataRetryManagerTest.java b/tests/telephonytests/src/com/android/internal/telephony/data/DataRetryManagerTest.java
index c413f83..acfa16d 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/DataRetryManagerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/DataRetryManagerTest.java
@@ -507,17 +507,38 @@
 
         // Test: cancelPendingHandoverRetry
         DataNetwork mockDn = Mockito.mock(DataNetwork.class);
+        NetworkRequest request = new NetworkRequest.Builder()
+                .addCapability(NetworkCapabilities.NET_CAPABILITY_IMS)
+                .build();
+        TelephonyNetworkRequest tnr = new TelephonyNetworkRequest(request, mPhone, mFeatureFlags);
+        DataNetworkController.NetworkRequestList
+                networkRequestList = new DataNetworkController.NetworkRequestList(tnr);
+        doReturn(networkRequestList).when(mockDn).getAttachedNetworkRequestList();
+        doReturn(AccessNetworkConstants.TRANSPORT_TYPE_WWAN).when(mockDn).getTransport();
+        doReturn(mDataProfile3).when(mockDn).getDataProfile();
         Field field = DataRetryManager.class.getDeclaredField("mDataRetryEntries");
         field.setAccessible(true);
         List<DataRetryEntry> mDataRetryEntries =
                 (List<DataRetryEntry>) field.get(mDataRetryManagerUT);
-        retry = new DataHandoverRetryEntry.Builder<>()
-                .setDataNetwork(mockDn)
-                .build();
-        mDataRetryEntries.add(retry);
-        mDataRetryManagerUT.cancelPendingHandoverRetry(mockDn);
+        mDataRetryManagerUT.evaluateDataHandoverRetry(mockDn, 123, 1000);
         processAllMessages();
+        mDataRetryManagerUT.cancelPendingHandoverRetry(mockDn);
+        Mockito.clearInvocations(mDataRetryManagerCallbackMock);
+        processAllMessages();
+        retry = mDataRetryEntries.get(mDataRetryEntries.size() - 1);
 
+        ArgumentCaptor<List<ThrottleStatus>> throttleStatusCaptor =
+                ArgumentCaptor.forClass(List.class);
+        verify(mDataRetryManagerCallbackMock).onThrottleStatusChanged(
+                throttleStatusCaptor.capture());
+        assertThat(throttleStatusCaptor.getValue()).hasSize(1);
+        ThrottleStatus throttleStatus = throttleStatusCaptor.getValue().get(0);
+        assertThat(throttleStatus.getApnType()).isEqualTo(ApnSetting.TYPE_IMS);
+        assertThat(throttleStatus.getRetryType())
+                .isEqualTo(ThrottleStatus.RETRY_TYPE_HANDOVER);
+        assertThat(throttleStatus.getThrottleExpiryTimeMillis()).isEqualTo(-1);
+        assertThat(throttleStatus.getTransportType())
+                .isEqualTo(AccessNetworkConstants.TRANSPORT_TYPE_WLAN);
         assertThat(mDataRetryManagerUT.isAnyHandoverRetryScheduled(mockDn)).isFalse();
         assertThat(retry.getState()).isEqualTo(DataRetryEntry.RETRY_STATE_CANCELLED);
     }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/data/LinkBandwidthEstimatorTest.java b/tests/telephonytests/src/com/android/internal/telephony/data/LinkBandwidthEstimatorTest.java
index d41be7d..dc4a58a 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/LinkBandwidthEstimatorTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/LinkBandwidthEstimatorTest.java
@@ -43,6 +43,7 @@
 
 import android.net.NetworkCapabilities;
 import android.os.Handler;
+import android.os.Looper;
 import android.telephony.CellIdentityLte;
 import android.telephony.ModemActivityInfo;
 import android.telephony.NetworkRegistrationInfo;
@@ -116,7 +117,7 @@
         when(mPhone.getSubId()).thenReturn(1);
         when(mSignalStrength.getDbm()).thenReturn(-100);
         when(mSignalStrength.getLevel()).thenReturn(1);
-        mLBE = new LinkBandwidthEstimator(mPhone, mTelephonyFacade);
+        mLBE = new LinkBandwidthEstimator(mPhone, Looper.myLooper(), mTelephonyFacade);
         mLBE.obtainMessage(MSG_DEFAULT_NETWORK_CHANGED, mNetworkCapabilities).sendToTarget();
         mLBE.obtainMessage(MSG_SCREEN_STATE_CHANGED, false).sendToTarget();
         mLBE.obtainMessage(MSG_ACTIVE_PHONE_CHANGED, 1).sendToTarget();
diff --git a/tests/telephonytests/src/com/android/internal/telephony/data/PhoneSwitcherTest.java b/tests/telephonytests/src/com/android/internal/telephony/data/PhoneSwitcherTest.java
index 8420acf..2a1fedb 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/PhoneSwitcherTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/PhoneSwitcherTest.java
@@ -23,9 +23,12 @@
 import static android.telephony.TelephonyManager.SET_OPPORTUNISTIC_SUB_SUCCESS;
 import static android.telephony.TelephonyManager.SET_OPPORTUNISTIC_SUB_VALIDATION_FAILED;
 import static android.telephony.TelephonyManager.SIM_STATE_LOADED;
+import static android.telephony.ims.RegistrationManager.REGISTRATION_STATE_NOT_REGISTERED;
+import static android.telephony.ims.RegistrationManager.REGISTRATION_STATE_REGISTERED;
 import static android.telephony.ims.stub.ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM;
 import static android.telephony.ims.stub.ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN;
 import static android.telephony.ims.stub.ImsRegistrationImplBase.REGISTRATION_TECH_LTE;
+import static android.telephony.ims.stub.ImsRegistrationImplBase.REGISTRATION_TECH_NONE;
 
 import static com.android.internal.telephony.data.AutoDataSwitchController.EVALUATION_REASON_VOICE_CALL_END;
 import static com.android.internal.telephony.data.PhoneSwitcher.ECBM_DEFAULT_DATA_SWITCH_BASE_TIME_MS;
@@ -42,6 +45,7 @@
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
@@ -73,6 +77,7 @@
 
 import androidx.test.filters.SmallTest;
 
+import com.android.ims.ImsException;
 import com.android.internal.telephony.Call;
 import com.android.internal.telephony.CommandException;
 import com.android.internal.telephony.CommandsInterface;
@@ -84,6 +89,8 @@
 import com.android.internal.telephony.ServiceStateTracker;
 import com.android.internal.telephony.TelephonyIntents;
 import com.android.internal.telephony.TelephonyTest;
+import com.android.internal.telephony.imsphone.ImsPhone;
+import com.android.internal.telephony.imsphone.ImsPhoneCall;
 import com.android.internal.telephony.subscription.SubscriptionInfoInternal;
 
 import org.junit.After;
@@ -126,6 +133,7 @@
     private ISetOpportunisticDataCallback mSetOpptDataCallback1;
     private ISetOpportunisticDataCallback mSetOpptDataCallback2;
     PhoneSwitcher.ImsRegTechProvider mMockImsRegTechProvider;
+    PhoneSwitcher.ImsRegisterCallback mMockImsRegisterCallback;
     private SubscriptionInfo mSubscriptionInfo;
     private ISub mMockedIsub;
     private AutoDataSwitchController mAutoDataSwitchController;
@@ -167,6 +175,7 @@
         mSetOpptDataCallback1 = mock(ISetOpportunisticDataCallback.class);
         mSetOpptDataCallback2 = mock(ISetOpportunisticDataCallback.class);
         mMockImsRegTechProvider = mock(PhoneSwitcher.ImsRegTechProvider.class);
+        mMockImsRegisterCallback = mock(PhoneSwitcher.ImsRegisterCallback.class);
         mSubscriptionInfo = mock(SubscriptionInfo.class);
         mMockedIsub = mock(ISub.class);
         mAutoDataSwitchController = mock(AutoDataSwitchController.class);
@@ -871,6 +880,11 @@
         mPhoneSwitcherUT.mImsRegTechProvider = mMockImsRegTechProvider;
     }
 
+    private void mockImsRegisterCallback(int phoneId) throws ImsException {
+        doNothing().when(mMockImsRegisterCallback).setCallback(any(), eq(phoneId), any(), any());
+        mPhoneSwitcherUT.mImsRegisterCallback = mMockImsRegisterCallback;
+    }
+
     @Test
     @SmallTest
     public void testNonDefaultDataPhoneInCall_ImsCallOnLte_shouldSwitchDds() throws Exception {
@@ -1756,6 +1770,101 @@
         verify(mCommandsInterface1, never()).setDataAllowed(anyBoolean(), any());
     }
 
+    @Test
+    @SmallTest
+    public void testRegisterForImsRegistrationCallback() throws Exception {
+        initialize();
+        setAllPhonesInactive();
+
+        // Phone 0 has sub 1, phone 1 has sub 2.
+        // Sub 1 is default data sub.
+        // Both are active subscriptions are active sub, as they are in both active slots.
+        setSlotIndexToSubId(0, 1);
+        setSlotIndexToSubId(1, 2);
+        setDefaultDataSubId(1);
+        processAllMessages();
+
+        // Phone 0 should be the default data phoneId.
+        assertEquals(0, mPhoneSwitcherUT.getPreferredDataPhoneId());
+
+        doReturn(true).when(mPhone).isUserDataEnabled();
+        mockImsRegTech(0, REGISTRATION_TECH_LTE);
+        mockImsRegisterCallback(0);
+        mockImsRegisterCallback(1);
+
+        notifyImsRegistrationTechChange(mPhone);
+
+        // Verify that the callback is re-registered when the IMS registration callback is called.
+        verify(mMockImsRegisterCallback, times(2)).setCallback(any(), anyInt(), any(), any());
+    }
+
+    @Test
+    @SmallTest
+    public void testReceivingImsRegistrationTech() throws Exception {
+        doReturn(true).when(mFeatureFlags).changeMethodOfObtainingImsRegistrationRadioTech();
+
+        // Set up input and output for testing
+        ImsPhone testImsPhone = mock(ImsPhone.class);
+        doReturn(testImsPhone).when(mPhone).getImsPhone();
+        doReturn(testImsPhone).when(mPhone2).getImsPhone();
+        ImsPhoneCall testImsPhoneCall = mock(ImsPhoneCall.class);
+        doReturn(Call.State.IDLE).when(testImsPhoneCall).getState();
+        doReturn(true).when(testImsPhoneCall).isIdle();
+        doReturn(testImsPhoneCall).when(testImsPhone).getForegroundCall();
+        doReturn(testImsPhoneCall).when(testImsPhone).getBackgroundCall();
+        doReturn(testImsPhoneCall).when(testImsPhone).getRingingCall();
+
+        doNothing().when(testImsPhone).registerForImsRegistrationChanges(any(), anyInt(), any());
+
+        initialize();
+        setAllPhonesInactive();
+
+        // Phone 0 has sub 1, phone 1 has sub 2.
+        // Sub 1 is default data sub.
+        // Both are active subscriptions are active sub, as they are in both active slots.
+        setSlotIndexToSubId(0, 1);
+        setSlotIndexToSubId(1, 2);
+        setDefaultDataSubId(1);
+        processAllMessages();
+
+        // Phone 0 should be the default data phoneId.
+        assertEquals(0, mPhoneSwitcherUT.getPreferredDataPhoneId());
+
+        doReturn(true).when(mPhone).isUserDataEnabled();
+        mockImsRegTech(0, REGISTRATION_TECH_NONE);
+        mockImsRegisterCallback(0);
+        mockImsRegisterCallback(1);
+
+        AsyncResult ar = new AsyncResult(null, new ImsPhone.ImsRegistrationRadioTechInfo(
+                0, REGISTRATION_TECH_LTE, REGISTRATION_STATE_REGISTERED), null);
+        notifyImsRegistrationTechChangeWithAsyncResult(ar);
+
+        // Verify cached IMS registration tech is LTE
+        assertTrue(REGISTRATION_TECH_LTE == mPhoneSwitcherUT.mImsRegistrationRadioTechMap.get(0));
+
+        ar = new AsyncResult(null, new ImsPhone.ImsRegistrationRadioTechInfo(
+                0, REGISTRATION_TECH_IWLAN, REGISTRATION_STATE_REGISTERED), null);
+        notifyImsRegistrationTechChangeWithAsyncResult(ar);
+
+        // Verify cached IMS registration tech is WiFi
+        assertTrue(REGISTRATION_TECH_IWLAN
+                == mPhoneSwitcherUT.mImsRegistrationRadioTechMap.get(0));
+
+        ar = new AsyncResult(null, new ImsPhone.ImsRegistrationRadioTechInfo(
+                0, REGISTRATION_TECH_NONE, REGISTRATION_STATE_NOT_REGISTERED), null);
+        notifyImsRegistrationTechChangeWithAsyncResult(ar);
+
+        // Verify cached IMS registration tech is NONE
+        assertTrue(REGISTRATION_TECH_NONE == mPhoneSwitcherUT.mImsRegistrationRadioTechMap.get(0));
+
+        // Verify there is no crash
+        notifyImsRegistrationTechChangeWithAsyncResult(null);
+
+        // Verify that the callback is not re-registered
+        // when the IMS registration callback is called.
+        verify(mMockImsRegisterCallback, never()).setCallback(any(), anyInt(), any(), any());
+    }
+
     /* Private utility methods start here */
 
     private void prepareIdealAutoSwitchCondition() {
@@ -1862,6 +1971,12 @@
         processAllMessages();
     }
 
+    private void notifyImsRegistrationTechChangeWithAsyncResult(AsyncResult ar) {
+        mPhoneSwitcherUT.sendMessage(
+                mPhoneSwitcherUT.obtainMessage(EVENT_IMS_RADIO_TECH_CHANGED, ar));
+        processAllMessages();
+    }
+
     private Message getEcbmRegistration(Phone phone) {
         ArgumentCaptor<Handler> handlerCaptor = ArgumentCaptor.forClass(Handler.class);
         ArgumentCaptor<Integer> intCaptor = ArgumentCaptor.forClass(Integer.class);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/euicc/EuiccControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/euicc/EuiccControllerTest.java
index 57ae9ed..e374551 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/euicc/EuiccControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/euicc/EuiccControllerTest.java
@@ -48,6 +48,7 @@
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.Signature;
+import android.os.Build;
 import android.os.Parcelable;
 import android.os.RemoteException;
 import android.os.UserManager;
@@ -230,6 +231,7 @@
                 Settings.Global.EUICC_PROVISIONED, 0);
         Settings.Global.putInt(mContext.getContentResolver(),
                 Settings.Global.EUICC_PROVISIONED, 0);
+        setHasManageDevicePolicyManagedSubscriptionsPermission(false);
     }
 
     @After
@@ -894,7 +896,7 @@
 
     @Test
     @DisableCompatChanges({EuiccManager.SHOULD_RESOLVE_PORT_INDEX_FOR_APPS})
-    public void testDownloadSubscription_adminPermission_usingSwitchAfterDownload()
+    public void testDownloadSubscription_adminPermission_usingSwitchAfterDownload_fails()
             throws Exception {
         mSetFlagsRule.enableFlags(Flags.FLAG_ESIM_MANAGEMENT_ENABLED);
         setHasWriteEmbeddedPermission(false);
@@ -909,18 +911,99 @@
         when(mPackageManager.getPackageInfo(eq(PACKAGE_NAME), anyInt())).thenReturn(pi);
         setCanManageSubscriptionOnTargetSim(false /* isTargetEuicc */, false /* hasPrivileges */);
 
-        callDownloadSubscription(SUBSCRIPTION, true /* switchAfterDownload */, true /* complete */,
+        callDownloadSubscription(SUBSCRIPTION, true /* switchAfterDownload */,
+                true /* complete */,
                 12345, 0 /* resolvableError */, PACKAGE_NAME /* callingPackage */);
 
-        verifyIntentSent(EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR,
-                0 /* detailedCode */);
+        verifyIntentSent(EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_ERROR, 0 /* detailedCode */);
         verify(mMockConnector, never()).downloadSubscription(anyInt(), anyInt(),
                 any(), anyBoolean(), anyBoolean(), any(), any());
     }
 
     @Test
     @DisableCompatChanges({EuiccManager.SHOULD_RESOLVE_PORT_INDEX_FOR_APPS})
-    public void testDownloadSubscription_onlyAdminManagedAllowed_callerNotAdmin_throws()
+    public void testDownloadSubscription_profileOwner_usingSwitchAfterDownload_fails()
+            throws Exception {
+        mSetFlagsRule.enableFlags(Flags.FLAG_ESIM_MANAGEMENT_ENABLED);
+        setHasWriteEmbeddedPermission(false);
+        setHasManageDevicePolicyManagedSubscriptionsPermission(true);
+        setUpUiccSlotData();
+        GetDownloadableSubscriptionMetadataResult result =
+                new GetDownloadableSubscriptionMetadataResult(EuiccService.RESULT_OK,
+                        SUBSCRIPTION_WITH_METADATA);
+        doReturn(true).when(mDevicePolicyManager).isProfileOwnerApp(PACKAGE_NAME);
+        doReturn(false).when(mDevicePolicyManager).isOrganizationOwnedDeviceWithManagedProfile();
+        doReturn(false).when(mDevicePolicyManager).isDeviceOwnerApp(PACKAGE_NAME);
+        prepareGetDownloadableSubscriptionMetadataCall(true /* complete */, result);
+        PackageInfo pi = new PackageInfo();
+        pi.packageName = PACKAGE_NAME;
+        when(mPackageManager.getPackageInfo(eq(PACKAGE_NAME), anyInt())).thenReturn(pi);
+        setCanManageSubscriptionOnTargetSim(false /* isTargetEuicc */, false /* hasPrivileges */);
+
+        callDownloadSubscription(SUBSCRIPTION, true /* switchAfterDownload */,
+                true /* complete */,
+                12345, 0 /* resolvableError */, PACKAGE_NAME /* callingPackage */);
+
+        verifyIntentSent(EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_ERROR, 0 /* detailedCode */);
+        verify(mMockConnector, never()).downloadSubscription(anyInt(), anyInt(), any(),
+                anyBoolean(), anyBoolean(), any(), any());
+    }
+
+    @Test
+    @DisableCompatChanges({EuiccManager.SHOULD_RESOLVE_PORT_INDEX_FOR_APPS})
+    public void testDownloadSubscription_orgOwnedProfileOwner_usingSwitchAfterDownload_success()
+            throws Exception {
+        mSetFlagsRule.enableFlags(Flags.FLAG_ESIM_MANAGEMENT_ENABLED);
+        setHasWriteEmbeddedPermission(false);
+        setHasManageDevicePolicyManagedSubscriptionsPermission(true);
+        setUpUiccSlotData();
+        GetDownloadableSubscriptionMetadataResult result =
+                new GetDownloadableSubscriptionMetadataResult(EuiccService.RESULT_OK,
+                        SUBSCRIPTION_WITH_METADATA);
+        doReturn(true).when(mDevicePolicyManager).isProfileOwnerApp(PACKAGE_NAME);
+        doReturn(true).when(mDevicePolicyManager).isOrganizationOwnedDeviceWithManagedProfile();
+        doReturn(false).when(mDevicePolicyManager).isDeviceOwnerApp(PACKAGE_NAME);
+        prepareGetDownloadableSubscriptionMetadataCall(true /* complete */, result);
+        PackageInfo pi = new PackageInfo();
+        pi.packageName = PACKAGE_NAME;
+        when(mPackageManager.getPackageInfo(eq(PACKAGE_NAME), anyInt())).thenReturn(pi);
+
+        callDownloadSubscription(SUBSCRIPTION, true /* switchAfterDownload */, true /* complete */,
+                EuiccService.RESULT_OK, 0 /* resolvableError */, PACKAGE_NAME /* callingPackage */);
+
+        verifyIntentSent(EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_OK, 0 /* detailedCode */);
+        assertFalse(mController.mCalledRefreshSubscriptionsAndSendResult);
+    }
+
+    @Test
+    @DisableCompatChanges({EuiccManager.SHOULD_RESOLVE_PORT_INDEX_FOR_APPS})
+    public void testDownloadSubscription_deviceOwner_usingSwitchAfterDownload_success()
+            throws Exception {
+        mSetFlagsRule.enableFlags(Flags.FLAG_ESIM_MANAGEMENT_ENABLED);
+        setHasWriteEmbeddedPermission(false);
+        setHasManageDevicePolicyManagedSubscriptionsPermission(true);
+        setUpUiccSlotData();
+        GetDownloadableSubscriptionMetadataResult result =
+                new GetDownloadableSubscriptionMetadataResult(EuiccService.RESULT_OK,
+                        SUBSCRIPTION_WITH_METADATA);
+        doReturn(false).when(mDevicePolicyManager).isProfileOwnerApp(PACKAGE_NAME);
+        doReturn(false).when(mDevicePolicyManager).isOrganizationOwnedDeviceWithManagedProfile();
+        doReturn(true).when(mDevicePolicyManager).isDeviceOwnerApp(PACKAGE_NAME);
+        prepareGetDownloadableSubscriptionMetadataCall(true /* complete */, result);
+        PackageInfo pi = new PackageInfo();
+        pi.packageName = PACKAGE_NAME;
+        when(mPackageManager.getPackageInfo(eq(PACKAGE_NAME), anyInt())).thenReturn(pi);
+
+        callDownloadSubscription(SUBSCRIPTION, true /* switchAfterDownload */, true /* complete */,
+                EuiccService.RESULT_OK, 0 /* resolvableError */, PACKAGE_NAME /* callingPackage */);
+
+        verifyIntentSent(EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_OK, 0 /* detailedCode */);
+        assertFalse(mController.mCalledRefreshSubscriptionsAndSendResult);
+    }
+
+    @Test
+    @DisableCompatChanges({EuiccManager.SHOULD_RESOLVE_PORT_INDEX_FOR_APPS})
+    public void testDownloadSubscription_onlyAdminManagedAllowed_callerNotAdmin_error()
             throws Exception {
         mSetFlagsRule.enableFlags(Flags.FLAG_ESIM_MANAGEMENT_ENABLED);
         setHasManageDevicePolicyManagedSubscriptionsPermission(false);
@@ -929,15 +1012,10 @@
                 .when(mUserManager)
                 .hasUserRestriction(UserManager.DISALLOW_SIM_GLOBALLY);
 
-        assertThrows(SecurityException.class,
-                () ->
-                        callDownloadSubscription(
-                                SUBSCRIPTION,
-                                false /* switchAfterDownload */,
-                                true /* complete */,
-                                EuiccService.RESULT_OK,
-                                0 /* resolvableError */,
-                                "whatever" /* callingPackage */));
+        callDownloadSubscription(SUBSCRIPTION, false /* switchAfterDownload */, true /* complete */,
+                12345, 0 /* resolvableError */, "whatever" /* callingPackage */);
+
+        verifyIntentSent(EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_ERROR, 0 /* detailedCode */);
         assertFalse(mController.mCalledRefreshSubscriptionsAndSendResult);
     }
 
@@ -1605,7 +1683,12 @@
     @Test
     @EnableCompatChanges({EuiccManager.INACTIVE_PORT_AVAILABILITY_CHECK,
             TelephonyManager.ENABLE_FEATURE_MAPPING})
-    public void testIsSimPortAvailable_WithTelephonyFeatureMapping() {
+    public void testIsSimPortAvailable_WithTelephonyFeatureMapping() throws Exception {
+        // Replace field to set SDK version of vendor partition to Android V
+        int vendorApiLevel = Build.VERSION_CODES.VANILLA_ICE_CREAM;
+        replaceInstance(EuiccController.class, "mVendorApiLevel", (EuiccController) mController,
+                vendorApiLevel);
+
         // Feature flag enabled, device has required telephony feature.
         doReturn(true).when(mFeatureFlags).enforceTelephonyFeatureMappingForPublicApis();
         doReturn(true).when(mPackageManager).hasSystemFeature(
diff --git a/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmMmiCodeTest.java b/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmMmiCodeTest.java
index 17a428b..4db0bc9 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmMmiCodeTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmMmiCodeTest.java
@@ -20,12 +20,15 @@
 
 import static junit.framework.Assert.fail;
 
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.verify;
 
+import android.content.Context;
 import android.os.AsyncResult;
 import android.os.PersistableBundle;
 import android.telephony.CarrierConfigManager;
@@ -332,6 +335,44 @@
                 eq(com.android.internal.R.string.mmiErrorNotSupported));
     }
 
+    @Test
+    public void testFacCode() {
+        // Put a valid FAC code into the carrier config.
+        CarrierConfigManager ccm = (CarrierConfigManager) mGsmCdmaPhoneUT.getContext()
+                .getSystemService(Context.CARRIER_CONFIG_SERVICE);
+        PersistableBundle cc = ccm.getConfigForSubId(0);
+        cc.putStringArray(CarrierConfigManager.KEY_FEATURE_ACCESS_CODES_STRING_ARRAY,
+                new String[] {"112"});
+
+        // Try using a dial string with the FAC; this should result in a NULL GsmMmiCode.
+        GsmMmiCode validFac = GsmMmiCode.newFromDialString("#112#6505551212*", mGsmCdmaPhoneUT,
+                null, null);
+        assertNull(validFac);
+
+        // Try using a dial string with the FAC; this should result in a NULL GsmMmiCode.
+        // Note this case is somewhat contrived, however the GsmMmiCode parsing does allow non-digit
+        // characters, so we will here too.
+        GsmMmiCode validFac2 = GsmMmiCode.newFromDialString("#112#650-555-1212*", mGsmCdmaPhoneUT,
+                null, null);
+        assertNull(validFac2);
+
+        // Try using a dial string with a different made up FAC; this one is not in the carrier
+        // config so should just return an MMI code.
+        GsmMmiCode invalidFac = GsmMmiCode.newFromDialString("#113#6505551212*", mGsmCdmaPhoneUT,
+                null, null);
+        assertNotNull(invalidFac);
+
+        // Now try the carrier config FAC code, but it's formatted as if it a USSD.
+        GsmMmiCode ussd = GsmMmiCode.newFromDialString("*#112*6505551212#", mGsmCdmaPhoneUT,
+                null, null);
+        assertNotNull(ussd);
+
+        // Now try the carrier config FAC code, but it's not a valid FAC formatted string
+        GsmMmiCode invalidFormat = GsmMmiCode.newFromDialString("*#112*6505551212", mGsmCdmaPhoneUT,
+                null, null);
+        assertNull(invalidFormat);
+    }
+
     private void setCarrierSupportsCallerIdVerticalServiceCodesCarrierConfig() {
         final PersistableBundle bundle = new PersistableBundle();
         bundle.putBoolean(CarrierConfigManager
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ims/ImsResolverTest.java b/tests/telephonytests/src/com/android/internal/telephony/ims/ImsResolverTest.java
index 7e65048..4abf33f 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ims/ImsResolverTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ims/ImsResolverTest.java
@@ -62,6 +62,7 @@
 
 import com.android.ims.ImsFeatureBinderRepository;
 import com.android.internal.telephony.PhoneConfigurationManager;
+import com.android.internal.telephony.flags.FeatureFlags;
 
 import org.junit.After;
 import org.junit.Before;
@@ -113,6 +114,7 @@
     private BroadcastReceiver mTestBootCompleteReceiver;
     private ImsServiceFeatureQueryManager.Listener mDynamicQueryListener;
     private PersistableBundle[] mCarrierConfigs;
+    private FeatureFlags mFeatureFlags;
 
     @Before
     @Override
@@ -127,6 +129,7 @@
         mMockQueryManagerFactory = mock(ImsResolver.ImsDynamicQueryManagerFactory.class);
         mMockQueryManager = mock(ImsServiceFeatureQueryManager.class);
         mMockRepo = mock(ImsFeatureBinderRepository.class);
+        mFeatureFlags = mock(FeatureFlags.class);
     }
 
     @After
@@ -1969,7 +1972,7 @@
         }
 
         mTestImsResolver = new ImsResolver(mMockContext, deviceMmTelPkgName, deviceRcsPkgName,
-                numSlots, mMockRepo, Looper.myLooper());
+                numSlots, mMockRepo, Looper.myLooper(), mFeatureFlags);
 
         mTestImsResolver.setSubscriptionManagerProxy(mTestSubscriptionManagerProxy);
         mTestImsResolver.setTelephonyManagerProxy(mTestTelephonyManagerProxy);
@@ -2008,7 +2011,7 @@
                     @Override
                     public ImsServiceController create(Context context, ComponentName componentName,
                             ImsServiceController.ImsServiceControllerCallbacks callbacks,
-                            ImsFeatureBinderRepository r) {
+                            ImsFeatureBinderRepository r, FeatureFlags featureFlags) {
                         when(controller.getComponentName()).thenReturn(componentName);
                         return controller;
                     }
@@ -2118,7 +2121,7 @@
                     @Override
                     public ImsServiceController create(Context context, ComponentName componentName,
                             ImsServiceController.ImsServiceControllerCallbacks callbacks,
-                            ImsFeatureBinderRepository r) {
+                            ImsFeatureBinderRepository r, FeatureFlags featureFlags) {
                         return controllerMap.get(componentName.getPackageName());
                     }
                 });
@@ -2136,7 +2139,7 @@
                     @Override
                     public ImsServiceController create(Context context, ComponentName componentName,
                             ImsServiceController.ImsServiceControllerCallbacks callbacks,
-                            ImsFeatureBinderRepository r) {
+                            ImsFeatureBinderRepository r, FeatureFlags featureFlags) {
                         if (TEST_DEVICE_DEFAULT_NAME.getPackageName().equals(
                                 componentName.getPackageName())) {
                             when(deviceController.getComponentName()).thenReturn(componentName);
@@ -2163,7 +2166,7 @@
                     @Override
                     public ImsServiceController create(Context context, ComponentName componentName,
                             ImsServiceController.ImsServiceControllerCallbacks callbacks,
-                            ImsFeatureBinderRepository r) {
+                            ImsFeatureBinderRepository r, FeatureFlags featureFlags) {
                         if (TEST_DEVICE_DEFAULT_NAME.getPackageName().equals(
                                 componentName.getPackageName())) {
                             when(deviceController.getComponentName()).thenReturn(componentName);
@@ -2195,7 +2198,7 @@
                     @Override
                     public ImsServiceController create(Context context, ComponentName componentName,
                             ImsServiceController.ImsServiceControllerCallbacks callbacks,
-                            ImsFeatureBinderRepository r) {
+                            ImsFeatureBinderRepository r, FeatureFlags featureFlags) {
                         if (TEST_DEVICE_DEFAULT_NAME.getPackageName().equals(
                                 componentName.getPackageName())) {
                             when(deviceController1.getComponentName()).thenReturn(componentName);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ims/ImsServiceControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/ims/ImsServiceControllerTest.java
index 0b6aa9f..65b73fb 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ims/ImsServiceControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ims/ImsServiceControllerTest.java
@@ -57,6 +57,7 @@
 import com.android.ims.ImsFeatureContainer;
 import com.android.ims.internal.IImsFeatureStatusCallback;
 import com.android.ims.internal.IImsServiceFeatureCallback;
+import com.android.internal.telephony.flags.FeatureFlags;
 
 import org.junit.After;
 import org.junit.Before;
@@ -126,6 +127,7 @@
     IImsRegistration mMockRcsRegistration;
     IImsServiceController mMockServiceControllerBinder;
     ImsServiceController.ImsServiceControllerCallbacks mMockCallbacks;
+    FeatureFlags mFeatureFlags;
     Context mMockContext;
 
     private final ComponentName mTestComponentName = new ComponentName("TestPkg",
@@ -146,11 +148,12 @@
         mMockRcsRegistration = mock(IImsRegistration.class);
         mMockServiceControllerBinder = mock(IImsServiceController.class);
         mMockCallbacks = mock(ImsServiceController.ImsServiceControllerCallbacks.class);
+        mFeatureFlags = mock(FeatureFlags.class);
         mMockContext = mock(Context.class);
 
         mRepo = new ImsFeatureBinderRepository();
         mTestImsServiceController = new ImsServiceController(mMockContext, mTestComponentName,
-                mMockCallbacks, mHandler, REBIND_RETRY, mRepo);
+                mMockCallbacks, mHandler, REBIND_RETRY, mRepo, mFeatureFlags);
         when(mMockContext.bindService(any(), any(), anyInt())).thenReturn(true);
         when(mMockServiceControllerBinder.createMmTelFeature(anyInt(), anyInt()))
                 .thenReturn(mMockMmTelFeature);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneConnectionTest.java b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneConnectionTest.java
index 6c4493b..58eca5d 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneConnectionTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneConnectionTest.java
@@ -40,6 +40,8 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
+import android.os.PersistableBundle;
+import android.telephony.CarrierConfigManager;
 import android.telephony.DisconnectCause;
 import android.telephony.PhoneNumberUtils;
 import android.telephony.ServiceState;
@@ -57,6 +59,7 @@
 import com.android.internal.telephony.Call;
 import com.android.internal.telephony.Connection;
 import com.android.internal.telephony.GsmCdmaCall;
+import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.TelephonyTest;
 import com.android.internal.telephony.imsphone.ImsPhone.ImsDialArgs;
@@ -95,6 +98,8 @@
         mForeGroundCall = mock(ImsPhoneCall.class);
         mBackGroundCall = mock(ImsPhoneCall.class);
         mRingGroundCall = mock(ImsPhoneCall.class);
+        mTelephonyManager = mock(TelephonyManager.class);
+        mCarrierConfigManager = mock(CarrierConfigManager.class);
         replaceInstance(Handler.class, "mLooper", mImsCT, Looper.myLooper());
         replaceInstance(ImsPhoneCallTracker.class, "mForegroundCall", mImsCT, mForeGroundCall);
         replaceInstance(ImsPhoneCallTracker.class, "mBackgroundCall", mImsCT, mBackGroundCall);
@@ -103,6 +108,10 @@
 
         mImsCallProfile.mCallExtras = mBundle;
         doReturn(ImsPhoneCall.State.IDLE).when(mForeGroundCall).getState();
+
+        // By default, turn off the business composer
+        setUserEnabledBusinessComposer(false);
+        setCarrierConfigBusinessComposer(false);
     }
 
     @After
@@ -431,6 +440,97 @@
                 ImsPhoneConnection.toTelecomVerificationStatus(90210));
     }
 
+    /**
+     * Assert the helper method
+     * {@link ImsPhoneConnection#isBusinessOnlyCallComposerEnabledByUser(Phone)} is Working As
+     * Intended.
+     */
+    @Test
+    @SmallTest
+    public void testIsBusinessOnlyCallComposerEnabledByUser() {
+        mConnectionUT = new ImsPhoneConnection(mImsPhone, mImsCall, mImsCT, mForeGroundCall, false);
+        assertFalse(mConnectionUT.isBusinessOnlyCallComposerEnabledByUser(mImsPhone));
+        setUserEnabledBusinessComposer(true);
+        assertTrue(mConnectionUT.isBusinessOnlyCallComposerEnabledByUser(mImsPhone));
+        setUserEnabledBusinessComposer(false);
+        assertFalse(mConnectionUT.isBusinessOnlyCallComposerEnabledByUser(mImsPhone));
+    }
+
+    /**
+     * Assert the helper method
+     * {@link ImsPhoneConnection#isBusinessComposerEnabledByConfig(Phone)} is Working As
+     * Intended.
+     */
+    @Test
+    @SmallTest
+    public void testBusinessComposerEnabledByCarrierConfig() {
+        mConnectionUT = new ImsPhoneConnection(mImsPhone, mImsCall, mImsCT, mForeGroundCall, false);
+        assertFalse(mConnectionUT.isBusinessComposerEnabledByConfig(mImsPhone));
+        setCarrierConfigBusinessComposer(true);
+        assertTrue(mConnectionUT.isBusinessComposerEnabledByConfig(mImsPhone));
+        setCarrierConfigBusinessComposer(false);
+        assertFalse(mConnectionUT.isBusinessComposerEnabledByConfig(mImsPhone));
+    }
+
+    /**
+     * Verify that the {@link ImsPhoneConnection#getIsBusinessComposerFeatureEnabled()} only
+     * returns true when it is enabled by the CarrierConfigManager and user.
+     */
+    @Test
+    @SmallTest
+    public void testIncomingImsCallSetsTheBusinessComposerFeatureStatus() {
+        mConnectionUT = new ImsPhoneConnection(mImsPhone, mImsCall, mImsCT, mForeGroundCall, false);
+        assertFalse(mConnectionUT.getIsBusinessComposerFeatureEnabled());
+
+        setUserEnabledBusinessComposer(true);
+        setCarrierConfigBusinessComposer(false);
+        mConnectionUT = new ImsPhoneConnection(mImsPhone, mImsCall, mImsCT, mForeGroundCall, false);
+        assertFalse(mConnectionUT.getIsBusinessComposerFeatureEnabled());
+
+        setUserEnabledBusinessComposer(false);
+        setCarrierConfigBusinessComposer(true);
+        mConnectionUT = new ImsPhoneConnection(mImsPhone, mImsCall, mImsCT, mForeGroundCall, false);
+        assertFalse(mConnectionUT.getIsBusinessComposerFeatureEnabled());
+
+        setUserEnabledBusinessComposer(true);
+        setCarrierConfigBusinessComposer(true);
+        mConnectionUT = new ImsPhoneConnection(mImsPhone, mImsCall, mImsCT, mForeGroundCall, false);
+        assertTrue(mConnectionUT.getIsBusinessComposerFeatureEnabled());
+    }
+
+    /**
+     * If the business composer feature is off but ImsCallProfile extras still injected by the lower
+     * layer, Telephony should NOT inject the telecom call extras.
+     */
+    @Test
+    @SmallTest
+    public void testMaybeInjectBusinessExtrasWithFeatureOff() {
+        setUserEnabledBusinessComposer(false);
+        setCarrierConfigBusinessComposer(false);
+        mConnectionUT = new ImsPhoneConnection(mImsPhone, mImsCall, mImsCT, mForeGroundCall, false);
+        assertFalse(mConnectionUT.getIsBusinessComposerFeatureEnabled());
+        Bundle businessExtras = getBusinessExtras();
+        mConnectionUT.maybeInjectBusinessComposerExtras(businessExtras);
+        assertFalse(businessExtras.containsKey(android.telecom.Call.EXTRA_IS_BUSINESS_CALL));
+        assertFalse(businessExtras.containsKey(android.telecom.Call.EXTRA_ASSERTED_DISPLAY_NAME));
+    }
+
+    /**
+     * Verify if the business composer feature is on, telephony is injecting the telecom call extras
+     */
+    @Test
+    @SmallTest
+    public void testMaybeInjectBusinessExtrasWithFeatureOn() {
+        setUserEnabledBusinessComposer(true);
+        setCarrierConfigBusinessComposer(true);
+        mConnectionUT = new ImsPhoneConnection(mImsPhone, mImsCall, mImsCT, mForeGroundCall, false);
+        assertTrue(mConnectionUT.getIsBusinessComposerFeatureEnabled());
+        Bundle businessExtras = getBusinessExtras();
+        mConnectionUT.maybeInjectBusinessComposerExtras(businessExtras);
+        assertTrue(businessExtras.containsKey(android.telecom.Call.EXTRA_IS_BUSINESS_CALL));
+        assertTrue(businessExtras.containsKey(android.telecom.Call.EXTRA_ASSERTED_DISPLAY_NAME));
+    }
+
     @Test
     @SmallTest
     public void testSetRedirectingAddress() {
@@ -481,4 +581,32 @@
         latch.await(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
         assertTrue(receivedCountCallback[0]);
     }
+
+    private void setUserEnabledBusinessComposer(boolean isEnabled) {
+        when(mPhone.getContext()).thenReturn(mContext);
+        when(mContext.getSystemService(TelephonyManager.class)).thenReturn(mTelephonyManager);
+        if (isEnabled) {
+            when(mTelephonyManager.getCallComposerStatus()).thenReturn(
+                    TelephonyManager.CALL_COMPOSER_STATUS_BUSINESS_ONLY);
+        } else {
+            when(mTelephonyManager.getCallComposerStatus()).thenReturn(
+                    TelephonyManager.CALL_COMPOSER_STATUS_OFF);
+        }
+    }
+
+    private void setCarrierConfigBusinessComposer(boolean isEnabled) {
+        when(mPhone.getContext()).thenReturn(mContext);
+        when(mContext.getSystemService(CarrierConfigManager.class)).thenReturn(
+                mCarrierConfigManager);
+        PersistableBundle b = new PersistableBundle();
+        b.putBoolean(CarrierConfigManager.KEY_SUPPORTS_BUSINESS_CALL_COMPOSER_BOOL, isEnabled);
+        when(mCarrierConfigManager.getConfigForSubId(mPhone.getSubId())).thenReturn(b);
+    }
+
+    private Bundle getBusinessExtras() {
+        Bundle businessExtras = new Bundle();
+        businessExtras.putBoolean(ImsCallProfile.EXTRA_IS_BUSINESS_CALL, true);
+        businessExtras.putString(ImsCallProfile.EXTRA_ASSERTED_DISPLAY_NAME, "Google");
+        return businessExtras;
+    }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneTest.java b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneTest.java
index 14cff4b..992f50a 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneTest.java
@@ -73,6 +73,7 @@
 import android.os.Message;
 import android.os.PersistableBundle;
 import android.sysprop.TelephonyProperties;
+import android.telecom.VideoProfile;
 import android.telephony.CarrierConfigManager;
 import android.telephony.ServiceState;
 import android.telephony.SubscriptionInfo;
@@ -284,6 +285,14 @@
         doReturn(Call.State.INCOMING).when(mRingingCall).getState();
         assertEquals(true, mImsPhoneUT.handleInCallMmiCommands("2"));
         verify(mImsCT).acceptCall(ImsCallProfile.CALL_TYPE_VOICE);
+
+        // Verify b/286499659, fixed media type
+        doReturn(true).when(mFeatureFlags).answerAudioOnlyWhenAnsweringViaMmiCode();
+        doReturn(Call.State.IDLE).when(mForegroundCall).getState();
+        doReturn(Call.State.IDLE).when(mBackgroundCall).getState();
+        doReturn(Call.State.INCOMING).when(mRingingCall).getState();
+        assertEquals(true, mImsPhoneUT.handleInCallMmiCommands("2"));
+        verify(mImsCT).acceptCall(VideoProfile.STATE_AUDIO_ONLY);
     }
 
     @Test
diff --git a/tests/telephonytests/src/com/android/internal/telephony/metrics/DataCallSessionStatsTest.java b/tests/telephonytests/src/com/android/internal/telephony/metrics/DataCallSessionStatsTest.java
index 2e64c46..5ac21f8 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/metrics/DataCallSessionStatsTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/metrics/DataCallSessionStatsTest.java
@@ -34,7 +34,8 @@
 import android.telephony.TelephonyManager;
 import android.telephony.data.ApnSetting;
 import android.telephony.data.DataCallResponse;
-import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.filters.SmallTest;
 
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.TelephonyTest;
@@ -96,7 +97,7 @@
     @Test
     @SmallTest
     public void testSetupDataCallOnCellularIms_success() {
-        mDataCallSessionStats.onSetupDataCall(ApnSetting.TYPE_IMS);
+        mDataCallSessionStats.onSetupDataCall(ApnSetting.TYPE_IMS, false);
         mDataCallSessionStats.onSetupDataCallResponse(
                 mDefaultImsResponse,
                 TelephonyManager.NETWORK_TYPE_LTE,
@@ -122,7 +123,7 @@
     @Test
     @SmallTest
     public void testSetupDataCallOnIwlan_success() {
-        mDataCallSessionStats.onSetupDataCall(ApnSetting.TYPE_IMS);
+        mDataCallSessionStats.onSetupDataCall(ApnSetting.TYPE_IMS, false);
         mDataCallSessionStats.onSetupDataCallResponse(
                 mDefaultImsResponse,
                 TelephonyManager.NETWORK_TYPE_IWLAN,
@@ -151,7 +152,7 @@
     public void testSetupDataCallOnCrossSimCalling_success() {
         doReturn(mCellularNetworkCapabilities)
                 .when(mDefaultNetworkMonitor).getNetworkCapabilities();
-        mDataCallSessionStats.onSetupDataCall(ApnSetting.TYPE_IMS);
+        mDataCallSessionStats.onSetupDataCall(ApnSetting.TYPE_IMS, false);
         mDataCallSessionStats.onSetupDataCallResponse(
                 mDefaultImsResponse,
                 TelephonyManager.NETWORK_TYPE_IWLAN,
@@ -178,7 +179,7 @@
     @Test
     @SmallTest
     public void testSetupDataCallOnCellularIms_failure() {
-        mDataCallSessionStats.onSetupDataCall(ApnSetting.TYPE_IMS);
+        mDataCallSessionStats.onSetupDataCall(ApnSetting.TYPE_IMS, false);
         mDataCallSessionStats.onSetupDataCallResponse(
                 mDefaultImsResponse,
                 TelephonyManager.NETWORK_TYPE_LTE,
@@ -201,7 +202,7 @@
     @Test
     @SmallTest
     public void testHandoverFromCellularToIwlan_success() {
-        mDataCallSessionStats.onSetupDataCall(ApnSetting.TYPE_IMS);
+        mDataCallSessionStats.onSetupDataCall(ApnSetting.TYPE_IMS, false);
         mDataCallSessionStats.onSetupDataCallResponse(
                 mDefaultImsResponse,
                 TelephonyManager.NETWORK_TYPE_LTE,
@@ -227,7 +228,7 @@
     @Test
     @SmallTest
     public void testHandoverFromCellularToCrossSimCalling_success() {
-        mDataCallSessionStats.onSetupDataCall(ApnSetting.TYPE_IMS);
+        mDataCallSessionStats.onSetupDataCall(ApnSetting.TYPE_IMS, false);
         mDataCallSessionStats.onSetupDataCallResponse(
                 mDefaultImsResponse,
                 TelephonyManager.NETWORK_TYPE_LTE,
@@ -256,7 +257,7 @@
     @Test
     @SmallTest
     public void testHandoverFromCellularToIwlan_failure() {
-        mDataCallSessionStats.onSetupDataCall(ApnSetting.TYPE_IMS);
+        mDataCallSessionStats.onSetupDataCall(ApnSetting.TYPE_IMS, false);
         mDataCallSessionStats.onSetupDataCallResponse(
                 mDefaultImsResponse,
                 TelephonyManager.NETWORK_TYPE_LTE,
@@ -288,7 +289,7 @@
     @Test
     @SmallTest
     public void testSetupDataCallOnIwlan_success_thenOOS() {
-        mDataCallSessionStats.onSetupDataCall(ApnSetting.TYPE_IMS);
+        mDataCallSessionStats.onSetupDataCall(ApnSetting.TYPE_IMS, false);
         mDataCallSessionStats.onSetupDataCallResponse(
                 mDefaultImsResponse,
                 TelephonyManager.NETWORK_TYPE_IWLAN,
@@ -315,7 +316,7 @@
     public void testIsNtn() {
         when(mSatelliteController.isInSatelliteModeForCarrierRoaming(any())).thenReturn(true);
 
-        mDataCallSessionStats.onSetupDataCall(ApnSetting.TYPE_IMS);
+        mDataCallSessionStats.onSetupDataCall(ApnSetting.TYPE_IMS, false);
         mDataCallSessionStats.onSetupDataCallResponse(
                 mDefaultImsResponse,
                 TelephonyManager.NETWORK_TYPE_LTE,
@@ -338,7 +339,7 @@
         when(mSatelliteController.isInSatelliteModeForCarrierRoaming(any()))
                 .thenReturn(false);
 
-        mDataCallSessionStats.onSetupDataCall(ApnSetting.TYPE_IMS);
+        mDataCallSessionStats.onSetupDataCall(ApnSetting.TYPE_IMS, false);
         mDataCallSessionStats.onSetupDataCallResponse(
                 mDefaultImsResponse,
                 TelephonyManager.NETWORK_TYPE_LTE,
diff --git a/tests/telephonytests/src/com/android/internal/telephony/metrics/DataNetworkValidationStatsTest.java b/tests/telephonytests/src/com/android/internal/telephony/metrics/DataNetworkValidationStatsTest.java
new file mode 100644
index 0000000..b9493b9
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/metrics/DataNetworkValidationStatsTest.java
@@ -0,0 +1,295 @@
+/*
+ * Copyright (C) 2024 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.metrics;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+
+import android.telephony.PreciseDataConnectionState;
+import android.telephony.SignalStrength;
+import android.telephony.TelephonyManager;
+import android.telephony.data.ApnSetting;
+
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.TelephonyStatsLog;
+import com.android.internal.telephony.TelephonyTest;
+import com.android.internal.telephony.nano.PersistAtomsProto;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+
+/** Test DataNetworkValidationStats */
+public class DataNetworkValidationStatsTest extends TelephonyTest {
+
+    /** Initial time at starting tests */
+    private static final Long STARTED_TIME_IN_MILLIS = 5000000L;
+
+    private TestableDataNetworkValidationStats mDataNetworkValidationStats;
+
+    /** Test Class for override elapsed time */
+    private static class TestableDataNetworkValidationStats extends DataNetworkValidationStats {
+        private long mSystemClockInMillis;
+        TestableDataNetworkValidationStats(Phone phone) {
+            super(phone);
+            mSystemClockInMillis = STARTED_TIME_IN_MILLIS;
+        }
+
+        @Override
+        protected long getTimeMillis() {
+            return mSystemClockInMillis;
+        }
+
+        public void elapseTimeInMillis(long elapse) {
+            mSystemClockInMillis += elapse;
+        }
+    }
+
+    @Before
+    public void setup() throws Exception {
+        super.setUp(getClass().getSimpleName());
+
+        mDataNetworkValidationStats = new TestableDataNetworkValidationStats(mPhone);
+        doReturn(SignalStrength.SIGNAL_STRENGTH_GREAT).when(mSignalStrength).getLevel();
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        mDataNetworkValidationStats = null;
+        super.tearDown();
+    }
+
+    @Test
+    public void testRequestDataNetworkValidation() {
+        mDataNetworkValidationStats.onRequestNetworkValidation(ApnSetting.TYPE_IMS);
+        verifyNoMoreInteractions(mPersistAtomsStorage);
+    }
+
+    @Test
+    public void testOnUpdateNetworkValidationStateWithSuccessStatus() {
+
+        // Test
+        mDataNetworkValidationStats.onRequestNetworkValidation(ApnSetting.TYPE_IMS);
+        mDataNetworkValidationStats.elapseTimeInMillis(100L);
+        mDataNetworkValidationStats.onUpdateNetworkValidationState(
+                PreciseDataConnectionState.NETWORK_VALIDATION_SUCCESS,
+                TelephonyManager.NETWORK_TYPE_LTE);
+
+        // Verify that atom was logged
+        ArgumentCaptor<PersistAtomsProto.DataNetworkValidation> captor =
+                ArgumentCaptor.forClass(PersistAtomsProto.DataNetworkValidation.class);
+        verify(mPersistAtomsStorage, times(1)).addDataNetworkValidation(
+                captor.capture());
+        PersistAtomsProto.DataNetworkValidation proto = captor.getValue();
+
+        // Make sure variables
+        assertEquals(
+                TelephonyStatsLog.DATA_NETWORK_VALIDATION__NETWORK_TYPE__NETWORK_TYPE_LTE,
+                proto.networkType);
+        assertEquals(ApnSetting.TYPE_IMS, proto.apnTypeBitmask);
+        assertEquals(
+                TelephonyStatsLog.DATA_NETWORK_VALIDATION__SIGNAL_STRENGTH__SIGNAL_STRENGTH_GREAT,
+                proto.signalStrength);
+        assertEquals(TelephonyStatsLog
+                        .DATA_NETWORK_VALIDATION__VALIDATION_RESULT__VALIDATION_RESULT_SUCCESS,
+                proto.validationResult);
+        assertEquals(100L, proto.elapsedTimeInMillis);
+        assertFalse(proto.handoverAttempted);
+        assertEquals(1, proto.networkValidationCount);
+    }
+
+    @Test
+    public void testOnUpdateNetworkValidationStateWithFailureStatus() {
+
+        // Test
+        mDataNetworkValidationStats.onRequestNetworkValidation(ApnSetting.TYPE_EMERGENCY);
+        mDataNetworkValidationStats.elapseTimeInMillis(100L);
+        mDataNetworkValidationStats.onHandoverAttempted();
+        mDataNetworkValidationStats.elapseTimeInMillis(100L);
+        mDataNetworkValidationStats.onUpdateNetworkValidationState(
+                PreciseDataConnectionState.NETWORK_VALIDATION_SUCCESS,
+                TelephonyManager.NETWORK_TYPE_IWLAN);
+
+        // Verify that atom was logged
+        ArgumentCaptor<PersistAtomsProto.DataNetworkValidation> captor =
+                ArgumentCaptor.forClass(PersistAtomsProto.DataNetworkValidation.class);
+        verify(mPersistAtomsStorage, times(1)).addDataNetworkValidation(
+                captor.capture());
+        PersistAtomsProto.DataNetworkValidation proto = captor.getValue();
+
+        // Make sure variables
+        assertEquals(
+                TelephonyStatsLog.DATA_NETWORK_VALIDATION__NETWORK_TYPE__NETWORK_TYPE_IWLAN,
+                proto.networkType);
+        assertEquals(ApnSetting.TYPE_EMERGENCY, proto.apnTypeBitmask);
+        assertEquals(
+                TelephonyStatsLog.DATA_NETWORK_VALIDATION__SIGNAL_STRENGTH__SIGNAL_STRENGTH_GREAT,
+                proto.signalStrength);
+        assertEquals(TelephonyStatsLog
+                        .DATA_NETWORK_VALIDATION__VALIDATION_RESULT__VALIDATION_RESULT_SUCCESS,
+                proto.validationResult);
+        assertEquals(200L, proto.elapsedTimeInMillis);
+        assertTrue(proto.handoverAttempted);
+        assertEquals(1, proto.networkValidationCount);
+    }
+
+    @Test
+    public void testOnUpdateNetworkValidationStateWithInProgressStatus() {
+
+        // Test
+        mDataNetworkValidationStats.onRequestNetworkValidation(ApnSetting.TYPE_IMS);
+        mDataNetworkValidationStats.elapseTimeInMillis(100L);
+        mDataNetworkValidationStats.onUpdateNetworkValidationState(
+                PreciseDataConnectionState.NETWORK_VALIDATION_NOT_REQUESTED,
+                TelephonyManager.NETWORK_TYPE_LTE);
+
+        // Verify
+        verifyNoMoreInteractions(mPersistAtomsStorage);
+    }
+
+    @Test
+    public void testOnUpdateNetworkValidationStateWithNotRequestedStatus() {
+
+        // Test
+        mDataNetworkValidationStats.onRequestNetworkValidation(ApnSetting.TYPE_IMS);
+        mDataNetworkValidationStats.elapseTimeInMillis(100L);
+        mDataNetworkValidationStats.onUpdateNetworkValidationState(
+                PreciseDataConnectionState.NETWORK_VALIDATION_NOT_REQUESTED,
+                TelephonyManager.NETWORK_TYPE_LTE);
+
+        // Verify
+        verifyNoMoreInteractions(mPersistAtomsStorage);
+    }
+
+    @Test
+    public void testOnUpdateNetworkValidationStateWithUnsupportedStatus() {
+
+        // Set up
+        doReturn(SignalStrength.SIGNAL_STRENGTH_POOR).when(mSignalStrength).getLevel();
+
+        // Test
+        mDataNetworkValidationStats.onRequestNetworkValidation(ApnSetting.TYPE_IMS);
+        mDataNetworkValidationStats.elapseTimeInMillis(300L);
+        mDataNetworkValidationStats.onUpdateNetworkValidationState(
+                PreciseDataConnectionState.NETWORK_VALIDATION_UNSUPPORTED,
+                TelephonyManager.NETWORK_TYPE_LTE);
+
+        // Verify that atom was logged
+        ArgumentCaptor<PersistAtomsProto.DataNetworkValidation> captor =
+                ArgumentCaptor.forClass(PersistAtomsProto.DataNetworkValidation.class);
+        verify(mPersistAtomsStorage, times(1)).addDataNetworkValidation(
+                captor.capture());
+        PersistAtomsProto.DataNetworkValidation proto = captor.getValue();
+
+        // Make sure variables
+        assertEquals(
+                TelephonyStatsLog.DATA_NETWORK_VALIDATION__NETWORK_TYPE__NETWORK_TYPE_LTE,
+                proto.networkType);
+        assertEquals(ApnSetting.TYPE_IMS, proto.apnTypeBitmask);
+        assertEquals(
+                TelephonyStatsLog.DATA_NETWORK_VALIDATION__SIGNAL_STRENGTH__SIGNAL_STRENGTH_POOR,
+                proto.signalStrength);
+        assertEquals(TelephonyStatsLog
+                .DATA_NETWORK_VALIDATION__VALIDATION_RESULT__VALIDATION_RESULT_NOT_SUPPORTED,
+                proto.validationResult);
+        assertEquals(300L, proto.elapsedTimeInMillis);
+        assertFalse(proto.handoverAttempted);
+        assertEquals(1, proto.networkValidationCount);
+    }
+
+
+    @Test
+    public void testOnDataNetworkDisconnected() {
+
+        // Set up
+        doReturn(SignalStrength.SIGNAL_STRENGTH_POOR).when(mSignalStrength).getLevel();
+
+        // Test
+        mDataNetworkValidationStats.onRequestNetworkValidation(ApnSetting.TYPE_IMS);
+        mDataNetworkValidationStats.elapseTimeInMillis(300L);
+        mDataNetworkValidationStats.onDataNetworkDisconnected(TelephonyManager.NETWORK_TYPE_LTE);
+
+        // Verify that atom was logged
+        ArgumentCaptor<PersistAtomsProto.DataNetworkValidation> captor =
+                ArgumentCaptor.forClass(PersistAtomsProto.DataNetworkValidation.class);
+        verify(mPersistAtomsStorage, times(1)).addDataNetworkValidation(
+                captor.capture());
+        PersistAtomsProto.DataNetworkValidation proto = captor.getValue();
+
+        // Make sure variables
+        assertEquals(
+                TelephonyStatsLog.DATA_NETWORK_VALIDATION__NETWORK_TYPE__NETWORK_TYPE_LTE,
+                proto.networkType);
+        assertEquals(ApnSetting.TYPE_IMS, proto.apnTypeBitmask);
+        assertEquals(
+                TelephonyStatsLog.DATA_NETWORK_VALIDATION__SIGNAL_STRENGTH__SIGNAL_STRENGTH_POOR,
+                proto.signalStrength);
+        assertEquals(TelephonyStatsLog
+                .DATA_NETWORK_VALIDATION__VALIDATION_RESULT__VALIDATION_RESULT_UNSPECIFIED,
+                proto.validationResult);
+        assertEquals(300L, proto.elapsedTimeInMillis);
+        assertFalse(proto.handoverAttempted);
+        assertEquals(1, proto.networkValidationCount);
+    }
+
+    @Test
+    public void testOnUpdateNetworkValidationState_DupStatus() {
+
+        // Test
+        mDataNetworkValidationStats.onRequestNetworkValidation(ApnSetting.TYPE_IMS);
+        mDataNetworkValidationStats.elapseTimeInMillis(100L);
+        mDataNetworkValidationStats.onUpdateNetworkValidationState(
+                PreciseDataConnectionState.NETWORK_VALIDATION_IN_PROGRESS,
+                TelephonyManager.NETWORK_TYPE_LTE);
+        mDataNetworkValidationStats.elapseTimeInMillis(100L);
+        mDataNetworkValidationStats.onUpdateNetworkValidationState(
+                PreciseDataConnectionState.NETWORK_VALIDATION_SUCCESS,
+                TelephonyManager.NETWORK_TYPE_UMTS);
+        mDataNetworkValidationStats.elapseTimeInMillis(100L);
+        mDataNetworkValidationStats.onUpdateNetworkValidationState(
+                PreciseDataConnectionState.NETWORK_VALIDATION_FAILURE,
+                TelephonyManager.NETWORK_TYPE_NR);
+
+        // Verify that atom was logged
+        ArgumentCaptor<PersistAtomsProto.DataNetworkValidation> captor =
+                ArgumentCaptor.forClass(PersistAtomsProto.DataNetworkValidation.class);
+        verify(mPersistAtomsStorage, times(1)).addDataNetworkValidation(
+                captor.capture());
+        PersistAtomsProto.DataNetworkValidation proto = captor.getValue();
+
+        // Make sure variables
+        assertEquals(
+                TelephonyStatsLog.DATA_NETWORK_VALIDATION__NETWORK_TYPE__NETWORK_TYPE_UMTS,
+                proto.networkType);
+        assertEquals(ApnSetting.TYPE_IMS, proto.apnTypeBitmask);
+        assertEquals(
+                TelephonyStatsLog.DATA_NETWORK_VALIDATION__SIGNAL_STRENGTH__SIGNAL_STRENGTH_GREAT,
+                proto.signalStrength);
+        assertEquals(TelephonyStatsLog
+                        .DATA_NETWORK_VALIDATION__VALIDATION_RESULT__VALIDATION_RESULT_SUCCESS,
+                proto.validationResult);
+        assertEquals(200L, proto.elapsedTimeInMillis);
+        assertFalse(proto.handoverAttempted);
+        assertEquals(1, proto.networkValidationCount);
+    }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/metrics/MetricsCollectorTest.java b/tests/telephonytests/src/com/android/internal/telephony/metrics/MetricsCollectorTest.java
index bdccb19..04b45b3 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/metrics/MetricsCollectorTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/metrics/MetricsCollectorTest.java
@@ -78,8 +78,8 @@
                     .setCoolDownMillis(24L * 3600L * 1000L)
                     .build();
     private static final long MIN_COOLDOWN_MILLIS = 23L * 3600L * 1000L;
-    private static final long CELL_SERVICE_MIN_COOLDOWN_MILLIS =
-            IS_DEBUGGABLE ? 4L *  60L * 1000L : MIN_COOLDOWN_MILLIS;
+    private static final long POWER_CORRELATED_MIN_COOLDOWN_MILLIS =
+            IS_DEBUGGABLE ? 4L *  60L * 1000L : 5L * 3600L * 1000L;
     private static final long MIN_CALLS_PER_BUCKET = 5L;
 
     // NOTE: these fields are currently 32-bit internally and padded to 64-bit by TelephonyManager
@@ -410,6 +410,9 @@
     @SmallTest
     public void onPullAtom_cellularServiceState_tooFrequent() throws Exception {
         doReturn(null).when(mPersistAtomsStorage).getCellularServiceStates(anyLong());
+        mContextFixture.putIntResource(
+                com.android.internal.R.integer.config_metrics_pull_cooldown_millis,
+                (int) POWER_CORRELATED_MIN_COOLDOWN_MILLIS);
         List<StatsEvent> actualAtoms = new ArrayList<>();
 
         int result = mMetricsCollector.onPullAtom(CELLULAR_SERVICE_STATE, actualAtoms);
@@ -417,7 +420,7 @@
         assertThat(actualAtoms).hasSize(0);
         assertThat(result).isEqualTo(StatsManager.PULL_SKIP);
         verify(mPersistAtomsStorage, times(1)).getCellularServiceStates(
-                eq(CELL_SERVICE_MIN_COOLDOWN_MILLIS));
+                eq(POWER_CORRELATED_MIN_COOLDOWN_MILLIS));
         verifyNoMoreInteractions(mPersistAtomsStorage);
     }
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/metrics/PersistAtomsStorageTest.java b/tests/telephonytests/src/com/android/internal/telephony/metrics/PersistAtomsStorageTest.java
index c1e4464..a9b183f 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/metrics/PersistAtomsStorageTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/metrics/PersistAtomsStorageTest.java
@@ -62,10 +62,13 @@
 import android.content.Context;
 import android.os.Build;
 import android.telephony.DisconnectCause;
+import android.telephony.PreciseDataConnectionState;
 import android.telephony.SatelliteProtoEnums;
 import android.telephony.ServiceState;
+import android.telephony.SignalStrength;
 import android.telephony.TelephonyManager;
 import android.telephony.TelephonyProtoEnums;
+import android.telephony.data.ApnSetting;
 import android.telephony.ims.ImsReasonInfo;
 import android.telephony.ims.SipDelegateManager;
 
@@ -78,6 +81,7 @@
 import com.android.internal.telephony.nano.PersistAtomsProto.CellularDataServiceSwitch;
 import com.android.internal.telephony.nano.PersistAtomsProto.CellularServiceState;
 import com.android.internal.telephony.nano.PersistAtomsProto.DataCallSession;
+import com.android.internal.telephony.nano.PersistAtomsProto.DataNetworkValidation;
 import com.android.internal.telephony.nano.PersistAtomsProto.GbaEvent;
 import com.android.internal.telephony.nano.PersistAtomsProto.ImsDedicatedBearerEvent;
 import com.android.internal.telephony.nano.PersistAtomsProto.ImsDedicatedBearerListenerEvent;
@@ -290,6 +294,12 @@
     private SatelliteSosMessageRecommender mSatelliteSosMessageRecommender2;
     private SatelliteSosMessageRecommender[] mSatelliteSosMessageRecommenders;
 
+    private DataNetworkValidation mDataNetworkValidationLte1;
+    private DataNetworkValidation mDataNetworkValidationLte2;
+    private DataNetworkValidation mDataNetworkValidationIwlan1;
+    private DataNetworkValidation mDataNetworkValidationIwlan2;
+    private DataNetworkValidation[] mDataNetworkValidations;
+
     private CarrierRoamingSatelliteSession mCarrierRoamingSatelliteSession1;
     private CarrierRoamingSatelliteSession mCarrierRoamingSatelliteSession2;
     private CarrierRoamingSatelliteSession[] mCarrierRoamingSatelliteSessions;
@@ -1090,6 +1100,10 @@
 
         mOutgoingShortCodeSms = new OutgoingShortCodeSms[] {mOutgoingShortCodeSms1,
                 mOutgoingShortCodeSms2};
+
+        generateTestSatelliteData();
+
+        generateTestDataNetworkValidationsData();
     }
 
     private void generateTestSatelliteData() {
@@ -1378,6 +1392,61 @@
                 mSatelliteConfigUpdater2};
     }
 
+    private void generateTestDataNetworkValidationsData() {
+
+        // for LTE #1
+        mDataNetworkValidationLte1 = new DataNetworkValidation();
+        mDataNetworkValidationLte1.networkType = TelephonyManager.NETWORK_TYPE_LTE;
+        mDataNetworkValidationLte1.apnTypeBitmask = ApnSetting.TYPE_IMS;
+        mDataNetworkValidationLte1.signalStrength = SignalStrength.SIGNAL_STRENGTH_GREAT;
+        mDataNetworkValidationLte1.validationResult =
+                PreciseDataConnectionState.NETWORK_VALIDATION_SUCCESS;
+        mDataNetworkValidationLte1.elapsedTimeInMillis = 100L;
+        mDataNetworkValidationLte1.handoverAttempted = false;
+        mDataNetworkValidationLte1.networkValidationCount = 1;
+
+        // for LTE #2
+        mDataNetworkValidationLte2 = new DataNetworkValidation();
+        mDataNetworkValidationLte2.networkType = TelephonyManager.NETWORK_TYPE_LTE;
+        mDataNetworkValidationLte2.apnTypeBitmask = ApnSetting.TYPE_IMS;
+        mDataNetworkValidationLte2.signalStrength = SignalStrength.SIGNAL_STRENGTH_GREAT;
+        mDataNetworkValidationLte2.validationResult =
+                PreciseDataConnectionState.NETWORK_VALIDATION_SUCCESS;
+        mDataNetworkValidationLte2.elapsedTimeInMillis = 100L;
+        mDataNetworkValidationLte2.handoverAttempted = false;
+        mDataNetworkValidationLte2.networkValidationCount = 1;
+
+        // for IWLAN #1
+        mDataNetworkValidationIwlan1 = new DataNetworkValidation();
+        mDataNetworkValidationIwlan1.networkType = TelephonyManager.NETWORK_TYPE_IWLAN;
+        mDataNetworkValidationIwlan1.apnTypeBitmask = ApnSetting.TYPE_IMS;
+        mDataNetworkValidationIwlan1.signalStrength = SignalStrength.SIGNAL_STRENGTH_POOR;
+        mDataNetworkValidationIwlan1.validationResult =
+                PreciseDataConnectionState.NETWORK_VALIDATION_FAILURE;
+        mDataNetworkValidationIwlan1.elapsedTimeInMillis = 10000L;
+        mDataNetworkValidationIwlan1.handoverAttempted = false;
+        mDataNetworkValidationIwlan1.networkValidationCount = 1;
+
+        // for IWLAN #2
+        mDataNetworkValidationIwlan2 = new DataNetworkValidation();
+        mDataNetworkValidationIwlan2.networkType = TelephonyManager.NETWORK_TYPE_IWLAN;
+        mDataNetworkValidationIwlan2.apnTypeBitmask = ApnSetting.TYPE_IMS;
+        mDataNetworkValidationIwlan2.signalStrength = SignalStrength.SIGNAL_STRENGTH_POOR;
+        mDataNetworkValidationIwlan2.validationResult =
+                PreciseDataConnectionState.NETWORK_VALIDATION_FAILURE;
+        mDataNetworkValidationIwlan2.elapsedTimeInMillis = 30000L;
+        mDataNetworkValidationIwlan2.handoverAttempted = false;
+        mDataNetworkValidationIwlan2.networkValidationCount = 1;
+
+        mDataNetworkValidations =
+                new DataNetworkValidation[] {
+                        mDataNetworkValidationLte1,
+                        mDataNetworkValidationLte1,
+                        mDataNetworkValidationIwlan1,
+                        mDataNetworkValidationIwlan2,
+                };
+    }
+
     private static class TestablePersistAtomsStorage extends PersistAtomsStorage {
         private long mTimeMillis = START_TIME_MILLIS;
 
@@ -1543,6 +1612,10 @@
         mSatelliteSosMessageRecommender1 = null;
         mSatelliteSosMessageRecommender2 = null;
         mSatelliteSosMessageRecommenders = null;
+        mDataNetworkValidationLte1 = null;
+        mDataNetworkValidationLte2 = null;
+        mDataNetworkValidationIwlan1 = null;
+        mDataNetworkValidationIwlan2 = null;
         mCarrierRoamingSatelliteSession1 = null;
         mCarrierRoamingSatelliteSession2 = null;
         mCarrierRoamingSatelliteSessions = null;
@@ -5110,6 +5183,77 @@
 
     @Test
     @SmallTest
+    public void addDataNetworkValidation_newEntry() throws Exception {
+        createEmptyTestFile();
+        mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+
+        mPersistAtomsStorage.addDataNetworkValidation(mDataNetworkValidationLte1);
+        mPersistAtomsStorage.addDataNetworkValidation(mDataNetworkValidationIwlan1);
+        mPersistAtomsStorage.incTimeMillis(100L);
+
+        // There should be 2 DataNetworkValidation
+        verifyCurrentStateSavedToFileOnce();
+        DataNetworkValidation[] output = mPersistAtomsStorage.getDataNetworkValidation(0L);
+        assertProtoArrayEqualsIgnoringOrder(
+                new DataNetworkValidation[] {
+                        mDataNetworkValidationLte1, mDataNetworkValidationIwlan1},
+                output);
+    }
+
+    @Test
+    public void addDataNetworkValidation_existingEntry() throws Exception {
+        createEmptyTestFile();
+        mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+
+        int expectedNetworkValidationCount =
+                mDataNetworkValidationLte1.networkValidationCount
+                        + mDataNetworkValidationLte2.networkValidationCount;
+
+        mPersistAtomsStorage.addDataNetworkValidation(mDataNetworkValidationLte1);
+        mPersistAtomsStorage.addDataNetworkValidation(mDataNetworkValidationLte2);
+        mPersistAtomsStorage.incTimeMillis(100L);
+
+        DataNetworkValidation expected = copyOf(mDataNetworkValidationLte1);
+        expected.networkValidationCount = expectedNetworkValidationCount;
+
+        // There should be 1 DataNetworkValidation
+        verifyCurrentStateSavedToFileOnce();
+        DataNetworkValidation[] output =
+                mPersistAtomsStorage.getDataNetworkValidation(0L);
+        assertProtoArrayEqualsIgnoringOrder(
+                new DataNetworkValidation[] {expected},
+                output);
+    }
+
+    @Test
+    public void addDataNetworkValidation_tooManyEntries() throws Exception {
+
+        // Set up
+        doReturn(false).when(mPackageManager).hasSystemFeature(anyString());
+
+        createEmptyTestFile();
+        mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+
+        // Currently, the maximum number that can be stored for DataNetworkValidation in Atom
+        // storage is 15. Try saving excess.
+        int maxNumDataNetworkValidation = 20;
+        mPersistAtomsStorage.addDataNetworkValidation(mDataNetworkValidationLte1);
+        for (int i = 0; i < maxNumDataNetworkValidation; i++) {
+            DataNetworkValidation copied = copyOf(mDataNetworkValidationLte1);
+            copied.apnTypeBitmask = mDataNetworkValidationLte1.apnTypeBitmask + i;
+            mPersistAtomsStorage.addDataNetworkValidation(copied);
+        }
+        mPersistAtomsStorage.incTimeMillis(100L);
+
+        // There should be less than or equal to maxNumDataNetworkValidation
+        verifyCurrentStateSavedToFileOnce();
+        DataNetworkValidation[] output =
+                mPersistAtomsStorage.getDataNetworkValidation(0L);
+        assertTrue(output.length <= maxNumDataNetworkValidation);
+    }
+
+    @Test
+    @SmallTest
     public void clearAtoms() throws Exception {
         createTestFile(START_TIME_MILLIS);
         mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
@@ -5193,6 +5337,8 @@
         atoms.satelliteProvisionPullTimestampMillis = lastPullTimeMillis;
         atoms.satelliteSosMessageRecommender = mSatelliteSosMessageRecommenders;
         atoms.satelliteSosMessageRecommenderPullTimestampMillis = lastPullTimeMillis;
+        atoms.dataNetworkValidation = mDataNetworkValidations;
+        atoms.dataNetworkValidationPullTimestampMillis = lastPullTimeMillis;
         atoms.carrierRoamingSatelliteSession = mCarrierRoamingSatelliteSessions;
         atoms.carrierRoamingSatelliteSessionPullTimestampMillis = lastPullTimeMillis;
         atoms.carrierRoamingSatelliteControllerStats = mCarrierRoamingSatelliteControllerStats;
@@ -5364,6 +5510,11 @@
         return SatelliteSosMessageRecommender.parseFrom(MessageNano.toByteArray(source));
     }
 
+    private static DataNetworkValidation copyOf(DataNetworkValidation source)
+            throws Exception {
+        return DataNetworkValidation.parseFrom(MessageNano.toByteArray(source));
+    }
+
     private static CarrierRoamingSatelliteSession copyOf(CarrierRoamingSatelliteSession source)
             throws Exception {
         return CarrierRoamingSatelliteSession.parseFrom(MessageNano.toByteArray(source));
diff --git a/tests/telephonytests/src/com/android/internal/telephony/security/CellularNetworkSecuritySafetySourceTest.java b/tests/telephonytests/src/com/android/internal/telephony/security/CellularNetworkSecuritySafetySourceTest.java
index 76118c4..e409b8d 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/security/CellularNetworkSecuritySafetySourceTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/security/CellularNetworkSecuritySafetySourceTest.java
@@ -36,6 +36,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.safetycenter.SafetySourceData;
+import android.safetycenter.SafetySourceIssue;
 import android.util.Singleton;
 
 import com.android.internal.R;
@@ -50,6 +51,7 @@
 import org.mockito.ArgumentCaptor;
 
 import java.time.Instant;
+import java.util.List;
 
 public final class CellularNetworkSecuritySafetySourceTest extends TelephonyTest {
 
@@ -76,13 +78,19 @@
 
         mContextFixture.putResource(R.string.scCellularNetworkSecurityTitle, "fake");
         mContextFixture.putResource(R.string.scCellularNetworkSecuritySummary, "fake");
-        mContextFixture.putResource(R.string.scNullCipherIssueNonEncryptedTitle, "fake %1$s");
-        mContextFixture.putResource(R.string.scNullCipherIssueNonEncryptedSummary, "fake");
+        mContextFixture.putResource(R.string.scCellularNetworkSecurityLearnMore,
+                "https://support.google.com/android?p=cellular_security");
+        mContextFixture.putResource(R.string.scNullCipherIssueNonEncryptedTitle, "fake");
+        mContextFixture.putResource(R.string.scNullCipherIssueNonEncryptedSummary, "fake %1$s");
+        mContextFixture.putResource(R.string.scNullCipherIssueNonEncryptedSummaryNotification,
+                "fake %1$s");
         mContextFixture.putResource(R.string.scNullCipherIssueEncryptedTitle, "fake %1$s");
-        mContextFixture.putResource(R.string.scNullCipherIssueEncryptedSummary, "fake");
+        mContextFixture.putResource(R.string.scNullCipherIssueEncryptedSummary, "fake %1$s");
         mContextFixture.putResource(R.string.scIdentifierDisclosureIssueTitle, "fake");
+        mContextFixture.putResource(R.string.scIdentifierDisclosureIssueSummaryNotification,
+                "fake %1$s %2$s");
         mContextFixture.putResource(
-                R.string.scIdentifierDisclosureIssueSummary, "fake %1$d %2$tr %3$tr %4$s");
+                R.string.scIdentifierDisclosureIssueSummary, "fake %1$s %2$s");
         mContextFixture.putResource(R.string.scNullCipherIssueActionSettings, "fake");
         mContextFixture.putResource(R.string.scNullCipherIssueActionLearnMore, "fake");
 
@@ -172,6 +180,26 @@
     }
 
     @Test
+    public void clearNullCipherState() {
+        ArgumentCaptor<SafetySourceData> data = ArgumentCaptor.forClass(SafetySourceData.class);
+
+        mSafetySource.setNullCipherIssueEnabled(mContext, true);
+        mSafetySource.setNullCipherState(mContext, 0, NULL_CIPHER_STATE_NOTIFY_ENCRYPTED);
+        mSafetySource.clearNullCipherState(mContext, 0);
+
+        // Once for enable, once for encrypted state, and once for clearing state
+        verify(mSafetyCenterManagerWrapper, times(3)).setSafetySourceData(data.capture());
+
+        // initial check that our encrypted state update created an issue
+        assertThat(data.getAllValues().get(1).getStatus()).isNotNull();
+        assertThat(data.getAllValues().get(1).getIssues()).hasSize(1);
+
+        // assert that our last call to clear state results in no issues sent to SC
+        assertThat(data.getAllValues().get(2).getStatus()).isNotNull();
+        assertThat(data.getAllValues().get(2).getIssues()).isEmpty();
+    }
+
+    @Test
     public void disableIdentifierDisclosueIssue_nullData() {
         // We must first enable before disabling, since a standalone call to disable may result in
         // a no-op when the default for a new notifier is to be disabled.
@@ -245,4 +273,24 @@
         assertThat(data.getAllValues().get(3).getStatus()).isNotNull();
         assertThat(data.getAllValues().get(3).getIssues()).hasSize(2);
     }
+
+    @Test
+    public void learnMoreLinkEmpty_learnMoreIsHidden() {
+        mContextFixture.putResource(R.string.scCellularNetworkSecurityLearnMore, "");
+
+        ArgumentCaptor<SafetySourceData> data = ArgumentCaptor.forClass(SafetySourceData.class);
+
+        mSafetySource.setNullCipherIssueEnabled(mContext, true);
+        mSafetySource.setNullCipherState(mContext, 0, NULL_CIPHER_STATE_NOTIFY_NON_ENCRYPTED);
+        mSafetySource.setIdentifierDisclosureIssueEnabled(mContext, true);
+        mSafetySource.setIdentifierDisclosure(mContext, 0, 12, Instant.now(), Instant.now());
+
+        verify(mSafetyCenterManagerWrapper, times(4)).setSafetySourceData(data.capture());
+        List<SafetySourceIssue.Action> actions = data.getAllValues().get(
+                3).getIssues().getFirst().getActions();
+
+        // we only see the action that takes you to the settings page
+        assertThat(actions).hasSize(1);
+        assertThat(actions.getFirst().getId()).isEqualTo("cellular_security_settings");
+    }
 }
\ No newline at end of file
diff --git a/tests/telephonytests/src/com/android/internal/telephony/security/NullCipherNotifierTest.java b/tests/telephonytests/src/com/android/internal/telephony/security/NullCipherNotifierTest.java
index 0bb7b76..6a4d2fb 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/security/NullCipherNotifierTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/security/NullCipherNotifierTest.java
@@ -45,6 +45,7 @@
 public class NullCipherNotifierTest {
 
     private static final int SUB_ID = 3425;
+    private static final int PHONE_ID = 999;
     private static final List<Integer> NON_TRANSPORT_LAYER_EVENTS =
             List.of(SecurityAlgorithmUpdate.CONNECTION_EVENT_VOLTE_SIP,
                     SecurityAlgorithmUpdate.CONNECTION_EVENT_VOLTE_SIP_SOS,
@@ -149,9 +150,11 @@
     @Test
     public void onSecurityAlgorithmUpdate_enabled_unprotectedEmergency_noSafetySourceUpdate() {
         NullCipherNotifier notifier = new NullCipherNotifier(mExecutor, mSafetySource);
+        notifier.enable(mContext);
 
         notifier.onSecurityAlgorithmUpdate(
                 mContext,
+                PHONE_ID,
                 SUB_ID,
                 new SecurityAlgorithmUpdate(
                         SecurityAlgorithmUpdate.CONNECTION_EVENT_AS_SIGNALLING_5G,
@@ -163,6 +166,24 @@
     }
 
     @Test
+    public void onSecurityAlgorithmUpdate_disabled_noSafetySourceUpdate() {
+        NullCipherNotifier notifier = new NullCipherNotifier(mExecutor, mSafetySource);
+        notifier.disable(mContext);
+
+        notifier.onSecurityAlgorithmUpdate(
+                mContext,
+                PHONE_ID,
+                SUB_ID,
+                new SecurityAlgorithmUpdate(
+                        SecurityAlgorithmUpdate.CONNECTION_EVENT_AS_SIGNALLING_5G,
+                        SecurityAlgorithmUpdate.SECURITY_ALGORITHM_EEA2,
+                        SecurityAlgorithmUpdate.SECURITY_ALGORITHM_HMAC_SHA1_96,
+                        /* isUnprotectedEmergency= */ false));
+
+        verify(mSafetySource, never()).setNullCipherState(any(), anyInt(), anyInt());
+    }
+
+    @Test
     public void onSecurityAlgorithmUpdate_enabled_nonTransportLayerEvent_noSafetySourceUpdate() {
         NullCipherNotifier notifier = new NullCipherNotifier(mExecutor, mSafetySource);
 
@@ -170,6 +191,7 @@
             clearInvocations(mSafetySource);
             notifier.onSecurityAlgorithmUpdate(
                     mContext,
+                    PHONE_ID,
                     SUB_ID,
                     new SecurityAlgorithmUpdate(
                             connectionEvent,
@@ -185,12 +207,14 @@
     @Test
     public void onUpdate_enabled_transportLayerEvent_encryptionNullCipher_notifyNonEncrypted() {
         NullCipherNotifier notifier = new NullCipherNotifier(mExecutor, mSafetySource);
+        notifier.enable(mContext);
 
         for (int connectionEvent : TRANSPORT_LAYER_EVENTS) {
             for (int encryptionAlgorithm : NULL_CIPHERS) {
                 clearInvocations(mSafetySource);
                 notifier.onSecurityAlgorithmUpdate(
                         mContext,
+                        PHONE_ID,
                         SUB_ID,
                         new SecurityAlgorithmUpdate(
                                 connectionEvent,
@@ -214,12 +238,14 @@
     @Test
     public void onUpdate_enabled_transportLayerEvent_integrityNullCipher_notifyNonEncrypted() {
         NullCipherNotifier notifier = new NullCipherNotifier(mExecutor, mSafetySource);
+        notifier.enable(mContext);
 
         for (int connectionEvent : TRANSPORT_LAYER_EVENTS) {
             for (int integrityAlgorithm : NULL_CIPHERS) {
                 clearInvocations(mSafetySource);
                 notifier.onSecurityAlgorithmUpdate(
                         mContext,
+                        PHONE_ID,
                         SUB_ID,
                         new SecurityAlgorithmUpdate(
                                 connectionEvent,
@@ -243,12 +269,14 @@
     @Test
     public void onUpdate_enabled_transportLayerEvent_encryptionNonNullCipher_encrypted() {
         NullCipherNotifier notifier = new NullCipherNotifier(mExecutor, mSafetySource);
+        notifier.enable(mContext);
 
         for (int connectionEvent : TRANSPORT_LAYER_EVENTS) {
             for (int encryptionAlgorithm : NON_NULL_CIPHERS) {
                 clearInvocations(mSafetySource);
                 notifier.onSecurityAlgorithmUpdate(
                         mContext,
+                        PHONE_ID,
                         SUB_ID,
                         new SecurityAlgorithmUpdate(
                                 connectionEvent,
@@ -272,12 +300,14 @@
     @Test
     public void onUpdate_enabled_transportLayerEvent_integrityNonNullCipher_encrypted() {
         NullCipherNotifier notifier = new NullCipherNotifier(mExecutor, mSafetySource);
+        notifier.enable(mContext);
 
         for (int connectionEvent : TRANSPORT_LAYER_EVENTS) {
             for (int integrityAlgorithm : NON_NULL_CIPHERS) {
                 clearInvocations(mSafetySource);
                 notifier.onSecurityAlgorithmUpdate(
                         mContext,
+                        PHONE_ID,
                         SUB_ID,
                         new SecurityAlgorithmUpdate(
                                 connectionEvent,
@@ -301,11 +331,13 @@
     @Test
     public void onUpdate_enabled_transportLayerEvent_encryptionNonNullCipher_notifyEncrypted() {
         NullCipherNotifier notifier = new NullCipherNotifier(mExecutor, mSafetySource);
+        notifier.enable(mContext);
 
         for (int connectionEvent : TRANSPORT_LAYER_EVENTS) {
             for (int encryptionAlgorithm : NON_NULL_CIPHERS) {
                 notifier.onSecurityAlgorithmUpdate(
                         mContext,
+                        PHONE_ID,
                         SUB_ID,
                         new SecurityAlgorithmUpdate(
                                 connectionEvent,
@@ -316,6 +348,7 @@
                 clearInvocations(mSafetySource);
                 notifier.onSecurityAlgorithmUpdate(
                         mContext,
+                        PHONE_ID,
                         SUB_ID,
                         new SecurityAlgorithmUpdate(
                                 connectionEvent,
@@ -345,6 +378,7 @@
             for (int integrityAlgorithm : NON_NULL_CIPHERS) {
                 notifier.onSecurityAlgorithmUpdate(
                         mContext,
+                        PHONE_ID,
                         SUB_ID,
                         new SecurityAlgorithmUpdate(
                                 connectionEvent,
@@ -355,6 +389,7 @@
                 clearInvocations(mSafetySource);
                 notifier.onSecurityAlgorithmUpdate(
                         mContext,
+                        PHONE_ID,
                         SUB_ID,
                         new SecurityAlgorithmUpdate(
                                 connectionEvent,
@@ -374,4 +409,88 @@
             }
         }
     }
+
+    @Test
+    public void onSecurityAlgorithmUpdate_updateSubscriptionId_clearsOldId() {
+        NullCipherNotifier notifier = new NullCipherNotifier(mExecutor, mSafetySource);
+        notifier.enable(mContext);
+
+        int connectionEvent = SecurityAlgorithmUpdate.CONNECTION_EVENT_AS_SIGNALLING_5G;
+        int encryptionAlgorithm = SecurityAlgorithmUpdate.SECURITY_ALGORITHM_EEA2;
+        int subId2 = 1337;
+
+        notifier.onSecurityAlgorithmUpdate(
+                mContext,
+                PHONE_ID,
+                SUB_ID,
+                getWellEncryptedUpdate());
+
+        notifier.setSubscriptionMapping(mContext, PHONE_ID, subId2);
+
+        verify(
+                mSafetySource,
+                times(1).description(
+                        "Connection event: " + connectionEvent
+                                + " Encryption algorithm: " + encryptionAlgorithm))
+                .setNullCipherState(
+                        eq(mContext),
+                        eq(SUB_ID),
+                        eq(NULL_CIPHER_STATE_ENCRYPTED));
+
+        verify(mSafetySource, times(1)).clearNullCipherState(eq(mContext), eq(SUB_ID));
+    }
+
+    @Test
+    public void onSecurityAlgorithmUpdate_newSubIdOnAlgoUpdate_clearsOldId() {
+        NullCipherNotifier notifier = new NullCipherNotifier(mExecutor, mSafetySource);
+        notifier.enable(mContext);
+
+        int connectionEvent = SecurityAlgorithmUpdate.CONNECTION_EVENT_AS_SIGNALLING_5G;
+        int encryptionAlgorithm = SecurityAlgorithmUpdate.SECURITY_ALGORITHM_EEA2;
+        int subId2 = 1337;
+
+        notifier.onSecurityAlgorithmUpdate(
+                mContext,
+                PHONE_ID,
+                SUB_ID,
+                getWellEncryptedUpdate());
+
+        notifier.onSecurityAlgorithmUpdate(
+                mContext,
+                PHONE_ID,
+                subId2,
+                getWellEncryptedUpdate());
+
+        verify(
+                mSafetySource,
+                times(1).description(
+                        "SubId: " + SUB_ID + "Connection event: " + connectionEvent
+                                + " Encryption algorithm: " + encryptionAlgorithm))
+                .setNullCipherState(
+                        eq(mContext),
+                        eq(SUB_ID),
+                        eq(NULL_CIPHER_STATE_ENCRYPTED));
+
+        // The update with subId2 should clear subId1 data since they have the same phone id
+        verify(mSafetySource, times(1)).clearNullCipherState(eq(mContext), eq(SUB_ID));
+
+        verify(
+                mSafetySource,
+                times(1).description(
+                        "SubId: " + SUB_ID + "Connection event: " + connectionEvent
+                                + " Encryption algorithm: " + encryptionAlgorithm))
+                .setNullCipherState(
+                        eq(mContext),
+                        eq(subId2),
+                        eq(NULL_CIPHER_STATE_ENCRYPTED));
+
+    }
+
+    private SecurityAlgorithmUpdate getWellEncryptedUpdate() {
+        return new SecurityAlgorithmUpdate(
+                SecurityAlgorithmUpdate.CONNECTION_EVENT_AS_SIGNALLING_5G,
+                SecurityAlgorithmUpdate.SECURITY_ALGORITHM_EEA2,
+                SecurityAlgorithmUpdate.SECURITY_ALGORITHM_HMAC_SHA1_96,
+                /* isUnprotectedEmergency= */ false);
+    }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/subscription/SubscriptionDatabaseManagerTest.java b/tests/telephonytests/src/com/android/internal/telephony/subscription/SubscriptionDatabaseManagerTest.java
index 7339e42..62b9def 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/subscription/SubscriptionDatabaseManagerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/subscription/SubscriptionDatabaseManagerTest.java
@@ -76,8 +76,8 @@
     static final String FAKE_DEFAULT_CARD_NAME = "CARD %d";
     static final String FAKE_ICCID1 = "123456";
     static final String FAKE_ICCID2 = "456789";
-    static final String FAKE_PHONE_NUMBER1 = "6502530000";
-    static final String FAKE_PHONE_NUMBER2 = "4089961010";
+    static final String FAKE_PHONE_NUMBER1 = "9995551234";
+    static final String FAKE_PHONE_NUMBER2 = "9998887777";
     static final String FAKE_CARRIER_NAME1 = "A-Mobile";
     static final String FAKE_CARRIER_NAME2 = "B-Mobile";
     static final int FAKE_COLOR1 = 1;
diff --git a/tests/telephonytests/src/com/android/internal/telephony/subscription/SubscriptionManagerServiceTest.java b/tests/telephonytests/src/com/android/internal/telephony/subscription/SubscriptionManagerServiceTest.java
index 9fee311..eb06ff1 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/subscription/SubscriptionManagerServiceTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/subscription/SubscriptionManagerServiceTest.java
@@ -44,6 +44,7 @@
 import static com.android.internal.telephony.subscription.SubscriptionDatabaseManagerTest.FAKE_RCS_CONFIG1;
 import static com.android.internal.telephony.subscription.SubscriptionDatabaseManagerTest.FAKE_RCS_CONFIG2;
 import static com.android.internal.telephony.subscription.SubscriptionDatabaseManagerTest.FAKE_SATELLITE_ENTITLEMENT_PLMNS1;
+import static com.android.internal.telephony.subscription.SubscriptionDatabaseManagerTest.FAKE_SATELLITE_IS_NTN_DISABLED;
 import static com.android.internal.telephony.subscription.SubscriptionDatabaseManagerTest.FAKE_SUBSCRIPTION_INFO1;
 import static com.android.internal.telephony.subscription.SubscriptionDatabaseManagerTest.FAKE_SUBSCRIPTION_INFO2;
 import static com.android.internal.telephony.subscription.SubscriptionDatabaseManagerTest.FAKE_UUID1;
@@ -243,7 +244,6 @@
         doReturn(false).when(mFlags).enforceTelephonyFeatureMappingForPublicApis();
         doReturn(true).when(mPackageManager).hasSystemFeature(
                 eq(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION));
-
         logd("SubscriptionManagerServiceTest -Setup!");
     }
 
@@ -426,21 +426,35 @@
     @Test
     public void testSetAdminOwned() {
         mContextFixture.addCallingOrSelfPermission(Manifest.permission.MODIFY_PHONE_STATE);
-        mSubscriptionManagerServiceUT.addSubInfo(FAKE_ICCID1, FAKE_CARRIER_NAME1,
-                0, SubscriptionManager.SUBSCRIPTION_TYPE_LOCAL_SIM);
+        mSubscriptionManagerServiceUT.addSubInfo(FAKE_ICCID1, FAKE_CARRIER_NAME1, 0,
+                SubscriptionManager.SUBSCRIPTION_TYPE_LOCAL_SIM);
         processAllMessages();
         String groupOwner = "test";
 
         mSubscriptionManagerServiceUT.setGroupOwner(1, groupOwner);
 
-        SubscriptionInfoInternal subInfo = mSubscriptionManagerServiceUT
-                .getSubscriptionInfoInternal(1);
+        SubscriptionInfoInternal subInfo =
+                mSubscriptionManagerServiceUT.getSubscriptionInfoInternal(1);
         assertThat(subInfo).isNotNull();
         assertThat(subInfo.getGroupOwner()).isEqualTo(groupOwner);
         verify(mMockedSubscriptionManagerServiceCallback).onSubscriptionChanged(eq(1));
     }
 
     @Test
+    public void testSetGroupOwner_callerMissingpPermission_throws() {
+        mContextFixture.addCallingOrSelfPermission(Manifest.permission.MODIFY_PHONE_STATE);
+        mSubscriptionManagerServiceUT.addSubInfo(FAKE_ICCID1, FAKE_CARRIER_NAME1, 0,
+                SubscriptionManager.SUBSCRIPTION_TYPE_LOCAL_SIM);
+        processAllMessages();
+        String groupOwner = "test";
+        // Remove MODIFY_PHONE_STATE
+        mContextFixture.removeCallingOrSelfPermission(Manifest.permission.MODIFY_PHONE_STATE);
+
+        assertThrows(SecurityException.class,
+                () -> mSubscriptionManagerServiceUT.setGroupOwner(1, groupOwner));
+    }
+
+    @Test
     @DisableCompatChanges({TelephonyManager.ENABLE_FEATURE_MAPPING})
     public void testSetPhoneNumber() {
         doReturn(false).when(mFlags).enforceTelephonyFeatureMapping();
@@ -484,7 +498,8 @@
 
     @Test
     @EnableCompatChanges({TelephonyManager.ENABLE_FEATURE_MAPPING})
-    public void testSetPhoneNumber_EnabledEnforceTelephonyFeatureMappingForPublicApis() {
+    public void testSetPhoneNumber_EnabledEnforceTelephonyFeatureMappingForPublicApis()
+            throws Exception {
         mContextFixture.addCallingOrSelfPermission(Manifest.permission.MODIFY_PHONE_STATE);
         mSubscriptionManagerServiceUT.addSubInfo(FAKE_ICCID1, FAKE_CARRIER_NAME1,
                 0, SubscriptionManager.SUBSCRIPTION_TYPE_LOCAL_SIM);
@@ -496,6 +511,11 @@
         // Grant carrier privilege
         setCarrierPrivilegesForSubId(true, 1);
 
+        // Replace field to set SDK version of vendor partition to Android V
+        int vendorApiLevel = Build.VERSION_CODES.VANILLA_ICE_CREAM;
+        replaceInstance(SubscriptionManagerService.class, "mVendorApiLevel",
+                mSubscriptionManagerServiceUT, vendorApiLevel);
+
         // Enabled FeatureFlags and ENABLE_FEATURE_MAPPING, telephony features are defined
         doReturn(true).when(mFlags).enforceTelephonyFeatureMappingForPublicApis();
         doReturn(true).when(mPackageManager).hasSystemFeature(
@@ -1788,6 +1808,45 @@
     }
 
     @Test
+    public void testGetPhoneNumberSourcePriority() throws Exception {
+        mContextFixture.addCallingOrSelfPermission(Manifest.permission.READ_PHONE_NUMBERS);
+
+        String phoneNumberFromCarrier = "8675309";
+        String phoneNumberFromUicc = "1112223333";
+        String phoneNumberFromIms = "5553466";
+        String phoneNumberFromPhoneObject = "8001234567";
+
+        doReturn(phoneNumberFromPhoneObject).when(mPhone).getLine1Number();
+
+        SubscriptionInfoInternal multiNumberSubInfo =
+                new SubscriptionInfoInternal.Builder(FAKE_SUBSCRIPTION_INFO1)
+                        .setNumberFromCarrier(phoneNumberFromCarrier)
+                        .setNumber(phoneNumberFromUicc)
+                        .setNumberFromIms(phoneNumberFromIms)
+                        .build();
+        int subId = insertSubscription(multiNumberSubInfo);
+
+        assertThat(mSubscriptionManagerServiceUT.getPhoneNumberFromFirstAvailableSource(
+                subId, CALLING_PACKAGE, CALLING_FEATURE)).isEqualTo(phoneNumberFromCarrier);
+
+        multiNumberSubInfo =
+                new SubscriptionInfoInternal.Builder(multiNumberSubInfo)
+                        .setNumberFromCarrier("")
+                        .setNumber(phoneNumberFromUicc)
+                        .setNumberFromIms(phoneNumberFromIms)
+                        .build();
+        subId = insertSubscription(multiNumberSubInfo);
+
+        assertThat(mSubscriptionManagerServiceUT.getPhoneNumberFromFirstAvailableSource(
+                subId, CALLING_PACKAGE, CALLING_FEATURE)).isEqualTo(phoneNumberFromPhoneObject);
+
+        doReturn("").when(mPhone).getLine1Number();
+
+        assertThat(mSubscriptionManagerServiceUT.getPhoneNumberFromFirstAvailableSource(
+                subId, CALLING_PACKAGE, CALLING_FEATURE)).isEqualTo(phoneNumberFromIms);
+    }
+
+    @Test
     public void testSetUiccApplicationsEnabled() {
         insertSubscription(FAKE_SUBSCRIPTION_INFO1);
 
@@ -2483,6 +2542,39 @@
     }
 
     @Test
+    public void testGetPhoneNumberFromDefaultSubscription() {
+        doReturn(true).when(mFlags).saferGetPhoneNumber();
+
+        mContextFixture.addCallingOrSelfPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
+        mContextFixture.addCallingOrSelfPermission(Manifest.permission.MODIFY_PHONE_STATE);
+        int subId = insertSubscription(FAKE_SUBSCRIPTION_INFO1);
+
+        mSubscriptionManagerServiceUT.setDefaultVoiceSubId(subId);
+
+        assertThat(
+                mSubscriptionManagerServiceUT.getPhoneNumberFromFirstAvailableSource(
+                        subId, CALLING_PACKAGE, CALLING_FEATURE)).isEqualTo(FAKE_PHONE_NUMBER1);
+        assertThat(
+                mSubscriptionManagerServiceUT.getPhoneNumber(
+                        SubscriptionManager.DEFAULT_SUBSCRIPTION_ID,
+                        SubscriptionManager.PHONE_NUMBER_SOURCE_UICC,
+                        CALLING_PACKAGE,
+                        CALLING_FEATURE)).isEqualTo(FAKE_PHONE_NUMBER1);
+        assertThat(
+                mSubscriptionManagerServiceUT.getPhoneNumber(
+                        SubscriptionManager.DEFAULT_SUBSCRIPTION_ID,
+                        SubscriptionManager.PHONE_NUMBER_SOURCE_CARRIER,
+                        CALLING_PACKAGE,
+                        CALLING_FEATURE)).isEqualTo(FAKE_PHONE_NUMBER1);
+        assertThat(
+                mSubscriptionManagerServiceUT.getPhoneNumber(
+                        SubscriptionManager.DEFAULT_SUBSCRIPTION_ID,
+                        SubscriptionManager.PHONE_NUMBER_SOURCE_IMS,
+                        CALLING_PACKAGE,
+                        CALLING_FEATURE)).isEqualTo(FAKE_PHONE_NUMBER1);
+    }
+
+    @Test
     public void testEsimActivation() {
         mContextFixture.addCallingOrSelfPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
         mContextFixture.addCallingOrSelfPermission(Manifest.permission.MODIFY_PHONE_STATE);
@@ -3134,6 +3226,69 @@
     }
 
     @Test
+    public void testIsSatelliteSpnWithEmptySpn() {
+        mContextFixture.putResource(R.string.config_satellite_sim_spn_identifier, ""); // Empty
+        System.setProperty("persist.radio.allow_mock_modem", "true");
+        doReturn(true).when(mFlags).oemEnabledSatelliteFlag();
+
+        EuiccProfileInfo profileInfo1 = new EuiccProfileInfo.Builder(FAKE_ICCID1)
+                .setIccid(FAKE_ICCID1)
+                .setNickname(FAKE_CARRIER_NAME1)
+                .setServiceProviderName(FAKE_CARRIER_NAME1)
+                .setProfileClass(SubscriptionManager.PROFILE_CLASS_OPERATIONAL)
+                .setCarrierIdentifier(new CarrierIdentifier(FAKE_MCC1, FAKE_MNC1,
+                        FAKE_CARRIER_NAME1, null, null, null, FAKE_CARRIER_ID1, FAKE_CARRIER_ID1))
+                .setUiccAccessRule(Arrays.asList(UiccAccessRule.decodeRules(
+                        FAKE_NATIVE_ACCESS_RULES1)))
+                .build();
+
+        GetEuiccProfileInfoListResult result = new GetEuiccProfileInfoListResult(
+                EuiccService.RESULT_OK, new EuiccProfileInfo[]{profileInfo1}, false);
+        doReturn(result).when(mEuiccController).blockingGetEuiccProfileInfoList(eq(1));
+        doReturn(TelephonyManager.INVALID_PORT_INDEX).when(mUiccSlot)
+                .getPortIndexFromIccId(anyString());
+        doReturn(FAKE_ICCID1).when(mUiccController).convertToCardString(eq(1));
+
+        mSubscriptionManagerServiceUT.updateEmbeddedSubscriptions(List.of(1), null);
+        processAllMessages();
+
+        SubscriptionInfoInternal subInfo = mSubscriptionManagerServiceUT
+                .getSubscriptionInfoInternal(1);
+        assertThat(subInfo.getOnlyNonTerrestrialNetwork())
+                .isEqualTo(FAKE_SATELLITE_IS_NTN_DISABLED);
+
+        mContextFixture.putResource(R.string.config_satellite_sim_spn_identifier,
+                FAKE_CARRIER_NAME1);
+        EuiccProfileInfo profileInfo2 = new EuiccProfileInfo.Builder(FAKE_ICCID2)
+                .setIccid(FAKE_ICCID2)
+                .setNickname(FAKE_CARRIER_NAME2)
+                .setServiceProviderName("")
+                .setProfileClass(SubscriptionManager.PROFILE_CLASS_OPERATIONAL)
+                .setCarrierIdentifier(new CarrierIdentifier(FAKE_MCC2, FAKE_MNC2,
+                        FAKE_CARRIER_NAME2, null, null, null, FAKE_CARRIER_ID2, FAKE_CARRIER_ID2))
+                .setUiccAccessRule(Arrays.asList(UiccAccessRule.decodeRules(
+                        FAKE_NATIVE_ACCESS_RULES2)))
+                .build();
+        result = new GetEuiccProfileInfoListResult(
+                EuiccService.RESULT_OK, new EuiccProfileInfo[]{profileInfo2}, false);
+        doReturn(result).when(mEuiccController).blockingGetEuiccProfileInfoList(eq(2));
+        doReturn(TelephonyManager.INVALID_PORT_INDEX).when(mUiccSlot)
+                .getPortIndexFromIccId(anyString());
+        doReturn(FAKE_ICCID2).when(mUiccController).convertToCardString(eq(2));
+
+        mSubscriptionManagerServiceUT.updateEmbeddedSubscriptions(List.of(2), null);
+        processAllMessages();
+
+        subInfo = mSubscriptionManagerServiceUT
+                .getSubscriptionInfoInternal(2);
+        assertThat(subInfo.getOnlyNonTerrestrialNetwork())
+                .isEqualTo(FAKE_SATELLITE_IS_NTN_DISABLED);
+
+        System.setProperty("persist.radio.allow_mock_modem", "false");
+        doReturn(false).when(mFlags).oemEnabledSatelliteFlag();
+    }
+
+    @Test
     public void testIsSatelliteSpnWithNullCarrierIdentifier() {
         mContextFixture.putResource(R.string.config_satellite_sim_spn_identifier,
                 FAKE_CARRIER_NAME1);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccPortTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccPortTest.java
index 1a846c4..5c1993f 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccPortTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccPortTest.java
@@ -20,11 +20,16 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNull;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 
 import android.os.Binder;
+import android.platform.test.flag.junit.SetFlagsRule;
 import android.telephony.TelephonyManager;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
@@ -33,9 +38,11 @@
 
 import com.android.internal.telephony.IccLogicalChannelRequest;
 import com.android.internal.telephony.TelephonyTest;
+import com.android.internal.telephony.flags.Flags;
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -54,9 +61,13 @@
 
     private int mPhoneId = 0;
 
+    @Rule
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
     @Before
     public void setUp() throws Exception {
         super.setUp(getClass().getSimpleName());
+        mSetFlagsRule.enableFlags(Flags.FLAG_CLEANUP_OPEN_LOGICAL_CHANNEL_RECORD_ON_DISPOSE);
         mUiccCard = mock(UiccCard.class);
         mIccCardStatus = mock(IccCardStatus.class);
         /* initially there are no application available */
@@ -144,6 +155,20 @@
         verify(mUiccProfile).iccCloseLogicalChannel(eq(CHANNEL_ID), eq(false), eq(null));
     }
 
+    @Test
+    @SmallTest
+    public void testOnOpenLogicalChannel_withPortDisposed_noRecordLeft() {
+        IccLogicalChannelRequest request = getIccLogicalChannelRequest();
+
+        mUiccPort.onLogicalChannelOpened(request);
+        mUiccPort.dispose();
+
+        UiccPort.OpenLogicalChannelRecord record = mUiccPort.getOpenLogicalChannelRecord(
+                CHANNEL_ID);
+        assertThat(record).isNull();
+        verify(mUiccProfile, never()).iccCloseLogicalChannel(anyInt(), anyBoolean(), any());
+    }
+
     private IccLogicalChannelRequest getIccLogicalChannelRequest() {
         IccLogicalChannelRequest request = new IccLogicalChannelRequest();
         request.channel = CHANNEL_ID;